diff --git a/lib/api/src/exports.rs b/lib/api/src/exports.rs index a961b47f5..5fd11bf0b 100644 --- a/lib/api/src/exports.rs +++ b/lib/api/src/exports.rs @@ -1,13 +1,13 @@ use crate::externals::{Extern, Function, Global, Memory, Table}; use crate::import_object::LikeNamespace; use crate::native::NativeFunc; +use crate::WasmTypeList; use indexmap::IndexMap; use std::{ iter::{ExactSizeIterator, FromIterator}, sync::Arc, }; use thiserror::Error; -use wasm_common::WasmTypeList; use wasmer_runtime::Export; /// The `ExportError` can happen when trying to get a specific diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs index 3ae9f982c..bd3bf7dbe 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/externals/function.rs @@ -5,12 +5,14 @@ use crate::types::Val; use crate::FunctionType; use crate::NativeFunc; use crate::RuntimeError; +pub use inner::{HostFunction, WasmTypeList}; +use inner::{WithEnv, WithoutEnv}; use std::cell::RefCell; use std::cmp::max; -use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv}; use wasmer_runtime::{ - wasmer_call_trampoline, Export, ExportFunction, VMCallerCheckedAnyfunc, VMContext, - VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, VMTrampoline, + raise_user_trap, resume_panic, wasmer_call_trampoline, Export, ExportFunction, + VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, + VMTrampoline, }; /// A function defined in the Wasm module @@ -58,7 +60,7 @@ impl Function { Rets: WasmTypeList, Env: Sized + 'static, { - let func: wasm_common::Func = wasm_common::Func::new(func); + let func: inner::Func = inner::Func::new(func); let address = func.address() as *const VMFunctionBody; let vmctx = std::ptr::null_mut() as *mut _ as *mut VMContext; let signature = func.ty(); @@ -143,7 +145,7 @@ impl Function { Rets: WasmTypeList, Env: Sized + 'static, { - let func: wasm_common::Func = wasm_common::Func::new(func); + let func: inner::Func = inner::Func::new(func); let address = func.address() as *const VMFunctionBody; // TODO: We need to refactor the Function context. // Right now is structured as it's always a `VMContext`. However, only @@ -441,8 +443,485 @@ impl VMDynamicFunctionCall for VMDynamicFunctionContext match result { Ok(Ok(())) => {} - Ok(Err(trap)) => wasmer_runtime::raise_user_trap(Box::new(trap)), - Err(panic) => wasmer_runtime::resume_panic(panic), + Ok(Err(trap)) => raise_user_trap(Box::new(trap)), + Err(panic) => resume_panic(panic), + } + } +} + +mod inner { + use std::convert::Infallible; + use std::error::Error; + use std::marker::PhantomData; + use std::panic::{self, AssertUnwindSafe}; + use wasm_common::{FunctionType, NativeWasmType, Type, WasmExternType}; + use wasmer_runtime::{raise_user_trap, resume_panic}; + + /// Represents a list of WebAssembly values. + pub trait WasmTypeList { + /// CStruct type. + type CStruct; + + /// Array of return values. + type Array: AsMut<[i128]>; + + /// Construct `Self` based on an array of returned values. + fn from_array(array: Self::Array) -> Self; + + /// Transforms Rust values into an Array + fn into_array(self) -> Self::Array; + + /// Generates an empty array that will hold the returned values of + /// the WebAssembly function. + fn empty_array() -> Self::Array; + + /// Transforms C values into Rust values. + fn from_c_struct(c_struct: Self::CStruct) -> Self; + + /// Transforms Rust values into C values. + fn into_c_struct(self) -> Self::CStruct; + + /// Get types of the current values. + fn wasm_types() -> &'static [Type]; + } + + /// Represents a TrapEarly type. + pub trait TrapEarly + where + Rets: WasmTypeList, + { + /// The error type for this trait. + type Error: Error + Sync + Send + 'static; + + /// Get returns or error result. + fn report(self) -> Result; + } + + impl TrapEarly for Rets + where + Rets: WasmTypeList, + { + type Error = Infallible; + + fn report(self) -> Result { + Ok(self) + } + } + + impl TrapEarly for Result + where + Rets: WasmTypeList, + E: Error + Sync + Send + 'static, + { + type Error = E; + + fn report(self) -> Self { + self + } + } + + /// Empty trait to specify the kind of `HostFunction`: With or + /// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the + /// `ImplicitVmCtx` structures. + /// + /// This trait is never aimed to be used by a user. It is used by the + /// trait system to automatically generate an appropriate `wrap` + /// function. + #[doc(hidden)] + pub trait HostFunctionKind {} + + /// An empty struct to help Rust typing to determine + /// when a `HostFunction` doesn't take an Environment + pub struct WithEnv {} + + impl HostFunctionKind for WithEnv {} + + /// An empty struct to help Rust typing to determine + /// when a `HostFunction` takes an Environment + pub struct WithoutEnv {} + + impl HostFunctionKind for WithoutEnv {} + + /// Represents a function that can be converted to a `vm::Func` + /// (function pointer) that can be called within WebAssembly. + pub trait HostFunction + where + Args: WasmTypeList, + Rets: WasmTypeList, + Kind: HostFunctionKind, + T: Sized, + Self: Sized, + { + /// Convert to function pointer. + fn to_raw(self) -> *const FunctionBody; + } + + #[repr(transparent)] + pub struct FunctionBody(*mut u8); + + /// Represents a function that can be used by WebAssembly. + #[derive(Clone, Debug, Hash, PartialEq, Eq)] + pub struct Func { + address: *const FunctionBody, + _phantom: PhantomData<(Args, Rets)>, + } + + unsafe impl Send for Func {} + + impl Func + where + Args: WasmTypeList, + Rets: WasmTypeList, + { + /// Creates a new `Func`. + pub fn new(func: F) -> Self + where + F: HostFunction, + T: HostFunctionKind, + E: Sized, + { + Self { + address: func.to_raw(), + _phantom: PhantomData, + } + } + + /// Get the type of the Func + pub fn ty(&self) -> FunctionType { + FunctionType::new(Args::wasm_types(), Rets::wasm_types()) + } + + /// Get the address of the Func + pub fn address(&self) -> *const FunctionBody { + self.address + } + } + + impl WasmTypeList for Infallible { + type CStruct = Self; + type Array = [i128; 0]; + + fn from_array(_: Self::Array) -> Self { + unreachable!() + } + + fn into_array(self) -> Self::Array { + [] + } + + fn empty_array() -> Self::Array { + unreachable!() + } + + fn from_c_struct(_: Self::CStruct) -> Self { + unreachable!() + } + + fn into_c_struct(self) -> Self::CStruct { + unreachable!() + } + + fn wasm_types() -> &'static [Type] { + &[] + } + } + + macro_rules! impl_traits { + ( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => { + /// Struct for typed funcs. + #[repr($repr)] + pub struct $struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* ) + where + $( $x: WasmExternType ),*; + + #[allow(unused_parens, dead_code)] + impl< $( $x ),* > WasmTypeList for ( $( $x ),* ) + where + $( $x: WasmExternType ),* + { + type CStruct = $struct_name<$( $x ),*>; + + type Array = [i128; count_idents!( $( $x ),* )]; + + fn from_array(array: Self::Array) -> Self { + #[allow(non_snake_case)] + let [ $( $x ),* ] = array; + + ( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* ) + } + + fn into_array(self) -> Self::Array { + #[allow(non_snake_case)] + let ( $( $x ),* ) = self; + [ $( WasmExternType::to_native($x).to_binary() ),* ] + } + + fn empty_array() -> Self::Array { + [0; count_idents!( $( $x ),* )] + } + + fn from_c_struct(c_struct: Self::CStruct) -> Self { + #[allow(non_snake_case)] + let $struct_name ( $( $x ),* ) = c_struct; + + ( $( WasmExternType::from_native($x) ),* ) + } + + #[allow(unused_parens, non_snake_case)] + fn into_c_struct(self) -> Self::CStruct { + let ( $( $x ),* ) = self; + + $struct_name ( $( WasmExternType::to_native($x) ),* ) + } + + fn wasm_types() -> &'static [Type] { + &[$( $x::Native::WASM_TYPE ),*] + } + } + + #[allow(unused_parens)] + impl< $( $x, )* Rets, Trap, FN > HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()> for FN + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn($( $x , )*) -> Trap + 'static + Send, + { + #[allow(non_snake_case)] + fn to_raw(self) -> *const FunctionBody { + extern fn wrap<$( $x, )* Rets, Trap, FN>( _: usize, $($x: $x::Native, )* ) -> Rets::CStruct + where + Rets: WasmTypeList, + Trap: TrapEarly, + $( $x: WasmExternType, )* + FN: Fn( $( $x ),* ) -> Trap + 'static + { + let f: &FN = unsafe { &*(&() as *const () as *const FN) }; + let result = panic::catch_unwind(AssertUnwindSafe(|| { + f( $( WasmExternType::from_native($x) ),* ).report() + })); + + match result { + Ok(Ok(result)) => return result.into_c_struct(), + Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) }, + Err(panic) => unsafe { resume_panic(panic) }, + } + } + + wrap::<$( $x, )* Rets, Trap, Self> as *const FunctionBody + } + } + + #[allow(unused_parens)] + impl< $( $x, )* Rets, Trap, T, FN > HostFunction<( $( $x ),* ), Rets, WithEnv, T> for FN + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + T: Sized, + FN: Fn(&mut T, $( $x , )*) -> Trap + 'static + Send + { + #[allow(non_snake_case)] + fn to_raw(self) -> *const FunctionBody { + extern fn wrap<$( $x, )* Rets, Trap, T, FN>( ctx: &mut T, $($x: $x::Native, )* ) -> Rets::CStruct + where + Rets: WasmTypeList, + Trap: TrapEarly, + $( $x: WasmExternType, )* + T: Sized, + FN: Fn(&mut T, $( $x ),* ) -> Trap + 'static + { + let f: &FN = unsafe { &*(&() as *const () as *const FN) }; + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + f(ctx, $( WasmExternType::from_native($x) ),* ).report() + })); + + match result { + Ok(Ok(result)) => return result.into_c_struct(), + Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) }, + Err(panic) => unsafe { resume_panic(panic) }, + } + } + + wrap::<$( $x, )* Rets, Trap, T, Self> as *const FunctionBody + } + } + }; + } + + macro_rules! count_idents { + ( $($idents:ident),* ) => {{ + #[allow(dead_code, non_camel_case_types)] + enum Idents { $($idents,)* __CountIdentsLast } + const COUNT: usize = Idents::__CountIdentsLast as usize; + COUNT + }}; + } + + //impl_traits!([C] S0,); + //impl_traits!([transparent] S1, A1); + impl_traits!([C] S2, A1, A2); + impl_traits!([C] S3, A1, A2, A3); + impl_traits!([C] S4, A1, A2, A3, A4); + impl_traits!([C] S5, A1, A2, A3, A4, A5); + impl_traits!([C] S6, A1, A2, A3, A4, A5, A6); + impl_traits!([C] S7, A1, A2, A3, A4, A5, A6, A7); + impl_traits!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); + impl_traits!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); + impl_traits!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); + impl_traits!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); + impl_traits!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); + impl_traits!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); + impl_traits!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); + impl_traits!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); + impl_traits!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); + impl_traits!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); + impl_traits!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); + impl_traits!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); + impl_traits!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); + impl_traits!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); + impl_traits!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); + impl_traits!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); + impl_traits!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); + impl_traits!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); + impl_traits!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); + + #[cfg(test)] + mod test_wasm_type_list { + use super::*; + use crate::types::Type; + // WasmTypeList + + #[test] + fn test_simple_values() { + // Simple values + assert_eq!(::wasm_types(), [Type::I32]); + assert_eq!(::wasm_types(), [Type::I64]); + assert_eq!(::wasm_types(), [Type::F32]); + assert_eq!(::wasm_types(), [Type::F64]); + + // Multi values + assert_eq!(<(i32, i32)>::wasm_types(), [Type::I32, Type::I32]); + assert_eq!(<(i64, i64)>::wasm_types(), [Type::I64, Type::I64]); + assert_eq!(<(f32, f32)>::wasm_types(), [Type::F32, Type::F32]); + assert_eq!(<(f64, f64)>::wasm_types(), [Type::F64, Type::F64]); + + // Mixed values + // assert_eq!(<(i32, i64, f32, f64)>::wasm_types(), [Type::I32, Type::I64, Type::F32, Type::F64]); + } + + #[test] + fn test_empty_array() { + assert_eq!(<()>::empty_array().len(), 0); + assert_eq!(::empty_array().len(), 1); + assert_eq!(<(i32, i64)>::empty_array().len(), 2); + } + + // #[test] + // fn test_from_array() { + // assert_eq!(<()>::from_array([]), ()); + // assert_eq!(<(i32)>::from_array([1]), (1)); + // assert_eq!(<(i32, i32)>::from_array([1, 1]), (1, 1)); + // // This doesn't work + // // assert_eq!(<(i32, i64, f32, f64)>::from_array([1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]), (1, 2, 3.1f32, 4.2f64)); + // } + + // #[test] + // fn test_into_array() { + // assert_eq!(().into_array(), []); + // assert_eq!((1).into_array(), [1]); + // assert_eq!((1, 2).into_array(), [1, 2]); + // assert_eq!((1, 2, 3).into_array(), [1, 2, 3]); + // // This doesn't work + // // assert_eq!(<(i32, i64, f32, f64)>::from_array([1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]), (1, 2, 3.1f32, 4.2f64)); + // } + + #[test] + fn test_into_c_struct() { + // assert_eq!(<()>::into_c_struct(), &[]); + } + } + + #[allow(non_snake_case)] + #[cfg(test)] + mod test_func { + use super::*; + use crate::types::Type; + use std::ptr; + // WasmTypeList + + fn func() {} + fn func__i32() -> i32 { + 0 + } + fn func_i32(_a: i32) {} + fn func_i32__i32(a: i32) -> i32 { + a * 2 + } + fn func_i32_i32__i32(a: i32, b: i32) -> i32 { + a + b + } + fn func_i32_i32__i32_i32(a: i32, b: i32) -> (i32, i32) { + (a, b) + } + fn func_f32_i32__i32_f32(a: f32, b: i32) -> (i32, f32) { + (b, a) + } + + #[test] + fn test_function_types() { + assert_eq!(Func::new(func).ty(), FunctionType::new(vec![], vec![])); + assert_eq!( + Func::new(func__i32).ty(), + FunctionType::new(vec![], vec![Type::I32]) + ); + assert_eq!( + Func::new(func_i32).ty(), + FunctionType::new(vec![Type::I32], vec![]) + ); + assert_eq!( + Func::new(func_i32__i32).ty(), + FunctionType::new(vec![Type::I32], vec![Type::I32]) + ); + assert_eq!( + Func::new(func_i32_i32__i32).ty(), + FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]) + ); + assert_eq!( + Func::new(func_i32_i32__i32_i32).ty(), + FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]) + ); + assert_eq!( + Func::new(func_f32_i32__i32_f32).ty(), + FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32]) + ); + } + + #[test] + fn test_function_pointer() { + let f = Func::new(func_i32__i32); + let function = unsafe { + std::mem::transmute::<*const FunctionBody, fn(usize, i32) -> i32>(f.address) + }; + assert_eq!(function(0, 3), 6); + } + + #[test] + fn test_function_call() { + let f = Func::new(func_i32__i32); + let x = |args: <(i32, i32) as WasmTypeList>::Array, + rets: &mut <(i32, i32) as WasmTypeList>::Array| { + let result = func_i32_i32__i32_i32(args[0] as _, args[1] as _); + rets[0] = result.0 as _; + rets[1] = result.1 as _; + }; + let mut rets = <(i64, i64)>::empty_array(); + x([20, 10], &mut rets); + // panic!("Rets: {:?}",rets); + let mut rets = <(i64)>::empty_array(); + // let result = f.call([1], &mut rets); + // assert_eq!(result.is_err(), true); } } } diff --git a/lib/api/src/externals/mod.rs b/lib/api/src/externals/mod.rs index 881bae009..fc6982ad8 100644 --- a/lib/api/src/externals/mod.rs +++ b/lib/api/src/externals/mod.rs @@ -3,7 +3,7 @@ mod global; mod memory; mod table; -pub use self::function::Function; +pub use self::function::{Function, HostFunction, WasmTypeList}; pub use self::global::Global; pub use self::memory::Memory; pub use self::table::Table; diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 03c7d5b33..feb65852f 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -18,7 +18,7 @@ mod types; mod utils; pub use crate::exports::{ExportError, Exportable, Exports}; -pub use crate::externals::{Extern, Function, Global, Memory, Table}; +pub use crate::externals::{Extern, Function, Global, HostFunction, Memory, Table, WasmTypeList}; pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace}; pub use crate::instance::Instance; pub use crate::memory_view::{Atomically, MemoryView}; @@ -37,8 +37,8 @@ pub use crate::utils::is_wasm; pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST}; pub use wasm_common::{ - Bytes, GlobalInit, LocalFunctionIndex, Pages, ValueType, WasmExternType, WasmTypeList, - WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, + Bytes, GlobalInit, LocalFunctionIndex, Pages, ValueType, WasmExternType, WASM_MAX_PAGES, + WASM_MIN_PAGES, WASM_PAGE_SIZE, }; #[cfg(feature = "compiler")] pub use wasmer_compiler::CompilerConfig; diff --git a/lib/api/src/native.rs b/lib/api/src/native.rs index 9ca966db1..c35d9e7b6 100644 --- a/lib/api/src/native.rs +++ b/lib/api/src/native.rs @@ -13,9 +13,9 @@ use crate::externals::function::{ FunctionDefinition, HostFunctionDefinition, VMDynamicFunction, VMDynamicFunctionWithEnv, VMDynamicFunctionWithoutEnv, WasmFunctionDefinition, }; -use crate::{Function, FunctionType, RuntimeError, Store}; +use crate::{Function, FunctionType, RuntimeError, Store, WasmTypeList}; use std::panic::{catch_unwind, AssertUnwindSafe}; -use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList}; +use wasm_common::{NativeWasmType, WasmExternType}; use wasmer_runtime::{ ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, }; diff --git a/lib/wasm-common/src/lib.rs b/lib/wasm-common/src/lib.rs index 7ce591e25..b2227f8f8 100644 --- a/lib/wasm-common/src/lib.rs +++ b/lib/wasm-common/src/lib.rs @@ -43,10 +43,7 @@ pub use crate::indexes::{ LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, SignatureIndex, TableIndex, }; -pub use crate::native::{ - Func, HostFunction, NativeWasmType, ValueType, WasmExternType, WasmTypeList, WithEnv, - WithoutEnv, -}; +pub use crate::native::{NativeWasmType, ValueType, WasmExternType}; pub use crate::r#ref::{ExternRef, HostInfo, HostRef}; pub use crate::units::{Bytes, Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE}; pub use crate::values::Value; diff --git a/lib/wasm-common/src/native.rs b/lib/wasm-common/src/native.rs index ba444fe40..196be00f2 100644 --- a/lib/wasm-common/src/native.rs +++ b/lib/wasm-common/src/native.rs @@ -1,11 +1,8 @@ //! This module permits to create native functions //! easily in Rust, thanks to its advanced typing system. -use crate::types::{FunctionType, Type}; +use crate::types::Type; use crate::values::Value; -use std::convert::Infallible; -use std::marker::PhantomData; -use std::panic::{self, AssertUnwindSafe}; /// `NativeWasmType` represents a native Wasm type. /// It uses the Rust Type system to automatically detect the @@ -244,470 +241,3 @@ macro_rules! impl_value_type_for { } impl_value_type_for!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); - -/// Represents a list of WebAssembly values. -pub trait WasmTypeList { - /// CStruct type. - type CStruct; - - /// Array of return values. - type Array: AsMut<[i128]>; - - /// Construct `Self` based on an array of returned values. - fn from_array(array: Self::Array) -> Self; - - /// Transforms Rust values into an Array - fn into_array(self) -> Self::Array; - - /// Generates an empty array that will hold the returned values of - /// the WebAssembly function. - fn empty_array() -> Self::Array; - - /// Transforms C values into Rust values. - fn from_c_struct(c_struct: Self::CStruct) -> Self; - - /// Transforms Rust values into C values. - fn into_c_struct(self) -> Self::CStruct; - - /// Get types of the current values. - fn wasm_types() -> &'static [Type]; -} - -/// Represents a TrapEarly type. -pub trait TrapEarly -where - Rets: WasmTypeList, -{ - /// The error type for this trait. - type Error: Send + 'static; - - /// Get returns or error result. - fn report(self) -> Result; -} - -impl TrapEarly for Rets -where - Rets: WasmTypeList, -{ - type Error = Infallible; - - fn report(self) -> Result { - Ok(self) - } -} - -impl TrapEarly for Result -where - Rets: WasmTypeList, - E: Send + 'static, -{ - type Error = E; - - fn report(self) -> Self { - self - } -} - -/// Empty trait to specify the kind of `HostFunction`: With or -/// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the -/// `ImplicitVmCtx` structures. -/// -/// This trait is never aimed to be used by a user. It is used by the -/// trait system to automatically generate an appropriate `wrap` -/// function. -#[doc(hidden)] -pub trait HostFunctionKind {} - -/// An empty struct to help Rust typing to determine -/// when a `HostFunction` doesn't take an Environment -pub struct WithEnv {} - -impl HostFunctionKind for WithEnv {} - -/// An empty struct to help Rust typing to determine -/// when a `HostFunction` takes an Environment -pub struct WithoutEnv {} - -impl HostFunctionKind for WithoutEnv {} - -/// Represents a function that can be converted to a `vm::Func` -/// (function pointer) that can be called within WebAssembly. -pub trait HostFunction -where - Args: WasmTypeList, - Rets: WasmTypeList, - Kind: HostFunctionKind, - T: Sized, - Self: Sized, -{ - /// Convert to function pointer. - fn to_raw(self) -> *const FunctionBody; -} - -#[repr(transparent)] -pub struct FunctionBody(*mut u8); - -/// Represents a function that can be used by WebAssembly. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct Func { - address: *const FunctionBody, - _phantom: PhantomData<(Args, Rets)>, -} - -unsafe impl Send for Func {} - -impl Func -where - Args: WasmTypeList, - Rets: WasmTypeList, -{ - /// Creates a new `Func`. - pub fn new(func: F) -> Self - where - F: HostFunction, - T: HostFunctionKind, - E: Sized, - { - Self { - address: func.to_raw(), - _phantom: PhantomData, - } - } - - /// Get the type of the Func - pub fn ty(&self) -> FunctionType { - FunctionType::new(Args::wasm_types(), Rets::wasm_types()) - } - - /// Get the address of the Func - pub fn address(&self) -> *const FunctionBody { - self.address - } -} - -impl WasmTypeList for Infallible { - type CStruct = Self; - type Array = [i128; 0]; - - fn from_array(_: Self::Array) -> Self { - unreachable!() - } - - fn into_array(self) -> Self::Array { - [] - } - - fn empty_array() -> Self::Array { - unreachable!() - } - - fn from_c_struct(_: Self::CStruct) -> Self { - unreachable!() - } - - fn into_c_struct(self) -> Self::CStruct { - unreachable!() - } - - fn wasm_types() -> &'static [Type] { - &[] - } -} - -macro_rules! impl_traits { - ( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => { - /// Struct for typed funcs. - #[repr($repr)] - pub struct $struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* ) - where - $( $x: WasmExternType ),*; - - #[allow(unused_parens, dead_code)] - impl< $( $x ),* > WasmTypeList for ( $( $x ),* ) - where - $( $x: WasmExternType ),* - { - type CStruct = $struct_name<$( $x ),*>; - - type Array = [i128; count_idents!( $( $x ),* )]; - - fn from_array(array: Self::Array) -> Self { - #[allow(non_snake_case)] - let [ $( $x ),* ] = array; - - ( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* ) - } - - fn into_array(self) -> Self::Array { - #[allow(non_snake_case)] - let ( $( $x ),* ) = self; - [ $( WasmExternType::to_native($x).to_binary() ),* ] - } - - fn empty_array() -> Self::Array { - [0; count_idents!( $( $x ),* )] - } - - fn from_c_struct(c_struct: Self::CStruct) -> Self { - #[allow(non_snake_case)] - let $struct_name ( $( $x ),* ) = c_struct; - - ( $( WasmExternType::from_native($x) ),* ) - } - - #[allow(unused_parens, non_snake_case)] - fn into_c_struct(self) -> Self::CStruct { - let ( $( $x ),* ) = self; - - $struct_name ( $( WasmExternType::to_native($x) ),* ) - } - - fn wasm_types() -> &'static [Type] { - &[$( $x::Native::WASM_TYPE ),*] - } - } - - #[allow(unused_parens)] - impl< $( $x, )* Rets, Trap, FN > HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()> for FN - where - $( $x: WasmExternType, )* - Rets: WasmTypeList, - Trap: TrapEarly, - FN: Fn($( $x , )*) -> Trap + 'static + Send, - { - #[allow(non_snake_case)] - fn to_raw(self) -> *const FunctionBody { - extern fn wrap<$( $x, )* Rets, Trap, FN>( _: usize, $($x: $x::Native, )* ) -> Rets::CStruct - where - Rets: WasmTypeList, - Trap: TrapEarly, - $( $x: WasmExternType, )* - FN: Fn( $( $x ),* ) -> Trap + 'static - { - let f: &FN = unsafe { &*(&() as *const () as *const FN) }; - let result = panic::catch_unwind(AssertUnwindSafe(|| { - f( $( WasmExternType::from_native($x) ),* ).report() - })); - - match result { - Ok(Ok(result)) => return result.into_c_struct(), - Ok(Err(trap)) => wasmer_runtime::raise_user_trap(Box::new(trap)), - Err(panic) => wasmer_runtime::resume_panic(panic), - } - } - - wrap::<$( $x, )* Rets, Trap, Self> as *const FunctionBody - } - } - - #[allow(unused_parens)] - impl< $( $x, )* Rets, Trap, T, FN > HostFunction<( $( $x ),* ), Rets, WithEnv, T> for FN - where - $( $x: WasmExternType, )* - Rets: WasmTypeList, - Trap: TrapEarly, - T: Sized, - FN: Fn(&mut T, $( $x , )*) -> Trap + 'static + Send - { - #[allow(non_snake_case)] - fn to_raw(self) -> *const FunctionBody { - extern fn wrap<$( $x, )* Rets, Trap, T, FN>( ctx: &mut T, $($x: $x::Native, )* ) -> Rets::CStruct - where - Rets: WasmTypeList, - Trap: TrapEarly, - $( $x: WasmExternType, )* - T: Sized, - FN: Fn(&mut T, $( $x ),* ) -> Trap + 'static - { - let f: &FN = unsafe { &*(&() as *const () as *const FN) }; - - let result = panic::catch_unwind(AssertUnwindSafe(|| { - f(ctx, $( WasmExternType::from_native($x) ),* ).report() - })); - - match result { - Ok(Ok(result)) => return result.into_c_struct(), - Ok(Err(trap)) => wasmer_runtime::raise_user_trap(Box::new(trap)), - Err(panic) => wasmer_runtime::resume_panic(panic), - } - } - - wrap::<$( $x, )* Rets, Trap, T, Self> as *const FunctionBody - } - } - }; -} - -macro_rules! count_idents { - ( $($idents:ident),* ) => {{ - #[allow(dead_code, non_camel_case_types)] - enum Idents { $($idents,)* __CountIdentsLast } - const COUNT: usize = Idents::__CountIdentsLast as usize; - COUNT - }}; -} - -impl_traits!([C] S0,); -impl_traits!([transparent] S1, A1); -impl_traits!([C] S2, A1, A2); -impl_traits!([C] S3, A1, A2, A3); -impl_traits!([C] S4, A1, A2, A3, A4); -impl_traits!([C] S5, A1, A2, A3, A4, A5); -impl_traits!([C] S6, A1, A2, A3, A4, A5, A6); -impl_traits!([C] S7, A1, A2, A3, A4, A5, A6, A7); -impl_traits!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); -impl_traits!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); -impl_traits!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); -impl_traits!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); -impl_traits!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); -impl_traits!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); -impl_traits!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); -impl_traits!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); -impl_traits!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); -impl_traits!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); -impl_traits!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); -impl_traits!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); -impl_traits!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); -// impl_traits!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); -// impl_traits!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); -// impl_traits!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); -// impl_traits!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); -// impl_traits!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); -// impl_traits!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); - -#[cfg(test)] -mod test_wasm_type_list { - use super::*; - use crate::types::Type; - // WasmTypeList - - #[test] - fn test_simple_values() { - // Simple values - assert_eq!(::wasm_types(), [Type::I32]); - assert_eq!(::wasm_types(), [Type::I64]); - assert_eq!(::wasm_types(), [Type::F32]); - assert_eq!(::wasm_types(), [Type::F64]); - - // Multi values - assert_eq!(<(i32, i32)>::wasm_types(), [Type::I32, Type::I32]); - assert_eq!(<(i64, i64)>::wasm_types(), [Type::I64, Type::I64]); - assert_eq!(<(f32, f32)>::wasm_types(), [Type::F32, Type::F32]); - assert_eq!(<(f64, f64)>::wasm_types(), [Type::F64, Type::F64]); - - // Mixed values - // assert_eq!(<(i32, i64, f32, f64)>::wasm_types(), [Type::I32, Type::I64, Type::F32, Type::F64]); - } - - #[test] - fn test_empty_array() { - assert_eq!(<()>::empty_array().len(), 0); - assert_eq!(::empty_array().len(), 1); - assert_eq!(<(i32, i64)>::empty_array().len(), 2); - } - - // #[test] - // fn test_from_array() { - // assert_eq!(<()>::from_array([]), ()); - // assert_eq!(<(i32)>::from_array([1]), (1)); - // assert_eq!(<(i32, i32)>::from_array([1, 1]), (1, 1)); - // // This doesn't work - // // assert_eq!(<(i32, i64, f32, f64)>::from_array([1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]), (1, 2, 3.1f32, 4.2f64)); - // } - - // #[test] - // fn test_into_array() { - // assert_eq!(().into_array(), []); - // assert_eq!((1).into_array(), [1]); - // assert_eq!((1, 2).into_array(), [1, 2]); - // assert_eq!((1, 2, 3).into_array(), [1, 2, 3]); - // // This doesn't work - // // assert_eq!(<(i32, i64, f32, f64)>::from_array([1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]), (1, 2, 3.1f32, 4.2f64)); - // } - - #[test] - fn test_into_c_struct() { - // assert_eq!(<()>::into_c_struct(), &[]); - } -} - -#[allow(non_snake_case)] -#[cfg(test)] -mod test_func { - use super::*; - use crate::types::Type; - use std::ptr; - // WasmTypeList - - fn func() {} - fn func__i32() -> i32 { - 0 - } - fn func_i32(_a: i32) {} - fn func_i32__i32(a: i32) -> i32 { - a * 2 - } - fn func_i32_i32__i32(a: i32, b: i32) -> i32 { - a + b - } - fn func_i32_i32__i32_i32(a: i32, b: i32) -> (i32, i32) { - (a, b) - } - fn func_f32_i32__i32_f32(a: f32, b: i32) -> (i32, f32) { - (b, a) - } - - #[test] - fn test_function_types() { - assert_eq!(Func::new(func).ty(), FunctionType::new(vec![], vec![])); - assert_eq!( - Func::new(func__i32).ty(), - FunctionType::new(vec![], vec![Type::I32]) - ); - assert_eq!( - Func::new(func_i32).ty(), - FunctionType::new(vec![Type::I32], vec![]) - ); - assert_eq!( - Func::new(func_i32__i32).ty(), - FunctionType::new(vec![Type::I32], vec![Type::I32]) - ); - assert_eq!( - Func::new(func_i32_i32__i32).ty(), - FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]) - ); - assert_eq!( - Func::new(func_i32_i32__i32_i32).ty(), - FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]) - ); - assert_eq!( - Func::new(func_f32_i32__i32_f32).ty(), - FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32]) - ); - } - - #[test] - fn test_function_pointer() { - let f = Func::new(func_i32__i32); - let function = - unsafe { std::mem::transmute::<*const FunctionBody, fn(usize, i32) -> i32>(f.address) }; - assert_eq!(function(0, 3), 6); - } - - #[test] - fn test_function_call() { - let f = Func::new(func_i32__i32); - let x = |args: <(i32, i32) as WasmTypeList>::Array, - rets: &mut <(i32, i32) as WasmTypeList>::Array| { - let result = func_i32_i32__i32_i32(args[0] as _, args[1] as _); - rets[0] = result.0 as _; - rets[1] = result.1 as _; - }; - let mut rets = <(i64, i64)>::empty_array(); - x([20, 10], &mut rets); - // panic!("Rets: {:?}",rets); - let mut rets = <(i64)>::empty_array(); - // let result = f.call([1], &mut rets); - // assert_eq!(result.is_err(), true); - } -}