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

stable/experimental binary bifurcation (rquickjs) #2150

Merged
merged 29 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4ad32cc
create stable crate as just a copy of the original crate
lastmjs Sep 30, 2024
4358b31
switch back to dfx 0.22.0
lastmjs Sep 30, 2024
f4b545f
remove many experimental features
lastmjs Sep 30, 2024
9035a70
first successful go at a stable method call for just a string
lastmjs Oct 1, 2024
18ddd5d
fix compilation errors for stable
lastmjs Oct 1, 2024
69850f2
refactored about half of the ic apis
lastmjs Oct 1, 2024
37376a7
finish many more APIs
lastmjs Oct 3, 2024
2c89d84
updated many ic objects got rid of many warnings
lastmjs Oct 4, 2024
1e4d6e8
adding azleIcStable and azleIcExperimental
lastmjs Oct 4, 2024
ba20857
add as any for the moment for experimental ic object in stable globals
lastmjs Oct 7, 2024
8abe4a2
refactor call_raw to be much more declarative first go
lastmjs Oct 7, 2024
f48925c
refactor call_raw very nicely
lastmjs Oct 8, 2024
b0a639a
call is beautifully refactored along with quickjs with ctx
lastmjs Oct 8, 2024
d816b29
add notify raw and set global init and post upgrade variables
lastmjs Oct 8, 2024
cd19bf9
get timers to work, change main module name to main instead of azle_main
lastmjs Oct 8, 2024
cae95e9
stable b tree map rust refactored
lastmjs Oct 8, 2024
0a57e3c
fix ic_api tests
lastmjs Oct 8, 2024
3e6ee53
fixed message cycles accept
lastmjs Oct 8, 2024
b05e04f
fix primitive types error issue
lastmjs Oct 8, 2024
ab54d5d
lower instruction test number of keys, refactor for better readability
lastmjs Oct 8, 2024
fad1f35
fixed issues with logging during the candid execution
lastmjs Oct 9, 2024
d64d239
refactored stable APIs to be usable in experimental binary
lastmjs Oct 18, 2024
540b70b
remove console.log
lastmjs Oct 18, 2024
a1ad39e
get rid of some extra prints
lastmjs Oct 18, 2024
4acbc6a
remove unnecessary cargo dependencies and alphabetize Cargo.toml
lastmjs Oct 18, 2024
67156ac
get rid of only in test
lastmjs Oct 18, 2024
b224ed6
remove typo in comments
lastmjs Oct 18, 2024
2d98b2a
remove any cast
lastmjs Oct 18, 2024
f96374a
remove unnecessary comment
lastmjs Oct 18, 2024
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
44 changes: 44 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
[workspace]
members = ["src/build/rust/canister", "src/build/rust/open_value_sharing"]
members = [
"src/build/rust/canister",
"src/build/rust/open_value_sharing",
"src/build/rust/stable",
]
Binary file modified canister_templates/experimental.wasm
Binary file not shown.
Binary file modified canister_templates/stable.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion src/build/rust/canister/src/candid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn get_candid_and_method_meta_pointer() -> *mut std::os::raw::c_char {
// TODO what do we do if there is an error in here?
context.eval_global_str("globalThis.exports = {};".to_string());
context.eval_global_str(format!("globalThis._azleExperimental = {EXPERIMENTAL};"));
context.eval_module_str(std::str::from_utf8(&js).unwrap().to_string(), "azle_main");
context.eval_module_str(std::str::from_utf8(&js).unwrap().to_string(), "main");

run_event_loop(context);

Expand Down
1 change: 1 addition & 0 deletions src/build/rust/canister/src/execute_method_js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub extern "C" fn execute_method_js(function_index: i32, pass_arg_data: i32) {
let global = context.get_global();

let callbacks = global.get("_azleCallbacks");

let method_callback = callbacks.get(function_name).unwrap();

let candid_args = if pass_arg_data {
Expand Down
2 changes: 1 addition & 1 deletion src/build/rust/canister/src/ic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,5 +312,5 @@ pub fn register(context: &mut wasmedge_quickjs::Context) {
context.new_function::<trap::NativeFunction>("").into(),
);

context.get_global().set("_azleIc", ic.into());
context.get_global().set("_azleIcExperimental", ic.into());
}
4 changes: 2 additions & 2 deletions src/build/rust/canister/src/init_and_post_upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ pub fn initialize_js(js: &str, init: bool, function_index: i32, pass_arg_data: i
// TODO what do we do if there is an error in here?
context.eval_global_str("globalThis.exports = {};".to_string());
context.eval_global_str(format!("globalThis._azleExperimental = {EXPERIMENTAL};"));
context.eval_module_str(js.to_string(), "azle_main");
context.eval_module_str(js.to_string(), "main");

run_event_loop(context);

// let temp = context.eval_module_str(std::str::from_utf8(MAIN_JS).unwrap().to_string(), "azle_main");
// let temp = context.eval_module_str(std::str::from_utf8(MAIN_JS).unwrap().to_string(), "main");

// match &temp {
// wasmedge_quickjs::JsValue::Exception(js_exception) => {
Expand Down
20 changes: 20 additions & 0 deletions src/build/rust/stable/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "stable"
version = "0.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
candid = "0.10.2"
candid_parser = "0.1.2"
ic-cdk = "0.12.2"
ic-cdk-macros = "0.8.4"
ic-cdk-timers = "0.6.0"
ic-stable-structures = "0.6.5"
ic-wasi-polyfill = "0.6.1"
rquickjs = { version = "0.6.2", features = ["array-buffer"] }
serde = "1.0.202"
serde_json = "1.0.107"
slotmap = "=1.0.6"
69 changes: 69 additions & 0 deletions src/build/rust/stable/src/candid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use crate::{
ic, quickjs_with_ctx, run_event_loop, wasm_binary_manipulation::get_js_code, CONTEXT_REF_CELL,
MODULE_NAME,
};

// TODO we might not need any of these panic hooks

// 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();

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();

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

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

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

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();

run_event_loop(ctx.clone());

let get_candid_and_method_meta: rquickjs::Function =
ctx.globals().get("_azleGetCandidAndMethodMeta").unwrap();

let candid_and_method_meta: String = get_candid_and_method_meta.call(()).unwrap();

let c_string = std::ffi::CString::new(candid_and_method_meta).unwrap();

c_string.into_raw()
})
}
7 changes: 7 additions & 0 deletions src/build/rust/stable/src/chunk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[allow(unused)]
pub async fn chunk() {
let id = ic_cdk::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;
}
26 changes: 26 additions & 0 deletions src/build/rust/stable/src/execute_method_js.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::{quickjs_with_ctx, run_event_loop};

#[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 };

quickjs_with_ctx(|ctx| {
let callbacks: rquickjs::Object = ctx.clone().globals().get("_azleCallbacks").unwrap();

let method_callback: rquickjs::Function = callbacks.get(function_name).unwrap();

let candid_args = if pass_arg_data {
ic_cdk::api::call::arg_data_raw()
} else {
vec![]
};

method_callback
.call::<_, rquickjs::Undefined>((candid_args,))
.unwrap();

run_event_loop(ctx.clone());
});
}
9 changes: 9 additions & 0 deletions src/build/rust/stable/src/guards.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[allow(unused)]
pub fn guard_against_non_controllers() -> Result<(), String> {
if ic_cdk::api::is_controller(&ic_cdk::api::caller()) {
return Ok(());
}
return Err(
"Not Authorized: only controllers of this canister may call this method".to_string(),
);
}
8 changes: 8 additions & 0 deletions src/build/rust/stable/src/ic/accept_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use rquickjs::{Ctx, Function};

pub fn get_function(context: Ctx) -> Function {
Function::new(context, || {
ic_cdk::api::call::accept_message();
})
.unwrap()
}
8 changes: 8 additions & 0 deletions src/build/rust/stable/src/ic/arg_data_raw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use rquickjs::{Ctx, Function, TypedArray};

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()
}
96 changes: 96 additions & 0 deletions src/build/rust/stable/src/ic/call_raw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::{quickjs_with_ctx, run_event_loop};
use rquickjs::{Ctx, Exception, Function, IntoJs, TypedArray};

pub fn get_function(ctx: Ctx) -> Function {
Function::new(
ctx,
|promise_id: String,
canister_id_bytes: TypedArray<u8>,
method: String,
args_raw: TypedArray<u8>,
payment_string: String| {
let canister_id = candid::Principal::from_slice(canister_id_bytes.as_ref());
let args_raw = args_raw.as_bytes().unwrap().to_vec();
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;

quickjs_with_ctx(move |ctx| {
resolve_or_reject(ctx.clone(), &call_result, &promise_id);
run_event_loop(ctx.clone());
});
});

rquickjs::Undefined
},
)
.unwrap()
}

fn resolve_or_reject<'a>(
ctx: Ctx<'a>,
call_result: &Result<Vec<u8>, (ic_cdk::api::call::RejectionCode, String)>,
promise_id: &str,
) {
let (should_resolve, js_value) = prepare_js_value(ctx.clone(), &call_result);
let callback = get_callback(ctx.clone(), &promise_id, should_resolve);

callback
.call::<_, rquickjs::Undefined>((js_value,))
.unwrap();
}

fn prepare_js_value<'a>(
ctx: Ctx<'a>,
call_result: &Result<Vec<u8>, (ic_cdk::api::call::RejectionCode, String)>,
) -> (bool, rquickjs::Value<'a>) {
match call_result {
Ok(candid_bytes) => {
let candid_bytes_js_value = TypedArray::<u8>::new(ctx.clone(), candid_bytes.clone())
.into_js(&ctx)
.unwrap();

(true, candid_bytes_js_value)
}
Err(err) => {
let err_js_value = Exception::from_message(
ctx.clone(),
&format!("Rejection code {}, {}", (err.0 as i32).to_string(), err.1),
)
.into_js(&ctx)
.unwrap();

(false, err_js_value)
}
}
}

fn get_callback<'a>(
ctx: Ctx<'a>,
promise_id: &str,
should_resolve: bool,
) -> rquickjs::Function<'a> {
let global_object = get_resolve_or_reject_global_object(ctx.clone(), should_resolve);
let callback_name = get_resolve_or_reject_callback_name(&promise_id, should_resolve);

global_object.get(callback_name).unwrap()
}

fn get_resolve_or_reject_global_object(ctx: Ctx, should_resolve: bool) -> rquickjs::Object {
let global = ctx.globals();
if should_resolve {
global.get("_azleResolveIds").unwrap()
} else {
global.get("_azleRejectIds").unwrap()
}
}

fn get_resolve_or_reject_callback_name(promise_id: &str, should_resolve: bool) -> String {
if should_resolve {
format!("_resolve_{promise_id}")
} else {
format!("_reject_{promise_id}")
}
}
8 changes: 8 additions & 0 deletions src/build/rust/stable/src/ic/caller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use rquickjs::{Ctx, Function, TypedArray};

pub fn get_function(context: Ctx) -> Function {
Function::new(context.clone(), move || {
TypedArray::<u8>::new(context.clone(), ic_cdk::api::caller().as_slice())
})
.unwrap()
}
11 changes: 11 additions & 0 deletions src/build/rust/stable/src/ic/candid_compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use rquickjs::{Ctx, Function};
use std::path::Path;

pub fn get_function(context: Ctx) -> Function {
Function::new(context, |candid_path: String| {
let (env, actor) = candid_parser::pretty_check_file(Path::new(&candid_path)).unwrap();

candid_parser::bindings::javascript::compile(&env, &actor)
})
.unwrap()
}
12 changes: 12 additions & 0 deletions src/build/rust/stable/src/ic/candid_decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use rquickjs::{Ctx, Function, TypedArray};

pub fn get_function(context: Ctx) -> Function {
Function::new(context, |candid_encoded: TypedArray<u8>| {
let candid_bytes = candid_encoded.as_ref();
let candid_args: candid::IDLArgs = candid::IDLArgs::from_bytes(candid_bytes).unwrap();
let candid_string = candid_args.to_string();

candid_string
})
.unwrap()
}
Loading