diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs index 5c4a26b4f..0ac487ea8 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/externals/function.rs @@ -9,7 +9,7 @@ use std::cmp::max; use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv}; use wasmer_runtime::{ wasmer_call_trampoline, Export, ExportFunction, VMCallerCheckedAnyfunc, VMContext, - VMDynamicFunctionImportContext, VMFunctionBody, VMFunctionKind, VMTrampoline, + VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, VMTrampoline, }; /// A function defined in the Wasm module @@ -35,6 +35,7 @@ pub struct Function { pub(crate) definition: FunctionDefinition, // If the Function is owned by the Store, not the instance pub(crate) owned_by_store: bool, + pub(crate) has_env: bool, pub(crate) exported: ExportFunction, } @@ -58,6 +59,7 @@ impl Function { store: store.clone(), owned_by_store: true, definition: FunctionDefinition::Host, + has_env: false, exported: ExportFunction { address, vmctx, @@ -72,17 +74,20 @@ impl Function { where F: Fn(&[Val]) -> Result, RuntimeError> + 'static, { - let dynamic_ctx = - VMDynamicFunctionImportContext::from_context(VMDynamicFunctionWithoutEnv { - func: Box::new(func), - function_type: ty.clone(), - }); + let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithoutEnv { + func: Box::new(func), + function_type: ty.clone(), + }); + // We don't yet have the address with the Wasm ABI signature. + // The engine linker will replace the address with one pointing to a + // generated dynamic trampoline. let address = std::ptr::null() as *const VMFunctionBody; let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext; Self { store: store.clone(), owned_by_store: true, definition: FunctionDefinition::Host, + has_env: false, exported: ExportFunction { address, kind: VMFunctionKind::Dynamic, @@ -98,17 +103,21 @@ impl Function { F: Fn(&mut Env, &[Val]) -> Result, RuntimeError> + 'static, Env: Sized, { - let dynamic_ctx = VMDynamicFunctionImportContext::from_context(VMDynamicFunctionWithEnv { + let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithEnv { env, func: Box::new(func), function_type: ty.clone(), }); + // We don't yet have the address with the Wasm ABI signature. + // The engine linker will replace the address with one pointing to a + // generated dynamic trampoline. let address = std::ptr::null() as *const VMFunctionBody; let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext; Self { store: store.clone(), owned_by_store: true, definition: FunctionDefinition::Host, + has_env: true, exported: ExportFunction { address, kind: VMFunctionKind::Dynamic, @@ -143,6 +152,7 @@ impl Function { store: store.clone(), owned_by_store: true, definition: FunctionDefinition::Host, + has_env: true, exported: ExportFunction { address, kind: VMFunctionKind::Static, @@ -269,6 +279,7 @@ impl Function { Self { store: store.clone(), owned_by_store: false, + has_env: true, definition: FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }), exported: wasmer_export, } @@ -307,6 +318,7 @@ impl Function { self.exported.vmctx, self.exported.kind, self.definition.clone(), + self.has_env, )) } } @@ -330,12 +342,12 @@ impl std::fmt::Debug for Function { } /// This trait is one that all dynamic functions must fulfill. -trait VMDynamicFunction { +pub(crate) trait VMDynamicFunction { fn call(&self, args: &[Val]) -> Result, RuntimeError>; fn function_type(&self) -> &FunctionType; } -struct VMDynamicFunctionWithoutEnv { +pub(crate) struct VMDynamicFunctionWithoutEnv { #[allow(clippy::type_complexity)] func: Box Result, RuntimeError> + 'static>, function_type: FunctionType, @@ -350,7 +362,7 @@ impl VMDynamicFunction for VMDynamicFunctionWithoutEnv { } } -struct VMDynamicFunctionWithEnv +pub(crate) struct VMDynamicFunctionWithEnv where Env: Sized, { @@ -372,13 +384,13 @@ where } } -trait VMDynamicFunctionImportCall { +trait VMDynamicFunctionCall { fn from_context(ctx: T) -> Self; fn address_ptr() -> *const VMFunctionBody; unsafe fn func_wrapper(&self, values_vec: *mut i128); } -impl VMDynamicFunctionImportCall for VMDynamicFunctionImportContext { +impl VMDynamicFunctionCall for VMDynamicFunctionContext { fn from_context(ctx: T) -> Self { Self { address: Self::address_ptr(), @@ -393,8 +405,8 @@ impl VMDynamicFunctionImportCall for VMDynamicFunctionI // This function wraps our func, to make it compatible with the // reverse trampoline signature unsafe fn func_wrapper( - // Note: we use the trick that the first param to this function is the `VMDynamicFunctionImportContext` - // itself, so rather than doing `dynamic_ctx: &VMDynamicFunctionImportContext`, we simplify it a bit + // Note: we use the trick that the first param to this function is the `VMDynamicFunctionContext` + // itself, so rather than doing `dynamic_ctx: &VMDynamicFunctionContext`, we simplify it a bit &self, values_vec: *mut i128, ) { diff --git a/lib/api/src/native.rs b/lib/api/src/native.rs index d3e4aa0d9..f442ca159 100644 --- a/lib/api/src/native.rs +++ b/lib/api/src/native.rs @@ -1,19 +1,22 @@ //! Native Functions. -//! +//! //! This module creates the helper `NativeFunc` that let us call WebAssembly //! functions with the native ABI, that is: -//! +//! //! ```ignore //! let add_one = instance.exports.get_function("func_name")?; //! let add_one_native: NativeFunc = add_one.native().unwrap(); //! ``` use std::marker::PhantomData; -use crate::externals::function::{FunctionDefinition, WasmFunctionDefinition}; +use crate::externals::function::{ + FunctionDefinition, VMDynamicFunction, VMDynamicFunctionWithoutEnv, WasmFunctionDefinition, +}; use crate::{Function, FunctionType, RuntimeError, Store}; use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList}; use wasmer_runtime::{ - wasmer_call_trampoline, ExportFunction, VMContext, VMFunctionBody, VMFunctionKind, + wasmer_call_trampoline, ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody, + VMFunctionKind, }; pub struct NativeFunc<'a, Args = (), Rets = ()> { @@ -22,6 +25,7 @@ pub struct NativeFunc<'a, Args = (), Rets = ()> { address: *const VMFunctionBody, vmctx: *mut VMContext, arg_kind: VMFunctionKind, + has_env: bool, // exported: ExportFunction, _phantom: PhantomData<(&'a (), Args, Rets)>, } @@ -39,6 +43,7 @@ where vmctx: *mut VMContext, arg_kind: VMFunctionKind, definition: FunctionDefinition, + has_env: bool, ) -> Self { Self { definition, @@ -46,6 +51,7 @@ where address, vmctx, arg_kind, + has_env, _phantom: PhantomData, } } @@ -78,6 +84,7 @@ where store: other.store, definition: other.definition, owned_by_store: true, // todo + has_env: other.has_env, exported: ExportFunction { address: other.address, vmctx: other.vmctx, @@ -169,7 +176,21 @@ macro_rules! impl_native_traits { return Ok(results); } } else { - todo!("dynamic host functions not yet implemented") + /// Is a dynamic function + if !self.has_env { + let ctx = self.vmctx as *mut VMDynamicFunctionContext; + let params_list = [ $( $x.to_native().to_value() ),* ]; + let results = unsafe { (*ctx).ctx.call(¶ms_list)? }; + let mut rets_list_array = Rets::empty_array(); + let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128; + for (i, ret) in results.iter().enumerate() { + unsafe { + ret.write_value_to(mut_rets.add(i)); + } + } + return Ok(Rets::from_array(rets_list_array)); + } + unimplemented!("native calls to dynamic function with env not yet implemented"); } }, } diff --git a/lib/api/src/ordered_resolver.rs b/lib/api/src/ordered_resolver.rs index 0f9fafe7f..adef4447a 100644 --- a/lib/api/src/ordered_resolver.rs +++ b/lib/api/src/ordered_resolver.rs @@ -1,6 +1,6 @@ //! Ordered Resolvers are a custom kind of [`Resolver`] that retrieves //! `Export`s based on the index of the import, and not the module or name. -//! +//! //! This resolver is used in the Wasm-C-API as the imports are provided //! by index and not by module and name. diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index b029c9078..1c812039e 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -47,7 +47,7 @@ pub use crate::sig_registry::SignatureRegistry; pub use crate::table::{Table, TableElements, TablePlan, TableStyle}; pub use crate::trap::*; pub use crate::vmcontext::{ - VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionImportContext, + VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, diff --git a/lib/runtime/src/vmcontext.rs b/lib/runtime/src/vmcontext.rs index b74c3b09c..b0f9fe592 100644 --- a/lib/runtime/src/vmcontext.rs +++ b/lib/runtime/src/vmcontext.rs @@ -46,7 +46,7 @@ mod test_vmfunction_import { } } -/// The `VMDynamicFunctionImportContext` is the context that dynamic +/// The `VMDynamicFunctionContext` is the context that dynamic /// functions will receive when called (rather than `vmctx`). /// A dynamic function is a function for which we don't know the signature /// until runtime. @@ -55,7 +55,7 @@ mod test_vmfunction_import { /// containing the relevant context for running the function indicated /// in `address`. #[repr(C)] -pub struct VMDynamicFunctionImportContext { +pub struct VMDynamicFunctionContext { /// The address of the inner dynamic function. /// /// Note: The function must be on the form of @@ -68,7 +68,7 @@ pub struct VMDynamicFunctionImportContext { #[cfg(test)] mod test_vmdynamicfunction_import_context { - use super::VMDynamicFunctionImportContext; + use super::VMDynamicFunctionContext; use crate::{ModuleInfo, VMOffsets}; use memoffset::offset_of; use std::mem::size_of; @@ -78,15 +78,15 @@ mod test_vmdynamicfunction_import_context { let module = ModuleInfo::new(); let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); assert_eq!( - size_of::>(), + size_of::>(), usize::from(offsets.size_of_vmdynamicfunction_import_context()) ); assert_eq!( - offset_of!(VMDynamicFunctionImportContext, address), + offset_of!(VMDynamicFunctionContext, address), usize::from(offsets.vmdynamicfunction_import_context_address()) ); assert_eq!( - offset_of!(VMDynamicFunctionImportContext, ctx), + offset_of!(VMDynamicFunctionContext, ctx), usize::from(offsets.vmdynamicfunction_import_context_ctx()) ); } diff --git a/lib/runtime/src/vmoffsets.rs b/lib/runtime/src/vmoffsets.rs index 9388d3838..d439a9a81 100644 --- a/lib/runtime/src/vmoffsets.rs +++ b/lib/runtime/src/vmoffsets.rs @@ -110,9 +110,9 @@ impl VMOffsets { } } -/// Offsets for [`VMDynamicFunctionImportContext`]. +/// Offsets for [`VMDynamicFunctionContext`]. /// -/// [`VMDynamicFunctionImportContext`]: crate::vmcontext::VMDynamicFunctionImportContext +/// [`VMDynamicFunctionContext`]: crate::vmcontext::VMDynamicFunctionContext impl VMOffsets { /// The offset of the `address` field. #[allow(clippy::erasing_op)] @@ -126,9 +126,9 @@ impl VMOffsets { 1 * self.pointer_size } - /// Return the size of [`VMDynamicFunctionImportContext`]. + /// Return the size of [`VMDynamicFunctionContext`]. /// - /// [`VMDynamicFunctionImportContext`]: crate::vmcontext::VMDynamicFunctionImportContext + /// [`VMDynamicFunctionContext`]: crate::vmcontext::VMDynamicFunctionContext pub fn size_of_vmdynamicfunction_import_context(&self) -> u8 { 2 * self.pointer_size } diff --git a/lib/wasm-common/src/native.rs b/lib/wasm-common/src/native.rs index 7b0d98657..33a5253f9 100644 --- a/lib/wasm-common/src/native.rs +++ b/lib/wasm-common/src/native.rs @@ -2,6 +2,7 @@ //! easily in Rust, thanks to it's advanced typing system. use crate::types::{FunctionType, Type}; +use crate::values::Value; use std::convert::Infallible; use std::marker::PhantomData; @@ -34,6 +35,15 @@ pub trait NativeWasmType { /// Convert self to i128 binary representation. fn to_binary(self) -> i128; + /// Convert self to a `Value` + fn to_value(self) -> Value + where + Self: std::marker::Sized, + { + let binary = self.to_binary(); + unsafe { Value::read_value_from(&binary, Self::WASM_TYPE) } + } + /// Convert to self from i128 binary representation. fn from_binary(binary: i128) -> Self; } diff --git a/lib/wasm-common/src/values.rs b/lib/wasm-common/src/values.rs index 108961def..e110714aa 100644 --- a/lib/wasm-common/src/values.rs +++ b/lib/wasm-common/src/values.rs @@ -199,6 +199,30 @@ impl From for Value { } } +impl Into for Value { + fn into(self) -> i32 { + self.unwrap_i32() + } +} + +impl Into for Value { + fn into(self) -> i64 { + self.unwrap_i64() + } +} + +impl Into for Value { + fn into(self) -> f32 { + self.unwrap_f32() + } +} + +impl Into for Value { + fn into(self) -> f64 { + self.unwrap_f64() + } +} + // impl From for Value { // fn from(val: T) -> Self { // Self::FuncRef(val) diff --git a/tests/compilers/functions.rs b/tests/compilers/functions.rs index ceae7602a..1d6d5817e 100644 --- a/tests/compilers/functions.rs +++ b/tests/compilers/functions.rs @@ -39,3 +39,32 @@ fn native_function_works_for_wasm() -> Result<()> { assert_eq!(result, 20); Ok(()) } + +fn dynamic_raw_call_no_env() -> anyhow::Result<()> { + let store = get_store(); + let reverse_duplicate = wasmer::Function::new_dynamic( + &store, + &wasmer::FunctionType::new( + vec![ + wasmer::ValType::I32, + wasmer::ValType::I64, + wasmer::ValType::F32, + wasmer::ValType::F64, + ], + vec![wasmer::ValType::F64], + ), + |values| { + Ok(vec![ + Value::F64(values[3].unwrap_f64() * 2.0), + Value::F32(values[2].unwrap_f32() * 2.0), + Value::I64(values[2].unwrap_i64() * 2.0), + Value::I32(values[2].unwrap_i32() * 2.0), + ]) + }, + ); + let reverse_duplicate_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = + reverse_duplicate.native().unwrap(); + let result = reverse_duplicate_native.call(1, 3, 5.0, 7.0)?; + assert_eq!(result, (14.0, 10.0, 6, 2)); + Ok(()) +} diff --git a/tests/compilers/multi_value_imports.rs b/tests/compilers/multi_value_imports.rs index ec75086ab..c095d71ee 100644 --- a/tests/compilers/multi_value_imports.rs +++ b/tests/compilers/multi_value_imports.rs @@ -67,11 +67,12 @@ macro_rules! mvr_test { fn dynamic() -> anyhow::Result<()> { let store = get_store(); let module = get_module(&store)?; + let callback_fn = wasmer::Function::new_dynamic(&store, &wasmer::FunctionType::new(vec![wasmer::ValType::I32], vec![ $( <$result_type>::expected_valtype() ),* ]), dynamic_callback_fn); let instance = wasmer::Instance::new( &module, &wasmer::imports! { "host" => { - "callback_fn" => wasmer::Function::new_dynamic(&store, &wasmer::FunctionType::new(vec![wasmer::ValType::I32], vec![ $( <$result_type>::expected_valtype() ),* ]), dynamic_callback_fn) + "callback_fn" => callback_fn } } )?;