//! 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("function_name")?; //! let add_one_native: NativeFunc = add_one.native().unwrap(); //! ``` use std::marker::PhantomData; use crate::externals::function::{ DynamicFunctionWithEnv, DynamicFunctionWithoutEnv, FunctionDefinition, HostFunctionDefinition, VMDynamicFunction, WasmFunctionDefinition, }; use crate::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList}; use std::panic::{catch_unwind, AssertUnwindSafe}; use wasmer_engine::ExportFunction; use wasmer_types::NativeWasmType; use wasmer_vm::{VMDynamicFunctionContext, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind}; /// A WebAssembly function that can be called natively /// (using the Native ABI). #[derive(Clone)] pub struct NativeFunc { definition: FunctionDefinition, store: Store, exported: ExportFunction, _phantom: PhantomData<(Args, Rets)>, } unsafe impl Send for NativeFunc {} impl NativeFunc where Args: WasmTypeList, Rets: WasmTypeList, { pub(crate) fn new( store: Store, exported: ExportFunction, definition: FunctionDefinition, ) -> Self { Self { definition, store, exported, _phantom: PhantomData, } } pub(crate) fn vmctx(&self) -> VMFunctionEnvironment { self.exported.vm_function.vmctx } pub(crate) fn address(&self) -> *const VMFunctionBody { self.exported.vm_function.address } pub(crate) fn arg_kind(&self) -> VMFunctionKind { self.exported.vm_function.kind } } /* impl From<&NativeFunc> for VMExportFunction where Args: WasmTypeList, Rets: WasmTypeList, { fn from(other: &NativeFunc) -> Self { let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types()); Self { address: other.address, vmctx: other.vmctx, signature, kind: other.arg_kind, call_trampoline: None, instance_ref: None, } } }*/ impl From<&NativeFunc> for ExportFunction where Args: WasmTypeList, Rets: WasmTypeList, { fn from(other: &NativeFunc) -> Self { other.exported.clone() } } impl From> for Function where Args: WasmTypeList, Rets: WasmTypeList, { fn from(other: NativeFunc) -> Self { Self { store: other.store, definition: other.definition, exported: other.exported, } } } macro_rules! impl_native_traits { ( $( $x:ident ),* ) => { #[allow(unused_parens, non_snake_case)] impl<$( $x , )* Rets> NativeFunc<( $( $x ),* ), Rets> where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, { /// Call the typed func and return results. pub fn call(&self, $( $x: $x, )* ) -> Result { match self.definition { FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }) => { // TODO: when `const fn` related features mature more, we can declare a single array // of the correct size here. let mut params_list = [ $( $x.to_native().to_binary() ),* ]; let mut rets_list_array = Rets::empty_array(); let rets_list = rets_list_array.as_mut(); let using_rets_array; let args_rets: &mut [i128] = if params_list.len() > rets_list.len() { using_rets_array = false; params_list.as_mut() } else { using_rets_array = true; for (i, &arg) in params_list.iter().enumerate() { rets_list[i] = arg; } rets_list.as_mut() }; unsafe { wasmer_vm::wasmer_call_trampoline( self.vmctx(), trampoline, self.address(), args_rets.as_mut_ptr() as *mut u8, ) }?; let num_rets = rets_list.len(); if !using_rets_array && num_rets > 0 { let src_pointer = params_list.as_ptr(); let rets_list = &mut rets_list_array.as_mut()[0] as *mut i128; unsafe { // TODO: we can probably remove this copy by doing some clever `transmute`s. // we know it's not overlapping because `using_rets_array` is false std::ptr::copy_nonoverlapping(src_pointer, rets_list, num_rets); } } Ok(Rets::from_array(rets_list_array)) // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead: // but we can't currently detect whether that's safe. // // let results = unsafe { // wasmer_vm::catch_traps_with_result(self.vmctx, || { // let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address()); // // We always pass the vmctx // f( self.vmctx, $( $x, )* ) // }).map_err(RuntimeError::from_trap)? // }; // Ok(Rets::from_c_struct(results)) } FunctionDefinition::Host(HostFunctionDefinition { has_env }) => { match self.arg_kind() { VMFunctionKind::Static => { let results = catch_unwind(AssertUnwindSafe(|| unsafe { let f = std::mem::transmute::<_, unsafe extern "C" fn( VMFunctionEnvironment, $( $x, )*) -> Rets::CStruct>(self.address()); // We always pass the vmctx f( self.vmctx(), $( $x, )* ) })).map_err(|e| RuntimeError::new(format!("{:?}", e)))?; Ok(Rets::from_c_struct(results)) }, VMFunctionKind::Dynamic => { let params_list = [ $( $x.to_native().to_value() ),* ]; let results = if !has_env { type VMContextWithoutEnv = VMDynamicFunctionContext; unsafe { let ctx = self.vmctx().host_env as *mut VMContextWithoutEnv; (*ctx).ctx.call(¶ms_list)? } } else { type VMContextWithEnv = VMDynamicFunctionContext>; unsafe { let ctx = self.vmctx().host_env as *mut VMContextWithEnv; (*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)); } } Ok(Rets::from_array(rets_list_array)) } } }, } } } #[allow(unused_parens)] impl<'a, $( $x, )* Rets> crate::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for NativeFunc<( $( $x ),* ), Rets> where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, { fn get_self_from_extern_with_generics(_extern: &crate::externals::Extern) -> Result { use crate::exports::Exportable; crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::exports::ExportError::IncompatibleType) } } }; } impl_native_traits!(); impl_native_traits!(A1); impl_native_traits!(A1, A2); impl_native_traits!(A1, A2, A3); impl_native_traits!(A1, A2, A3, A4); impl_native_traits!(A1, A2, A3, A4, A5); impl_native_traits!(A1, A2, A3, A4, A5, A6); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); impl_native_traits!( A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18 ); impl_native_traits!( A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19 ); impl_native_traits!( A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20 );