-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2197 from demergent-labs/beautiful_errors
Beautiful errors for Rust
- Loading branch information
Showing
67 changed files
with
1,083 additions
and
805 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 47 additions & 44 deletions
91
src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/candid.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,69 @@ | ||
use std::{error::Error, ffi::CString, os::raw::c_char, str}; | ||
|
||
use ic_cdk::trap; | ||
use rquickjs::{Context, Function, Module, Object, Runtime}; | ||
|
||
use crate::{ | ||
ic, quickjs_with_ctx, wasm_binary_manipulation::get_js_code, CONTEXT_REF_CELL, MODULE_NAME, | ||
error::{handle_promise_error, quickjs_call_with_error_handling}, | ||
ic::register, | ||
quickjs_with_ctx, | ||
wasm_binary_manipulation::get_js_code, | ||
CONTEXT_REF_CELL, MODULE_NAME, | ||
}; | ||
|
||
// TODO we might not need any of these panic hooks | ||
type CCharPtr = *mut c_char; | ||
|
||
// Heavily inspired by https://stackoverflow.com/a/47676844 | ||
#[no_mangle] | ||
pub fn get_candid_and_method_meta_pointer() -> *mut std::os::raw::c_char { | ||
std::panic::set_hook(Box::new(|panic_info| { | ||
let msg = match panic_info.payload().downcast_ref::<&str>() { | ||
Some(s) => *s, | ||
None => "Unknown panic message", | ||
}; | ||
let location = if let Some(location) = panic_info.location() { | ||
format!(" at {}:{}", location.file(), location.line()) | ||
} else { | ||
" (unknown location)".to_string() | ||
}; | ||
|
||
let message = &format!("Panic occurred: {}{}", msg, location); | ||
|
||
ic_cdk::println!("{}", message); | ||
})); | ||
|
||
let runtime = rquickjs::Runtime::new().unwrap(); | ||
let context = rquickjs::Context::full(&runtime).unwrap(); | ||
pub fn get_candid_and_method_meta_pointer() -> CCharPtr { | ||
match initialize_and_get_candid() { | ||
Ok(c_char_ptr) => c_char_ptr, | ||
Err(error) => { | ||
trap(&format!("Azle CandidAndMethodMetaError: {error}")); | ||
} | ||
} | ||
} | ||
|
||
fn initialize_and_get_candid() -> Result<CCharPtr, Box<dyn Error>> { | ||
let runtime = Runtime::new()?; | ||
let context = Context::full(&runtime)?; | ||
|
||
CONTEXT_REF_CELL.with(|context_ref_cell| { | ||
*context_ref_cell.borrow_mut() = Some(context); | ||
}); | ||
|
||
quickjs_with_ctx(|ctx| { | ||
ctx.clone() | ||
.globals() | ||
.set("_azleNodeWasmEnvironment", true) | ||
.unwrap(); | ||
quickjs_with_ctx(|ctx| -> Result<CCharPtr, Box<dyn Error>> { | ||
let globals = ctx.globals(); | ||
|
||
ic::register(ctx.clone()); | ||
globals.set("_azleNodeWasmEnvironment", true)?; | ||
|
||
ctx.clone() | ||
.globals() | ||
.set("exports", rquickjs::Object::new(ctx.clone()).unwrap()) | ||
.unwrap(); | ||
globals.set("exports", Object::new(ctx.clone())?)?; | ||
|
||
ctx.clone() | ||
.globals() | ||
.set("_azleExperimental", false) | ||
.unwrap(); | ||
globals.set("_azleExperimental", false)?; | ||
|
||
register(ctx.clone())?; | ||
|
||
let js = get_js_code(); | ||
|
||
// TODO is there a better name for this main module? | ||
// TODO this returns a promise...make sure we handle it appropriately | ||
rquickjs::Module::evaluate(ctx.clone(), MODULE_NAME, js).unwrap(); | ||
let promise = Module::evaluate(ctx.clone(), MODULE_NAME, str::from_utf8(&js)?)?; | ||
|
||
handle_promise_error(ctx.clone(), promise)?; | ||
|
||
let get_candid_and_method_meta: Function = ctx | ||
.globals() | ||
.get("_azleGetCandidAndMethodMeta") | ||
.map_err(|e| format!("Failed to get globalThis._azleGetCandidAndMethodMeta: {e}"))?; | ||
|
||
let get_candid_and_method_meta: rquickjs::Function = | ||
ctx.globals().get("_azleGetCandidAndMethodMeta").unwrap(); | ||
let candid_and_method_meta_js_value = | ||
quickjs_call_with_error_handling(ctx.clone(), get_candid_and_method_meta, ())?; | ||
|
||
let candid_and_method_meta: String = get_candid_and_method_meta.call(()).unwrap(); | ||
let candid_and_method_meta: String = candid_and_method_meta_js_value | ||
.as_string() | ||
.ok_or("Failed to convert candidAndMethodMeta JS value to string")? | ||
.to_string()?; | ||
|
||
let c_string = std::ffi::CString::new(candid_and_method_meta).unwrap(); | ||
let c_string = CString::new(candid_and_method_meta)?; | ||
let c_char_ptr = c_string.into_raw(); | ||
|
||
c_string.into_raw() | ||
Ok(c_char_ptr) | ||
}) | ||
} |
6 changes: 4 additions & 2 deletions
6
src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/chunk.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
use ic_cdk::{api::call::call_raw128, id}; | ||
|
||
#[allow(unused)] | ||
pub async fn chunk() { | ||
let id = ic_cdk::id(); | ||
let id = id(); | ||
let method = "_azle_chunk"; | ||
let args_raw = [68, 73, 68, 76, 0, 0]; // '()' pre encoded | ||
let _ = ic_cdk::api::call::call_raw128(id, method, args_raw, 0).await; | ||
let _ = call_raw128(id, method, args_raw, 0).await; | ||
} |
57 changes: 57 additions & 0 deletions
57
src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/error.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use std::error::Error; | ||
|
||
use ic_cdk::trap; | ||
use rquickjs::{ | ||
function::IntoArgs, promise::PromiseState, Ctx, Exception, Function, Promise, Value, | ||
}; | ||
|
||
use crate::quickjs_with_ctx::run_event_loop; | ||
|
||
pub fn quickjs_call_with_error_handling<'a>( | ||
ctx: Ctx<'a>, | ||
function: Function<'a>, | ||
args: impl IntoArgs<'a>, | ||
) -> Result<Value<'a>, Box<dyn Error>> { | ||
let result: Value = match function.call(args) { | ||
Ok(value) => value, | ||
Err(_) => trap_on_last_exception(ctx.clone())?, | ||
}; | ||
|
||
// TODO we run the event loop here and also in handle_promise_error, is that a problem? | ||
run_event_loop(ctx.clone()); | ||
|
||
if result.is_promise() { | ||
let promise: Promise = result | ||
.clone() | ||
.into_promise() | ||
.ok_or("Failed to convert function call return JS value to promise")?; | ||
handle_promise_error(ctx.clone(), promise)?; | ||
} | ||
|
||
Ok(result) | ||
} | ||
|
||
fn trap_on_last_exception<T>(ctx: Ctx) -> Result<T, Box<dyn Error>> { | ||
let exception: Exception = ctx | ||
.clone() | ||
.catch() | ||
.as_exception() | ||
.ok_or("No exception found")? | ||
.clone(); | ||
|
||
trap(&exception.to_string()); | ||
} | ||
|
||
pub fn handle_promise_error(ctx: Ctx, promise: Promise) -> Result<(), Box<dyn Error>> { | ||
run_event_loop(ctx.clone()); | ||
|
||
match promise.state() { | ||
PromiseState::Rejected => { | ||
promise.result::<Value>(); | ||
trap_on_last_exception(ctx.clone())?; | ||
} | ||
_ => {} | ||
}; | ||
|
||
Ok(()) | ||
} |
72 changes: 51 additions & 21 deletions
72
...table/commands/compile/wasm_binary/rust/stable_canister_template/src/execute_method_js.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,65 @@ | ||
use crate::{benchmarking::record_benchmark, quickjs_with_ctx, WASM_DATA_REF_CELL}; | ||
use std::error::Error; | ||
|
||
use ic_cdk::{ | ||
api::{call::arg_data_raw, performance_counter}, | ||
trap, | ||
}; | ||
use rquickjs::{Function, Object}; | ||
|
||
use crate::{ | ||
benchmarking::record_benchmark, error::quickjs_call_with_error_handling, quickjs_with_ctx, | ||
WASM_DATA_REF_CELL, | ||
}; | ||
|
||
#[no_mangle] | ||
#[allow(unused)] | ||
pub extern "C" fn execute_method_js(function_index: i32, pass_arg_data: i32) { | ||
let function_name = &function_index.to_string(); | ||
let pass_arg_data = if pass_arg_data == 1 { true } else { false }; | ||
pub extern "C" fn execute_method_js(function_index: i32, pass_arg_data_raw: i32) { | ||
let function_name = function_index.to_string(); | ||
let pass_arg_data = pass_arg_data_raw == 1; | ||
|
||
let result = execute_method_js_with_result(function_name, pass_arg_data); | ||
|
||
if let Err(e) = result { | ||
trap(&format!("Azle CanisterMethodError: {}", e)); | ||
} | ||
} | ||
|
||
fn execute_method_js_with_result( | ||
function_name: String, | ||
pass_arg_data: bool, | ||
) -> Result<(), Box<dyn Error>> { | ||
quickjs_with_ctx(|ctx| { | ||
let callbacks: rquickjs::Object = ctx.clone().globals().get("_azleCallbacks").unwrap(); | ||
let callbacks: Object = ctx | ||
.clone() | ||
.globals() | ||
.get("_azleCallbacks") | ||
.map_err(|e| format!("Failed to get globalThis._azleCallbacks: {e}"))?; | ||
|
||
let method_callback: rquickjs::Function = callbacks.get(function_name).unwrap(); | ||
let method_callback: Function = callbacks.get(&function_name).map_err(|e| { | ||
format!("Failed to get globalThis._azleCallbacks[{function_name}]: {e}") | ||
})?; | ||
|
||
let candid_args = if pass_arg_data { | ||
ic_cdk::api::call::arg_data_raw() | ||
arg_data_raw() | ||
} else { | ||
vec![] | ||
}; | ||
|
||
method_callback | ||
.call::<_, rquickjs::Undefined>((candid_args,)) | ||
.unwrap(); | ||
}); | ||
|
||
if WASM_DATA_REF_CELL.with(|wasm_data_ref_cell| { | ||
wasm_data_ref_cell | ||
.borrow() | ||
.as_ref() | ||
.unwrap() | ||
.record_benchmarks | ||
}) { | ||
let instructions = ic_cdk::api::performance_counter(1); | ||
record_benchmark(&function_name, instructions); | ||
quickjs_call_with_error_handling(ctx.clone(), method_callback, (candid_args,))?; | ||
|
||
Ok(()) | ||
})?; | ||
|
||
let record_benchmarks = WASM_DATA_REF_CELL | ||
.with(|wasm_data_ref_cell| wasm_data_ref_cell.borrow().clone()) | ||
.as_ref() | ||
.ok_or("could not convert wasm_data_ref_cell to ref")? | ||
.record_benchmarks; | ||
|
||
if record_benchmarks { | ||
let instructions = performance_counter(1); | ||
record_benchmark(&function_name, instructions)?; | ||
} | ||
|
||
Ok(()) | ||
} |
4 changes: 3 additions & 1 deletion
4
src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/guards.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 4 additions & 6 deletions
10
...table/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/accept_message.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
use rquickjs::{Ctx, Function}; | ||
use ic_cdk::api::call::accept_message; | ||
use rquickjs::{Ctx, Function, Result}; | ||
|
||
pub fn get_function(context: Ctx) -> Function { | ||
Function::new(context, || { | ||
ic_cdk::api::call::accept_message(); | ||
}) | ||
.unwrap() | ||
pub fn get_function(ctx: Ctx) -> Result<Function> { | ||
Function::new(ctx, || accept_message()) | ||
} |
10 changes: 4 additions & 6 deletions
10
.../stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/arg_data_raw.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
use rquickjs::{Ctx, Function, TypedArray}; | ||
use ic_cdk::api::call::arg_data_raw; | ||
use rquickjs::{Ctx, Function, Result}; | ||
|
||
pub fn get_function(context: Ctx) -> Function { | ||
Function::new(context.clone(), move || { | ||
TypedArray::<u8>::new(context.clone(), ic_cdk::api::call::arg_data_raw()) | ||
}) | ||
.unwrap() | ||
pub fn get_function(ctx: Ctx) -> Result<Function> { | ||
Function::new(ctx, || arg_data_raw()) | ||
} |
Oops, something went wrong.