diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42944e5..fa49916 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: with: profile: default toolchain: stable - target: wasm32-wasi + target: wasm32-wasip1 default: true - name: Setup Go diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a0529be..ef5b8a1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ jobs: with: profile: default toolchain: stable - target: wasm32-wasi + target: wasm32-wasip1 default: true - name: Get wasm-opt @@ -35,13 +35,13 @@ jobs: run: make core - name: Opt core - run: wasm-opt --enable-reference-types --enable-bulk-memory --strip -O3 target/wasm32-wasi/release/js_pdk_core.wasm -o target/wasm32-wasi/release/js_pdk_core.wasm + run: wasm-opt --enable-reference-types --enable-bulk-memory --strip -O3 target/wasm32-wasip1/release/js_pdk_core.wasm -o target/wasm32-wasip1/release/js_pdk_core.wasm - name: Upload core binary to artifacts uses: actions/upload-artifact@v4 with: name: engine - path: target/wasm32-wasi/release/js_pdk_core.wasm + path: target/wasm32-wasip1/release/js_pdk_core.wasm compile_cli: name: Compile CLI diff --git a/Cargo.toml b/Cargo.toml index a0fa097..05128fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ ] [workspace.package] -version = "1.3.0" +version = "1.3.1" edition = "2021" authors = ["The Extism Authors"] license = "BSD-Clause-3" diff --git a/Makefile b/Makefile index 4c497c8..b0b397a 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,8 @@ core: && npm run build \ && npx -y -p typescript tsc src/index.ts --lib es2020 --declaration --emitDeclarationOnly --outDir dist \ && cd ../.. \ - && cargo build --release --target=wasm32-wasi \ - && wasm-opt --enable-reference-types --enable-bulk-memory --strip -O3 ../../target/wasm32-wasi/release/js_pdk_core.wasm -o ../../target/wasm32-wasi/release/js_pdk_core.wasm \ + && cargo build --release --target=wasm32-wasip1 \ + && wasm-opt --enable-reference-types --enable-bulk-memory --strip -O3 ../../target/wasm32-wasip1/release/js_pdk_core.wasm -o ../../target/wasm32-wasip1/release/js_pdk_core.wasm \ && cd - fmt: fmt-core fmt-cli @@ -30,7 +30,7 @@ fmt: fmt-core fmt-cli fmt-core: cd crates/core/ \ && cargo fmt -- --check \ - && cargo clippy --target=wasm32-wasi -- -D warnings \ + && cargo clippy --target=wasm32-wasip1 -- -D warnings \ && cd - fmt-cli: @@ -50,21 +50,30 @@ clean-wasi-sdk: test: compile-examples @extism call examples/simple_js.wasm greet --wasi --input="Benjamin" @extism call examples/bundled.wasm greet --wasi --input="Benjamin" --allow-host "example.com" -ifeq ($(OS),Windows_NT) - @python3 -m venv ./.venv && \ - ./.venv/Scripts/activate.bat && \ - pip install -r examples/host_funcs/requirements.txt && \ - python examples/host_funcs/host.py examples/host_funcs.wasm && \ - ./.venv/Scripts/deactivate.bat -else - @python3 -m venv ./.venv && \ - . ./.venv/bin/activate && \ - pip install -r examples/host_funcs/requirements.txt && \ - python3 examples/host_funcs/host.py examples/host_funcs.wasm && \ - deactivate -endif + # TODO uncomment block after python sdk is updated +# ifeq ($(OS),Windows_NT) +# @python3 -m venv ./.venv && \ +# ./.venv/Scripts/activate.bat && \ +# pip install -r examples/host_funcs/requirements.txt && \ +# python examples/host_funcs/host.py examples/host_funcs.wasm && \ +# ./.venv/Scripts/deactivate.bat +# else +# @python3 -m venv ./.venv && \ +# . ./.venv/bin/activate && \ +# pip install -r examples/host_funcs/requirements.txt && \ +# python3 examples/host_funcs/host.py examples/host_funcs.wasm && \ +# deactivate +# endif @extism call examples/react.wasm render --wasi @extism call examples/react.wasm setState --input='{"action": "SET_SETTING", "payload": { "backgroundColor": "tomato" }}' --wasi + @error_msg=$$(extism call examples/exception.wasm greet --wasi --input="Benjamin" 2>&1); \ + if echo "$$error_msg" | grep -q "shibboleth"; then \ + echo "Test passed - found expected error"; \ + else \ + echo "Test failed - did not find expected error message"; \ + echo "Got: $$error_msg"; \ + exit 1; \ + fi compile-examples: cli cd examples/react && npm install && npm run build && cd ../.. @@ -72,8 +81,9 @@ compile-examples: cli cd examples/bundled && npm install && npm run build && cd ../.. ./target/release/extism-js examples/host_funcs/script.js -i examples/host_funcs/script.d.ts -o examples/host_funcs.wasm ./target/release/extism-js examples/exports/script.js -i examples/exports/script.d.ts -o examples/exports.wasm + ./target/release/extism-js examples/exception/script.js -i examples/exception/script.d.ts -o examples/exception.wasm kitchen: cd examples/kitchen-sink && npm install && npm run build && cd ../.. ./target/release/extism-js examples/kitchen-sink/dist/index.js -i examples/kitchen-sink/src/index.d.ts -o examples/kitchen-sink.wasm - @extism call examples/kitchen-sink.wasm greet --input "Steve" --wasi --allow-host "*" --config "last_name=Manuel" \ No newline at end of file + @extism call examples/kitchen-sink.wasm greet --input "Steve" --wasi --allow-host "*" --config "last_name=Manuel" diff --git a/README.md b/README.md index 1e4e9ac..7167ba3 100644 --- a/README.md +++ b/README.md @@ -594,7 +594,7 @@ Before compiling the compiler, you need to install prerequisites. 1. Install Rust using [rustup](https://rustup.rs) 2. Install the WASI target platform via - `rustup target add --toolchain stable wasm32-wasi` + `rustup target add --toolchain stable wasm32-wasip1` 3. Install the wasi sdk using the makefile command: `make download-wasi-sdk` 4. Install [CMake](https://cmake.org/install/) (on macOS with homebrew, `brew install cmake`) diff --git a/crates/cli/build.rs b/crates/cli/build.rs index 3823fce..e5e2d28 100644 --- a/crates/cli/build.rs +++ b/crates/cli/build.rs @@ -32,7 +32,7 @@ fn copy_engine_binary() { if !is_override { engine_path.pop(); engine_path.pop(); - engine_path = engine_path.join("target/wasm32-wasi/release/js_pdk_core.wasm"); + engine_path = engine_path.join("target/wasm32-wasip1/release/js_pdk_core.wasm"); } println!("cargo:rerun-if-changed={}", engine_path.to_str().unwrap()); diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index b97aed3..50aef50 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -13,7 +13,7 @@ once_cell = "1.16" anyhow = { workspace = true } quickjs-wasm-rs = "3" chrono = { version = "0.4", default-features = false, features = ["clock"] } -rquickjs = { version = "0.6.2", features = ["array-buffer"]} +rquickjs = { version = "0.8", features = ["array-buffer", "bindgen"]} base64 = "0.22.1" [lib] diff --git a/crates/core/src/globals.rs b/crates/core/src/globals.rs index a80e6b5..9816056 100644 --- a/crates/core/src/globals.rs +++ b/crates/core/src/globals.rs @@ -77,7 +77,8 @@ extern "C" { fn get_args_as_str(args: &Rest) -> anyhow::Result { args.iter() .map(|arg| { - arg.as_string() + arg.clone() + .into_string() .ok_or(rquickjs::Error::Unknown) .and_then(|s| s.to_string()) }) @@ -89,9 +90,9 @@ fn get_args_as_str(args: &Rest) -> anyhow::Result { fn to_js_error(cx: Ctx, e: anyhow::Error) -> rquickjs::Error { match e.downcast::() { Ok(e) => e, - Err(e) => cx.throw(Value::from_exception( - rquickjs::Exception::from_message(cx.clone(), &e.to_string()) - .expect("Creating an exception should succeed"), + Err(e) => cx.throw(rquickjs::Value::from_string( + rquickjs::String::from_str(cx.clone(), &e.to_string()) + .expect("rquickjs error conversion"), )), } } @@ -144,7 +145,7 @@ fn build_module_object(this: Ctx) -> anyhow::Result { Ok(module) } -fn build_host_object<'js>(this: Ctx<'js>) -> anyhow::Result { +fn build_host_object<'js>(this: Ctx<'js>) -> anyhow::Result> { let host_input_bytes = Function::new( this.clone(), MutFn::new(move |cx| { @@ -253,361 +254,177 @@ fn build_host_object<'js>(this: Ctx<'js>) -> anyhow::Result { Ok(host_object) } -fn add_host_functions(this: Ctx<'_>) -> anyhow::Result<()> { +fn add_host_functions<'a>(this: Ctx<'a>) -> anyhow::Result<()> { let globals = this.globals(); let host_object = globals.get::<_, Object>("Host")?; let invoke_host = host_object.get::<_, Value>("invokeHost")?; if invoke_host.is_null() || invoke_host.is_undefined() { - let host_invoke_func = Function::new(this.clone(), move |cx, args: Rest>| { - let func_id = args.first().unwrap().as_int().unwrap() as u32; - println!("Invoke host func: {func_id}"); - let len = args.len() - 1; - match len { - 0 => { - let result = unsafe { __invokeHostFunc_0_1(func_id) }; - - Ok(Value::new_float(cx, result as f64)) - } - 1 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let result = unsafe { __invokeHostFunc_1_1(func_id, ptr as u64) }; - Ok(Value::new_float(cx, result as f64)) - } - 2 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr2 = args - .get(2) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let result = unsafe { __invokeHostFunc_2_1(func_id, ptr as u64, ptr2 as u64) }; - Ok(Value::new_float(cx, result as f64)) - } - 3 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr2 = args - .get(2) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr3 = args - .get(3) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let result = unsafe { - __invokeHostFunc_3_1(func_id, ptr as u64, ptr2 as u64, ptr3 as u64) - }; - Ok(Value::new_float(cx, result as f64)) - } - 4 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr2 = args - .get(2) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr3 = args - .get(3) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr4 = args - .get(4) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let result = unsafe { - __invokeHostFunc_4_1( - func_id, - ptr as u64, - ptr2 as u64, - ptr3 as u64, - ptr4 as u64, - ) - }; - Ok(Value::new_float(cx, result as f64)) - } - 5 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr2 = args - .get(2) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr3 = args - .get(3) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr4 = args - .get(4) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr5 = args - .get(5) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let result = unsafe { - __invokeHostFunc_5_1( - func_id, - ptr as u64, - ptr2 as u64, - ptr3 as u64, - ptr4 as u64, - ptr5 as u64, - ) - }; - Ok(Value::new_float(cx, result as f64)) - } - n => Err(to_js_error( - cx, - anyhow!("__invokeHostFunc with {n} parameters is not implemented"), - )), - } - })?; - let host_invoke_func0 = Function::new(this.clone(), move |cx: Ctx, args: Rest| { - let func_id = args.first().unwrap().as_int().unwrap() as u32; - let len = args.len() - 1; - match len { - 0 => { - unsafe { __invokeHostFunc_0_0(func_id) }; - } - 1 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - unsafe { __invokeHostFunc_1_0(func_id, ptr as u64) }; - } - 2 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr2 = args - .get(2) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - unsafe { __invokeHostFunc_2_0(func_id, ptr as u64, ptr2 as u64) }; - } - 3 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr2 = args - .get(2) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr3 = args - .get(3) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - unsafe { __invokeHostFunc_3_0(func_id, ptr as u64, ptr2 as u64, ptr3 as u64) }; - } - 4 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr2 = args - .get(2) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr3 = args - .get(3) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr4 = args - .get(4) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - unsafe { - __invokeHostFunc_4_0( - func_id, - ptr as u64, - ptr2 as u64, - ptr3 as u64, - ptr4 as u64, - ) - }; - } - 5 => { - let ptr = args - .get(1) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr2 = args - .get(2) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr3 = args - .get(3) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr4 = args - .get(4) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - let ptr5 = args - .get(5) - .unwrap() - .as_big_int() - .unwrap() - .clone() - .to_i64() - .unwrap(); - unsafe { - __invokeHostFunc_5_0( - func_id, - ptr as u64, - ptr2 as u64, - ptr3 as u64, - ptr4 as u64, - ptr5 as u64, - ) - }; - } - n => { - return Err(to_js_error( + let host_invoke_func = + Function::new(this.clone(), move |cx: Ctx<'a>, args: Rest>| { + let func_id = args.first().unwrap().as_int().unwrap() as u32; + let len = args.len() - 1; + let get_arg = |index: usize| -> i64 { + let x: &Value = args.get(index).unwrap(); + x.as_big_int() + .cloned() + .and_then(|x| match x.to_i64() { + Ok(x) => Some(x), + Err(_) => None, + }) + .or_else(|| x.as_float().or_else(|| x.as_number()).map(|x| x as i64)) + .unwrap() + }; + match len { + 0 => { + let result = unsafe { __invokeHostFunc_0_1(func_id) }; + + Ok(Value::new_float(cx, result as f64)) + } + 1 => { + let ptr = get_arg(1); + let result = unsafe { __invokeHostFunc_1_1(func_id, ptr as u64) }; + Ok(Value::new_float(cx.clone(), result as f64)) + } + 2 => { + let ptr = get_arg(1); + let ptr2 = get_arg(2); + let result = + unsafe { __invokeHostFunc_2_1(func_id, ptr as u64, ptr2 as u64) }; + Ok(Value::new_float(cx, result as f64)) + } + 3 => { + let ptr = get_arg(1); + let ptr2 = get_arg(2); + let ptr3 = get_arg(3); + let result = unsafe { + __invokeHostFunc_3_1(func_id, ptr as u64, ptr2 as u64, ptr3 as u64) + }; + Ok(Value::new_float(cx, result as f64)) + } + 4 => { + let ptr = get_arg(1); + let ptr2 = get_arg(2); + let ptr3 = get_arg(3); + let ptr4 = get_arg(4); + let result = unsafe { + __invokeHostFunc_4_1( + func_id, + ptr as u64, + ptr2 as u64, + ptr3 as u64, + ptr4 as u64, + ) + }; + Ok(Value::new_float(cx, result as f64)) + } + 5 => { + let ptr = get_arg(1); + let ptr2 = get_arg(2); + let ptr3 = get_arg(3); + let ptr4 = get_arg(4); + let ptr5 = get_arg(5); + let result = unsafe { + __invokeHostFunc_5_1( + func_id, + ptr as u64, + ptr2 as u64, + ptr3 as u64, + ptr4 as u64, + ptr5 as u64, + ) + }; + Ok(Value::new_float(cx, result as f64)) + } + n => Err(to_js_error( cx, anyhow!("__invokeHostFunc with {n} parameters is not implemented"), - )) + )), } - } - Ok(Undefined) - })?; + })?; + let host_invoke_func0 = + Function::new(this.clone(), move |cx: Ctx<'_>, args: Rest>| { + let func_id = args.first().unwrap().as_int().unwrap() as u32; + let len = args.len() - 1; + let get_arg = |index: usize| -> i64 { + let x: &Value = args.get(index).unwrap(); + x.as_big_int() + .cloned() + .and_then(|x| match x.to_i64() { + Ok(x) => Some(x), + Err(_) => None, + }) + .or_else(|| x.as_float().or_else(|| x.as_number()).map(|x| x as i64)) + .unwrap() + }; + match len { + 0 => { + unsafe { __invokeHostFunc_0_0(func_id) }; + } + 1 => { + let ptr = get_arg(1); + unsafe { __invokeHostFunc_1_0(func_id, ptr as u64) }; + } + 2 => { + let ptr = get_arg(1); + let ptr2 = get_arg(2); + unsafe { __invokeHostFunc_2_0(func_id, ptr as u64, ptr2 as u64) }; + } + 3 => { + let ptr = get_arg(1); + let ptr2 = get_arg(2); + let ptr3 = get_arg(3); + unsafe { + __invokeHostFunc_3_0(func_id, ptr as u64, ptr2 as u64, ptr3 as u64) + }; + } + 4 => { + let ptr = get_arg(1); + let ptr2 = get_arg(2); + let ptr3 = get_arg(3); + let ptr4 = get_arg(4); + unsafe { + __invokeHostFunc_4_0( + func_id, + ptr as u64, + ptr2 as u64, + ptr3 as u64, + ptr4 as u64, + ) + }; + } + 5 => { + let ptr = get_arg(1); + let ptr2 = get_arg(2); + let ptr3 = get_arg(3); + let ptr4 = get_arg(4); + let ptr5 = get_arg(5); + unsafe { + __invokeHostFunc_5_0( + func_id, + ptr as u64, + ptr2 as u64, + ptr3 as u64, + ptr4 as u64, + ptr5 as u64, + ) + }; + } + n => { + return Err(to_js_error( + cx, + anyhow!("__invokeHostFunc with {n} parameters is not implemented"), + )) + } + } + Ok(Undefined) + })?; host_object.set("invokeFunc", host_invoke_func)?; host_object.set("invokeFunc0", host_invoke_func0)?; } Ok(()) } -fn build_var_object<'js>(this: Ctx<'js>) -> anyhow::Result { +fn build_var_object<'js>(this: Ctx<'js>) -> anyhow::Result> { let var_set = Function::new( this.clone(), MutFn::new(move |cx: Ctx, args: Rest>| { @@ -714,7 +531,7 @@ fn build_var_object<'js>(this: Ctx<'js>) -> anyhow::Result { Ok(var_object) } -fn build_http_object<'js>(this: Ctx<'js>) -> anyhow::Result { +fn build_http_object<'js>(this: Ctx<'js>) -> anyhow::Result> { let http_req = Function::new(this.clone(), |cx: Ctx<'js>, args: Rest| { let req = args .first() @@ -860,7 +677,7 @@ fn build_config_object<'js>(this: Ctx<'js>) -> anyhow::Result> { Ok(config_obj) } -fn build_memory<'js>(this: Ctx<'js>) -> anyhow::Result { +fn build_memory<'js>(this: Ctx<'js>) -> anyhow::Result> { let memory_from_buffer = Function::new(this.clone(), |cx: Ctx<'js>, args: Rest| { let data = args .first() diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index b1ba889..a38a68e 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,4 +1,3 @@ -use once_cell::sync::OnceCell; use rquickjs::{ function::Args, object::ObjectKeysIter, Context, Ctx, Function, Object, Runtime, Undefined, Value, @@ -8,8 +7,30 @@ use std::io::Read; mod globals; -static mut CONTEXT: OnceCell = OnceCell::new(); -static mut CALL_ARGS: Vec> = vec![]; +struct Cx(Context); + +unsafe impl Send for Cx {} +unsafe impl Sync for Cx {} + +static CONTEXT: std::sync::OnceLock = std::sync::OnceLock::new(); +static CALL_ARGS: std::sync::Mutex>> = std::sync::Mutex::new(vec![]); + +fn check_exception(this: &Ctx, err: rquickjs::Error) -> anyhow::Error { + let s = match err { + rquickjs::Error::Exception => { + let err = this.catch().into_exception().unwrap(); + let msg = err.message().unwrap_or_default(); + format!("Exception: {}\n{}", msg, err.stack().unwrap_or_default()) + } + err => err.to_string(), + }; + + let mem = extism_pdk::Memory::from_bytes(&s).unwrap(); + unsafe { + extism_pdk::extism::error_set(mem.offset()); + } + anyhow::Error::msg(s) +} #[export_name = "wizer.initialize"] extern "C" fn init() { @@ -20,39 +41,33 @@ extern "C" fn init() { let mut code = String::new(); io::stdin().read_to_string(&mut code).unwrap(); - let _ = context.with(|this| -> Result { - match this.eval(code) { - Ok(()) => (), - Err(e) => return Err(e), - } - Ok(Undefined) - }); - - unsafe { - CONTEXT - .set(context) - .map_err(|_| anyhow::anyhow!("Could not intialize JS Context")) - .unwrap() - } + context + .with(|this| -> Result { + match this.eval(code) { + Ok(()) => (), + Err(err) => return Err(check_exception(&this, err)), + } + Ok(Undefined) + }) + .unwrap(); + let _ = CONTEXT.set(Cx(context)); } -fn js_context() -> &'static Context { - unsafe { - if CONTEXT.get().is_none() { - init() - } - let context = CONTEXT.get_unchecked(); - context +fn js_context() -> Context { + if CONTEXT.get().is_none() { + init() } + let context = CONTEXT.get().unwrap(); + context.0.clone() } fn invoke<'a, T, F: for<'b> Fn(Ctx<'b>, Value<'b>) -> T>( idx: i32, conv: F, ) -> Result { - let call_args = unsafe { CALL_ARGS.pop() }; + let call_args = CALL_ARGS.lock().unwrap().pop(); let context = js_context(); - let result = context.with(|ctx| { + context.with(|ctx| { let call_args = call_args.unwrap(); let args: Args = call_args.iter().fold( Args::new(ctx.clone(), call_args.len()), @@ -62,7 +77,7 @@ fn invoke<'a, T, F: for<'b> Fn(Ctx<'b>, Value<'b>) -> T>( .push_arg(v) .expect("Should be able to convert i32 to JS arg"), ArgType::I64(v) => args - .push_arg(v) + .push_arg(rquickjs::BigInt::from_i64(ctx.clone(), *v)) .expect("Should be able to convert i64 to JS arg"), ArgType::F32(v) => args .push_arg(v) @@ -90,111 +105,87 @@ fn invoke<'a, T, F: for<'b> Fn(Ctx<'b>, Value<'b>) -> T>( let res = conv(ctx.clone(), r); Ok(res) } - Err(err) => { - let e = format!("{:?}", err); - let mem = extism_pdk::Memory::from_bytes(&e).unwrap(); - unsafe { - extism_pdk::extism::error_set(mem.offset()); - } - Err(err) - } + Err(err) => Err(check_exception(&ctx, err)), } - })?; - Ok(result) + }) } #[no_mangle] pub extern "C" fn __arg_start() { - unsafe { - CALL_ARGS.push(vec![]); - } + CALL_ARGS.lock().unwrap().push(vec![]); } #[no_mangle] pub extern "C" fn __arg_i32(arg: i32) { - unsafe { - CALL_ARGS.last_mut().unwrap().push(ArgType::I32(arg)); - } + CALL_ARGS + .lock() + .unwrap() + .last_mut() + .unwrap() + .push(ArgType::I32(arg)); } #[no_mangle] pub extern "C" fn __arg_i64(arg: i64) { - unsafe { - CALL_ARGS.last_mut().unwrap().push(ArgType::I64(arg)); - } + CALL_ARGS + .lock() + .unwrap() + .last_mut() + .unwrap() + .push(ArgType::I64(arg)); } #[no_mangle] pub extern "C" fn __arg_f32(arg: f32) { - unsafe { - CALL_ARGS.last_mut().unwrap().push(ArgType::F32(arg)); - } + CALL_ARGS + .lock() + .unwrap() + .last_mut() + .unwrap() + .push(ArgType::F32(arg)); } #[no_mangle] pub extern "C" fn __arg_f64(arg: f64) { - unsafe { - CALL_ARGS.last_mut().unwrap().push(ArgType::F64(arg)); - } -} - -macro_rules! unwrap_value { - ($d:expr, $x:expr) => { - #[allow(clippy::blocks_in_conditions)] - match $x { - Ok(x) => x, - Err(e) => { - let err = format!("{:?}", e); - let mem = extism_pdk::Memory::from_bytes(&err).unwrap(); - unsafe { - extism_pdk::extism::error_set(mem.offset()); - } - $d - } - } - }; + CALL_ARGS + .lock() + .unwrap() + .last_mut() + .unwrap() + .push(ArgType::F64(arg)); } #[no_mangle] pub extern "C" fn __invoke_i32(idx: i32) -> i32 { - unwrap_value!( - -1, - invoke(idx, |_ctx, r| r.as_number().unwrap_or_default() as i32) - ) + invoke(idx, |_ctx, r| r.as_number().unwrap_or_default() as i32).unwrap_or(-1) } #[no_mangle] pub extern "C" fn __invoke_i64(idx: i32) -> i64 { - unwrap_value!( - -1, - invoke(idx, |_ctx, r| { - let Some(number) = r.as_big_int() else { - return 0; - }; - number.clone().to_i64().unwrap_or_default() - }) - ) + invoke(idx, |_ctx, r| { + if let Some(number) = r.as_big_int() { + return number.clone().to_i64().unwrap_or_default(); + } else if let Some(number) = r.as_number() { + return number as i64; + } + 0 + }) + .unwrap_or(-1) } #[no_mangle] pub extern "C" fn __invoke_f64(idx: i32) -> f64 { - unwrap_value!( - -1.0, - invoke(idx, |_ctx, r| r.as_float().unwrap_or_default()) - ) + invoke(idx, |_ctx, r| r.as_float().unwrap_or_default()).unwrap_or(-1.0) } #[no_mangle] pub extern "C" fn __invoke_f32(idx: i32) -> f32 { - unwrap_value!( - -1.0, - invoke(idx, |_ctx, r| r.as_number().unwrap_or_default() as f32) - ) + invoke(idx, |_ctx, r| r.as_number().unwrap_or_default() as f32).unwrap_or(-1.0) } #[no_mangle] pub extern "C" fn __invoke(idx: i32) { - unwrap_value!((), invoke(idx, |_ctx, _r| ())) + invoke(idx, |_ctx, _r| ()).unwrap() } fn export_names(exports: Object) -> anyhow::Result> { diff --git a/examples/exception/jsconfig.json b/examples/exception/jsconfig.json new file mode 100644 index 0000000..ca7514f --- /dev/null +++ b/examples/exception/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "lib": [], + "types": [ + "../../crates/core/src/prelude" + ] + } +} diff --git a/examples/exception/script.d.ts b/examples/exception/script.d.ts new file mode 100644 index 0000000..9c91c78 --- /dev/null +++ b/examples/exception/script.d.ts @@ -0,0 +1,3 @@ +declare module "main" { + export function greet(): I32; +} diff --git a/examples/exception/script.js b/examples/exception/script.js new file mode 100644 index 0000000..5dbfdb9 --- /dev/null +++ b/examples/exception/script.js @@ -0,0 +1,6 @@ + +function greet() { + throw new Error("I am a JS exception shibboleth") +} + +module.exports = { greet }; diff --git a/examples/host_funcs/requirements.txt b/examples/host_funcs/requirements.txt index ad15621..6836b9c 100644 --- a/examples/host_funcs/requirements.txt +++ b/examples/host_funcs/requirements.txt @@ -1 +1 @@ -extism==1.0.2 +extism==1.0.3