Unified Function into js/sys

This commit is contained in:
Syrus Akbary
2023-02-15 12:19:00 -08:00
parent cd4cb7a75b
commit b4c190bf72
13 changed files with 1172 additions and 1944 deletions

View File

@@ -1,8 +1,510 @@
#[cfg(feature = "js")]
pub use crate::js::externals::function::{
FromToNativeWasmType, Function, HostFunction, WasmTypeList,
};
use crate::js::externals::function as function_impl;
#[cfg(feature = "sys")]
pub use crate::sys::externals::function::{
FromToNativeWasmType, Function, HostFunction, WasmTypeList,
use crate::sys::externals::function as function_impl;
use crate::exports::{ExportError, Exportable};
use crate::store::{AsStoreMut, AsStoreRef};
use crate::vm::{VMExtern, VMExternFunction, VMFuncRef, VMFunctionBody, VMTrampoline};
use crate::{
Extern, FunctionEnv, FunctionEnvMut, FunctionType, RuntimeError, TypedFunction, Value,
};
use wasmer_types::RawValue;
use crate::native_type::WasmTypeList;
/// The `HostFunction` trait represents the set of functions that
/// can be used as host function. To uphold this statement, it is
/// necessary for a function to be transformed into a pointer to
/// `VMFunctionBody`.
pub trait HostFunction<T, Args, Rets, Kind>
where
Args: WasmTypeList,
Rets: WasmTypeList,
Kind: HostFunctionKind,
{
/// Get the pointer to the function body.
fn function_body_ptr(&self) -> *const VMFunctionBody;
/// Get the pointer to the function call trampoline.
fn call_trampoline_address() -> VMTrampoline {
// This is not implemented in JS
unimplemented!();
}
}
/// Empty trait to specify the kind of `HostFunction`: With or
/// without an environment.
///
/// This trait is never aimed to be used by a user. It is used by
/// the trait system to automatically generate the appropriate
/// host functions.
#[doc(hidden)]
pub trait HostFunctionKind: private::HostFunctionKindSealed {}
/// An empty struct to help Rust typing to determine
/// when a `HostFunction` does have an environment.
pub struct WithEnv;
impl HostFunctionKind for WithEnv {}
/// An empty struct to help Rust typing to determine
/// when a `HostFunction` does not have an environment.
pub struct WithoutEnv;
impl HostFunctionKind for WithoutEnv {}
mod private {
//! Sealing the HostFunctionKind because it shouldn't be implemented
//! by any type outside.
//! See:
//! https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed
pub trait HostFunctionKindSealed {}
impl HostFunctionKindSealed for super::WithEnv {}
impl HostFunctionKindSealed for super::WithoutEnv {}
}
/// A WebAssembly `function` instance.
///
/// A function instance is the runtime representation of a function.
/// It effectively is a closure of the original function (defined in either
/// the host or the WebAssembly module) over the runtime `Instance` of its
/// originating `Module`.
///
/// The module instance is used to resolve references to other definitions
/// during execution of the function.
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances>
///
/// # Panics
/// - Closures (functions with captured environments) are not currently supported
/// with native functions. Attempting to create a native `Function` with one will
/// result in a panic.
/// [Closures as host functions tracking issue](https://github.com/wasmerio/wasmer/issues/1840)
#[derive(Debug, Clone)]
pub struct Function(pub(crate) function_impl::Function);
impl Function {
/// Creates a new host `Function` (dynamic) with the provided signature.
///
/// If you know the signature of the host function at compile time,
/// consider using [`Function::new_typed`] for less runtime overhead.
pub fn new<FT, F>(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self
where
FT: Into<FunctionType>,
F: Fn(&[Value]) -> Result<Vec<Value>, RuntimeError> + 'static + Send + Sync,
{
let env = FunctionEnv::new(&mut store.as_store_mut(), ());
let wrapped_func = move |_env: FunctionEnvMut<()>,
args: &[Value]|
-> Result<Vec<Value>, RuntimeError> { func(args) };
Self::new_with_env(store, &env, ty, wrapped_func)
}
/// Creates a new host `Function` (dynamic) with the provided signature.
///
/// If you know the signature of the host function at compile time,
/// consider using [`Function::new_typed_with_env`] for less runtime overhead.
///
/// Takes a [`FunctionEnv`] that is passed into func. If that is not required,
/// [`Function::new`] might be an option as well.
///
/// # Examples
///
/// ```
/// # use wasmer::{Function, FunctionEnv, FunctionType, Type, Store, Value};
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// #
/// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]);
///
/// let f = Function::new_with_env(&mut store, &env, &signature, |_env, args| {
/// let sum = args[0].unwrap_i32() + args[1].unwrap_i32();
/// Ok(vec![Value::I32(sum)])
/// });
/// ```
///
/// With constant signature:
///
/// ```
/// # use wasmer::{Function, FunctionEnv, FunctionType, Type, Store, Value};
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// #
/// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]);
///
/// let f = Function::new_with_env(&mut store, &env, I32_I32_TO_I32, |_env, args| {
/// let sum = args[0].unwrap_i32() + args[1].unwrap_i32();
/// Ok(vec![Value::I32(sum)])
/// });
/// ```
pub fn new_with_env<FT, F, T: Send + 'static>(
store: &mut impl AsStoreMut,
env: &FunctionEnv<T>,
ty: FT,
func: F,
) -> Self
where
FT: Into<FunctionType>,
F: Fn(FunctionEnvMut<T>, &[Value]) -> Result<Vec<Value>, RuntimeError>
+ 'static
+ Send
+ Sync,
{
Function(function_impl::Function::new_with_env(store, env, ty, func))
}
#[deprecated(
since = "3.0.0",
note = "new_native() has been renamed to new_typed()."
)]
/// Creates a new host `Function` from a native function.
pub fn new_native<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
where
F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
Args: WasmTypeList,
Rets: WasmTypeList,
{
Self::new_typed(store, func)
}
/// Creates a new host `Function` from a native function.
pub fn new_typed<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
where
F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
Args: WasmTypeList,
Rets: WasmTypeList,
{
Function(function_impl::Function::new_typed(store, func))
}
#[deprecated(
since = "3.0.0",
note = "new_native_with_env() has been renamed to new_typed_with_env()."
)]
/// Creates a new host `Function` with an environment from a native function.
pub fn new_native_with_env<T: Send + 'static, F, Args, Rets>(
store: &mut impl AsStoreMut,
env: &FunctionEnv<T>,
func: F,
) -> Self
where
F: HostFunction<T, Args, Rets, WithEnv> + 'static + Send + Sync,
Args: WasmTypeList,
Rets: WasmTypeList,
{
Self::new_typed_with_env(store, env, func)
}
/// Creates a new host `Function` with an environment from a typed function.
///
/// The function signature is automatically retrieved using the
/// Rust typing system.
///
/// # Example
///
/// ```
/// # use wasmer::{Store, Function, FunctionEnv, FunctionEnvMut};
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// #
/// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// let f = Function::new_typed_with_env(&mut store, &env, sum);
/// ```
pub fn new_typed_with_env<T: Send + 'static, F, Args, Rets>(
store: &mut impl AsStoreMut,
env: &FunctionEnv<T>,
func: F,
) -> Self
where
F: HostFunction<T, Args, Rets, WithEnv> + 'static + Send + Sync,
Args: WasmTypeList,
Rets: WasmTypeList,
{
Function(function_impl::Function::new_typed_with_env(
store, env, func,
))
}
/// Returns the [`FunctionType`] of the `Function`.
///
/// # Example
///
/// ```
/// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type};
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// #
/// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// let f = Function::new_typed_with_env(&mut store, &env, sum);
///
/// assert_eq!(f.ty(&mut store).params(), vec![Type::I32, Type::I32]);
/// assert_eq!(f.ty(&mut store).results(), vec![Type::I32]);
/// ```
pub fn ty(&self, store: &impl AsStoreRef) -> FunctionType {
self.0.ty(store)
}
/// Returns the number of parameters that this function takes.
///
/// # Example
///
/// ```
/// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type};
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// #
/// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// let f = Function::new_typed_with_env(&mut store, &env, sum);
///
/// assert_eq!(f.param_arity(&mut store), 2);
/// ```
pub fn param_arity(&self, store: &impl AsStoreRef) -> usize {
self.ty(store).params().len()
}
/// Returns the number of results this function produces.
///
/// # Example
///
/// ```
/// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type};
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// #
/// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// let f = Function::new_typed_with_env(&mut store, &env, sum);
///
/// assert_eq!(f.result_arity(&mut store), 1);
/// ```
pub fn result_arity(&self, store: &impl AsStoreRef) -> usize {
self.ty(store).results().len()
}
/// Call the `Function` function.
///
/// Depending on where the Function is defined, it will call it.
/// 1. If the function is defined inside a WebAssembly, it will call the trampoline
/// for the function signature.
/// 2. If the function is defined in the host (in a native way), it will
/// call the trampoline.
///
/// # Examples
///
/// ```
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value};
/// # use wasmer::FunctionEnv;
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// # let wasm_bytes = wat2wasm(r#"
/// # (module
/// # (func (export "sum") (param $x i32) (param $y i32) (result i32)
/// # local.get $x
/// # local.get $y
/// # i32.add
/// # ))
/// # "#.as_bytes()).unwrap();
/// # let module = Module::new(&store, wasm_bytes).unwrap();
/// # let import_object = imports! {};
/// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
/// #
/// let sum = instance.exports.get_function("sum").unwrap();
///
/// assert_eq!(sum.call(&mut store, &[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]);
/// ```
pub fn call(
&self,
store: &mut impl AsStoreMut,
params: &[Value],
) -> Result<Box<[Value]>, RuntimeError> {
self.0.call(store, params)
}
#[doc(hidden)]
#[allow(missing_docs)]
pub fn call_raw(
&self,
store: &mut impl AsStoreMut,
params: Vec<RawValue>,
) -> Result<Box<[Value]>, RuntimeError> {
self.0.call_raw(store, params)
}
pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef {
self.0.vm_funcref(store)
}
pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self {
Function(function_impl::Function::from_vm_funcref(store, funcref))
}
/// Transform this WebAssembly function into a native function.
/// See [`TypedFunction`] to learn more.
#[deprecated(since = "3.0.0", note = "native() has been renamed to typed().")]
pub fn native<Args, Rets>(
&self,
store: &impl AsStoreRef,
) -> Result<TypedFunction<Args, Rets>, RuntimeError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
self.typed(store)
}
/// Transform this WebAssembly function into a typed function.
/// See [`TypedFunction`] to learn more.
///
/// # Examples
///
/// ```
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value};
/// # use wasmer::FunctionEnv;
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// # let wasm_bytes = wat2wasm(r#"
/// # (module
/// # (func (export "sum") (param $x i32) (param $y i32) (result i32)
/// # local.get $x
/// # local.get $y
/// # i32.add
/// # ))
/// # "#.as_bytes()).unwrap();
/// # let module = Module::new(&store, wasm_bytes).unwrap();
/// # let import_object = imports! {};
/// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
/// #
/// let sum = instance.exports.get_function("sum").unwrap();
/// let sum_typed: TypedFunction<(i32, i32), i32> = sum.typed(&mut store).unwrap();
///
/// assert_eq!(sum_typed.call(&mut store, 1, 2).unwrap(), 3);
/// ```
///
/// # Errors
///
/// If the `Args` generic parameter does not match the exported function
/// an error will be raised:
///
/// ```should_panic
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value};
/// # use wasmer::FunctionEnv;
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// # let wasm_bytes = wat2wasm(r#"
/// # (module
/// # (func (export "sum") (param $x i32) (param $y i32) (result i32)
/// # local.get $x
/// # local.get $y
/// # i32.add
/// # ))
/// # "#.as_bytes()).unwrap();
/// # let module = Module::new(&store, wasm_bytes).unwrap();
/// # let import_object = imports! {};
/// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
/// #
/// let sum = instance.exports.get_function("sum").unwrap();
///
/// // This results in an error: `RuntimeError`
/// let sum_typed : TypedFunction<(i64, i64), i32> = sum.typed(&mut store).unwrap();
/// ```
///
/// If the `Rets` generic parameter does not match the exported function
/// an error will be raised:
///
/// ```should_panic
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value};
/// # use wasmer::FunctionEnv;
/// # let mut store = Store::default();
/// # let env = FunctionEnv::new(&mut store, ());
/// # let wasm_bytes = wat2wasm(r#"
/// # (module
/// # (func (export "sum") (param $x i32) (param $y i32) (result i32)
/// # local.get $x
/// # local.get $y
/// # i32.add
/// # ))
/// # "#.as_bytes()).unwrap();
/// # let module = Module::new(&store, wasm_bytes).unwrap();
/// # let import_object = imports! {};
/// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
/// #
/// let sum = instance.exports.get_function("sum").unwrap();
///
/// // This results in an error: `RuntimeError`
/// let sum_typed: TypedFunction<(i32, i32), i64> = sum.typed(&mut store).unwrap();
/// ```
pub fn typed<Args, Rets>(
&self,
store: &impl AsStoreRef,
) -> Result<TypedFunction<Args, Rets>, RuntimeError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
let ty = self.ty(store);
// type check
{
let expected = ty.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 = ty.results();
let given = Rets::wasm_types();
if expected != given {
// todo: error result types don't match
return Err(RuntimeError::new(format!(
"given types (`{:?}`) for the function results don't match the actual types (`{:?}`)",
given,
expected,
)));
}
}
Ok(TypedFunction::new(store, self.clone()))
}
pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self {
Function(function_impl::Function::from_vm_extern(store, vm_extern))
}
/// Checks whether this `Function` can be used with the given store.
pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
self.0.is_from_store(store)
}
pub(crate) fn to_vm_extern(&self) -> VMExtern {
self.0.to_vm_extern()
}
}
impl<'a> Exportable<'a> for Function {
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
match _extern {
Extern::Function(func) => Ok(func),
_ => Err(ExportError::IncompatibleType),
}
}
}

View File

@@ -1,10 +1,10 @@
mod function;
pub(crate) mod function;
mod global;
mod memory;
mod memory_view;
mod table;
pub use self::function::{FromToNativeWasmType, Function, HostFunction, WasmTypeList};
pub use self::function::{Function, HostFunction};
pub use self::global::Global;
pub use self::memory::Memory;
pub use self::memory_view::MemoryView;

View File

@@ -56,7 +56,7 @@ impl AsJs for Value {
Self::F32(f) => JsValue::from_f64(*f as f64),
Self::F64(f) => JsValue::from_f64(*f),
Self::V128(f) => JsValue::from_f64(*f as f64),
Self::FuncRef(Some(func)) => func.handle.function.clone().into(),
Self::FuncRef(Some(func)) => func.0.handle.function.clone().into(),
Self::FuncRef(None) => JsValue::null(),
Self::ExternRef(_) => unimplemented!(),
}
@@ -207,7 +207,7 @@ impl AsJs for Extern {
fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue {
match self {
Self::Memory(memory) => memory.0.handle.memory.clone().into(),
Self::Function(function) => function.handle.function.clone().into(),
Self::Function(function) => function.0.handle.function.clone().into(),
Self::Table(table) => table.0.handle.table.clone().into(),
Self::Global(global) => global.0.handle.global.clone().into(),
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@ fn get_function(store: &mut impl AsStoreMut, val: Value) -> Result<Function, Run
return Err(RuntimeError::new("cannot pass Value across contexts"));
}
match val {
Value::FuncRef(Some(ref func)) => Ok(func.handle.function.clone().into()),
Value::FuncRef(Some(ref func)) => Ok(func.0.handle.function.clone().into()),
// Only funcrefs is supported by the spec atm
_ => unimplemented!(),
}

View File

@@ -45,7 +45,7 @@ macro_rules! impl_native_traits {
let mut r;
// TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451
loop {
r = self.func.handle.function.apply(
r = self.func.0.handle.function.apply(
&JsValue::UNDEFINED,
&Array::from_iter(params_list.iter())
);

View File

@@ -252,6 +252,8 @@ impl VMFuncRef {
}
}
pub struct VMTrampoline;
pub(crate) type VMExternTable = VMTable;
pub(crate) type VMExternMemory = VMMemory;
pub(crate) type VMExternGlobal = VMGlobal;

View File

@@ -459,10 +459,7 @@ mod js;
#[cfg(feature = "js")]
pub use js::*;
pub use crate::externals::{
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, MemoryView, Table,
WasmTypeList,
};
pub use crate::externals::{Extern, Function, Global, HostFunction, Memory, MemoryView, Table};
pub use engine::{AsEngineRef, Engine};
pub use errors::InstantiationError;
pub use exports::{ExportError, Exportable, Exports, ExportsIterator};
@@ -473,7 +470,7 @@ pub use instance::Instance;
pub use into_bytes::IntoBytes;
pub use mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter};
pub use module::{IoCompileError, Module};
pub use native_type::NativeWasmTypeInto;
pub use native_type::{FromToNativeWasmType, NativeWasmTypeInto, WasmTypeList};
pub use ptr::{Memory32, Memory64, MemorySize, WasmPtr, WasmPtr64};
pub use store::{AsStoreMut, AsStoreRef, OnCalledHandler, Store, StoreId, StoreMut, StoreRef};
#[cfg(feature = "sys")]

View File

@@ -3,9 +3,13 @@
use wasmer_types::{NativeWasmType, RawValue, Type};
use crate::store::AsStoreRef;
use crate::vm::{VMExternRef, VMFuncRef};
use crate::{ExternRef, Function, TypedFunction, WasmTypeList};
use crate::{ExternRef, Function, TypedFunction};
use std::array::TryFromSliceError;
use std::convert::Infallible;
use std::error::Error;
use crate::store::AsStoreMut;
@@ -205,6 +209,508 @@ impl NativeWasmTypeInto for Option<Function> {
}
}
/// A trait to convert a Rust value to a `WasmNativeType` value,
/// or to convert `WasmNativeType` value to a Rust value.
///
/// This trait should ideally be split into two traits:
/// `FromNativeWasmType` and `ToNativeWasmType` but it creates a
/// non-negligible complexity in the `WasmTypeList`
/// implementation.
///
/// # Safety
/// This trait is unsafe given the nature of how values are written and read from the native
/// stack
pub unsafe trait FromToNativeWasmType
where
Self: Sized,
{
/// Native Wasm type.
type Native: NativeWasmTypeInto;
/// Convert a value of kind `Self::Native` to `Self`.
///
/// # Panics
///
/// This method panics if `native` cannot fit in the `Self`
/// type`.
fn from_native(native: Self::Native) -> Self;
/// Convert self to `Self::Native`.
///
/// # Panics
///
/// This method panics if `self` cannot fit in the
/// `Self::Native` type.
fn to_native(self) -> Self::Native;
/// Returns whether the given value is from the given store.
///
/// This always returns true for primitive types that can be used with
/// any context.
fn is_from_store(&self, _store: &impl AsStoreRef) -> bool {
true
}
}
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
);
unsafe impl FromToNativeWasmType for Option<ExternRef> {
type Native = Self;
fn to_native(self) -> Self::Native {
self
}
fn from_native(n: Self::Native) -> Self {
n
}
fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
self.as_ref().map_or(true, |e| e.is_from_store(store))
}
}
unsafe impl FromToNativeWasmType for Option<Function> {
type Native = Self;
fn to_native(self) -> Self::Native {
self
}
fn from_native(n: Self::Native) -> Self {
n
}
fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
self.as_ref().map_or(true, |f| f.is_from_store(store))
}
}
#[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);
}
}
/// The `WasmTypeList` trait represents a tuple (list) of Wasm
/// typed values. It is used to get low-level representation of
/// such a tuple.
pub trait WasmTypeList
where
Self: Sized,
{
/// The C type (a struct) that can hold/represent all the
/// represented values.
type CStruct;
/// The array type that can hold all the represented values.
///
/// Note that all values are stored in their binary form.
type Array: AsMut<[RawValue]>;
/// The size of the array
fn size() -> u32;
/// Constructs `Self` based on an array of values.
///
/// # Safety
unsafe fn from_array(store: &mut impl AsStoreMut, array: Self::Array) -> Self;
/// Constructs `Self` based on a slice of values.
///
/// `from_slice` returns a `Result` because it is possible
/// that the slice doesn't have the same size than
/// `Self::Array`, in which circumstance an error of kind
/// `TryFromSliceError` will be returned.
///
/// # Safety
unsafe fn from_slice(
store: &mut impl AsStoreMut,
slice: &[RawValue],
) -> Result<Self, TryFromSliceError>;
/// Builds and returns an array of type `Array` from a tuple
/// (list) of values.
///
/// # Safety
unsafe fn into_array(self, store: &mut impl AsStoreMut) -> Self::Array;
/// Allocates and return an empty array of type `Array` that
/// will hold a tuple (list) of values, usually to hold the
/// returned values of a WebAssembly function call.
fn empty_array() -> Self::Array;
/// Builds a tuple (list) of values from a C struct of type
/// `CStruct`.
///
/// # Safety
unsafe fn from_c_struct(store: &mut impl AsStoreMut, c_struct: Self::CStruct) -> Self;
/// Builds and returns a C struct of type `CStruct` from a
/// tuple (list) of values.
///
/// # Safety
unsafe fn into_c_struct(self, store: &mut impl AsStoreMut) -> Self::CStruct;
/// Writes the contents of a C struct to an array of `RawValue`.
///
/// # Safety
unsafe fn write_c_struct_to_ptr(c_struct: Self::CStruct, ptr: *mut RawValue);
/// Get the Wasm types for the tuple (list) of currently
/// represented values.
fn wasm_types() -> &'static [Type];
}
/// The `IntoResult` trait turns a `WasmTypeList` into a
/// `Result<WasmTypeList, Self::Error>`.
///
/// It is mostly used to turn result values of a Wasm function
/// call into a `Result`.
pub trait IntoResult<T>
where
T: WasmTypeList,
{
/// The error type for this trait.
type Error: Error + Sync + Send + 'static;
/// Transforms `Self` into a `Result`.
fn into_result(self) -> Result<T, Self::Error>;
}
impl<T> IntoResult<T> for T
where
T: WasmTypeList,
{
// `T` is not a `Result`, it's already a value, so no error
// can be built.
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);
}
}
}
// Implement `WasmTypeList` on `Infallible`, which means that
// `Infallible` can be used as a returned type of a host function
// to express that it doesn't return, or to express that it cannot
// fail (with `Result<_, Infallible>`).
impl WasmTypeList for Infallible {
type CStruct = Self;
type Array = [RawValue; 0];
fn size() -> u32 {
0
}
unsafe fn from_array(_: &mut impl AsStoreMut, _: Self::Array) -> Self {
unreachable!()
}
unsafe fn from_slice(
_: &mut impl AsStoreMut,
_: &[RawValue],
) -> Result<Self, TryFromSliceError> {
unreachable!()
}
unsafe fn into_array(self, _: &mut impl AsStoreMut) -> Self::Array {
[]
}
fn empty_array() -> Self::Array {
[]
}
unsafe fn from_c_struct(_: &mut impl AsStoreMut, self_: Self::CStruct) -> Self {
self_
}
unsafe fn into_c_struct(self, _: &mut impl AsStoreMut) -> Self::CStruct {
self
}
unsafe fn write_c_struct_to_ptr(_: Self::CStruct, _: *mut RawValue) {}
fn wasm_types() -> &'static [Type] {
&[]
}
}
#[cfg(test)]
mod test_wasm_type_list {
use super::*;
use wasmer_types::Type;
/*
#[test]
fn test_from_array() {
let mut store = Store::default();
let env = FunctionEnv::new(&mut store, ());
assert_eq!(<()>::from_array(&mut env, []), ());
assert_eq!(<i32>::from_array(&mut env, [RawValue{i32: 1}]), (1i32));
assert_eq!(<(i32, i64)>::from_array(&mut env, [RawValue{i32:1}, RawValue{i64:2}]), (1i32, 2i64));
assert_eq!(
<(i32, i64, f32, f64)>::from_array(&mut env, [
RawValue{i32:1},
RawValue{i64:2},
RawValue{f32: 3.1f32},
RawValue{f64: 4.2f64}
]),
(1, 2, 3.1f32, 4.2f64)
);
}
#[test]
fn test_into_array() {
let mut store = Store::default();
let env = FunctionEnv::new(&mut store, ());
assert_eq!(().into_array(&mut store), [0i128; 0]);
assert_eq!((1i32).into_array(&mut store), [1i32]);
assert_eq!((1i32, 2i64).into_array(&mut store), [RawValue{i32: 1}, RawValue{i64: 2}]);
assert_eq!(
(1i32, 2i32, 3.1f32, 4.2f64).into_array(&mut store),
[RawValue{i32: 1}, RawValue{i32: 2}, RawValue{ f32: 3.1f32}, RawValue{f64: 4.2f64}]
);
}
*/
#[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() {
let mut store = Store::default();
let env = FunctionEnv::new(&mut store, ());
assert_eq!(<()>::from_c_struct(&mut store, S0()), ());
assert_eq!(<i32>::from_c_struct(&mut store, S1(1)), (1i32));
assert_eq!(<(i32, i64)>::from_c_struct(&mut store, S2(1, 2)), (1i32, 2i64));
assert_eq!(
<(i32, i64, f32, f64)>::from_c_struct(&mut store, 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 crate::Store;
use crate::FunctionEnv;
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() {
let mut store = Store::default();
let env = FunctionEnv::new(&mut store, ());
use wasmer_types::FunctionType;
assert_eq!(
StaticFunction::new(func).ty(&mut store),
FunctionType::new(vec![], vec![])
);
assert_eq!(
StaticFunction::new(func__i32).ty(&mut store),
FunctionType::new(vec![], vec![Type::I32])
);
assert_eq!(
StaticFunction::new(func_i32).ty(),
FunctionType::new(vec![Type::I32], vec![])
);
assert_eq!(
StaticFunction::new(func_i32__i32).ty(),
FunctionType::new(vec![Type::I32], vec![Type::I32])
);
assert_eq!(
StaticFunction::new(func_i32_i32__i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32])
);
assert_eq!(
StaticFunction::new(func_i32_i32__i32_i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32])
);
assert_eq!(
StaticFunction::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 = StaticFunction::new(func_i32__i32);
let function = unsafe { std::mem::transmute::<_, fn(usize, i32) -> i32>(f.address) };
assert_eq!(function(0, 3), 6);
}
}
*/
#[cfg(test)]
mod test_native_type {
use super::*;

File diff suppressed because it is too large Load Diff

View File

@@ -20,8 +20,8 @@ pub(crate) mod vm {
//! The `vm` module re-exports wasmer-vm types.
use wasmer_vm::InternalStoreHandle;
pub(crate) use wasmer_vm::{
VMExtern, VMExternRef, VMFuncRef, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory,
VMTable,
VMExtern, VMExternRef, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionEnvironment,
VMGlobal, VMMemory, VMTable, VMTrampoline,
};
pub(crate) type VMExternTable = InternalStoreHandle<VMTable>;

View File

@@ -17,7 +17,7 @@ macro_rules! impl_native_traits {
#[allow(clippy::too_many_arguments)]
pub fn call(&self, store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
let anyfunc = unsafe {
*self.func
*self.func.0
.handle
.get(store.as_store_ref().objects())
.anyfunc
@@ -103,7 +103,7 @@ macro_rules! impl_native_traits {
#[allow(clippy::too_many_arguments)]
pub fn call_raw(&self, store: &mut impl AsStoreMut, mut params_list: Vec<RawValue> ) -> Result<Rets, RuntimeError> {
let anyfunc = unsafe {
*self.func
*self.func.0
.handle
.get(store.as_store_ref().objects())
.anyfunc

View File

@@ -3,13 +3,14 @@
#[cfg(feature = "js")]
pub(crate) use crate::js::vm::{
VMExtern, VMExternFunction, VMExternGlobal, VMExternMemory, VMExternRef, VMExternTable,
VMFuncRef, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable,
VMFuncRef, VMFunction, VMFunctionBody, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable,
VMTrampoline,
};
#[cfg(feature = "sys")]
pub(crate) use crate::sys::vm::{
VMExtern, VMExternFunction, VMExternGlobal, VMExternMemory, VMExternRef, VMExternTable,
VMFuncRef, VMFunctionEnvironment,
VMFuncRef, VMFunctionBody, VMFunctionEnvironment, VMTrampoline,
};
// Needed for tunables customization