use crate::exports::{ExportError, Exportable};
use crate::externals::Extern;
use crate::store::Store;
use crate::types::Val;
use crate::FunctionType;
use crate::NativeFunc;
use crate::RuntimeError;
use crate::WasmerEnv;
pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv};
#[cfg(feature = "deprecated")]
pub use inner::{UnsafeMutableEnv, WithUnsafeMutableEnv};
use std::cmp::max;
use std::fmt;
use std::sync::Arc;
use wasmer_engine::{Export, ExportFunction, ExportFunctionMetadata};
use wasmer_vm::{
raise_user_trap, resume_panic, wasmer_call_trampoline, VMCallerCheckedAnyfunc,
VMDynamicFunctionContext, VMExportFunction, VMFunctionBody, VMFunctionEnvironment,
VMFunctionKind, VMTrampoline,
};
#[derive(Clone, PartialEq)]
pub struct WasmFunctionDefinition {
pub(crate) trampoline: VMTrampoline,
}
#[derive(Clone, PartialEq)]
pub struct HostFunctionDefinition {
pub(crate) has_env: bool,
}
#[derive(Clone, PartialEq)]
pub enum FunctionDefinition {
Wasm(WasmFunctionDefinition),
Host(HostFunctionDefinition),
}
#[derive(Clone, PartialEq)]
pub struct Function {
pub(crate) store: Store,
pub(crate) definition: FunctionDefinition,
pub(crate) exported: ExportFunction,
}
fn build_export_function_metadata<Env>(
env: Env,
import_init_function_ptr: for<'a> fn(
&'a mut Env,
&'a crate::Instance,
) -> Result<(), crate::HostEnvInitError>,
) -> (*mut std::ffi::c_void, ExportFunctionMetadata)
where
Env: Clone + Sized + 'static + Send + Sync,
{
let import_init_function_ptr = Some(unsafe {
std::mem::transmute::<fn(_, _) -> Result<(), _>, fn(_, _) -> Result<(), _>>(
import_init_function_ptr,
)
});
let host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void = |ptr| {
let env_ref: &Env = unsafe {
ptr.cast::<Env>()
.as_ref()
.expect("`ptr` to the environment is null when cloning it")
};
Box::into_raw(Box::new(env_ref.clone())) as _
};
let host_env_drop_fn: fn(*mut std::ffi::c_void) = |ptr| {
unsafe { Box::from_raw(ptr.cast::<Env>()) };
};
let env = Box::into_raw(Box::new(env)) as _;
let metadata = unsafe {
ExportFunctionMetadata::new(
env,
import_init_function_ptr,
host_env_clone_fn,
host_env_drop_fn,
)
};
(env, metadata)
}
impl Function {
#[allow(clippy::cast_ptr_alignment)]
pub fn new<FT, F>(store: &Store, ty: FT, func: F) -> Self
where
FT: Into<FunctionType>,
F: Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static + Send + Sync,
{
let ty: FunctionType = ty.into();
let dynamic_ctx: VMDynamicFunctionContext<DynamicFunctionWithoutEnv> =
VMDynamicFunctionContext::from_context(DynamicFunctionWithoutEnv {
func: Arc::new(func),
function_type: ty.clone(),
});
let address = std::ptr::null() as *const VMFunctionBody;
let host_env = Box::into_raw(Box::new(dynamic_ctx)) as *mut _;
let vmctx = VMFunctionEnvironment { host_env };
let host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void = |ptr| {
let duped_env: VMDynamicFunctionContext<DynamicFunctionWithoutEnv> = unsafe {
let ptr: *mut VMDynamicFunctionContext<DynamicFunctionWithoutEnv> = ptr as _;
let item: &VMDynamicFunctionContext<DynamicFunctionWithoutEnv> = &*ptr;
item.clone()
};
Box::into_raw(Box::new(duped_env)) as _
};
let host_env_drop_fn: fn(*mut std::ffi::c_void) = |ptr: *mut std::ffi::c_void| {
unsafe {
Box::from_raw(ptr as *mut VMDynamicFunctionContext<DynamicFunctionWithoutEnv>)
};
};
Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: false }),
exported: ExportFunction {
metadata: Some(Arc::new(
unsafe {
ExportFunctionMetadata::new(
host_env,
None,
host_env_clone_fn,
host_env_drop_fn,
)
},
)),
vm_function: VMExportFunction {
address,
kind: VMFunctionKind::Dynamic,
vmctx,
signature: ty,
call_trampoline: None,
instance_ref: None,
},
},
}
}
#[allow(clippy::cast_ptr_alignment)]
pub fn new_with_env<FT, F, Env>(store: &Store, ty: FT, env: Env, func: F) -> Self
where
FT: Into<FunctionType>,
F: Fn(&Env, &[Val]) -> Result<Vec<Val>, RuntimeError> + 'static + Send + Sync,
Env: Sized + WasmerEnv + 'static,
{
let ty: FunctionType = ty.into();
let dynamic_ctx: VMDynamicFunctionContext<DynamicFunctionWithEnv<Env>> =
VMDynamicFunctionContext::from_context(DynamicFunctionWithEnv {
env: Box::new(env),
func: Arc::new(func),
function_type: ty.clone(),
});
let import_init_function_ptr: for<'a> fn(&'a mut _, &'a _) -> Result<(), _> =
|env: &mut VMDynamicFunctionContext<DynamicFunctionWithEnv<Env>>,
instance: &crate::Instance| {
Env::init_with_instance(&mut *env.ctx.env, instance)
};
let (host_env, metadata) = build_export_function_metadata::<
VMDynamicFunctionContext<DynamicFunctionWithEnv<Env>>,
>(dynamic_ctx, import_init_function_ptr);
let address = std::ptr::null() as *const VMFunctionBody;
let vmctx = VMFunctionEnvironment { host_env };
Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
exported: ExportFunction {
metadata: Some(Arc::new(metadata)),
vm_function: VMExportFunction {
address,
kind: VMFunctionKind::Dynamic,
vmctx,
signature: ty,
call_trampoline: None,
instance_ref: None,
},
},
}
}
pub fn new_native<F, Args, Rets, Env>(store: &Store, func: F) -> Self
where
F: HostFunction<Args, Rets, WithoutEnv, Env>,
Args: WasmTypeList,
Rets: WasmTypeList,
Env: Sized + 'static,
{
if std::mem::size_of::<F>() != 0 {
Self::closures_unsupported_panic();
}
let function = inner::Function::<Args, Rets>::new(func);
let address = function.address() as *const VMFunctionBody;
let vmctx = VMFunctionEnvironment {
host_env: std::ptr::null_mut() as *mut _,
};
let signature = function.ty();
Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: false }),
exported: ExportFunction {
metadata: None,
vm_function: VMExportFunction {
address,
vmctx,
signature,
kind: VMFunctionKind::Static,
call_trampoline: None,
instance_ref: None,
},
},
}
}
pub fn new_native_with_env<F, Args, Rets, Env>(store: &Store, env: Env, func: F) -> Self
where
F: HostFunction<Args, Rets, WithEnv, Env>,
Args: WasmTypeList,
Rets: WasmTypeList,
Env: Sized + WasmerEnv + 'static,
{
if std::mem::size_of::<F>() != 0 {
Self::closures_unsupported_panic();
}
let function = inner::Function::<Args, Rets>::new(func);
let address = function.address();
let (host_env, metadata) =
build_export_function_metadata::<Env>(env, Env::init_with_instance);
let vmctx = VMFunctionEnvironment { host_env };
let signature = function.ty();
Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
exported: ExportFunction {
metadata: Some(Arc::new(metadata)),
vm_function: VMExportFunction {
address,
kind: VMFunctionKind::Static,
vmctx,
signature,
call_trampoline: None,
instance_ref: None,
},
},
}
}
#[doc(hidden)]
#[cfg(feature = "deprecated")]
pub unsafe fn new_native_with_unsafe_mutable_env<F, Args, Rets, Env>(
store: &Store,
env: Env,
func: F,
) -> Self
where
F: HostFunction<Args, Rets, WithUnsafeMutableEnv, Env>,
Args: WasmTypeList,
Rets: WasmTypeList,
Env: UnsafeMutableEnv + WasmerEnv + 'static,
{
if std::mem::size_of::<F>() != 0 {
Self::closures_unsupported_panic();
}
let function = inner::Function::<Args, Rets>::new(func);
let address = function.address();
let (host_env, metadata) =
build_export_function_metadata::<Env>(env, Env::init_with_instance);
let vmctx = VMFunctionEnvironment { host_env };
let signature = function.ty();
Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
exported: ExportFunction {
metadata: Some(Arc::new(metadata)),
vm_function: VMExportFunction {
address,
kind: VMFunctionKind::Static,
vmctx,
signature,
call_trampoline: None,
instance_ref: None,
},
},
}
}
pub fn ty(&self) -> &FunctionType {
&self.exported.vm_function.signature
}
pub fn store(&self) -> &Store {
&self.store
}
fn call_wasm(
&self,
func: &WasmFunctionDefinition,
params: &[Val],
results: &mut [Val],
) -> Result<(), RuntimeError> {
let format_types_for_error_message = |items: &[Val]| {
items
.iter()
.map(|param| param.ty().to_string())
.collect::<Vec<String>>()
.join(", ")
};
let signature = self.ty();
if signature.params().len() != params.len() {
return Err(RuntimeError::new(format!(
"Parameters of type [{}] did not match signature {}",
format_types_for_error_message(params),
&signature
)));
}
if signature.results().len() != results.len() {
return Err(RuntimeError::new(format!(
"Results of type [{}] did not match signature {}",
format_types_for_error_message(results),
&signature,
)));
}
let mut values_vec = vec![0; max(params.len(), results.len())];
let param_tys = signature.params().iter();
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
if arg.ty() != *ty {
let param_types = format_types_for_error_message(params);
return Err(RuntimeError::new(format!(
"Parameters of type [{}] did not match signature {}",
param_types, &signature,
)));
}
unsafe {
arg.write_value_to(slot);
}
}
if let Err(error) = unsafe {
wasmer_call_trampoline(
self.exported.vm_function.vmctx,
func.trampoline,
self.exported.vm_function.address,
values_vec.as_mut_ptr() as *mut u8,
)
} {
return Err(RuntimeError::from_trap(error));
}
for (index, &value_type) in signature.results().iter().enumerate() {
unsafe {
let ptr = values_vec.as_ptr().add(index);
results[index] = Val::read_value_from(ptr, value_type);
}
}
Ok(())
}
pub fn param_arity(&self) -> usize {
self.ty().params().len()
}
pub fn result_arity(&self) -> usize {
self.ty().results().len()
}
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, RuntimeError> {
let mut results = vec![Val::null(); self.result_arity()];
match &self.definition {
FunctionDefinition::Wasm(wasm) => {
self.call_wasm(&wasm, params, &mut results)?;
}
_ => unimplemented!("The function definition isn't supported for the moment"),
}
Ok(results.into_boxed_slice())
}
pub(crate) fn from_vm_export(store: &Store, wasmer_export: ExportFunction) -> Self {
if let Some(trampoline) = wasmer_export.vm_function.call_trampoline {
Self {
store: store.clone(),
definition: FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }),
exported: wasmer_export,
}
} else {
Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition {
has_env: !wasmer_export.vm_function.vmctx.is_null(),
}),
exported: wasmer_export,
}
}
}
pub(crate) fn checked_anyfunc(&self) -> VMCallerCheckedAnyfunc {
let vmsignature = self
.store
.engine()
.register_signature(&self.exported.vm_function.signature);
VMCallerCheckedAnyfunc {
func_ptr: self.exported.vm_function.address,
type_index: vmsignature,
vmctx: self.exported.vm_function.vmctx,
}
}
pub fn native<Args, Rets>(&self) -> Result<NativeFunc<Args, Rets>, RuntimeError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
{
let expected = self.exported.vm_function.signature.params();
let given = Args::wasm_types();
if expected != given {
return Err(RuntimeError::new(format!(
"given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)",
given,
expected,
)));
}
}
{
let expected = self.exported.vm_function.signature.results();
let given = Rets::wasm_types();
if expected != given {
return Err(RuntimeError::new(format!(
"given types (`{:?}`) for the function results don't match the actual types (`{:?}`)",
given,
expected,
)));
}
}
Ok(NativeFunc::new(
self.store.clone(),
self.exported.clone(),
self.definition.clone(),
))
}
#[track_caller]
fn closures_unsupported_panic() -> ! {
unimplemented!("Closures (functions with captured environments) are currently unsupported with native functions. See: https://github.com/wasmerio/wasmer/issues/1840")
}
}
impl<'a> Exportable<'a> for Function {
fn to_export(&self) -> Export {
self.exported.clone().into()
}
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
match _extern {
Extern::Function(func) => Ok(func),
_ => Err(ExportError::IncompatibleType),
}
}
}
impl fmt::Debug for Function {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter
.debug_struct("Function")
.field("ty", &self.ty())
.finish()
}
}
pub(crate) trait VMDynamicFunction: Send + Sync {
fn call(&self, args: &[Val]) -> Result<Vec<Val>, RuntimeError>;
fn function_type(&self) -> &FunctionType;
}
#[derive(Clone)]
pub(crate) struct DynamicFunctionWithoutEnv {
#[allow(clippy::type_complexity)]
func: Arc<dyn Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static + Send + Sync>,
function_type: FunctionType,
}
impl VMDynamicFunction for DynamicFunctionWithoutEnv {
fn call(&self, args: &[Val]) -> Result<Vec<Val>, RuntimeError> {
(*self.func)(&args)
}
fn function_type(&self) -> &FunctionType {
&self.function_type
}
}
pub(crate) struct DynamicFunctionWithEnv<Env>
where
Env: Sized + 'static + Send + Sync,
{
function_type: FunctionType,
#[allow(clippy::type_complexity)]
func: Arc<dyn Fn(&Env, &[Val]) -> Result<Vec<Val>, RuntimeError> + 'static + Send + Sync>,
env: Box<Env>,
}
impl<Env: Sized + Clone + 'static + Send + Sync> Clone for DynamicFunctionWithEnv<Env> {
fn clone(&self) -> Self {
Self {
env: self.env.clone(),
function_type: self.function_type.clone(),
func: self.func.clone(),
}
}
}
impl<Env> VMDynamicFunction for DynamicFunctionWithEnv<Env>
where
Env: Sized + 'static + Send + Sync,
{
fn call(&self, args: &[Val]) -> Result<Vec<Val>, RuntimeError> {
(*self.func)(&*self.env, &args)
}
fn function_type(&self) -> &FunctionType {
&self.function_type
}
}
trait VMDynamicFunctionCall<T: VMDynamicFunction> {
fn from_context(ctx: T) -> Self;
fn address_ptr() -> *const VMFunctionBody;
unsafe fn func_wrapper(&self, values_vec: *mut i128);
}
impl<T: VMDynamicFunction> VMDynamicFunctionCall<T> for VMDynamicFunctionContext<T> {
fn from_context(ctx: T) -> Self {
Self {
address: Self::address_ptr(),
ctx,
}
}
fn address_ptr() -> *const VMFunctionBody {
Self::func_wrapper as *const () as *const VMFunctionBody
}
unsafe fn func_wrapper(
&self,
values_vec: *mut i128,
) {
use std::panic::{self, AssertUnwindSafe};
let result = panic::catch_unwind(AssertUnwindSafe(|| {
let func_ty = self.ctx.function_type();
let mut args = Vec::with_capacity(func_ty.params().len());
for (i, ty) in func_ty.params().iter().enumerate() {
args.push(Val::read_value_from(values_vec.add(i), *ty));
}
let returns = self.ctx.call(&args)?;
let return_types = returns.iter().map(|ret| ret.ty()).collect::<Vec<_>>();
if return_types != func_ty.results() {
return Err(RuntimeError::new(format!(
"Dynamic function returned wrong signature. Expected {:?} but got {:?}",
func_ty.results(),
return_types
)));
}
for (i, ret) in returns.iter().enumerate() {
ret.write_value_to(values_vec.add(i));
}
Ok(())
}));
match result {
Ok(Ok(())) => {}
Ok(Err(trap)) => raise_user_trap(Box::new(trap)),
Err(panic) => resume_panic(panic),
}
}
}
mod inner {
use std::array::TryFromSliceError;
use std::convert::{Infallible, TryInto};
use std::error::Error;
use std::marker::PhantomData;
use std::panic::{self, AssertUnwindSafe};
use wasmer_types::{FunctionType, NativeWasmType, Type};
use wasmer_vm::{raise_user_trap, resume_panic, VMFunctionBody};
pub unsafe trait FromToNativeWasmType: Copy
where
Self: Sized,
{
type Native: NativeWasmType;
fn from_native(native: Self::Native) -> Self;
fn to_native(self) -> Self::Native;
}
macro_rules! from_to_native_wasm_type {
( $( $type:ty => $native_type:ty ),* ) => {
$(
#[allow(clippy::use_self)]
unsafe impl FromToNativeWasmType for $type {
type Native = $native_type;
#[inline]
fn from_native(native: Self::Native) -> Self {
native as Self
}
#[inline]
fn to_native(self) -> Self::Native {
self as Self::Native
}
}
)*
};
}
macro_rules! from_to_native_wasm_type_same_size {
( $( $type:ty => $native_type:ty ),* ) => {
$(
#[allow(clippy::use_self)]
unsafe impl FromToNativeWasmType for $type {
type Native = $native_type;
#[inline]
fn from_native(native: Self::Native) -> Self {
Self::from_ne_bytes(Self::Native::to_ne_bytes(native))
}
#[inline]
fn to_native(self) -> Self::Native {
Self::Native::from_ne_bytes(Self::to_ne_bytes(self))
}
}
)*
};
}
from_to_native_wasm_type!(
i8 => i32,
u8 => i32,
i16 => i32,
u16 => i32
);
from_to_native_wasm_type_same_size!(
i32 => i32,
u32 => i32,
i64 => i64,
u64 => i64,
f32 => f32,
f64 => f64
);
#[cfg(test)]
mod test_from_to_native_wasm_type {
use super::*;
#[test]
fn test_to_native() {
assert_eq!(7i8.to_native(), 7i32);
assert_eq!(7u8.to_native(), 7i32);
assert_eq!(7i16.to_native(), 7i32);
assert_eq!(7u16.to_native(), 7i32);
assert_eq!(u32::MAX.to_native(), -1);
}
#[test]
fn test_to_native_same_size() {
assert_eq!(7i32.to_native(), 7i32);
assert_eq!(7u32.to_native(), 7i32);
assert_eq!(7i64.to_native(), 7i64);
assert_eq!(7u64.to_native(), 7i64);
assert_eq!(7f32.to_native(), 7f32);
assert_eq!(7f64.to_native(), 7f64);
}
}
pub trait WasmTypeList
where
Self: Sized,
{
type CStruct;
type Array: AsMut<[i128]>;
fn from_array(array: Self::Array) -> Self;
fn from_slice(slice: &[i128]) -> Result<Self, TryFromSliceError>;
fn into_array(self) -> Self::Array;
fn empty_array() -> Self::Array;
fn from_c_struct(c_struct: Self::CStruct) -> Self;
fn into_c_struct(self) -> Self::CStruct;
fn wasm_types() -> &'static [Type];
}
pub trait IntoResult<T>
where
T: WasmTypeList,
{
type Error: Error + Sync + Send + 'static;
fn into_result(self) -> Result<T, Self::Error>;
}
impl<T> IntoResult<T> for T
where
T: WasmTypeList,
{
type Error = Infallible;
fn into_result(self) -> Result<Self, Infallible> {
Ok(self)
}
}
impl<T, E> IntoResult<T> for Result<T, E>
where
T: WasmTypeList,
E: Error + Sync + Send + 'static,
{
type Error = E;
fn into_result(self) -> Self {
self
}
}
#[cfg(test)]
mod test_into_result {
use super::*;
use std::convert::Infallible;
#[test]
fn test_into_result_over_t() {
let x: i32 = 42;
let result_of_x: Result<i32, Infallible> = x.into_result();
assert_eq!(result_of_x.unwrap(), x);
}
#[test]
fn test_into_result_over_result() {
{
let x: Result<i32, Infallible> = Ok(42);
let result_of_x: Result<i32, Infallible> = x.into_result();
assert_eq!(result_of_x, x);
}
{
use std::{error, fmt};
#[derive(Debug, PartialEq)]
struct E;
impl fmt::Display for E {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "E")
}
}
impl error::Error for E {}
let x: Result<Infallible, E> = Err(E);
let result_of_x: Result<Infallible, E> = x.into_result();
assert_eq!(result_of_x.unwrap_err(), E);
}
}
}
pub trait HostFunction<Args, Rets, Kind, T>
where
Args: WasmTypeList,
Rets: WasmTypeList,
Kind: HostFunctionKind,
T: Sized,
Self: Sized,
{
fn function_body_ptr(self) -> *const VMFunctionBody;
}
#[cfg(feature = "deprecated")]
#[doc(hidden)]
pub unsafe trait UnsafeMutableEnv: Sized {}
#[doc(hidden)]
pub trait HostFunctionKind {}
pub struct WithEnv;
impl HostFunctionKind for WithEnv {}
#[cfg(feature = "deprecated")]
#[doc(hidden)]
pub struct WithUnsafeMutableEnv;
#[cfg(feature = "deprecated")]
impl HostFunctionKind for WithUnsafeMutableEnv {}
pub struct WithoutEnv;
impl HostFunctionKind for WithoutEnv {}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Function<Args = (), Rets = ()> {
address: *const VMFunctionBody,
_phantom: PhantomData<(Args, Rets)>,
}
unsafe impl<Args, Rets> Send for Function<Args, Rets> {}
impl<Args, Rets> Function<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
pub fn new<F, T, E>(function: F) -> Self
where
F: HostFunction<Args, Rets, T, E>,
T: HostFunctionKind,
E: Sized,
{
Self {
address: function.function_body_ptr(),
_phantom: PhantomData,
}
}
pub fn ty(&self) -> FunctionType {
FunctionType::new(Args::wasm_types(), Rets::wasm_types())
}
pub fn address(&self) -> *const VMFunctionBody {
self.address
}
}
macro_rules! impl_host_function {
( [$c_struct_representation:ident]
$c_struct_name:ident,
$( $x:ident ),* ) => {
#[repr($c_struct_representation)]
pub struct $c_struct_name< $( $x ),* > ( $( <$x as FromToNativeWasmType>::Native ),* )
where
$( $x: FromToNativeWasmType ),*;
#[allow(unused_parens, dead_code)]
impl< $( $x ),* >
WasmTypeList
for
( $( $x ),* )
where
$( $x: FromToNativeWasmType ),*
{
type CStruct = $c_struct_name< $( $x ),* >;
type Array = [i128; count_idents!( $( $x ),* )];
fn from_array(array: Self::Array) -> Self {
#[allow(non_snake_case)]
let [ $( $x ),* ] = array;
(
$(
FromToNativeWasmType::from_native(NativeWasmType::from_binary($x))
),*
)
}
fn from_slice(slice: &[i128]) -> Result<Self, TryFromSliceError> {
Ok(Self::from_array(slice.try_into()?))
}
fn into_array(self) -> Self::Array {
#[allow(non_snake_case)]
let ( $( $x ),* ) = self;
[
$(
FromToNativeWasmType::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 $c_struct_name( $( $x ),* ) = c_struct;
(
$(
FromToNativeWasmType::from_native($x)
),*
)
}
#[allow(unused_parens, non_snake_case)]
fn into_c_struct(self) -> Self::CStruct {
let ( $( $x ),* ) = self;
$c_struct_name(
$(
FromToNativeWasmType::to_native($x)
),*
)
}
fn wasm_types() -> &'static [Type] {
&[
$(
$x::Native::WASM_TYPE
),*
]
}
}
#[allow(unused_parens)]
impl< $( $x, )* Rets, RetsAsResult, Func >
HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()>
for
Func
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Func: Fn($( $x , )*) -> RetsAsResult + 'static + Send,
{
#[allow(non_snake_case)]
fn function_body_ptr(self) -> *const VMFunctionBody {
extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( _: usize, $( $x: $x::Native, )* ) -> Rets::CStruct
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Func: Fn( $( $x ),* ) -> RetsAsResult + 'static
{
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
let result = panic::catch_unwind(AssertUnwindSafe(|| {
func( $( FromToNativeWasmType::from_native($x) ),* ).into_result()
}));
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) },
}
}
func_wrapper::< $( $x, )* Rets, RetsAsResult, Self > as *const VMFunctionBody
}
}
#[allow(unused_parens)]
impl< $( $x, )* Rets, RetsAsResult, Env, Func >
HostFunction<( $( $x ),* ), Rets, WithEnv, Env>
for
Func
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Env: Sized,
Func: Fn(&Env, $( $x , )*) -> RetsAsResult + Send + 'static,
{
#[allow(non_snake_case)]
fn function_body_ptr(self) -> *const VMFunctionBody {
extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Env, Func>( env: &Env, $( $x: $x::Native, )* ) -> Rets::CStruct
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Env: Sized,
Func: Fn(&Env, $( $x ),* ) -> RetsAsResult + 'static
{
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
let result = panic::catch_unwind(AssertUnwindSafe(|| {
func(env, $( FromToNativeWasmType::from_native($x) ),* ).into_result()
}));
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) },
}
}
func_wrapper::< $( $x, )* Rets, RetsAsResult, Env, Self > as *const VMFunctionBody
}
}
#[doc(hidden)]
#[cfg(feature = "deprecated")]
#[allow(unused_parens)]
impl< $( $x, )* Rets, RetsAsResult, Env, Func >
HostFunction<( $( $x ),* ), Rets, WithUnsafeMutableEnv, Env>
for
Func
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Env: UnsafeMutableEnv,
Func: Fn(&mut Env, $( $x , )*) -> RetsAsResult + Send + 'static,
{
#[allow(non_snake_case)]
fn function_body_ptr(self) -> *const VMFunctionBody {
extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Env, Func>( env: &mut Env, $( $x: $x::Native, )* ) -> Rets::CStruct
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Env: Sized,
Func: Fn(&mut Env, $( $x ),* ) -> RetsAsResult + 'static
{
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
let result = panic::catch_unwind(AssertUnwindSafe(|| {
func(env, $( FromToNativeWasmType::from_native($x) ),* ).into_result()
}));
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) },
}
}
func_wrapper::< $( $x, )* Rets, RetsAsResult, Env, Self > as *const VMFunctionBody
}
}
};
}
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_host_function!([C] S0,);
impl_host_function!([transparent] S1, A1);
impl_host_function!([C] S2, A1, A2);
impl_host_function!([C] S3, A1, A2, A3);
impl_host_function!([C] S4, A1, A2, A3, A4);
impl_host_function!([C] S5, A1, A2, A3, A4, A5);
impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6);
impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7);
impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8);
impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
impl_host_function!([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_host_function!([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_host_function!([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_host_function!([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_host_function!([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);
impl WasmTypeList for Infallible {
type CStruct = Self;
type Array = [i128; 0];
fn from_array(_: Self::Array) -> Self {
unreachable!()
}
fn from_slice(_: &[i128]) -> Result<Self, TryFromSliceError> {
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] {
&[]
}
}
#[cfg(test)]
mod test_wasm_type_list {
use super::*;
use wasmer_types::Type;
#[test]
fn test_from_array() {
assert_eq!(<()>::from_array([]), ());
assert_eq!(<i32>::from_array([1]), (1i32));
assert_eq!(<(i32, i64)>::from_array([1, 2]), (1i32, 2i64));
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!((1i32, 2i64).into_array(), [1, 2]);
assert_eq!(
(1i32, 2i32, 3.1f32, 4.2f64).into_array(),
[1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]
);
}
#[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_c_struct() {
assert_eq!(<()>::from_c_struct(S0()), ());
assert_eq!(<i32>::from_c_struct(S1(1)), (1i32));
assert_eq!(<(i32, i64)>::from_c_struct(S2(1, 2)), (1i32, 2i64));
assert_eq!(
<(i32, i64, f32, f64)>::from_c_struct(S4(1, 2, 3.1, 4.2)),
(1i32, 2i64, 3.1f32, 4.2f64)
);
}
#[test]
fn test_wasm_types_for_uni_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]);
}
#[test]
fn test_wasm_types_for_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]);
assert_eq!(
<(i32, i64, f32, f64)>::wasm_types(),
[Type::I32, Type::I64, Type::F32, Type::F64]
);
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod test_function {
use super::*;
use wasmer_types::Type;
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!(Function::new(func).ty(), FunctionType::new(vec![], vec![]));
assert_eq!(
Function::new(func__i32).ty(),
FunctionType::new(vec![], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32).ty(),
FunctionType::new(vec![Type::I32], vec![])
);
assert_eq!(
Function::new(func_i32__i32).ty(),
FunctionType::new(vec![Type::I32], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32_i32__i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32_i32__i32_i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32])
);
assert_eq!(
Function::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 = Function::new(func_i32__i32);
let function = unsafe { std::mem::transmute::<_, fn(usize, i32) -> i32>(f.address) };
assert_eq!(function(0, 3), 6);
}
}
}