From 77e01f01bd147004c36e46f1716bc5ca28fc51cf Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 30 Oct 2019 13:59:27 +0100 Subject: [PATCH 1/6] chore(runtime-core) Use `vm::Ctx` to access it. Just to be consistent with the rest of the code. --- lib/runtime-core/src/typed_func.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 74318747c2f..4bcd6804ae6 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -3,7 +3,7 @@ use crate::{ export::{Context, Export, FuncPointer}, import::IsExport, types::{FuncSig, NativeWasmType, Type, WasmExternType}, - vm::{self, Ctx}, + vm, }; use std::{ any::Any, @@ -52,10 +52,10 @@ impl fmt::Display for WasmTrapInfo { /// of the `Func` struct. pub trait Kind {} -pub type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull, *const u64, *mut u64); +pub type Trampoline = unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64); pub type Invoke = unsafe extern "C" fn( Trampoline, - *mut Ctx, + *mut vm::Ctx, NonNull, *const u64, *mut u64, @@ -173,7 +173,7 @@ where pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { inner: Inner, f: NonNull, - ctx: *mut Ctx, + ctx: *mut vm::Ctx, _phantom: PhantomData<(&'a (), Args, Rets)>, } @@ -188,7 +188,7 @@ where pub(crate) unsafe fn from_raw_parts( inner: Wasm, f: NonNull, - ctx: *mut Ctx, + ctx: *mut vm::Ctx, ) -> Func<'a, Args, Rets, Wasm> { Func { inner, @@ -267,7 +267,7 @@ impl WasmTypeList for Infallible { self, _: NonNull, _: Wasm, - _: *mut Ctx, + _: *mut vm::Ctx, ) -> Result where Rets: WasmTypeList, @@ -313,7 +313,7 @@ where self, f: NonNull, wasm: Wasm, - ctx: *mut Ctx, + ctx: *mut vm::Ctx, ) -> Result where Rets: WasmTypeList, @@ -405,7 +405,7 @@ macro_rules! impl_traits { self, f: NonNull, wasm: Wasm, - ctx: *mut Ctx, + ctx: *mut vm::Ctx, ) -> Result where Rets: WasmTypeList @@ -443,7 +443,7 @@ macro_rules! impl_traits { $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, - FN: Fn(&mut Ctx $( , $x )*) -> Trap, + FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap, { #[allow(non_snake_case)] fn to_raw(&self) -> NonNull { @@ -451,13 +451,13 @@ macro_rules! impl_traits { /// This is required for the llvm backend to be able to unwind through this function. #[cfg_attr(nightly, unwind(allowed))] extern fn wrap<$( $x, )* Rets, Trap, FN>( - ctx: &mut Ctx $( , $x: <$x as WasmExternType>::Native )* + ctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct where $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, - FN: Fn(&mut Ctx $( , $x )*) -> Trap, + FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap, { let f: FN = unsafe { mem::transmute_copy(&()) }; @@ -564,7 +564,7 @@ mod tests { use super::*; #[test] fn test_call() { - fn foo(_ctx: &mut Ctx, a: i32, b: i32) -> (i32, i32) { + fn foo(_ctx: &mut vm::Ctx, a: i32, b: i32) -> (i32, i32) { (a, b) } @@ -575,7 +575,7 @@ mod tests { fn test_imports() { use crate::{func, imports}; - fn foo(_ctx: &mut Ctx, a: i32) -> i32 { + fn foo(_ctx: &mut vm::Ctx, a: i32) -> i32 { a } From bd9d4d9cc8a17e25f7ef0ed699eaf7873e73ff56 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 30 Oct 2019 13:59:59 +0100 Subject: [PATCH 2/6] test(runtime-core) Add tests for the `Func` structure. --- lib/runtime-core/src/typed_func.rs | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 4bcd6804ae6..fab028f30ab 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -562,6 +562,44 @@ where #[cfg(test)] mod tests { use super::*; + + macro_rules! test_func_arity_n { + ($test_name:ident, $($x:ident),*) => { + #[test] + fn $test_name() { + use crate::vm; + + fn with_vmctx(_: &mut vm::Ctx, $($x: i32),*) -> i32 { + vec![$($x),*].iter().sum() + } + + let _func = Func::new(with_vmctx); + } + } + } + + #[test] + fn test_func_arity_0() { + fn foo(_: &mut vm::Ctx) -> i32 { + 0 + } + + let _ = Func::new(foo); + } + + test_func_arity_n!(test_func_arity_1, a); + test_func_arity_n!(test_func_arity_2, a, b); + test_func_arity_n!(test_func_arity_3, a, b, c); + test_func_arity_n!(test_func_arity_4, a, b, c, d); + test_func_arity_n!(test_func_arity_5, a, b, c, d, e); + test_func_arity_n!(test_func_arity_6, a, b, c, d, e, f); + test_func_arity_n!(test_func_arity_7, a, b, c, d, e, f, g); + test_func_arity_n!(test_func_arity_8, a, b, c, d, e, f, g, h); + test_func_arity_n!(test_func_arity_9, a, b, c, d, e, f, g, h, i); + test_func_arity_n!(test_func_arity_10, a, b, c, d, e, f, g, h, i, j); + test_func_arity_n!(test_func_arity_11, a, b, c, d, e, f, g, h, i, j, k); + test_func_arity_n!(test_func_arity_12, a, b, c, d, e, f, g, h, i, j, k, l); + #[test] fn test_call() { fn foo(_ctx: &mut vm::Ctx, a: i32, b: i32) -> (i32, i32) { From 95e1b85c5669573e00dda8795cf8c776c107f6b7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 30 Oct 2019 14:46:22 +0100 Subject: [PATCH 3/6] feat(runtime-core) Allow host functions without an explicit `vm::Ctx` argument. This patch allows host functions to get a signature without an explicit `vm::Ctx` argument. It is for Rust only. The C API receives a function pointer and has no clue whether a `vm::Ctx` argument is present or not, so it assumes it is always declared. From the backend point of view, the pointer to `vm::Ctx` is always inserted in the stack, but it is not used by the function when the argument is absent. --- lib/runtime-core/src/typed_func.rs | 90 ++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index fab028f30ab..c9c0b332bd2 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -124,16 +124,27 @@ pub trait WasmTypeList { self, f: NonNull, wasm: Wasm, - ctx: *mut Ctx, + ctx: *mut vm::Ctx, ) -> Result where Rets: WasmTypeList; } +/// Empty trait to specify the kind of `ExternalFunction`: With or +/// without a `vm::Ctx` argument. +pub trait ExternalFunctionKind {} + +pub struct ExplicitVmCtx {} +pub struct ImplicitVmCtx {} + +impl ExternalFunctionKind for ExplicitVmCtx {} +impl ExternalFunctionKind for ImplicitVmCtx {} + /// Represents a function that can be converted to a `vm::Func` /// (function pointer) that can be called within WebAssembly. -pub trait ExternalFunction +pub trait ExternalFunction where + Kind: ExternalFunctionKind, Args: WasmTypeList, Rets: WasmTypeList, { @@ -208,9 +219,10 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - pub fn new(f: F) -> Func<'a, Args, Rets, Host> + pub fn new(f: F) -> Func<'a, Args, Rets, Host> where - F: ExternalFunction, + Kind: ExternalFunctionKind, + F: ExternalFunction, { Func { inner: Host(()), @@ -438,7 +450,7 @@ macro_rules! impl_traits { } } - impl< $( $x, )* Rets, Trap, FN > ExternalFunction<( $( $x ),* ), Rets> for FN + impl< $( $x, )* Rets, Trap, FN > ExternalFunction for FN where $( $x: WasmExternType, )* Rets: WasmTypeList, @@ -451,20 +463,78 @@ macro_rules! impl_traits { /// This is required for the llvm backend to be able to unwind through this function. #[cfg_attr(nightly, unwind(allowed))] extern fn wrap<$( $x, )* Rets, Trap, FN>( - ctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )* + vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )* + ) -> Rets::CStruct + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn(&mut vm::Ctx, $( $x, )*) -> Trap, + { + let f: FN = unsafe { mem::transmute_copy(&()) }; + + let err = match panic::catch_unwind( + panic::AssertUnwindSafe( + || { + f(vmctx $( , WasmExternType::from_native($x) )* ).report() + } + ) + ) { + Ok(Ok(returns)) => return returns.into_c_struct(), + Ok(Err(err)) => { + let b: Box<_> = err.into(); + b as Box + }, + Err(err) => err, + }; + + unsafe { + (&*vmctx.module).runnable_module.do_early_trap(err) + } + } + + NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap() + } else { + assert_eq!( + mem::size_of::(), + mem::size_of::(), + "you cannot use a closure that captures state for `Func`." + ); + + NonNull::new(unsafe { + mem::transmute_copy::<_, *mut vm::Func>(self) + }).unwrap() + } + } + } + + impl< $( $x, )* Rets, Trap, FN > ExternalFunction for FN + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn($( $x, )*) -> Trap, + { + #[allow(non_snake_case)] + fn to_raw(&self) -> NonNull { + if mem::size_of::() == 0 { + /// This is required for the llvm backend to be able to unwind through this function. + #[cfg_attr(nightly, unwind(allowed))] + extern fn wrap<$( $x, )* Rets, Trap, FN>( + vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct where $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, - FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap, + FN: Fn($( $x, )*) -> Trap, { let f: FN = unsafe { mem::transmute_copy(&()) }; let err = match panic::catch_unwind( panic::AssertUnwindSafe( || { - f(ctx $( , WasmExternType::from_native($x) )* ).report() + f($( WasmExternType::from_native($x), )* ).report() } ) ) { @@ -477,7 +547,7 @@ macro_rules! impl_traits { }; unsafe { - (&*ctx.module).runnable_module.do_early_trap(err) + (&*vmctx.module).runnable_module.do_early_trap(err) } } @@ -490,7 +560,7 @@ macro_rules! impl_traits { ); NonNull::new(unsafe { - ::std::mem::transmute_copy::<_, *mut vm::Func>(self) + mem::transmute_copy::<_, *mut vm::Func>(self) }).unwrap() } } From 7eef49be127f7259fbbb124652752165bac58c8c Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 30 Oct 2019 14:58:57 +0100 Subject: [PATCH 4/6] test(runtime-core) Test host functions without a `vm::Ctx` argument. --- lib/runtime-core-tests/tests/imports.rs | 25 +++++++++++++++++++++++++ lib/runtime-core/src/typed_func.rs | 10 ++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index 85bec1eb7c6..69b9040cdc6 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -10,11 +10,19 @@ fn imported_functions_forms() { (module (type $type (func (param i32) (result i32))) (import "env" "memory" (memory 1 1)) + (import "env" "callback_fn" (func $callback_fn (type $type))) (import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type))) + (import "env" "callback_fn_trap" (func $callback_fn_trap (type $type))) (import "env" "callback_fn_trap_with_vmctx" (func $callback_fn_trap_with_vmctx (type $type))) + (func (export "function_fn") (type $type) + get_local 0 + call $callback_fn) (func (export "function_fn_with_vmctx") (type $type) get_local 0 call $callback_fn_with_vmctx) + (func (export "function_fn_trap") (type $type) + get_local 0 + call $callback_fn_trap) (func (export "function_fn_trap_with_vmctx") (type $type) get_local 0 call $callback_fn_trap_with_vmctx)) @@ -31,7 +39,9 @@ fn imported_functions_forms() { let import_object = imports! { "env" => { "memory" => memory.clone(), + "callback_fn" => Func::new(callback_fn), "callback_fn_with_vmctx" => Func::new(callback_fn_with_vmctx), + "callback_fn_trap" => Func::new(callback_fn_trap), "callback_fn_trap_with_vmctx" => Func::new(callback_fn_trap_with_vmctx), }, }; @@ -88,7 +98,14 @@ fn imported_functions_forms() { }; } + call_and_assert!(function_fn, Ok(2)); call_and_assert!(function_fn_with_vmctx, Ok(2 + SHIFT)); + call_and_assert!( + function_fn_trap, + Err(RuntimeError::Error { + data: Box::new(format!("foo {}", 1)) + }) + ); call_and_assert!( function_fn_trap_with_vmctx, Err(RuntimeError::Error { @@ -97,6 +114,10 @@ fn imported_functions_forms() { ); } +fn callback_fn(n: i32) -> Result { + Ok(n + 1) +} + fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { let memory = vmctx.memory(0); let shift: i32 = memory.view()[0].get(); @@ -104,6 +125,10 @@ fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { Ok(shift + n + 1) } +fn callback_fn_trap(n: i32) -> Result { + Err(format!("foo {}", n)) +} + fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { let memory = vmctx.memory(0); let shift: i32 = memory.view()[0].get(); diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index c9c0b332bd2..66c68845e6c 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -643,7 +643,12 @@ mod tests { vec![$($x),*].iter().sum() } + fn without_vmctx($($x: i32),*) -> i32 { + vec![$($x),*].iter().sum() + } + let _func = Func::new(with_vmctx); + let _func = Func::new(without_vmctx); } } } @@ -654,7 +659,12 @@ mod tests { 0 } + fn bar() -> i32 { + 0 + } + let _ = Func::new(foo); + let _ = Func::new(bar); } test_func_arity_n!(test_func_arity_1, a); From 6a1d490a30bbfd6710d73c575979c0b2e64755e4 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 30 Oct 2019 14:59:20 +0100 Subject: [PATCH 5/6] doc(runtimecore) Explain `ExternalFunctionKind` with more details. --- lib/runtime-core/src/typed_func.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 66c68845e6c..f70f169297e 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -131,10 +131,32 @@ pub trait WasmTypeList { } /// Empty trait to specify the kind of `ExternalFunction`: With or -/// without a `vm::Ctx` argument. +/// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the +/// `ImplicitVmCtx` structures. +/// +/// This type is never aimed to be used by a user. It is used by the +/// trait system to automatically generate an appropriate `wrap` +/// function. pub trait ExternalFunctionKind {} +/// This empty structure indicates that an external function must +/// contain an explicit `vm::Ctx` argument (at first position). +/// +/// ```rs,ignore +/// fn add_one(_: mut &vm::Ctx, x: i32) -> i32 { +/// x + 1 +/// } +/// ``` pub struct ExplicitVmCtx {} + +/// This empty structure indicates that an external function has no +/// `vm::Ctx` argument (at first position). Its signature is: +/// +/// ```rs,ignore +/// fn add_one(x: i32) -> i32 { +/// x + 1 +/// } +/// ``` pub struct ImplicitVmCtx {} impl ExternalFunctionKind for ExplicitVmCtx {} From 912eb32be884aca7a7ee423e7b953d6d1ab69d9d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 31 Oct 2019 11:00:07 +0100 Subject: [PATCH 6/6] chore(cargo) Update `Cargo.lock`. --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index d15d45dd695..f23b0c2e298 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1540,7 +1540,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core-tests" -version = "0.8.0" +version = "0.9.0" dependencies = [ "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-backend 0.9.0",