Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beautiful errors for Rust #2197

Merged
merged 17 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified canister_templates/stable.wasm
bdemann marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::{cell::RefCell, error::Error};

use candid::CandidType;
use ic_cdk::api::time;
use rquickjs::Object;

use crate::quickjs_with_ctx;

Expand All @@ -17,9 +19,9 @@ thread_local! {

pub fn record_benchmark(function_name: &str, instructions: u64) -> Result<(), Box<dyn Error>> {
quickjs_with_ctx(|ctx| {
let timestamp = ic_cdk::api::time();
let timestamp = time();

let method_names: rquickjs::Object = ctx
let method_names: Object = ctx
.clone()
.globals()
.get("_azleCanisterMethodNames")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::error::Error;
use std::{error::Error, ffi::CString, os::raw::c_char, str};

use ic_cdk::trap;
use rquickjs::{Context, Function, Module, Object, Runtime};

use crate::{
error::{handle_promise_error, quickjs_call_with_error_handling},
Expand All @@ -7,48 +10,44 @@ use crate::{
CONTEXT_REF_CELL, MODULE_NAME,
};

type CCharPtr = *mut std::os::raw::c_char;
type CCharPtr = *mut c_char;

#[no_mangle]
pub fn get_candid_and_method_meta_pointer() -> CCharPtr {
match initialize_and_get_candid() {
Ok(c_char_ptr) => c_char_ptr,
Err(error) => {
ic_cdk::trap(&format!("Azle CandidAndMethodMetaError: {error}"));
trap(&format!("Azle CandidAndMethodMetaError: {error}"));
}
}
}

fn initialize_and_get_candid() -> Result<CCharPtr, Box<dyn Error>> {
let runtime = rquickjs::Runtime::new()?;
let context = rquickjs::Context::full(&runtime)?;
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| -> Result<CCharPtr, Box<dyn Error>> {
ctx.clone()
.globals()
.set("_azleNodeWasmEnvironment", true)?;
let globals = ctx.globals();

ctx.clone()
.globals()
.set("exports", rquickjs::Object::new(ctx.clone())?)?;
globals.set("_azleNodeWasmEnvironment", true)?;

globals.set("exports", Object::new(ctx.clone())?)?;

ctx.clone().globals().set("_azleExperimental", false)?;
globals.set("_azleExperimental", false)?;

ic::register(ctx.clone())?;

lastmjs marked this conversation as resolved.
Show resolved Hide resolved
let js = get_js_code();

let promise =
rquickjs::Module::evaluate(ctx.clone(), MODULE_NAME, std::str::from_utf8(&js)?)?;
let promise = Module::evaluate(ctx.clone(), MODULE_NAME, str::from_utf8(&js)?)?;

handle_promise_error(ctx.clone(), promise)?;

let get_candid_and_method_meta: rquickjs::Function = ctx
.clone()
let get_candid_and_method_meta: Function = ctx
.globals()
.get("_azleGetCandidAndMethodMeta")
.map_err(|e| format!("Failed to get globalThis._azleGetCandidAndMethodMeta: {e}"))?;
Expand All @@ -61,7 +60,7 @@ fn initialize_and_get_candid() -> Result<CCharPtr, Box<dyn Error>> {
.ok_or("Failed to convert candidAndMethodMeta JS value to string")?
.to_string()?;

let c_string = std::ffi::CString::new(candid_and_method_meta)?;
let c_string = CString::new(candid_and_method_meta)?;
let c_char_ptr = c_string.into_raw();

Ok(c_char_ptr)
Expand Down
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;
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use std::error::Error;

use rquickjs::function::IntoArgs;
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: rquickjs::Ctx<'a>,
function: rquickjs::Function<'a>,
ctx: Ctx<'a>,
function: Function<'a>,
args: impl IntoArgs<'a>,
) -> Result<rquickjs::Value<'a>, Box<dyn std::error::Error>> {
let result: rquickjs::Value = match function.call(args) {
) -> Result<Value<'a>, Box<dyn Error>> {
let result: Value = match function.call(args) {
Ok(value) => value,
Err(_) => trap_on_last_exception(ctx.clone())?,
};
Expand All @@ -18,7 +21,7 @@ pub fn quickjs_call_with_error_handling<'a>(
run_event_loop(ctx.clone());

if result.is_promise() {
let promise: rquickjs::Promise = result
let promise: Promise = result
.clone()
.into_promise()
.ok_or("Failed to convert function call return JS value to promise")?;
Expand All @@ -28,26 +31,23 @@ pub fn quickjs_call_with_error_handling<'a>(
Ok(result)
}

fn trap_on_last_exception<T>(ctx: rquickjs::Ctx) -> Result<T, Box<dyn std::error::Error>> {
let exception: rquickjs::Exception = ctx
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();

ic_cdk::trap(&exception.to_string());
trap(&exception.to_string());
}

pub fn handle_promise_error(
ctx: rquickjs::Ctx,
promise: rquickjs::Promise,
) -> Result<(), Box<dyn Error>> {
pub fn handle_promise_error(ctx: Ctx, promise: Promise) -> Result<(), Box<dyn Error>> {
run_event_loop(ctx.clone());

match promise.state() {
rquickjs::promise::PromiseState::Rejected => {
promise.result::<rquickjs::Value>();
PromiseState::Rejected => {
promise.result::<Value>();
trap_on_last_exception(ctx.clone())?;
}
_ => {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
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,
Expand All @@ -12,27 +20,27 @@ pub extern "C" fn execute_method_js(function_index: i32, pass_arg_data_raw: i32)
let result = execute_method_js_with_result(function_name, pass_arg_data);

if let Err(e) = result {
ic_cdk::trap(&format!("Azle CanisterMethodError: {}", e));
trap(&format!("Azle CanisterMethodError: {}", e));
}
}

fn execute_method_js_with_result(
function_name: String,
pass_arg_data: bool,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<(), Box<dyn Error>> {
quickjs_with_ctx(|ctx| {
let callbacks: rquickjs::Object = ctx
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).map_err(|e| {
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![]
};
Expand All @@ -49,7 +57,7 @@ fn execute_method_js_with_result(
.record_benchmarks;

if record_benchmarks {
let instructions = ic_cdk::api::performance_counter(1);
let instructions = performance_counter(1);
record_benchmark(&function_name, instructions)?;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use ic_cdk::api::{caller, is_controller};

#[allow(unused)]
pub fn guard_against_non_controllers() -> Result<(), String> {
if ic_cdk::api::is_controller(&ic_cdk::api::caller()) {
if is_controller(&caller()) {
return Ok(());
}
return Err(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::error::Error;
use candid::Principal;
use ic_cdk::{
api::call::{call_raw128, RejectionCode},
spawn,
spawn, trap,
};
use rquickjs::{
Ctx, Exception, Function, IntoJs, Object, Result as QuickJsResult, TypedArray, Value,
Expand Down Expand Up @@ -42,7 +42,7 @@ pub fn get_function(ctx: Ctx) -> QuickJsResult<Function> {
});

if let Err(e) = result {
ic_cdk::trap(&format!("Azle CallRawError: {e}"));
trap(&format!("Azle CallRawError: {e}"));
}
});

Expand Down Expand Up @@ -102,12 +102,12 @@ fn get_resolve_or_reject_global_object(
ctx: Ctx,
should_resolve: bool,
) -> Result<Object, Box<dyn Error>> {
let global = ctx.globals();
let globals = ctx.globals();

if should_resolve {
Ok(global.get("_azleResolveIds")?)
Ok(globals.get("_azleResolveIds")?)
} else {
Ok(global.get("_azleRejectIds")?)
Ok(globals.get("_azleRejectIds")?)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ pub fn get_function(ctx: Ctx) -> Result<Function> {
Function::new(ctx.clone(), move || -> Result<Value> {
match data_certificate() {
Some(data_certificate_vec_u8) => {
Ok(TypedArray::<u8>::new(ctx.clone(), data_certificate_vec_u8)?.into_js(&ctx)?)
TypedArray::<u8>::new(ctx.clone(), data_certificate_vec_u8)?.into_js(&ctx)
}
None => Ok(Undefined.into_js(&ctx)?),
None => Undefined.into_js(&ctx),
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ use rquickjs::{Ctx, Function, Result, TypedArray};

pub fn get_function(ctx: Ctx) -> Result<Function> {
Function::new(ctx.clone(), move || -> Result<TypedArray<u8>> {
Ok(TypedArray::<u8>::new(ctx.clone(), id().as_slice())?)
TypedArray::<u8>::new(ctx.clone(), id().as_slice())
})
}
Loading
Loading