diff --git a/Cargo.lock b/Cargo.lock index 5ea25a92e1..062c56fad6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1875,7 +1875,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasmedge_quickjs" version = "0.5.0-alpha" -source = "git+https://github.com/demergent-labs/wasmedge-quickjs?rev=09edab8fbfa15e17f91d834003589fc7e60357ba#09edab8fbfa15e17f91d834003589fc7e60357ba" +source = "git+https://github.com/demergent-labs/wasmedge-quickjs?rev=218238f970b2d1c3e34d712c6be9f558b8364716#218238f970b2d1c3e34d712c6be9f558b8364716" dependencies = [ "argparse", "encoding", diff --git a/src/compiler/rust/canister/Cargo.toml b/src/compiler/rust/canister/Cargo.toml index 7e07b6a862..b4b540da29 100644 --- a/src/compiler/rust/canister/Cargo.toml +++ b/src/compiler/rust/canister/Cargo.toml @@ -20,5 +20,5 @@ ic-cdk-macros = "0.7.0" ic-wasi-polyfill = { git = "https://github.com/wasm-forge/ic-wasi-polyfill", rev = "1693665ed57adc4f997a0313555ec0b0b5de1c07", features = [ "transient", ] } -wasmedge_quickjs = { git = "https://github.com/demergent-labs/wasmedge-quickjs", rev = "09edab8fbfa15e17f91d834003589fc7e60357ba" } +wasmedge_quickjs = { git = "https://github.com/demergent-labs/wasmedge-quickjs", rev = "218238f970b2d1c3e34d712c6be9f558b8364716" } # wasmedge_quickjs = { path = "/home/wasmedge-quickjs" } diff --git a/src/compiler/rust/canister/src/ic/call_raw.rs b/src/compiler/rust/canister/src/ic/call_raw.rs index 2d5f9b4d71..46e830bae2 100644 --- a/src/compiler/rust/canister/src/ic/call_raw.rs +++ b/src/compiler/rust/canister/src/ic/call_raw.rs @@ -1,89 +1,106 @@ // TODO basically copied into call_raw128 -use std::convert::TryInto; - -use quickjs_wasm_rs::{to_qjs_value, CallbackArg, JSContextRef, JSValue, JSValueRef}; - -use crate::CONTEXT; - -pub fn native_function<'a>( - context: &'a JSContextRef, - _this: &CallbackArg, - args: &[CallbackArg], -) -> Result, anyhow::Error> { - let promise_id: String = args - .get(0) - .expect("call_raw promise_id argument is undefined") - .to_js_value()? - .try_into()?; - let canister_id_bytes: Vec = args - .get(1) - .expect("call_raw canister_id_bytes is undefined") - .to_js_value()? - .try_into()?; - let canister_id = candid::Principal::from_slice(&canister_id_bytes); - let method: String = args - .get(2) - .expect("call_raw method argument is undefined") - .to_js_value()? - .try_into()?; - let args_raw: Vec = args - .get(3) - .expect("call_raw args_raw argument is undefined") - .to_js_value()? - .try_into()?; - let payment_candid_bytes: Vec = args - .get(4) - .expect("call_raw payment_candid_bytes argument is undefined") - .to_js_value()? - .try_into()?; - let payment: u64 = candid::decode_one(&payment_candid_bytes)?; - - ic_cdk::spawn(async move { - let call_result = - ic_cdk::api::call::call_raw(canister_id, &method, &args_raw, payment).await; - - let (should_resolve, js_value) = match call_result { - Ok(candid_bytes) => { - let candid_bytes_js_value: JSValue = candid_bytes.into(); - (true, candid_bytes_js_value) - } - Err(err) => { - let err_js_value: JSValue = format!( - "Rejection code {rejection_code}, {error_message}", - rejection_code = (err.0 as i32).to_string(), - error_message = err.1 - ) - .into(); - - (false, err_js_value) - } + +use wasmedge_quickjs::{AsObject, Context, JsFn, JsValue}; + +use crate::RUNTIME; + +pub struct NativeFunction; +impl JsFn for NativeFunction { + fn call(context: &mut Context, this_val: JsValue, argv: &[JsValue]) -> JsValue { + let promise_id = if let JsValue::String(js_string) = argv.get(0).unwrap() { + js_string.to_string() + } else { + panic!("conversion from JsValue to JsString failed") + }; + + let canister_id_bytes = if let JsValue::ArrayBuffer(js_array_buffer) = argv.get(1).unwrap() + { + js_array_buffer.to_vec() + } else { + panic!("conversion from JsValue to JsArrayBuffer failed") + }; + let canister_id = candid::Principal::from_slice(&canister_id_bytes); + + let method = if let JsValue::String(js_string) = argv.get(2).unwrap() { + js_string.to_string() + } else { + panic!("conversion from JsValue to JsString failed") + }; + + let args_raw = if let JsValue::ArrayBuffer(js_array_buffer) = argv.get(3).unwrap() { + js_array_buffer.to_vec() + } else { + panic!("conversion from JsValue to JsArrayBuffer failed") + }; + + let payment_string = if let JsValue::String(js_string) = argv.get(4).unwrap() { + js_string.to_string() + } else { + panic!("conversion from JsValue to JsString failed") }; + let payment: u64 = payment_string.parse().unwrap(); + + ic_cdk::spawn(async move { + let call_result = + ic_cdk::api::call::call_raw(canister_id, &method, &args_raw, payment).await; + + RUNTIME.with(|runtime| { + let mut runtime = runtime.borrow_mut(); + let runtime = runtime.as_mut().unwrap(); + + runtime.run_with_context(|context| { + let global = context.get_global(); + + let (should_resolve, js_value) = match &call_result { + Ok(candid_bytes) => { + let candid_bytes_js_value: JsValue = + context.new_array_buffer(candid_bytes).into(); + + (true, candid_bytes_js_value) + } + Err(err) => { + let err_js_value: JsValue = context + .new_error(&format!( + "Rejection code {rejection_code}, {error_message}", + rejection_code = (err.0 as i32).to_string(), + error_message = err.1 + )) + .into(); + + (false, err_js_value) + } + }; + + if should_resolve { + let resolve = global + .get("_azleResolveIds") + .to_obj() + .unwrap() + .get(format!("_resolve_{promise_id}").as_str()) + .to_function() + .unwrap(); + + resolve.call(&[js_value.clone()]); + } else { + let reject = global + .get("_azleRejectIds") + .to_obj() + .unwrap() + .get(format!("_reject_{promise_id}").as_str()) + .to_function() + .unwrap(); + + reject.call(&[js_value.clone()]); + } - CONTEXT.with(|context| { - let mut context = context.borrow_mut(); - let context = context.as_mut().unwrap(); - - let global = context.global_object().unwrap(); - - let js_value_ref = to_qjs_value(&context, &js_value).unwrap(); - - if should_resolve { - let resolve = global - .get_property("_azleResolveIds").unwrap() - .get_property(format!("_resolve_{promise_id}").as_str()) - .unwrap(); - resolve.call(&resolve, &[js_value_ref]).unwrap(); - } else { - let reject = global - .get_property("_azleRejectIds").unwrap() - .get_property(format!("_reject_{promise_id}").as_str()) - .unwrap(); - reject.call(&reject, &[js_value_ref]).unwrap(); - } - - context.execute_pending().unwrap(); + // TODO do we need to progress setTimeouts and stuff? + // TODO make sure to test this + // context.event_loop().unwrap().run_tick_task(); + context.promise_loop_poll(); + }); + }); }); - }); - context.undefined_value() + JsValue::UnDefined + } } diff --git a/src/compiler/rust/canister/src/ic/call_raw128.rs b/src/compiler/rust/canister/src/ic/call_raw128.rs index b7da057038..de3819f07a 100644 --- a/src/compiler/rust/canister/src/ic/call_raw128.rs +++ b/src/compiler/rust/canister/src/ic/call_raw128.rs @@ -1,89 +1,106 @@ // TODO basically copied from call_raw -use std::convert::TryInto; - -use quickjs_wasm_rs::{to_qjs_value, CallbackArg, JSContextRef, JSValue, JSValueRef}; - -use crate::CONTEXT; - -pub fn native_function<'a>( - context: &'a JSContextRef, - _this: &CallbackArg, - args: &[CallbackArg], -) -> Result, anyhow::Error> { - let promise_id: String = args - .get(0) - .expect("call_raw promise_id argument is undefined") - .to_js_value()? - .try_into()?; - let canister_id_bytes: Vec = args - .get(1) - .expect("call_raw canister_id_bytes is undefined") - .to_js_value()? - .try_into()?; - let canister_id = candid::Principal::from_slice(&canister_id_bytes); - let method: String = args - .get(2) - .expect("call_raw method argument is undefined") - .to_js_value()? - .try_into()?; - let args_raw: Vec = args - .get(3) - .expect("call_raw args_raw argument is undefined") - .to_js_value()? - .try_into()?; - let payment_candid_bytes: Vec = args - .get(4) - .expect("call_raw payment_candid_bytes argument is undefined") - .to_js_value()? - .try_into()?; - let payment: u128 = candid::decode_one(&payment_candid_bytes)?; - - ic_cdk::spawn(async move { - let call_result = - ic_cdk::api::call::call_raw128(canister_id, &method, &args_raw, payment).await; - - let (should_resolve, js_value) = match call_result { - Ok(candid_bytes) => { - let candid_bytes_js_value: JSValue = candid_bytes.into(); - (true, candid_bytes_js_value) - } - Err(err) => { - let err_js_value: JSValue = format!( - "Rejection code {rejection_code}, {error_message}", - rejection_code = (err.0 as i32).to_string(), - error_message = err.1 - ) - .into(); - - (false, err_js_value) - } + +use wasmedge_quickjs::{AsObject, Context, JsFn, JsValue}; + +use crate::RUNTIME; + +pub struct NativeFunction; +impl JsFn for NativeFunction { + fn call(context: &mut Context, this_val: JsValue, argv: &[JsValue]) -> JsValue { + let promise_id = if let JsValue::String(js_string) = argv.get(0).unwrap() { + js_string.to_string() + } else { + panic!("conversion from JsValue to JsString failed") + }; + + let canister_id_bytes = if let JsValue::ArrayBuffer(js_array_buffer) = argv.get(1).unwrap() + { + js_array_buffer.to_vec() + } else { + panic!("conversion from JsValue to JsArrayBuffer failed") + }; + let canister_id = candid::Principal::from_slice(&canister_id_bytes); + + let method = if let JsValue::String(js_string) = argv.get(2).unwrap() { + js_string.to_string() + } else { + panic!("conversion from JsValue to JsString failed") + }; + + let args_raw = if let JsValue::ArrayBuffer(js_array_buffer) = argv.get(3).unwrap() { + js_array_buffer.to_vec() + } else { + panic!("conversion from JsValue to JsArrayBuffer failed") + }; + + let payment_string = if let JsValue::String(js_string) = argv.get(4).unwrap() { + js_string.to_string() + } else { + panic!("conversion from JsValue to JsString failed") }; + let payment: u128 = payment_string.parse().unwrap(); + + ic_cdk::spawn(async move { + let call_result = + ic_cdk::api::call::call_raw128(canister_id, &method, &args_raw, payment).await; + + RUNTIME.with(|runtime| { + let mut runtime = runtime.borrow_mut(); + let runtime = runtime.as_mut().unwrap(); + + runtime.run_with_context(|context| { + let global = context.get_global(); + + let (should_resolve, js_value) = match &call_result { + Ok(candid_bytes) => { + let candid_bytes_js_value: JsValue = + context.new_array_buffer(candid_bytes).into(); + + (true, candid_bytes_js_value) + } + Err(err) => { + let err_js_value: JsValue = context + .new_error(&format!( + "Rejection code {rejection_code}, {error_message}", + rejection_code = (err.0 as i32).to_string(), + error_message = err.1 + )) + .into(); + + (false, err_js_value) + } + }; + + if should_resolve { + let resolve = global + .get("_azleResolveIds") + .to_obj() + .unwrap() + .get(format!("_resolve_{promise_id}").as_str()) + .to_function() + .unwrap(); + + resolve.call(&[js_value.clone()]); + } else { + let reject = global + .get("_azleRejectIds") + .to_obj() + .unwrap() + .get(format!("_reject_{promise_id}").as_str()) + .to_function() + .unwrap(); + + reject.call(&[js_value.clone()]); + } - CONTEXT.with(|context| { - let mut context = context.borrow_mut(); - let context = context.as_mut().unwrap(); - - let global = context.global_object().unwrap(); - - let js_value_ref = to_qjs_value(&context, &js_value).unwrap(); - - if should_resolve { - let resolve = global - .get_property("_azleResolveIds").unwrap() - .get_property(format!("_resolve_{promise_id}").as_str()) - .unwrap(); - resolve.call(&resolve, &[js_value_ref]).unwrap(); - } else { - let reject = global - .get_property("_azleRejectIds").unwrap() - .get_property(format!("_reject_{promise_id}").as_str()) - .unwrap(); - reject.call(&reject, &[js_value_ref]).unwrap(); - } - - context.execute_pending().unwrap(); + // TODO do we need to progress setTimeouts and stuff? + // TODO make sure to test this + // context.event_loop().unwrap().run_tick_task(); + context.promise_loop_poll(); + }); + }); }); - }); - context.undefined_value() + JsValue::UnDefined + } } diff --git a/src/compiler/rust/canister/src/ic/mod.rs b/src/compiler/rust/canister/src/ic/mod.rs index c8330f05ea..585e3cb187 100644 --- a/src/compiler/rust/canister/src/ic/mod.rs +++ b/src/compiler/rust/canister/src/ic/mod.rs @@ -1,8 +1,8 @@ // mod accept_message; mod arg_data_raw; mod arg_data_raw_size; -// mod call_raw; -// mod call_raw128; +mod call_raw; +mod call_raw128; mod caller; mod candid_decode; mod candid_encode; @@ -81,18 +81,17 @@ pub fn register(context: &mut wasmedge_quickjs::Context) { .into(), ); - // ic.set_property( - // "callRaw", - // context.wrap_callback2(call_raw::native_function).unwrap(), - // ) - // .unwrap(); - // ic.set_property( - // "callRaw128", - // context - // .wrap_callback2(call_raw128::native_function) - // .unwrap(), - // ) - // .unwrap(); + ic.set( + "callRaw", + context.new_function::("").into(), + ); + + ic.set( + "callRaw128", + context + .new_function::("") + .into(), + ); ic.set( "caller", diff --git a/src/compiler/rust/canister/src/lib.rs b/src/compiler/rust/canister/src/lib.rs index b728d3f370..f8cf253f12 100644 --- a/src/compiler/rust/canister/src/lib.rs +++ b/src/compiler/rust/canister/src/lib.rs @@ -1,3 +1,5 @@ +use std::{cell::RefCell, collections::BTreeMap, convert::TryInto}; + #[allow(unused)] use canister_methods::canister_methods; use ic_stable_structures::{ @@ -5,8 +7,6 @@ use ic_stable_structures::{ storable::Bound, DefaultMemoryImpl, StableBTreeMap, Storable, }; -use std::collections::BTreeMap; -use std::{cell::RefCell, convert::TryInto}; use wasmedge_quickjs::AsObject; mod ic; diff --git a/src/lib/ic/call_raw.ts b/src/lib/ic/call_raw.ts index a0b33cf530..a6d33ddce0 100644 --- a/src/lib/ic/call_raw.ts +++ b/src/lib/ic/call_raw.ts @@ -3,7 +3,6 @@ import { blob } from '../candid/types/constructed/blob'; import { nat64 } from '../candid/types/primitive/nats/nat64'; import { v4 } from 'uuid'; import { text } from '../candid/types/primitive/text'; -import { encode } from '../candid/serde/encode'; /** * Performs an asynchronous call to another canister using the [System API]( @@ -54,7 +53,7 @@ export function callRaw( const canisterIdBytes = canisterId.toUint8Array().buffer; const argsRawBuffer = argsRaw.buffer; - const paymentCandidBytes = encode(nat64, payment).buffer; + const paymentString = payment.toString(); // TODO consider finally, what if deletion goes wrong try { @@ -63,7 +62,7 @@ export function callRaw( canisterIdBytes, method, argsRawBuffer, - paymentCandidBytes + paymentString ); } catch (error) { delete globalThis._azleResolveIds[globalResolveId]; diff --git a/src/lib/ic/call_raw_128.ts b/src/lib/ic/call_raw_128.ts index 76ad349e0f..6c4b4cdb01 100644 --- a/src/lib/ic/call_raw_128.ts +++ b/src/lib/ic/call_raw_128.ts @@ -54,7 +54,7 @@ export function callRaw128( const canisterIdBytes = canisterId.toUint8Array().buffer; const argsRawBuffer = argsRaw.buffer; - const paymentCandidBytes = encode(nat, payment).buffer; + const paymentString = payment.toString(); // TODO consider finally, what if deletion goes wrong try { @@ -63,7 +63,7 @@ export function callRaw128( canisterIdBytes, method, argsRawBuffer, - paymentCandidBytes + paymentString ); } catch (error) { delete globalThis._azleResolveIds[globalResolveId]; diff --git a/src/lib/ic/types/azle_ic.ts b/src/lib/ic/types/azle_ic.ts index f9a7a0886f..a6a4691f81 100644 --- a/src/lib/ic/types/azle_ic.ts +++ b/src/lib/ic/types/azle_ic.ts @@ -10,14 +10,14 @@ export type AzleIc = { canisterIdBytes: ArrayBufferLike, method: string, argsRaw: ArrayBufferLike, - paymentCandidBytes: ArrayBufferLike + paymentString: string ) => void; callRaw128: ( promiseId: string, canisterIdBytes: ArrayBufferLike, method: string, argsRaw: ArrayBufferLike, - paymentCandidBytes: ArrayBufferLike + paymentString: string ) => void; caller: () => ArrayBufferLike; candidDecode: (candidBytes: ArrayBufferLike) => string;