Skip to content

Commit

Permalink
fix guard functions example, add better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
lastmjs committed Jan 17, 2024
1 parent 1d8c04a commit cfc281f
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 34 deletions.
6 changes: 3 additions & 3 deletions examples/guard_functions/test/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function getTests(
} catch (error) {
return {
Ok: (error as AgentError).message.includes(
`"Message": "Uncaught Execution halted by \\"unpassable\\" guard function"`
`Uncaught Error: Execution halted by \\"unpassable\\" guard function`
)
};
}
Expand All @@ -107,7 +107,7 @@ export function getTests(
} catch (error) {
return {
Ok: (error as AgentError).message.includes(
`Uncaught Execution halted by \\"throw string\\" guard function`
`Uncaught Error: Execution halted by \\"throw string\\" guard function`
)
};
}
Expand Down Expand Up @@ -143,7 +143,7 @@ export function getTests(
} catch (error) {
return {
Ok: (error as AgentError).message.includes(
`Uncaught [object Object]`
`Uncaught Error: [object Object]`
)
};
}
Expand Down
28 changes: 26 additions & 2 deletions src/compiler/rust/canister/src/ic/call_raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,19 @@ impl JsFn for NativeFunction {
.to_function()
.unwrap();

resolve.call(&[js_value.clone()]);
let result = resolve.call(&[js_value.clone()]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &result {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
js_exception.dump_error();
panic!("TODO needs error info");
}
_ => {}
};
} else {
let reject = global
.get("_azleRejectIds")
Expand All @@ -90,7 +102,19 @@ impl JsFn for NativeFunction {
.to_function()
.unwrap();

reject.call(&[js_value.clone()]);
let result = reject.call(&[js_value.clone()]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &result {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
js_exception.dump_error();
panic!("TODO needs error info");
}
_ => {}
};
}

// TODO Is this all we need to do for promises and timeouts?
Expand Down
28 changes: 26 additions & 2 deletions src/compiler/rust/canister/src/ic/call_raw128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,19 @@ impl JsFn for NativeFunction {
.to_function()
.unwrap();

resolve.call(&[js_value.clone()]);
let result = resolve.call(&[js_value.clone()]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &result {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
js_exception.dump_error();
panic!("TODO needs error info");
}
_ => {}
};
} else {
let reject = global
.get("_azleRejectIds")
Expand All @@ -90,7 +102,19 @@ impl JsFn for NativeFunction {
.to_function()
.unwrap();

reject.call(&[js_value.clone()]);
let result = reject.call(&[js_value.clone()]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &result {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
js_exception.dump_error();
panic!("TODO needs error info");
}
_ => {}
};
}

// TODO Is this all we need to do for promises and timeouts?
Expand Down
14 changes: 13 additions & 1 deletion src/compiler/rust/canister/src/ic/set_timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,19 @@ impl JsFn for NativeFunction {
.to_function()
.unwrap();

timer_callback.call(&[]);
let result = timer_callback.call(&[]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &result {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
js_exception.dump_error();
panic!("TODO needs error info");
}
_ => {}
};

// TODO Is this all we need to do for promises and timeouts?
context.event_loop().unwrap().run_tick_task();
Expand Down
14 changes: 13 additions & 1 deletion src/compiler/rust/canister/src/ic/set_timer_interval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,19 @@ impl JsFn for NativeFunction {
.to_function()
.unwrap();

timer_callback.call(&[]);
let result = timer_callback.call(&[]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &result {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
js_exception.dump_error();
panic!("TODO needs error info");
}
_ => {}
};

// TODO Is this all we need to do for promises and timeouts?
context.event_loop().unwrap().run_tick_task();
Expand Down
19 changes: 17 additions & 2 deletions src/compiler/rust/canister/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,14 @@ fn execute_js(function_name: &str, pass_arg_data: bool) {

let method_callback_function = method_callback.to_function().unwrap();

// TODO this returns a value so I think we need to check it to get an error
let result = method_callback_function.call(&[candid_args_js_value]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &result {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
// TODO maybe we can just call toString() on the error object
js_exception.dump_error();
panic!("TODO needs error info");
}
Expand Down Expand Up @@ -130,6 +132,7 @@ pub fn get_candid_pointer() -> *mut std::os::raw::c_char {

ic::register(context);

// TODO what do we do if there is an error in here?
context.eval_global_str("globalThis.exports = {};".to_string());
context.eval_module_str(
std::str::from_utf8(MAIN_JS).unwrap().to_string(),
Expand All @@ -142,6 +145,18 @@ pub fn get_candid_pointer() -> *mut std::os::raw::c_char {

let candid_info = candid_info_function.call(&[]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &candid_info {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
js_exception.dump_error();
panic!("TODO needs error info");
}
_ => {}
};

let candid_info_string = candid_info.to_string().unwrap().to_string();

let c_string = std::ffi::CString::new(candid_info_string).unwrap();
Expand Down
38 changes: 22 additions & 16 deletions src/compiler/rust/canister_methods/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub fn canister_methods(_: TokenStream) -> TokenStream {
let r = rt.run_with_context(|context| {
ic::register(context);

// TODO what do we do if there is an error in here?
context.eval_global_str("globalThis.exports = {};".to_string());
context.eval_module_str(std::str::from_utf8(MAIN_JS).unwrap().to_string(), "azle_main");

Expand Down Expand Up @@ -112,6 +113,7 @@ pub fn canister_methods(_: TokenStream) -> TokenStream {
let r = rt.run_with_context(|context| {
ic::register(context);

// TODO what do we do if there is an error in here?
context.eval_global_str("globalThis.exports = {};".to_string());
context.eval_module_str(std::str::from_utf8(MAIN_JS).unwrap().to_string(), "azle_main");

Expand Down Expand Up @@ -284,27 +286,31 @@ fn get_guard_token_stream(
quote! {
// TODO should the guard function have access to the raw args?
fn #guard_name_ident() -> Result<(), String> {
CONTEXT.with(|context| {
let mut context = context.borrow_mut();
let context = context.as_mut().unwrap();
RUNTIME.with(|runtime| {
let mut runtime = runtime.borrow_mut();
let runtime = runtime.as_mut().unwrap();

let global = context.global_object().unwrap();
let guard_functions = global.get_property("_azleGuardFunctions").unwrap();
let guard_function = guard_functions.get_property(#guard_name).unwrap();
runtime.run_with_context(|context| {
let global = context.get_global();

// TODO I am not sure what the first parameter to call is supposed to be
let result = guard_function.call(&guard_function, &[]);
let guard_functions = global.get("_azleGuardFunctions").to_obj().unwrap();

match result {
Ok(_) => {
Ok(())
},
Err(err_js_value_ref) => {
let err: String = err_js_value_ref.to_string();
let guard_function = guard_functions.get(#guard_name).to_function().unwrap();

Err(err)
let result = guard_function.call(&[]);

// TODO error handling is mostly done in JS right now
// TODO we would really like wasmedge-quickjs to add
// TODO good error info to JsException and move error handling
// TODO out of our own code
match &result {
wasmedge_quickjs::JsValue::Exception(js_exception) => {
js_exception.dump_error();
Err("TODO needs error info".to_string())
}
_ => Ok(())
}
}
})
})
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CanisterOptions } from '.';
import { handleUncaughtError } from '../../../../../error';

type QueryMethod = {
name: string;
Expand Down Expand Up @@ -67,7 +68,13 @@ function createGlobalGuard(

const guardName = `_azleGuard_${guardedMethodName}`;

globalThis._azleGuardFunctions[guardName] = guard;
globalThis._azleGuardFunctions[guardName] = () => {
try {
guard();
} catch (error) {
handleUncaughtError(error);
}
};

return guardName;
}
21 changes: 15 additions & 6 deletions src/lib/canister_methods/execute_method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CandidType } from '../candid/candid_type';
import { decode } from '../candid/serde/decode';
import { encode } from '../candid/serde/encode';
import { CanisterMethodInfo } from './types/canister_method_info';
import { handleUncaughtError } from '../error';

export function executeMethod(
mode: CanisterMethodInfo<any, any>['mode'],
Expand All @@ -14,7 +15,7 @@ export function executeMethod(
) {
const decodedArgs = decode(paramCandidTypes, args[0]);

const result = callback(...decodedArgs);
const result = getResult(decodedArgs, callback);

if (
mode === 'init' ||
Expand All @@ -31,16 +32,16 @@ export function executeMethod(
) {
result
.then((result: any) => {
// TODO this won't be accurate because we have most likely had
// TODO cross-canister calls
reportFinalInstructions();

if (!manual) {
ic.replyRaw(encode(returnCandidType, result));
}

// TODO this won't be accurate because we have most likely had
// TODO cross-canister calls
reportFinalInstructions();
})
.catch((error: any) => {
ic.trap(error.toString());
handleUncaughtError(error);
});
} else {
if (!manual) {
Expand All @@ -51,6 +52,14 @@ export function executeMethod(
}
}

function getResult(args: any[], callback: any): any {
try {
return callback(...args);
} catch (error) {
handleUncaughtError(error);
}
}

function reportFinalInstructions() {
if (process.env.AZLE_INSTRUCTION_COUNT === 'true') {
console.log(`final instructions: ${ic.instructionCounter()}`);
Expand Down
9 changes: 9 additions & 0 deletions src/lib/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ic } from '.';

export function handleUncaughtError(rawError: any) {
const error = rawError instanceof Error ? rawError : new Error(rawError);

const azleError = `Uncaught ${error.name}: ${error.message}${error.stack}`;

ic.trap(azleError);
}

0 comments on commit cfc281f

Please sign in to comment.