!temp Move HostFunction, Func & co. into wasmer.

Because `Func` needs an access to the runtime API (`wasmer-runtime`)
to trap properly, either we move parts of `wasmer-runtime` to
`wasm-common`, or we move parts of `wasm-common` into `wasmer`. I
decided to go with the second approach since `wasmer` is the only
crate to use `HostFunction` & co. It's not “common” by definition, and
it's way easier (for the moment).
This commit is contained in:
Ivan Enderlin
2020-06-19 15:20:28 +02:00
parent 4afdd94d7a
commit 5e26d9a74b
7 changed files with 495 additions and 489 deletions

View File

@@ -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

View File

@@ -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<Args, Rets> = wasm_common::Func::new(func);
let func: inner::Func<Args, Rets> = 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<Args, Rets> = wasm_common::Func::new(func);
let func: inner::Func<Args, Rets> = 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<T: VMDynamicFunction> VMDynamicFunctionCall<T> 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<Rets>
where
Rets: WasmTypeList,
{
/// The error type for this trait.
type Error: Error + Sync + Send + 'static;
/// Get returns or error result.
fn report(self) -> Result<Rets, Self::Error>;
}
impl<Rets> TrapEarly<Rets> for Rets
where
Rets: WasmTypeList,
{
type Error = Infallible;
fn report(self) -> Result<Self, Infallible> {
Ok(self)
}
}
impl<Rets, E> TrapEarly<Rets> for Result<Rets, E>
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<Args, Rets, Kind, T>
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<Args = (), Rets = ()> {
address: *const FunctionBody,
_phantom: PhantomData<(Args, Rets)>,
}
unsafe impl<Args, Rets> Send for Func<Args, Rets> {}
impl<Args, Rets> Func<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
/// Creates a new `Func`.
pub fn new<F, T, E>(func: F) -> Self
where
F: HostFunction<Args, Rets, T, E>,
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<Rets>,
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<Rets>,
$( $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<Rets>,
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<Rets>,
$( $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!(<i32>::wasm_types(), [Type::I32]);
assert_eq!(<i64>::wasm_types(), [Type::I64]);
assert_eq!(<f32>::wasm_types(), [Type::F32]);
assert_eq!(<f64>::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!(<i32>::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);
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -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<Rets>
where
Rets: WasmTypeList,
{
/// The error type for this trait.
type Error: Send + 'static;
/// Get returns or error result.
fn report(self) -> Result<Rets, Self::Error>;
}
impl<Rets> TrapEarly<Rets> for Rets
where
Rets: WasmTypeList,
{
type Error = Infallible;
fn report(self) -> Result<Self, Infallible> {
Ok(self)
}
}
impl<Rets, E> TrapEarly<Rets> for Result<Rets, E>
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<Args, Rets, Kind, T>
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<Args = (), Rets = ()> {
address: *const FunctionBody,
_phantom: PhantomData<(Args, Rets)>,
}
unsafe impl<Args, Rets> Send for Func<Args, Rets> {}
impl<Args, Rets> Func<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
/// Creates a new `Func`.
pub fn new<F, T, E>(func: F) -> Self
where
F: HostFunction<Args, Rets, T, E>,
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<Rets>,
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<Rets>,
$( $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<Rets>,
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<Rets>,
$( $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!(<i32>::wasm_types(), [Type::I32]);
assert_eq!(<i64>::wasm_types(), [Type::I64]);
assert_eq!(<f32>::wasm_types(), [Type::F32]);
assert_eq!(<f64>::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!(<i32>::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);
}
}