Skip to content

Commit

Permalink
Merge pull request #426 from demergent-labs/203-generator-recursion
Browse files Browse the repository at this point in the history
203 generator recursion
  • Loading branch information
lastmjs authored Jun 18, 2022
2 parents cd76c8e + fad6c57 commit 93d428e
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 26 deletions.
1 change: 1 addition & 0 deletions examples/generators/src/generators.did
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,5 @@ type CanisterStatus = variant { "running": null; "stopping": null; "stopped": nu
service: {
"get_randomness_directly": () -> (vec nat8);
"get_randomness_indirectly": () -> (vec nat8);
"get_randomness_super_indirectly": () -> (vec nat8);
}
44 changes: 26 additions & 18 deletions examples/generators/src/generators.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {
UpdateAsync,
nat8,
ic,
Async,
CanisterResult,
Async
nat8,
UpdateAsync
} from 'azle';
import { ManagementCanister } from 'azle/canisters/management';

Expand All @@ -18,25 +17,34 @@ export function* get_randomness_directly(): UpdateAsync<nat8[]> {
}
}

// TODO I think the yield's are non-negotiable whenever you call a generator
// TODO To deal with dependencies on the generator result, like if any code depends on the result
// TODO of a generator being called, we need to use a yield
export function* get_randomness_indirectly(): UpdateAsync<nat8[]> {
const indirect_randomness: nat8[] = yield get_randomness();

// TODO I think the appropriate syntax might be to always yield on any generator, and to never use the .next().value syntax
return indirect_randomness;
}

// TODO Yes I think the correct syntax is to always yield the object, never call .next().value, and now we need to recursively
// TODO or emulating recursively walk through the generators
export function* get_randomness_super_indirectly(): UpdateAsync<nat8[]> {
const randomness0: nat8[] = yield get_randomness_level0();
const randomness1: nat8[] = yield get_randomness_level1();
const randomness2: nat8[] = yield get_randomness_level2();

// TODO the problem is that there has to be a direct yield path from the cross canister call
// TODO there can be no indirection, because the informational object that is returned from the
// TODO cross canister call has to be yielding out of the first generator
// TODO you cannot yield another generator, and that I believe is the main issue
// TODO let's hoep this can be solved!!!
return [
...randomness0,
...randomness1,
...randomness2
];
}

export function* get_randomness_indirectly(): UpdateAsync<nat8[]> {
const indirect_randomness: nat8[] = yield get_randomness();
function* get_randomness_level0(): Async<nat8[]> {
return yield get_randomness_level1();
}

return indirect_randomness;
function* get_randomness_level1(): Async<nat8[]> {
return yield get_randomness_level2();
}

function* get_randomness_level2(): Async<nat8[]> {
return yield get_randomness();
}

function* get_randomness(): Async<nat8[]> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,5 @@ type CanisterStatus = variant { "running": null; "stopping": null; "stopped": nu
service: {
"get_randomness_directly": () -> (vec nat8);
"get_randomness_indirectly": () -> (vec nat8);
"get_randomness_super_indirectly": () -> (vec nat8);
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ export interface UpdateSettingsArgs {
export interface _SERVICE {
'get_randomness_directly' : () => Promise<Array<number>>,
'get_randomness_indirectly' : () => Promise<Array<number>>,
'get_randomness_super_indirectly' : () => Promise<Array<number>>,
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const idlFactory = ({ IDL }) => {
return IDL.Service({
'get_randomness_directly' : IDL.Func([], [IDL.Vec(IDL.Nat8)], []),
'get_randomness_indirectly' : IDL.Func([], [IDL.Vec(IDL.Nat8)], []),
'get_randomness_super_indirectly' : IDL.Func([], [IDL.Vec(IDL.Nat8)], []),
});
};
export const init = ({ IDL }) => { return []; };
54 changes: 53 additions & 1 deletion examples/generators/test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,58 @@ const generators_canister = createActor(
}
);

const tests: Test[] = [];
const tests: Test[] = [
{
name: 'clear canister memory',
prep: async () => {
execSync(`dfx canister uninstall-code generators || true`, {
stdio: 'inherit'
});
}
},
{
// TODO hopefully we can get rid of this: https://forum.dfinity.org/t/generated-declarations-in-node-js-environment-break/12686/16?u=lastmjs
name: 'waiting for createActor fetchRootKey',
wait: 5000
},
{
name: 'deploy',
prep: async () => {
execSync(`dfx deploy`, {
stdio: 'inherit'
});
}
},
{
name: 'get_randomness_directly',
test: async () => {
const result = await generators_canister.get_randomness_directly();

return {
ok: result.length === 32
};
}
},
{
name: 'get_randomness_indirectly',
test: async () => {
const result = await generators_canister.get_randomness_indirectly();

return {
ok: result.length === 32
};
}
},
{
name: 'get_randomness_super_indirectly',
test: async () => {
const result = await generators_canister.get_randomness_super_indirectly();

return {
ok: result.length === 96
};
}
}
];

run_tests(tests);
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,10 @@ export function generateReturnValueHandler(implItemMethod: ImplItemMethod): Rust
// return `serde_json::from_value(_azle_return_value.to_json(&mut boa_context).unwrap()).unwrap()`;
}

// TODO the generator implementation is not sufficient
// TODO I am afraid we might have to do something recursively...well we can just mutate things I guess
// TODO but if we return a generator as the call to next, we need to deal with that as well it seems
// TODO Now that we are using the async_recursion crate we can probably rewrite this entirely recursively (get rid of the mutations)
export function generateHandleGeneratorResultFunction(callFunctionInfos: CallFunctionInfo[]): Rust {
return `
return /* rust */ `
#[async_recursion::async_recursion(?Send)]
async fn handle_generator_result(
_azle_boa_context: &mut boa_engine::Context,
_azle_return_value: &boa_engine::JsValue
Expand All @@ -128,12 +127,21 @@ export function generateHandleGeneratorResultFunction(callFunctionInfos: CallFun
let yield_result_done_js_value = yield_result_js_object.get("done", _azle_boa_context).unwrap();
let yield_result_done_bool = yield_result_done_js_value.as_boolean().unwrap();
// TODO we need to handle this return value being a generator
// TODO we will have to emulate recursion with mutations
let yield_result_value_js_value = yield_result_js_object.get("value", _azle_boa_context).unwrap();
if yield_result_done_bool == false {
let yield_result_value_js_object = yield_result_value_js_value.as_object().unwrap();
if yield_result_value_js_object.is_generator() {
let recursed_generator_js_value = handle_generator_result(
_azle_boa_context,
&yield_result_value_js_value
).await;
_azle_args = vec![recursed_generator_js_value];
continue;
}
let name_js_value = yield_result_value_js_object.get("name", _azle_boa_context).unwrap();
let name_string = name_js_value.as_string().unwrap();
Expand Down Expand Up @@ -306,7 +314,7 @@ export function generateHandleGeneratorResultFunction(callFunctionInfos: CallFun
match &call_function_name_string[..] {
${callFunctionInfos.map((callFunctionInfo) => {
return `
return /* rust */`
"${callFunctionInfo.functionName}" => {
let canister_id_js_value = call_args_js_object.get("1", _azle_boa_context).unwrap();
let canister_id_principal: ic_cdk::export::Principal = canister_id_js_value.azle_try_from_js_value(_azle_boa_context).unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ export function generateLibCargoToml(canisterName: string): Toml {
getrandom = { version = "0.2.3", features = ["custom"] }
serde = "1.0.137"
azle-js-value-derive = { path = "./azle_js_value_derive" }
async-recursion = "1.0.0"
`;
}

0 comments on commit 93d428e

Please sign in to comment.