diff --git a/CHANGELOG.md b/CHANGELOG.md index e2dc56dd2e7..b12e4fa224e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * [#1881](https://github.com/wasmerio/wasmer/pull/1881) Added `UnsupportedTarget` error to `CompileError` * [#1908](https://github.com/wasmerio/wasmer/pull/1908) Implemented `TryFrom>` for `i32`/`u32`/`i64`/`u64`/`f32`/`f64` * [#1927](https://github.com/wasmerio/wasmer/pull/1927) Added mmap support in `Engine::deserialize_from_file` to speed up artifact loading +* [#1911](https://github.com/wasmerio/wasmer/pull/1911) Generalized signature type in `Function::new` and `Function::new_with_env` to accept owned and reference `FunctionType` as well as array pairs. This allows users to define signatures as constants. Implemented `From<([Type; $N], [Type; $M])>` for `FunctionType` to support this. ### Changed diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs index 4618f380f88..ca5ff59d4ad 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/externals/function.rs @@ -69,7 +69,7 @@ pub struct Function { impl Function { /// Creates a new host `Function` (dynamic) with the provided signature. /// - /// # Example + /// # Examples /// /// ``` /// # use wasmer::{Function, FunctionType, Type, Store, Value}; @@ -82,11 +82,27 @@ impl Function { /// Ok(vec![Value::I32(sum)]) /// }); /// ``` + /// + /// With constant signature: + /// + /// ``` + /// # use wasmer::{Function, FunctionType, Type, Store, Value}; + /// # let store = Store::default(); + /// # + /// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]); + /// + /// let f = Function::new(&store, I32_I32_TO_I32, |args| { + /// let sum = args[0].unwrap_i32() + args[1].unwrap_i32(); + /// Ok(vec![Value::I32(sum)]) + /// }); + /// ``` #[allow(clippy::cast_ptr_alignment)] - pub fn new(store: &Store, ty: &FunctionType, func: F) -> Self + pub fn new(store: &Store, ty: FT, func: F) -> Self where + FT: Into, F: Fn(&[Val]) -> Result, RuntimeError> + 'static, { + let ty: FunctionType = ty.into(); let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithoutEnv { func: Box::new(func), function_type: ty.clone(), @@ -108,7 +124,7 @@ impl Function { address, kind: VMFunctionKind::Dynamic, vmctx, - signature: ty.clone(), + signature: ty, call_trampoline: None, instance_allocator: None, }, @@ -118,7 +134,7 @@ impl Function { /// Creates a new host `Function` (dynamic) with the provided signature and environment. /// - /// # Example + /// # Examples /// /// ``` /// # use wasmer::{Function, FunctionType, Type, Store, Value, WasmerEnv}; @@ -137,12 +153,33 @@ impl Function { /// Ok(vec![Value::I32(result)]) /// }); /// ``` + /// + /// With constant signature: + /// + /// ``` + /// # use wasmer::{Function, FunctionType, Type, Store, Value, WasmerEnv}; + /// # let store = Store::default(); + /// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]); + /// + /// #[derive(WasmerEnv)] + /// struct Env { + /// multiplier: i32, + /// }; + /// let env = Env { multiplier: 2 }; + /// + /// let f = Function::new_with_env(&store, I32_I32_TO_I32, env, |env, args| { + /// let result = env.multiplier * (args[0].unwrap_i32() + args[1].unwrap_i32()); + /// Ok(vec![Value::I32(result)]) + /// }); + /// ``` #[allow(clippy::cast_ptr_alignment)] - pub fn new_with_env(store: &Store, ty: &FunctionType, env: Env, func: F) -> Self + pub fn new_with_env(store: &Store, ty: FT, env: Env, func: F) -> Self where + FT: Into, F: Fn(&Env, &[Val]) -> Result, RuntimeError> + 'static, Env: Sized + WasmerEnv + 'static, { + let ty: FunctionType = ty.into(); let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithEnv { env: Box::new(env), func: Box::new(func), @@ -171,7 +208,7 @@ impl Function { address, kind: VMFunctionKind::Dynamic, vmctx, - signature: ty.clone(), + signature: ty, call_trampoline: None, instance_allocator: None, }, diff --git a/lib/api/tests/externals.rs b/lib/api/tests/externals.rs index 7160fe832d1..9d2125ccae1 100644 --- a/lib/api/tests/externals.rs +++ b/lib/api/tests/externals.rs @@ -250,6 +250,8 @@ fn function_new_env() -> Result<()> { #[test] fn function_new_dynamic() -> Result<()> { let store = Store::default(); + + // Using &FunctionType signature let function_type = FunctionType::new(vec![], vec![]); let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!()); assert_eq!(function.ty().clone(), function_type); @@ -265,6 +267,13 @@ fn function_new_dynamic() -> Result<()> { let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!()); assert_eq!(function.ty().clone(), function_type); + + // Using array signature + let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); + let function = Function::new(&store, function_type, |_values: &[Value]| unimplemented!()); + assert_eq!(function.ty().params(), [Type::V128]); + assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]); + Ok(()) } @@ -275,6 +284,7 @@ fn function_new_dynamic_env() -> Result<()> { struct MyEnv {}; let my_env = MyEnv {}; + // Using &FunctionType signature let function_type = FunctionType::new(vec![], vec![]); let function = Function::new_with_env( &store, @@ -315,6 +325,18 @@ fn function_new_dynamic_env() -> Result<()> { |_env: &MyEnv, _values: &[Value]| unimplemented!(), ); assert_eq!(function.ty().clone(), function_type); + + // Using array signature + let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); + let function = Function::new_with_env( + &store, + function_type, + my_env.clone(), + |_env: &MyEnv, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty().params(), [Type::V128]); + assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]); + Ok(()) } diff --git a/lib/c-api/src/wasm_c_api/externals/function.rs b/lib/c-api/src/wasm_c_api/externals/function.rs index f34baa3a370..eb2e1fb7859 100644 --- a/lib/c-api/src/wasm_c_api/externals/function.rs +++ b/lib/c-api/src/wasm_c_api/externals/function.rs @@ -78,7 +78,7 @@ pub unsafe extern "C" fn wasm_func_new( Ok(processed_results) }; - let function = Function::new(&store.inner, &func_sig, inner_callback); + let function = Function::new(&store.inner, func_sig, inner_callback); Some(Box::new(wasm_func_t { instance: None, @@ -154,7 +154,7 @@ pub unsafe extern "C" fn wasm_func_new_with_env( let function = Function::new_with_env( &store.inner, - &func_sig, + func_sig, WrapperEnv { env, finalizer }, inner_callback, ); diff --git a/lib/deprecated/runtime-core/src/typed_func.rs b/lib/deprecated/runtime-core/src/typed_func.rs index 0bfe47d61a5..53c88a4f070 100644 --- a/lib/deprecated/runtime-core/src/typed_func.rs +++ b/lib/deprecated/runtime-core/src/typed_func.rs @@ -265,6 +265,7 @@ impl DynamicFunc { Self { new_function: new::wasmer::Function::new_with_env::< + _, fn(&DynamicCtx, &[Value]) -> Result, RuntimeError>, DynamicCtx, >(&get_global_store(), signature, ctx, inner), diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 075ee83b3ff..c7700fbd24d 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -132,7 +132,7 @@ fn get_path_open_for_store(store: &Store, env: WasiEnv) -> Function { #[cfg(all(target_os = "macos", target_arch = "aarch64",))] let path_open = Function::new_with_env( store, - &FunctionType::new( + FunctionType::new( vec![ ValType::I32, ValType::I32, diff --git a/lib/wasmer-types/src/types.rs b/lib/wasmer-types/src/types.rs index fbd5d8aa503..725370e1aca 100644 --- a/lib/wasmer-types/src/types.rs +++ b/lib/wasmer-types/src/types.rs @@ -276,6 +276,39 @@ impl fmt::Display for FunctionType { } } +// Macro needed until https://rust-lang.github.io/rfcs/2000-const-generics.html is stable. +// See https://users.rust-lang.org/t/how-to-implement-trait-for-fixed-size-array-of-any-size/31494 +macro_rules! implement_from_pair_to_functiontype { + ($($N:literal,$M:literal)+) => { + $( + impl From<([Type; $N], [Type; $M])> for FunctionType { + fn from(pair: ([Type; $N], [Type; $M])) -> Self { + Self::new(pair.0, pair.1) + } + } + )+ + } +} + +implement_from_pair_to_functiontype! { + 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9 + 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9 + 2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9 + 3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9 + 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9 + 5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9 + 6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9 + 7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9 + 8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9 + 9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9 +} + +impl From<&FunctionType> for FunctionType { + fn from(as_ref: &FunctionType) -> Self { + as_ref.clone() + } +} + /// Indicator of whether a global is mutable or not #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -566,3 +599,37 @@ impl ExportType { &self.ty } } + +#[cfg(test)] +mod tests { + use super::*; + + const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []); + const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []); + const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]); + const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]); + + #[test] + fn convert_tuple_to_functiontype() { + let ty: FunctionType = VOID_TO_VOID.into(); + assert_eq!(ty.params().len(), 0); + assert_eq!(ty.results().len(), 0); + + let ty: FunctionType = I32_I32_TO_VOID.into(); + assert_eq!(ty.params().len(), 2); + assert_eq!(ty.params()[0], Type::I32); + assert_eq!(ty.params()[1], Type::I32); + assert_eq!(ty.results().len(), 0); + + let ty: FunctionType = V128_I64_TO_I32.into(); + assert_eq!(ty.params().len(), 2); + assert_eq!(ty.params()[0], Type::V128); + assert_eq!(ty.params()[1], Type::I64); + assert_eq!(ty.results().len(), 1); + assert_eq!(ty.results()[0], Type::I32); + + let ty: FunctionType = NINE_V128_TO_NINE_I32.into(); + assert_eq!(ty.params().len(), 9); + assert_eq!(ty.results().len(), 9); + } +} diff --git a/tests/compilers/imports.rs b/tests/compilers/imports.rs index 3d35944f4db..5751f4798f9 100644 --- a/tests/compilers/imports.rs +++ b/tests/compilers/imports.rs @@ -52,22 +52,22 @@ fn dynamic_function() -> Result<()> { &module, &imports! { "host" => { - "0" => Function::new(&store, &FunctionType::new(vec![], vec![]), |_values| { + "0" => Function::new(&store, FunctionType::new(vec![], vec![]), |_values| { assert_eq!(HITS.fetch_add(1, SeqCst), 0); Ok(vec![]) }), - "1" => Function::new(&store, &FunctionType::new(vec![ValType::I32], vec![ValType::I32]), |values| { + "1" => Function::new(&store, FunctionType::new(vec![ValType::I32], vec![ValType::I32]), |values| { assert_eq!(values[0], Value::I32(0)); assert_eq!(HITS.fetch_add(1, SeqCst), 1); Ok(vec![Value::I32(1)]) }), - "2" => Function::new(&store, &FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), |values| { + "2" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), |values| { assert_eq!(values[0], Value::I32(2)); assert_eq!(values[1], Value::I64(3)); assert_eq!(HITS.fetch_add(1, SeqCst), 2); Ok(vec![]) }), - "3" => Function::new(&store, &FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), |values| { + "3" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), |values| { assert_eq!(values[0], Value::I32(100)); assert_eq!(values[1], Value::I64(200)); assert_eq!(values[2], Value::I32(300)); @@ -107,22 +107,22 @@ fn dynamic_function_with_env() -> Result<()> { &module, &imports! { "host" => { - "0" => Function::new_with_env(&store, &FunctionType::new(vec![], vec![]), env.clone(), |env, _values| { + "0" => Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env.clone(), |env, _values| { assert_eq!(env.fetch_add(1, SeqCst), 0); Ok(vec![]) }), - "1" => Function::new_with_env(&store, &FunctionType::new(vec![ValType::I32], vec![ValType::I32]), env.clone(), |env, values| { + "1" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32], vec![ValType::I32]), env.clone(), |env, values| { assert_eq!(values[0], Value::I32(0)); assert_eq!(env.fetch_add(1, SeqCst), 1); Ok(vec![Value::I32(1)]) }), - "2" => Function::new_with_env(&store, &FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), env.clone(), |env, values| { + "2" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), env.clone(), |env, values| { assert_eq!(values[0], Value::I32(2)); assert_eq!(values[1], Value::I64(3)); assert_eq!(env.fetch_add(1, SeqCst), 2); Ok(vec![]) }), - "3" => Function::new_with_env(&store, &FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), env.clone(), |env, values| { + "3" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), env.clone(), |env, values| { assert_eq!(values[0], Value::I32(100)); assert_eq!(values[1], Value::I64(200)); assert_eq!(values[2], Value::I32(300)); @@ -332,7 +332,7 @@ fn dynamic_function_with_env_wasmer_env_init_works() -> Result<()> { &module, &imports! { "host" => { - "fn" => Function::new_with_env(&store, &FunctionType::new(vec![], vec![]), env.clone(), |env, _values| { + "fn" => Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env.clone(), |env, _values| { assert!(env.memory_ref().is_some()); Ok(vec![]) }), diff --git a/tests/compilers/native_functions.rs b/tests/compilers/native_functions.rs index 1bd6ddff784..f8bfe33d58a 100644 --- a/tests/compilers/native_functions.rs +++ b/tests/compilers/native_functions.rs @@ -225,7 +225,7 @@ fn native_function_works_for_wasm_function_manyparams_dynamic() -> Result<()> { let import_object = imports! { "env" => { - "longf" => Function::new(&store, &FunctionType::new(vec![ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I64 , ValType::I64 ,ValType::I32, ValType::I32], vec![ValType::I64]), long_f_dynamic), + "longf" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I64 , ValType::I64 ,ValType::I32, ValType::I32], vec![ValType::I64]), long_f_dynamic), }, }; @@ -377,7 +377,7 @@ fn dynamic_host_function_without_env() -> anyhow::Result<()> { let f = Function::new( &store, - &FunctionType::new( + FunctionType::new( vec![ValType::I32, ValType::I64, ValType::F32, ValType::F64], vec![ValType::F64, ValType::F32, ValType::I64, ValType::I32], ), @@ -415,7 +415,7 @@ fn dynamic_host_function_with_env() -> anyhow::Result<()> { let env = Env(Rc::new(RefCell::new(100))); let f = Function::new_with_env( &store, - &FunctionType::new( + FunctionType::new( vec![ValType::I32, ValType::I64, ValType::F32, ValType::F64], vec![ValType::F64, ValType::F32, ValType::I64, ValType::I32], ),