diff --git a/examples/imports_function_env_global.rs b/examples/imports_function_env_global.rs index fb9cb41debd..6102905fe2d 100644 --- a/examples/imports_function_env_global.rs +++ b/examples/imports_function_env_global.rs @@ -92,7 +92,8 @@ fn main() -> Result<(), Box> { let global_count = data.g_counter.get(&mut storemut).unwrap_i32(); data.g_counter - .set(&mut storemut, Value::I32(global_count + add)); + .set(&mut storemut, Value::I32(global_count + add)) + .unwrap(); *counter_ref += add; *counter_ref diff --git a/examples/tunables_limit_memory.rs b/examples/tunables_limit_memory.rs index 104f1acc681..da2c7a4ece1 100644 --- a/examples/tunables_limit_memory.rs +++ b/examples/tunables_limit_memory.rs @@ -3,10 +3,12 @@ use std::ptr::NonNull; use wasmer::{ imports, vm::{self, MemoryError, MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition}, - wat2wasm, BaseTunables, Instance, Memory, MemoryType, Module, Pages, Store, TableType, Target, - Tunables, + wat2wasm, BaseTunables, Engine, Instance, Memory, MemoryType, Module, Pages, Store, TableType, + Target, Tunables, }; use wasmer_compiler_cranelift::Cranelift; +// This is to be able to set the tunables +use wasmer::NativeEngineExt; /// A custom tunables that allows you to set a memory limit. /// @@ -140,9 +142,11 @@ fn main() -> Result<(), Box> { // Here is where the fun begins let base = BaseTunables::for_target(&Target::default()); let tunables = LimitingTunables::new(base, Pages(24)); + let mut engine: Engine = compiler.into(); + engine.set_tunables(tunables); // Create a store, that holds the engine and our custom tunables - let mut store = Store::new_with_tunables(compiler, tunables); + let mut store = Store::new(engine); println!("Compiling module..."); let module = Module::new(&store, wasm_bytes)?; diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 5d736dd1511..73e5cd33a26 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -100,6 +100,7 @@ core = ["hashbrown"] sys = [ "wasmer-compiler/translator", "wasmer-compiler/compiler", + "std", ] sys-default = ["sys", "wat", "cranelift"] # - Compilers. diff --git a/lib/api/src/access.rs b/lib/api/src/access.rs index da34c8ec407..e1d5cc16c95 100644 --- a/lib/api/src/access.rs +++ b/lib/api/src/access.rs @@ -1,6 +1,6 @@ use std::mem::MaybeUninit; -use crate::{WasmRef, WasmSlice}; +use crate::mem_access::{WasmRef, WasmSlice}; pub(super) enum SliceCow<'a, T> { #[allow(dead_code)] @@ -167,30 +167,6 @@ where } } -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - /// Reads the address pointed to by this `WasmPtr` in a memory. - #[inline] - #[allow(clippy::clone_on_copy)] - pub fn read(&self) -> T - where - T: Clone, - { - self.as_ref().clone() - } - - /// Writes to the address pointed to by this `WasmPtr` in a memory. - #[inline] - pub fn write(&mut self, val: T) { - // Note: Zero padding is not required here as its a typed copy which does - // not leak the bytes into the memory - // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes - *(self.as_mut()) = val; - } -} - impl<'a, T> Drop for WasmRefAccess<'a, T> where T: wasmer_types::ValueType, diff --git a/lib/api/src/engine.rs b/lib/api/src/engine.rs new file mode 100644 index 00000000000..565a00da0be --- /dev/null +++ b/lib/api/src/engine.rs @@ -0,0 +1,92 @@ +use core::ops::Deref; + +#[cfg(feature = "sys")] +use crate::sys::engine as engine_imp; +#[cfg(feature = "sys")] +pub(crate) use crate::sys::engine::default_engine; + +#[cfg(feature = "js")] +use crate::js::engine as engine_imp; +#[cfg(feature = "js")] +pub(crate) use crate::js::engine::default_engine; + +/// The engine type +#[derive(Clone, Debug)] +pub struct Engine(pub(crate) engine_imp::Engine); + +impl Engine { + #[deprecated( + since = "3.2.0", + note = "engine.cloned() has been deprecated in favor of engine.clone()" + )] + /// Returns the [`Engine`]. + pub fn cloned(&self) -> Self { + self.clone() + } + + /// Returns the deterministic id of this engine + pub fn deterministic_id(&self) -> &str { + self.0.deterministic_id() + } +} + +impl AsEngineRef for Engine { + fn as_engine_ref(&self) -> EngineRef { + EngineRef { inner: self } + } +} + +impl Default for Engine { + fn default() -> Self { + Self(default_engine()) + } +} + +impl> From for Engine { + fn from(t: T) -> Self { + Self(t.into()) + } +} + +/// A temporary handle to an [`Engine`] +/// EngineRef can be used to build a [`Module`][wasmer::Module] +/// It can be created directly with an [`Engine`] +/// Or from anything implementing [`AsEngineRef`] +/// like from [`Store`][wasmer::Store] typicaly. +pub struct EngineRef<'a> { + /// The inner engine + pub(crate) inner: &'a Engine, +} + +impl<'a> EngineRef<'a> { + /// Get inner [`Engine`] + pub fn engine(&self) -> &Engine { + self.inner + } + /// Create an EngineRef from an Engine + pub fn new(engine: &'a Engine) -> Self { + EngineRef { inner: engine } + } +} + +/// Helper trait for a value that is convertible to a [`EngineRef`]. +pub trait AsEngineRef { + /// Returns a `EngineRef` pointing to the underlying context. + fn as_engine_ref(&self) -> EngineRef<'_>; +} + +impl AsEngineRef for EngineRef<'_> { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef { inner: self.inner } + } +} + +impl

AsEngineRef for P +where + P: Deref, + P::Target: AsEngineRef, +{ + fn as_engine_ref(&self) -> EngineRef<'_> { + (**self).as_engine_ref() + } +} diff --git a/lib/api/src/errors.rs b/lib/api/src/errors.rs new file mode 100644 index 00000000000..4b65e654f46 --- /dev/null +++ b/lib/api/src/errors.rs @@ -0,0 +1,47 @@ +#[cfg(feature = "js")] +pub use crate::js::errors::{LinkError, RuntimeError}; +use thiserror::Error; +#[cfg(feature = "sys")] +pub use wasmer_compiler::{LinkError, RuntimeError}; + +/// An error while instantiating a module. +/// +/// This is not a common WebAssembly error, however +/// we need to differentiate from a `LinkError` (an error +/// that happens while linking, on instantiation), a +/// Trap that occurs when calling the WebAssembly module +/// start function, and an error when initializing the user's +/// host environments. +#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Error))] +pub enum InstantiationError { + /// A linking ocurred during instantiation. + #[cfg_attr(feature = "std", error(transparent))] + Link(LinkError), + + /// A runtime error occured while invoking the start function + #[cfg_attr(feature = "std", error(transparent))] + Start(RuntimeError), + + /// The module was compiled with a CPU feature that is not available on + /// the current host. + #[cfg_attr(feature = "std", error("missing required CPU features: {0:?}"))] + CpuFeature(String), + + /// Import from a different [`Store`]. + /// This error occurs when an import from a different store is used. + #[cfg_attr(feature = "std", error("cannot mix imports from different stores"))] + DifferentStores, + + /// Import from a different Store. + /// This error occurs when an import from a different store is used. + #[cfg_attr(feature = "std", error("incorrect OS or architecture"))] + DifferentArchOS, +} + +#[cfg(feature = "core")] +impl std::fmt::Display for InstantiationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "InstantiationError") + } +} diff --git a/lib/api/src/sys/exports.rs b/lib/api/src/exports.rs similarity index 96% rename from lib/api/src/sys/exports.rs rename to lib/api/src/exports.rs index 571419deefb..a9304341f7c 100644 --- a/lib/api/src/sys/exports.rs +++ b/lib/api/src/exports.rs @@ -1,7 +1,5 @@ -use super::store::AsStoreRef; -use crate::sys::externals::{Extern, Function, Global, Memory, Table}; -use crate::sys::native::TypedFunction; -use crate::sys::WasmTypeList; +use crate::store::AsStoreRef; +use crate::{Extern, Function, Global, Memory, Table, TypedFunction, WasmTypeList}; use indexmap::IndexMap; use std::fmt; use std::iter::{ExactSizeIterator, FromIterator}; @@ -18,9 +16,7 @@ use thiserror::Error; /// /// ```should_panic /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError}; -/// # use wasmer::FunctionEnv; /// # let mut store = Store::default(); -/// # let env = FunctionEnv::new(&mut store, ()); /// # let wasm_bytes = wat2wasm(r#" /// # (module /// # (global $one (export "glob") f32 (f32.const 1))) @@ -37,9 +33,7 @@ use thiserror::Error; /// /// ```should_panic /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError}; -/// # use wasmer::FunctionEnv; /// # let mut store = Store::default(); -/// # let env = FunctionEnv::new(&mut store, ()); /// # let wasm_bytes = wat2wasm("(module)".as_bytes()).unwrap(); /// # let module = Module::new(&store, wasm_bytes).unwrap(); /// # let import_object = imports! {}; @@ -63,7 +57,7 @@ pub enum ExportError { /// the types of instances. /// /// TODO: add examples of using exports -#[derive(Clone, Default)] +#[derive(Clone, Default, PartialEq, Eq)] pub struct Exports { map: IndexMap, } diff --git a/lib/api/src/extern_ref.rs b/lib/api/src/extern_ref.rs new file mode 100644 index 00000000000..3b5bf1b8c74 --- /dev/null +++ b/lib/api/src/extern_ref.rs @@ -0,0 +1,57 @@ +use std::any::Any; + +use crate::store::{AsStoreMut, AsStoreRef}; + +#[cfg(feature = "js")] +use crate::js::extern_ref as extern_ref_imp; +#[cfg(feature = "sys")] +use crate::sys::extern_ref as extern_ref_imp; +use crate::vm::VMExternRef; + +#[derive(Debug, Clone)] +#[repr(transparent)] +/// An opaque reference to some data. This reference can be passed through Wasm. +pub struct ExternRef(pub(crate) extern_ref_imp::ExternRef); + +impl ExternRef { + /// Make a new extern reference + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + Self(extern_ref_imp::ExternRef::new(store, value)) + } + + /// Try to downcast to the given value. + pub fn downcast<'a, T>(&self, store: &'a impl AsStoreRef) -> Option<&'a T> + where + T: Any + Send + Sync + 'static + Sized, + { + self.0.downcast(store) + } + + pub(crate) fn vm_externref(&self) -> VMExternRef { + self.0.vm_externref() + } + + pub(crate) unsafe fn from_vm_externref( + store: &mut impl AsStoreMut, + vm_externref: VMExternRef, + ) -> Self { + Self(extern_ref_imp::ExternRef::from_vm_externref( + store, + vm_externref, + )) + } + + /// Checks whether this `ExternRef` can be used with the given context. + /// + /// Primitive (`i32`, `i64`, etc) and null funcref/externref values are not + /// tied to a context and can be freely shared between contexts. + /// + /// Externref and funcref values are tied to a context and can only be used + /// with that context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + self.0.is_from_store(store) + } +} diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs new file mode 100644 index 00000000000..1a2cbc3dab7 --- /dev/null +++ b/lib/api/src/externals/function.rs @@ -0,0 +1,512 @@ +#[cfg(feature = "js")] +use crate::js::externals::function as function_impl; +#[cfg(feature = "sys")] +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 +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: +/// +/// # 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, PartialEq)] +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(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self + where + FT: Into, + F: Fn(&[Value]) -> Result, RuntimeError> + 'static + Send + Sync, + { + let env = FunctionEnv::new(&mut store.as_store_mut(), ()); + let wrapped_func = move |_env: FunctionEnvMut<()>, + args: &[Value]| + -> Result, 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( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + ty: FT, + func: F, + ) -> Self + where + FT: Into, + F: Fn(FunctionEnvMut, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + Self(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(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(store: &mut impl AsStoreMut, func: F) -> Self + where + F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + Self(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( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: HostFunction + '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( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: HostFunction + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + Self(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, RuntimeError> { + self.0.call(store, params) + } + + #[doc(hidden)] + #[allow(missing_docs)] + pub fn call_raw( + &self, + store: &mut impl AsStoreMut, + params: Vec, + ) -> Result, 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 { + Self(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( + &self, + store: &impl AsStoreRef, + ) -> Result, 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( + &self, + store: &impl AsStoreRef, + ) -> Result, 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 { + Self(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 std::cmp::Eq for Function {} + +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), + } + } +} diff --git a/lib/api/src/externals/global.rs b/lib/api/src/externals/global.rs new file mode 100644 index 00000000000..24d1279be3d --- /dev/null +++ b/lib/api/src/externals/global.rs @@ -0,0 +1,173 @@ +use crate::exports::{ExportError, Exportable}; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::value::Value; +use crate::vm::VMExtern; +use crate::vm::VMExternGlobal; +use crate::Extern; +use crate::GlobalType; +use crate::Mutability; +use crate::RuntimeError; + +#[cfg(feature = "js")] +use crate::js::externals::global as global_impl; +#[cfg(feature = "sys")] +use crate::sys::externals::global as global_impl; + +/// A WebAssembly `global` instance. +/// +/// A global instance is the runtime representation of a global variable. +/// It consists of an individual value and a flag indicating whether it is mutable. +/// +/// Spec: +#[derive(Debug, Clone, PartialEq)] +pub struct Global(pub(crate) global_impl::Global); + +impl Global { + /// Create a new `Global` with the initial value [`Value`]. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new(&mut store, Value::I32(1)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(1)); + /// assert_eq!(g.ty(&mut store).mutability, Mutability::Const); + /// ``` + pub fn new(store: &mut impl AsStoreMut, val: Value) -> Self { + Self::from_value(store, val, Mutability::Const).unwrap() + } + + /// Create a mutable `Global` with the initial value [`Value`]. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new_mut(&mut store, Value::I32(1)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(1)); + /// assert_eq!(g.ty(&mut store).mutability, Mutability::Var); + /// ``` + pub fn new_mut(store: &mut impl AsStoreMut, val: Value) -> Self { + Self::from_value(store, val, Mutability::Var).unwrap() + } + + /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. + fn from_value( + store: &mut impl AsStoreMut, + val: Value, + mutability: Mutability, + ) -> Result { + Ok(Self(global_impl::Global::from_value( + store, val, mutability, + )?)) + } + + /// Returns the [`GlobalType`] of the `Global`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Type, Value, GlobalType}; + /// # let mut store = Store::default(); + /// # + /// let c = Global::new(&mut store, Value::I32(1)); + /// let v = Global::new_mut(&mut store, Value::I64(1)); + /// + /// assert_eq!(c.ty(&mut store), GlobalType::new(Type::I32, Mutability::Const)); + /// assert_eq!(v.ty(&mut store), GlobalType::new(Type::I64, Mutability::Var)); + /// ``` + pub fn ty(&self, store: &impl AsStoreRef) -> GlobalType { + self.0.ty(store) + } + + /// Retrieves the current value [`Value`] that the Global has. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new(&mut store, Value::I32(1)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(1)); + /// ``` + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { + self.0.get(store) + } + + /// Sets a custom value [`Value`] to the runtime Global. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new_mut(&mut store, Value::I32(1)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(1)); + /// + /// g.set(&mut store, Value::I32(2)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(2)); + /// ``` + /// + /// # Errors + /// + /// Trying to mutate a immutable global will raise an error: + /// + /// ```should_panic + /// # use wasmer::{Global, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new(&mut store, Value::I32(1)); + /// + /// g.set(&mut store, Value::I32(2)).unwrap(); + /// ``` + /// + /// Trying to set a value of a incompatible type will raise an error: + /// + /// ```should_panic + /// # use wasmer::{Global, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new(&mut store, Value::I32(1)); + /// + /// // This results in an error: `RuntimeError`. + /// g.set(&mut store, Value::I64(2)).unwrap(); + /// ``` + pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { + self.0.set(store, val) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternGlobal) -> Self { + Self(global_impl::Global::from_vm_extern(store, vm_extern)) + } + + /// Checks whether this `Global` can be used with the given context. + 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 std::cmp::Eq for Global {} + +impl<'a> Exportable<'a> for Global { + fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + match _extern { + Extern::Global(global) => Ok(global), + _ => Err(ExportError::IncompatibleType), + } + } +} diff --git a/lib/api/src/externals/memory.rs b/lib/api/src/externals/memory.rs new file mode 100644 index 00000000000..d29322c91e9 --- /dev/null +++ b/lib/api/src/externals/memory.rs @@ -0,0 +1,185 @@ +#[cfg(feature = "js")] +use crate::js::externals::memory as memory_impl; +#[cfg(feature = "sys")] +use crate::sys::externals::memory as memory_impl; + +use super::memory_view::MemoryView; +use crate::exports::{ExportError, Exportable}; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::vm::{VMExtern, VMExternMemory, VMMemory}; +use crate::Extern; +use crate::MemoryAccessError; +use crate::MemoryType; +use std::mem::MaybeUninit; +use wasmer_types::{MemoryError, Pages}; + +/// A WebAssembly `memory` instance. +/// +/// A memory instance is the runtime representation of a linear memory. +/// It consists of a vector of bytes and an optional maximum size. +/// +/// The length of the vector always is a multiple of the WebAssembly +/// page size, which is defined to be the constant 65536 – abbreviated 64Ki. +/// Like in a memory type, the maximum size in a memory instance is +/// given in units of this page size. +/// +/// A memory created by the host or in WebAssembly code will be accessible and +/// mutable from both host and WebAssembly. +/// +/// Spec: +#[derive(Debug, Clone, PartialEq)] +pub struct Memory(pub(crate) memory_impl::Memory); + +impl Memory { + /// Creates a new host `Memory` from the provided [`MemoryType`]. + /// + /// This function will construct the `Memory` using the store + /// [`BaseTunables`][crate::sys::BaseTunables]. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); + /// ``` + pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { + Ok(Self(memory_impl::Memory::new(store, ty)?)) + } + + /// Create a memory object from an existing memory and attaches it to the store + pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + Self(memory_impl::Memory::new_from_existing(new_store, memory)) + } + + /// Returns the [`MemoryType`] of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let mt = MemoryType::new(1, None, false); + /// let m = Memory::new(&mut store, mt).unwrap(); + /// + /// assert_eq!(m.ty(&mut store), mt); + /// ``` + pub fn ty(&self, store: &impl AsStoreRef) -> MemoryType { + self.0.ty(store) + } + + /// Creates a view into the memory that then allows for + /// read and write + pub fn view<'a>(&'a self, store: &impl AsStoreRef) -> MemoryView<'a> { + MemoryView::new(self, store) + } + + /// Grow memory by the specified amount of WebAssembly [`Pages`] and return + /// the previous memory size. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, Some(3), false)).unwrap(); + /// let p = m.grow(&mut store, 2).unwrap(); + /// + /// assert_eq!(p, Pages(1)); + /// assert_eq!(m.view(&mut store).size(), Pages(3)); + /// ``` + /// + /// # Errors + /// + /// Returns an error if memory can't be grown by the specified amount + /// of pages. + /// + /// ```should_panic + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; + /// # use wasmer::FunctionEnv; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, Some(1), false)).unwrap(); + /// + /// // This results in an error: `MemoryError::CouldNotGrow`. + /// let s = m.grow(&mut store, 1).unwrap(); + /// ``` + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + self.0.grow(store, delta) + } + + /// Copies the memory to a new store and returns a memory reference to it + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + Ok(Self(self.0.copy_to_store(store, new_store)?)) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternMemory) -> Self { + Self(memory_impl::Memory::from_vm_extern(store, vm_extern)) + } + + /// Checks whether this `Memory` can be used with the given context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + self.0.is_from_store(store) + } + + /// Attempts to clone this memory (if its clonable) + pub fn try_clone(&self, store: &impl AsStoreRef) -> Option { + self.0.try_clone(store) + } + + /// To `VMExtern`. + pub(crate) fn to_vm_extern(&self) -> VMExtern { + self.0.to_vm_extern() + } +} + +impl std::cmp::Eq for Memory {} + +impl<'a> Exportable<'a> for Memory { + fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + match _extern { + Extern::Memory(memory) => Ok(memory), + _ => Err(ExportError::IncompatibleType), + } + } +} + +/// Underlying buffer for a memory. +#[derive(Debug, Copy, Clone)] +pub(crate) struct MemoryBuffer<'a>(pub(crate) memory_impl::MemoryBuffer<'a>); + +impl<'a> MemoryBuffer<'a> { + #[allow(unused)] + pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + self.0.read(offset, buf) + } + + #[allow(unused)] + pub(crate) fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + self.0.read_uninit(offset, buf) + } + + #[allow(unused)] + pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + self.0.write(offset, data) + } +} diff --git a/lib/api/src/externals/memory_view.rs b/lib/api/src/externals/memory_view.rs new file mode 100644 index 00000000000..54cb696a6a6 --- /dev/null +++ b/lib/api/src/externals/memory_view.rs @@ -0,0 +1,174 @@ +use super::memory::{Memory, MemoryBuffer}; +use crate::store::AsStoreRef; +use crate::MemoryAccessError; +use std::mem::MaybeUninit; +use wasmer_types::Pages; + +#[cfg(feature = "js")] +use crate::js::externals::memory_view as memory_view_impl; +#[cfg(feature = "sys")] +use crate::sys::externals::memory_view as memory_view_impl; + +/// A WebAssembly `memory` view. +/// +/// A memory view is used to read and write to the linear memory. +/// +/// After a memory is grown a view must not be used anymore. Views are +/// created using the Memory.grow() method. +#[derive(Debug)] +pub struct MemoryView<'a>(pub(crate) memory_view_impl::MemoryView<'a>); + +impl<'a> MemoryView<'a> { + pub(crate) fn new(memory: &'a Memory, store: &impl AsStoreRef) -> Self { + MemoryView(memory_view_impl::MemoryView::new(&memory.0, store)) + } + + /// Returns the pointer to the raw bytes of the `Memory`. + // + // This used by wasmer-emscripten and wasmer-c-api, but should be treated + // as deprecated and not used in future code. + #[doc(hidden)] + pub fn data_ptr(&self) -> *mut u8 { + self.0.data_ptr() + } + + /// Returns the size (in bytes) of the `Memory`. + pub fn data_size(&self) -> u64 { + self.0.data_size() + } + + /// Retrieve a slice of the memory contents. + /// + /// # Safety + /// + /// Until the returned slice is dropped, it is undefined behaviour to + /// modify the memory contents in any way including by calling a wasm + /// function that writes to the memory or by resizing the memory. + #[doc(hidden)] + pub unsafe fn data_unchecked(&self) -> &[u8] { + self.0.data_unchecked() + } + + /// Retrieve a mutable slice of the memory contents. + /// + /// # Safety + /// + /// This method provides interior mutability without an UnsafeCell. Until + /// the returned value is dropped, it is undefined behaviour to read or + /// write to the pointed-to memory in any way except through this slice, + /// including by calling a wasm function that reads the memory contents or + /// by resizing this Memory. + #[allow(clippy::mut_from_ref)] + #[doc(hidden)] + pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { + self.0.data_unchecked_mut() + } + + /// Returns the size (in [`Pages`]) of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); + /// + /// assert_eq!(m.view(&mut store).size(), Pages(1)); + /// ``` + pub fn size(&self) -> Pages { + self.0.size() + } + + #[inline] + pub(crate) fn buffer(&'a self) -> MemoryBuffer<'a> { + MemoryBuffer(self.0.buffer()) + } + + /// Safely reads bytes from the memory at the given offset. + /// + /// The full buffer will be filled, otherwise a `MemoryAccessError` is returned + /// to indicate an out-of-bounds access. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + self.0.read(offset, buf) + } + + /// Safely reads a single byte from memory at the given offset + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read_u8(&self, offset: u64) -> Result { + self.0.read_u8(offset) + } + + /// Safely reads bytes from the memory at the given offset. + /// + /// This method is similar to `read` but allows reading into an + /// uninitialized buffer. An initialized view of the buffer is returned. + /// + /// The full buffer will be filled, otherwise a `MemoryAccessError` is returned + /// to indicate an out-of-bounds access. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + self.0.read_uninit(offset, buf) + } + + /// Safely writes bytes to the memory at the given offset. + /// + /// If the write exceeds the bounds of the memory then a `MemoryAccessError` is + /// returned. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent reads/writes. + pub fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + self.0.write(offset, data) + } + + /// Safely reads a single byte from memory at the given offset + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn write_u8(&self, offset: u64, val: u8) -> Result<(), MemoryAccessError> { + self.0.write_u8(offset, val) + } + + /// Copies the memory and returns it as a vector of bytes + pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { + let mut new_memory = Vec::new(); + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < self.data_size() { + let remaining = self.data_size() - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + new_memory.extend_from_slice(&chunk[..sublen]); + offset += sublen as u64; + } + Ok(new_memory) + } + + /// Copies the memory to another new memory object + pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < amount { + let remaining = amount - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + + new_memory.write(offset, &chunk[..sublen])?; + + offset += sublen as u64; + } + Ok(()) + } +} diff --git a/lib/api/src/externals/mod.rs b/lib/api/src/externals/mod.rs new file mode 100644 index 00000000000..748a08834e3 --- /dev/null +++ b/lib/api/src/externals/mod.rs @@ -0,0 +1,126 @@ +pub(crate) mod function; +mod global; +pub(crate) mod memory; +mod memory_view; +mod table; + +pub use self::function::{Function, HostFunction}; +pub use self::global::Global; +pub use self::memory::Memory; +pub use self::memory_view::MemoryView; +pub use self::table::Table; + +use crate::exports::{ExportError, Exportable}; +use crate::ExternType; +use std::fmt; + +#[cfg(feature = "js")] +use crate::js::vm::VMExtern; +#[cfg(feature = "sys")] +use wasmer_vm::VMExtern; + +use crate::store::{AsStoreMut, AsStoreRef}; + +/// An `Extern` is the runtime representation of an entity that +/// can be imported or exported. +/// +/// Spec: +#[derive(Clone, PartialEq, Eq)] +pub enum Extern { + /// A external [`Function`]. + Function(Function), + /// A external [`Global`]. + Global(Global), + /// A external [`Table`]. + Table(Table), + /// A external [`Memory`]. + Memory(Memory), +} + +impl Extern { + /// Return the underlying type of the inner `Extern`. + pub fn ty(&self, store: &impl AsStoreRef) -> ExternType { + match self { + Self::Function(ft) => ExternType::Function(ft.ty(store)), + Self::Memory(ft) => ExternType::Memory(ft.ty(store)), + Self::Table(tt) => ExternType::Table(tt.ty(store)), + Self::Global(gt) => ExternType::Global(gt.ty(store)), + } + } + + /// Create an `Extern` from an `wasmer_engine::Export`. + pub fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExtern) -> Self { + match vm_extern { + VMExtern::Function(f) => Self::Function(Function::from_vm_extern(store, f)), + VMExtern::Memory(m) => Self::Memory(Memory::from_vm_extern(store, m)), + VMExtern::Global(g) => Self::Global(Global::from_vm_extern(store, g)), + VMExtern::Table(t) => Self::Table(Table::from_vm_extern(store, t)), + } + } + + /// Checks whether this `Extern` can be used with the given context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + match self { + Self::Function(f) => f.is_from_store(store), + Self::Global(g) => g.is_from_store(store), + Self::Memory(m) => m.is_from_store(store), + Self::Table(t) => t.is_from_store(store), + } + } + + /// To `VMExtern`. + pub fn to_vm_extern(&self) -> VMExtern { + match self { + Self::Function(f) => f.to_vm_extern(), + Self::Global(g) => g.to_vm_extern(), + Self::Memory(m) => m.to_vm_extern(), + Self::Table(t) => t.to_vm_extern(), + } + } +} + +impl<'a> Exportable<'a> for Extern { + fn get_self_from_extern(_extern: &'a Self) -> Result<&'a Self, ExportError> { + // Since this is already an extern, we can just return it. + Ok(_extern) + } +} + +impl fmt::Debug for Extern { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Function(_) => "Function(...)", + Self::Global(_) => "Global(...)", + Self::Memory(_) => "Memory(...)", + Self::Table(_) => "Table(...)", + } + ) + } +} + +impl From for Extern { + fn from(r: Function) -> Self { + Self::Function(r) + } +} + +impl From for Extern { + fn from(r: Global) -> Self { + Self::Global(r) + } +} + +impl From for Extern { + fn from(r: Memory) -> Self { + Self::Memory(r) + } +} + +impl From for Extern { + fn from(r: Table) -> Self { + Self::Table(r) + } +} diff --git a/lib/api/src/externals/table.rs b/lib/api/src/externals/table.rs new file mode 100644 index 00000000000..b2d8cde5535 --- /dev/null +++ b/lib/api/src/externals/table.rs @@ -0,0 +1,144 @@ +#[cfg(feature = "js")] +use crate::js::externals::table as table_impl; +#[cfg(feature = "sys")] +use crate::sys::externals::table as table_impl; + +use crate::exports::{ExportError, Exportable}; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::vm::{VMExtern, VMExternTable}; +use crate::Extern; +use crate::RuntimeError; +use crate::TableType; +use crate::Value; + +/// A WebAssembly `table` instance. +/// +/// The `Table` struct is an array-like structure representing a WebAssembly Table, +/// which stores function references. +/// +/// A table created by the host or in WebAssembly code will be accessible and +/// mutable from both host and WebAssembly. +/// +/// Spec: +#[derive(Debug, Clone, PartialEq)] +pub struct Table(pub(crate) table_impl::Table); + +impl Table { + /// Creates a new `Table` with the provided [`TableType`] definition. + /// + /// All the elements in the table will be set to the `init` value. + /// + /// This function will construct the `Table` using the store + /// [`BaseTunables`][crate::sys::BaseTunables]. + pub fn new( + store: &mut impl AsStoreMut, + ty: TableType, + init: Value, + ) -> Result { + Ok(Self(table_impl::Table::new(store, ty, init)?)) + } + + /// Returns the [`TableType`] of the `Table`. + pub fn ty(&self, store: &impl AsStoreRef) -> TableType { + self.0.ty(store) + } + + /// Retrieves an element of the table at the provided `index`. + pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { + self.0.get(store, index) + } + + /// Sets an element `val` in the Table at the provided `index`. + pub fn set( + &self, + store: &mut impl AsStoreMut, + index: u32, + val: Value, + ) -> Result<(), RuntimeError> { + self.0.set(store, index, val) + } + + /// Retrieves the size of the `Table` (in elements) + pub fn size(&self, store: &impl AsStoreRef) -> u32 { + self.0.size(store) + } + + /// Grows the size of the `Table` by `delta`, initializating + /// the elements with the provided `init` value. + /// + /// It returns the previous size of the `Table` in case is able + /// to grow the Table successfully. + /// + /// # Errors + /// + /// Returns an error if the `delta` is out of bounds for the table. + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: u32, + init: Value, + ) -> Result { + self.0.grow(store, delta, init) + } + + /// Copies the `len` elements of `src_table` starting at `src_index` + /// to the destination table `dst_table` at index `dst_index`. + /// + /// # Errors + /// + /// Returns an error if the range is out of bounds of either the source or + /// destination tables. + pub fn copy( + store: &mut impl AsStoreMut, + dst_table: &Self, + dst_index: u32, + src_table: &Self, + src_index: u32, + len: u32, + ) -> Result<(), RuntimeError> { + table_impl::Table::copy(store, &dst_table.0, dst_index, &src_table.0, src_index, len) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, extern_: VMExternTable) -> Self { + Self(table_impl::Table::from_vm_extern(store, extern_)) + } + + /// Checks whether this `Table` can be used with the given context. + 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 std::cmp::Eq for Table {} + +impl<'a> Exportable<'a> for Table { + fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + match _extern { + Extern::Table(table) => Ok(table), + _ => Err(ExportError::IncompatibleType), + } + } +} + +/// Check the example from . +#[test] +fn test_table_grow_issue_3197() { + use crate::{imports, Instance, Module, Store, Table, TableType, Type, Value}; + + const WAT: &str = r#"(module (table (import "env" "table") 100 funcref))"#; + + // Tests that the table type of `table` is compatible with the export in the WAT + // This tests that `wasmer_types::types::is_table_compatible` works as expected. + let mut store = Store::default(); + let module = Module::new(&store, WAT).unwrap(); + let ty = TableType::new(Type::FuncRef, 0, None); + let table = Table::new(&mut store, ty, Value::FuncRef(None)).unwrap(); + table.grow(&mut store, 100, Value::FuncRef(None)).unwrap(); + assert_eq!(table.ty(&store).minimum, 0); + let imports = imports! {"env" => {"table" => table}}; + let _instance = Instance::new(&mut store, &module, &imports).unwrap(); +} diff --git a/lib/api/src/sys/function_env.rs b/lib/api/src/function_env.rs similarity index 91% rename from lib/api/src/sys/function_env.rs rename to lib/api/src/function_env.rs index 08a0b68ec50..2209d57c8eb 100644 --- a/lib/api/src/sys/function_env.rs +++ b/lib/api/src/function_env.rs @@ -1,8 +1,8 @@ use std::{any::Any, marker::PhantomData}; -use wasmer_vm::{StoreHandle, StoreObjects, VMFunctionEnvironment}; +use crate::vm::VMFunctionEnvironment; -use crate::{AsStoreMut, AsStoreRef, StoreMut, StoreRef}; +use crate::store::{AsStoreMut, AsStoreRef, StoreHandle, StoreMut, StoreObjects, StoreRef}; #[derive(Debug)] #[repr(transparent)] @@ -40,6 +40,14 @@ impl FunctionEnv { .unwrap() } + #[allow(dead_code)] // This function is only used in js + pub(crate) fn from_handle(handle: StoreHandle) -> Self { + Self { + handle, + marker: PhantomData, + } + } + /// Get the data as mutable pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T where diff --git a/lib/api/src/sys/imports.rs b/lib/api/src/imports.rs similarity index 89% rename from lib/api/src/sys/imports.rs rename to lib/api/src/imports.rs index a08d1b6adad..f50b2c2e7f7 100644 --- a/lib/api/src/sys/imports.rs +++ b/lib/api/src/imports.rs @@ -1,13 +1,10 @@ //! The import module contains the implementation data structures and helper functions used to //! manipulate and access a wasm module's imports including memories, tables, globals, and //! functions. -use crate::{Exports, Extern, Module}; +use crate::{Exports, Extern, LinkError, Module}; use std::collections::HashMap; use std::fmt; -use wasmer_compiler::LinkError; use wasmer_types::ImportError; -#[cfg(feature = "compiler")] -use wasmer_vm::VMSharedMemory; /// All of the import data used when instantiating. /// @@ -38,7 +35,7 @@ use wasmer_vm::VMSharedMemory; /// ``` #[derive(Clone, Default)] pub struct Imports { - map: HashMap<(String, String), Extern>, + pub(crate) map: HashMap<(String, String), Extern>, } impl Imports { @@ -123,37 +120,6 @@ impl Imports { .insert((ns.to_string(), name.to_string()), val.into()); } - /// Imports (any) shared memory into the imports. - /// (if the module does not import memory then this function is ignored) - #[cfg(feature = "compiler")] - pub fn import_shared_memory( - &mut self, - module: &Module, - store: &mut impl crate::AsStoreMut, - ) -> Option { - // Determine if shared memory needs to be created and imported - let shared_memory = module - .imports() - .memories() - .next() - .map(|a| *a.ty()) - .map(|ty| { - let style = store.as_store_ref().tunables().memory_style(&ty); - VMSharedMemory::new(&ty, &style).unwrap() - }); - - if let Some(memory) = shared_memory { - self.define( - "env", - "memory", - crate::Memory::new_from_existing(store, memory.clone().into()), - ); - Some(memory) - } else { - None - } - } - /// Returns the contents of a namespace as an `Exports`. /// /// Returns `None` if the namespace doesn't exist. @@ -338,9 +304,11 @@ macro_rules! import_namespace { #[cfg(test)] mod test { - use crate::sys::{AsStoreMut, Global, Store, Value}; + use crate::store::Store; + use crate::value::Value; + use crate::Extern; + use crate::Global; use wasmer_types::Type; - use wasmer_vm::VMExtern; #[test] fn namespace() { @@ -355,18 +323,16 @@ mod test { let happy_dog_entry = imports1.get_export("dog", "happy").unwrap(); - assert!( - if let VMExtern::Global(happy_dog_global) = happy_dog_entry.to_vm_extern() { - (*happy_dog_global.get(store.objects_mut()).ty()).ty == Type::I32 - } else { - false - } - ); + assert!(if let Extern::Global(happy_dog_global) = happy_dog_entry { + happy_dog_global.get(&mut store).ty() == Type::I32 + } else { + false + }); } #[test] fn imports_macro_allows_trailing_comma_and_none() { - use crate::sys::Function; + use crate::Function; let mut store: Store = Default::default(); diff --git a/lib/api/src/instance.rs b/lib/api/src/instance.rs new file mode 100644 index 00000000000..14a8066e0ae --- /dev/null +++ b/lib/api/src/instance.rs @@ -0,0 +1,109 @@ +use crate::exports::Exports; +use crate::module::Module; +use crate::{Extern, InstantiationError}; +use std::fmt; + +use crate::imports::Imports; +use crate::store::AsStoreMut; + +#[cfg(feature = "js")] +use crate::js::instance as instance_imp; +#[cfg(feature = "sys")] +use crate::sys::instance as instance_imp; + +/// A WebAssembly Instance is a stateful, executable +/// instance of a WebAssembly [`Module`]. +/// +/// Instance objects contain all the exported WebAssembly +/// functions, memories, tables and globals that allow +/// interacting with WebAssembly. +/// +/// Spec: +#[derive(Clone, PartialEq, Eq)] +pub struct Instance { + pub(crate) _inner: instance_imp::Instance, + pub(crate) module: Module, + /// The exports for an instance. + pub exports: Exports, +} + +impl Instance { + /// Creates a new `Instance` from a WebAssembly [`Module`] and a + /// set of imports using [`Imports`] or the [`imports`] macro helper. + /// + /// [`imports`]: crate::imports + /// [`Imports`]: crate::Imports + /// + /// ``` + /// # use wasmer::{imports, Store, Module, Global, Value, Instance}; + /// # use wasmer::FunctionEnv; + /// # fn main() -> anyhow::Result<()> { + /// let mut store = Store::default(); + /// let env = FunctionEnv::new(&mut store, ()); + /// let module = Module::new(&store, "(module)")?; + /// let imports = imports!{ + /// "host" => { + /// "var" => Global::new(&mut store, Value::I32(2)) + /// } + /// }; + /// let instance = Instance::new(&mut store, &module, &imports)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Errors + /// + /// The function can return [`InstantiationError`]s. + /// + /// Those are, as defined by the spec: + /// * Link errors that happen when plugging the imports into the instance + /// * Runtime errors that happen when running the module `start` function. + pub fn new( + store: &mut impl AsStoreMut, + module: &Module, + imports: &Imports, + ) -> Result { + let (_inner, exports) = instance_imp::Instance::new(store, module, imports)?; + Ok(Self { + _inner, + module: module.clone(), + exports, + }) + } + + /// Creates a new `Instance` from a WebAssembly [`Module`] and a + /// vector of imports. + /// + /// ## Errors + /// + /// The function can return [`InstantiationError`]s. + /// + /// Those are, as defined by the spec: + /// * Link errors that happen when plugging the imports into the instance + /// * Runtime errors that happen when running the module `start` function. + pub fn new_by_index( + store: &mut impl AsStoreMut, + module: &Module, + externs: &[Extern], + ) -> Result { + let (_inner, exports) = instance_imp::Instance::new_by_index(store, module, externs)?; + Ok(Self { + _inner, + module: module.clone(), + exports, + }) + } + + /// Gets the [`Module`] associated with this instance. + pub fn module(&self) -> &Module { + &self.module + } +} + +impl fmt::Debug for Instance { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Instance") + .field("exports", &self.exports) + .finish() + } +} diff --git a/lib/api/src/js/as_js.rs b/lib/api/src/js/as_js.rs new file mode 100644 index 00000000000..c1347a90dc7 --- /dev/null +++ b/lib/api/src/js/as_js.rs @@ -0,0 +1,275 @@ +//use crate::js::externals::Function; +// use crate::store::{Store, StoreObject}; +// use crate::js::RuntimeError; +use crate::imports::Imports; +use crate::instance::Instance; +use crate::js::instance::Instance as JsInstance; +use crate::js::vm::{VMFunction, VMGlobal, VMMemory, VMTable}; +use crate::js::wasm_bindgen_polyfill::Global as JsGlobal; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::value::Value; +use crate::Type; +use crate::{Extern, Function, Global, Memory, Table}; +use js_sys::Function as JsFunction; +use js_sys::WebAssembly::{Memory as JsMemory, Table as JsTable}; +use std::collections::HashMap; +use std::convert::TryInto; +use wasm_bindgen::JsCast; +use wasm_bindgen::{JsError, JsValue}; +use wasmer_types::ExternType; + +/// Convert the given type to a [`JsValue`]. +pub trait AsJs: Sized { + /// The inner definition type from this Javascript object + type DefinitionType; + /// Convert the given type to a [`JsValue`]. + fn as_jsvalue(&self, store: &impl AsStoreRef) -> JsValue; + /// Convert the given type to a [`JsValue`]. + fn from_jsvalue( + store: &mut impl AsStoreMut, + type_: &Self::DefinitionType, + value: &JsValue, + ) -> Result; +} + +#[inline] +pub fn param_from_js(ty: &Type, js_val: &JsValue) -> Value { + match ty { + Type::I32 => Value::I32(js_val.as_f64().unwrap() as _), + Type::I64 => { + let number = js_val.as_f64().map(|f| f as i64).unwrap_or_else(|| { + if js_val.is_bigint() { + // To support BigInt + let big_num: u128 = js_sys::BigInt::from(js_val.clone()).try_into().unwrap(); + big_num as i64 + } else { + (js_sys::Number::from(js_val.clone()).as_f64().unwrap()) as i64 + } + }); + Value::I64(number) + } + Type::F32 => Value::F32(js_val.as_f64().unwrap() as _), + Type::F64 => Value::F64(js_val.as_f64().unwrap()), + Type::V128 => { + let big_num: u128 = js_sys::BigInt::from(js_val.clone()).try_into().unwrap(); + Value::V128(big_num) + } + Type::ExternRef | Type::FuncRef => unimplemented!( + "The type `{:?}` is not yet supported in the JS Function API", + ty + ), + } +} + +impl AsJs for Value { + type DefinitionType = Type; + + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> JsValue { + match self { + Self::I32(i) => JsValue::from(*i), + Self::I64(i) => JsValue::from(*i), + Self::F32(f) => JsValue::from(*f), + Self::F64(f) => JsValue::from(*f), + Self::V128(v) => JsValue::from(*v), + Self::FuncRef(Some(func)) => func.0.handle.function.clone().into(), + Self::FuncRef(None) => JsValue::null(), + Self::ExternRef(_) => unimplemented!(), + } + } + + fn from_jsvalue( + _store: &mut impl AsStoreMut, + type_: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + Ok(param_from_js(type_, value)) + } +} + +impl AsJs for Imports { + type DefinitionType = crate::module::Module; + + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + fn as_jsvalue(&self, store: &impl AsStoreRef) -> wasm_bindgen::JsValue { + let imports_object = js_sys::Object::new(); + for (namespace, name, extern_) in self.iter() { + let val = unsafe { js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap() }; + if !val.is_undefined() { + // If the namespace is already set + + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set( + &val, + &name.into(), + &extern_.as_jsvalue(&store.as_store_ref()), + ) + .unwrap(); + } + } else { + // If the namespace doesn't exist + let import_namespace = js_sys::Object::new(); + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set( + &import_namespace, + &name.into(), + &extern_.as_jsvalue(&store.as_store_ref()), + ) + .unwrap(); + js_sys::Reflect::set( + &imports_object, + &namespace.into(), + &import_namespace.into(), + ) + .unwrap(); + } + } + } + imports_object.into() + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + module: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + let module_imports: HashMap<(String, String), ExternType> = module + .imports() + .map(|import| { + ( + (import.module().to_string(), import.name().to_string()), + import.ty().clone(), + ) + }) + .collect::>(); + + let mut map: HashMap<(String, String), Extern> = HashMap::new(); + let object: js_sys::Object = value.clone().into(); + for module_entry in js_sys::Object::entries(&object).iter() { + let module_entry: js_sys::Array = module_entry.into(); + let module_name = module_entry.get(0).as_string().unwrap().to_string(); + let module_import_object: js_sys::Object = module_entry.get(1).into(); + for import_entry in js_sys::Object::entries(&module_import_object).iter() { + let import_entry: js_sys::Array = import_entry.into(); + let import_name = import_entry.get(0).as_string().unwrap().to_string(); + let import_js: wasm_bindgen::JsValue = import_entry.get(1); + let key = (module_name.clone(), import_name); + let extern_type = module_imports.get(&key).unwrap(); + let extern_ = Extern::from_jsvalue(store, extern_type, &import_js)?; + map.insert(key, extern_); + } + } + + Ok(Self { map }) + } +} + +impl AsJs for Extern { + type DefinitionType = ExternType; + + 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.0.handle.function.clone().into(), + Self::Table(table) => table.0.handle.table.clone().into(), + Self::Global(global) => global.0.handle.global.clone().into(), + } + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + extern_type: &Self::DefinitionType, + val: &JsValue, + ) -> Result { + // Note: this function do a soft check over the type + // We only check the "kind" of Extern, but nothing else + match extern_type { + ExternType::Memory(memory_type) => { + if val.is_instance_of::() { + Ok(Self::Memory(Memory::from_vm_extern( + store, + VMMemory::new( + val.clone().unchecked_into::(), + memory_type.clone(), + ), + ))) + } else { + Err(JsError::new(&format!( + "Extern expect to be of type Memory, but received {:?}", + val + ))) + } + } + ExternType::Global(global_type) => { + if val.is_instance_of::() { + Ok(Self::Global(Global::from_vm_extern( + store, + VMGlobal::new( + val.clone().unchecked_into::(), + global_type.clone(), + ), + ))) + } else { + Err(JsError::new(&format!( + "Extern expect to be of type Global, but received {:?}", + val + ))) + } + } + ExternType::Function(function_type) => { + if val.is_instance_of::() { + Ok(Self::Function(Function::from_vm_extern( + store, + VMFunction::new( + val.clone().unchecked_into::(), + function_type.clone(), + ), + ))) + } else { + Err(JsError::new(&format!( + "Extern expect to be of type Function, but received {:?}", + val + ))) + } + } + ExternType::Table(table_type) => { + if val.is_instance_of::() { + Ok(Self::Table(Table::from_vm_extern( + store, + VMTable::new(val.clone().unchecked_into::(), table_type.clone()), + ))) + } else { + Err(JsError::new(&format!( + "Extern expect to be of type Table, but received {:?}", + val + ))) + } + } + } + } +} + +impl AsJs for Instance { + type DefinitionType = crate::module::Module; + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { + self._inner._handle.clone().into() + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + module: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + let js_instance: js_sys::WebAssembly::Instance = value.clone().into(); + let (instance, exports) = JsInstance::from_module_and_instance(store, module, js_instance) + .map_err(|e| JsError::new(&format!("Can't get the instance: {:?}", e)))?; + Ok(Instance { + _inner: instance, + module: module.clone(), + exports, + }) + } +} diff --git a/lib/api/src/js/engine.rs b/lib/api/src/js/engine.rs new file mode 100644 index 00000000000..b56d104ec12 --- /dev/null +++ b/lib/api/src/js/engine.rs @@ -0,0 +1,21 @@ +/// A WebAssembly `Universal` Engine. +#[derive(Clone, Debug)] +pub struct Engine; + +impl Engine { + pub(crate) fn deterministic_id(&self) -> &str { + // All js engines have the same id + "js-generic" + } +} + +impl Default for Engine { + fn default() -> Self { + Engine + } +} + +/// Returns the default engine for the JS engine +pub(crate) fn default_engine() -> Engine { + Engine::default() +} diff --git a/lib/api/src/js/error.rs b/lib/api/src/js/error.rs deleted file mode 100644 index ffc9eeff8b3..00000000000 --- a/lib/api/src/js/error.rs +++ /dev/null @@ -1,223 +0,0 @@ -#[cfg(feature = "core")] -use crate::alloc::borrow::Cow; -use crate::js::lib::std::string::String; -use crate::js::trap::RuntimeError; -#[cfg(feature = "std")] -use std::borrow::Cow; -#[cfg(feature = "std")] -use thiserror::Error; -use wasmer_types::ImportError; - -// Compilation Errors -// -// If `std` feature is enable, we can't use `thiserror` until -// https://github.com/dtolnay/thiserror/pull/64 is merged. - -/// The WebAssembly.CompileError object indicates an error during -/// WebAssembly decoding or validation. -/// -/// This is based on the [Wasm Compile Error][compile-error] API. -/// -/// [compiler-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/CompileError -#[derive(Debug)] -#[cfg_attr(feature = "std", derive(Error))] -pub enum CompileError { - /// A Wasm translation error occured. - #[cfg_attr(feature = "std", error("WebAssembly translation error: {0}"))] - Wasm(WasmError), - - /// A compilation error occured. - #[cfg_attr(feature = "std", error("Compilation error: {0}"))] - Codegen(String), - - /// The module did not pass validation. - #[cfg_attr(feature = "std", error("Validation error: {0}"))] - Validate(String), - - /// The compiler doesn't support a Wasm feature - #[cfg_attr(feature = "std", error("Feature {0} is not yet supported"))] - UnsupportedFeature(String), - - /// The compiler cannot compile for the given target. - /// This can refer to the OS, the chipset or any other aspect of the target system. - #[cfg_attr(feature = "std", error("The target {0} is not yet supported (see https://docs.wasmer.io/ecosystem/wasmer/wasmer-features)"))] - UnsupportedTarget(String), - - /// Insufficient resources available for execution. - #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))] - Resource(String), -} - -#[cfg(feature = "core")] -impl std::fmt::Display for CompileError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "CompileError") - } -} - -impl From for CompileError { - fn from(original: WasmError) -> Self { - Self::Wasm(original) - } -} - -/// A WebAssembly translation error. -/// -/// When a WebAssembly function can't be translated, one of these error codes will be returned -/// to describe the failure. -#[derive(Debug)] -#[cfg_attr(feature = "std", derive(Error))] -pub enum WasmError { - /// The input WebAssembly code is invalid. - /// - /// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly - /// code. This should never happen for validated WebAssembly code. - #[cfg_attr( - feature = "std", - error("Invalid input WebAssembly code at offset {offset}: {message}") - )] - InvalidWebAssembly { - /// A string describing the validation error. - message: String, - /// The bytecode offset where the error occurred. - offset: usize, - }, - - /// A feature used by the WebAssembly code is not supported by the embedding environment. - /// - /// Embedding environments may have their own limitations and feature restrictions. - #[cfg_attr(feature = "std", error("Unsupported feature: {0}"))] - Unsupported(String), - - /// A Javascript value could not be converted to the requested type. - #[cfg_attr(feature = "std", error("{0} doesn't match js value type {1}"))] - TypeMismatch(Cow<'static, str>, Cow<'static, str>), - - /// A generic error. - #[cfg_attr(feature = "std", error("{0}"))] - Generic(String), -} - -impl From for WasmError { - fn from(err: wasm_bindgen::JsValue) -> Self { - Self::Generic( - if err.is_string() && err.as_string().filter(|s| !s.is_empty()).is_some() { - err.as_string().unwrap_or_default() - } else { - format!("Unexpected Javascript error: {:?}", err) - }, - ) - } -} - -/// The Serialize error can occur when serializing a -/// compiled Module into a binary. -/// Copied from wasmer_compiler::SerializeError -#[derive(Debug)] -#[cfg_attr(feature = "std", derive(Error))] -pub enum SerializeError { - /// An IO error - #[cfg_attr(feature = "std", error(transparent))] - Io(#[cfg_attr(feature = "std", from)] std::io::Error), - /// A generic serialization error - #[cfg_attr(feature = "std", error("{0}"))] - Generic(String), -} - -/// The Deserialize error can occur when loading a -/// compiled Module from a binary. -/// Copied from wasmer_compiler::DeSerializeError -#[derive(Debug)] -#[cfg_attr(feature = "std", derive(Error))] -pub enum DeserializeError { - /// An IO error - #[cfg_attr(feature = "std", error(transparent))] - Io(#[cfg_attr(feature = "std", from)] std::io::Error), - /// A generic deserialization error - #[cfg_attr(feature = "std", error("{0}"))] - Generic(String), - /// Incompatible serialized binary - #[cfg_attr(feature = "std", error("incompatible binary: {0}"))] - Incompatible(String), - /// The provided binary is corrupted - #[cfg_attr(feature = "std", error("corrupted binary: {0}"))] - CorruptedBinary(String), - /// The binary was valid, but we got an error when - /// trying to allocate the required resources. - #[cfg_attr(feature = "std", error(transparent))] - Compiler(CompileError), -} - -/// The WebAssembly.LinkError object indicates an error during -/// module instantiation (besides traps from the start function). -/// -/// This is based on the [link error][link-error] API. -/// -/// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError -#[derive(Debug)] -#[cfg_attr(feature = "std", derive(Error))] -#[cfg_attr(feature = "std", error("Link error: {0}"))] -pub enum LinkError { - /// An error occurred when checking the import types. - #[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))] - Import(String, String, ImportError), - - #[cfg(not(target_arch = "wasm32"))] - /// A trap ocurred during linking. - #[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))] - Trap(#[source] RuntimeError), - /// Insufficient resources available for linking. - #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))] - Resource(String), -} - -/// An error while instantiating a module. -/// -/// This is not a common WebAssembly error, however -/// we need to differentiate from a `LinkError` (an error -/// that happens while linking, on instantiation), a -/// Trap that occurs when calling the WebAssembly module -/// start function, and an error when initializing the user's -/// host environments. -#[derive(Debug)] -#[cfg_attr(feature = "std", derive(Error))] -pub enum InstantiationError { - /// A linking ocurred during instantiation. - #[cfg_attr(feature = "std", error(transparent))] - Link(LinkError), - - /// A runtime error occured while invoking the start function - #[cfg_attr(feature = "std", error(transparent))] - Start(RuntimeError), - - /// The module was compiled with a CPU feature that is not available on - /// the current host. - #[cfg_attr(feature = "std", error("missing required CPU features: {0:?}"))] - CpuFeature(String), - - /// Import from a different [`Store`]. - /// This error occurs when an import from a different store is used. - #[cfg_attr(feature = "std", error("cannot mix imports from different stores"))] - DifferentStores, - - /// A generic error occured while invoking API functions - #[cfg_attr(feature = "std", error(transparent))] - Wasm(WasmError), - - /// Insufficient resources available for execution. - #[cfg_attr(feature = "std", error("Can't get {0} from the instance exports"))] - NotInExports(String), -} - -impl From for InstantiationError { - fn from(original: WasmError) -> Self { - Self::Wasm(original) - } -} - -#[cfg(feature = "core")] -impl std::fmt::Display for InstantiationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "InstantiationError") - } -} diff --git a/lib/api/src/js/errors.rs b/lib/api/src/js/errors.rs new file mode 100644 index 00000000000..2f93a80b119 --- /dev/null +++ b/lib/api/src/js/errors.rs @@ -0,0 +1,27 @@ +use crate::js::lib::std::string::String; +pub use crate::js::trap::RuntimeError; +#[cfg(feature = "std")] +use thiserror::Error; +use wasmer_types::ImportError; + +/// The WebAssembly.LinkError object indicates an error during +/// module instantiation (besides traps from the start function). +/// +/// This is based on the [link error][link-error] API. +/// +/// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError +#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Error))] +#[cfg_attr(feature = "std", error("Link error: {0}"))] +pub enum LinkError { + /// An error occurred when checking the import types. + #[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))] + Import(String, String, ImportError), + + /// A trap ocurred during linking. + #[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))] + Trap(#[source] RuntimeError), + /// Insufficient resources available for linking. + #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))] + Resource(String), +} diff --git a/lib/api/src/js/exports.rs b/lib/api/src/js/exports.rs deleted file mode 100644 index 024ab8b9696..00000000000 --- a/lib/api/src/js/exports.rs +++ /dev/null @@ -1,351 +0,0 @@ -use crate::js::externals::{Extern, Function, Global, Memory, Table}; -use crate::js::native::TypedFunction; -use crate::js::store::AsStoreRef; -use crate::js::WasmTypeList; -use indexmap::IndexMap; -use std::fmt; -use std::iter::{ExactSizeIterator, FromIterator}; -use thiserror::Error; - -/// The `ExportError` can happen when trying to get a specific -/// export [`Extern`] from the [`Instance`] exports. -/// -/// [`Instance`]: crate::js::Instance -/// -/// # Examples -/// -/// ## Incompatible export type -/// -/// ```should_panic -/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError}; -/// # let mut store = Store::default(); -/// # let wasm_bytes = wat2wasm(r#" -/// # (module -/// # (global $one (export "glob") f32 (f32.const 1))) -/// # "#.as_bytes()).unwrap(); -/// # let module = Module::new(&store, wasm_bytes).unwrap(); -/// # let import_object = imports! {}; -/// # let instance = Instance::new(&module, &import_object).unwrap(); -/// # -/// // This results with an error: `ExportError::IncompatibleType`. -/// let export = instance.exports.get_function("glob").unwrap(); -/// ``` -/// -/// ## Missing export -/// -/// ```should_panic -/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError}; -/// # let mut store = Store::default(); -/// # let wasm_bytes = wat2wasm("(module)".as_bytes()).unwrap(); -/// # let module = Module::new(&store, wasm_bytes).unwrap(); -/// # let import_object = imports! {}; -/// # let instance = Instance::new(&module, &import_object).unwrap(); -/// # -/// // This results with an error: `ExportError::Missing`. -/// let export = instance.exports.get_function("unknown").unwrap(); -/// ``` -#[derive(Error, Debug)] -pub enum ExportError { - /// An error than occurs when the exported type and the expected type - /// are incompatible. - #[error("Incompatible Export Type")] - IncompatibleType, - /// This error arises when an export is missing - #[error("Missing export {0}")] - Missing(String), -} - -/// Exports is a special kind of map that allows easily unwrapping -/// the types of instances. -/// -/// TODO: add examples of using exports -#[derive(Clone, Default)] -pub struct Exports { - map: IndexMap, -} - -impl Exports { - /// Creates a new `Exports`. - pub fn new() -> Self { - Default::default() - } - - /// Creates a new `Exports` with capacity `n`. - pub fn with_capacity(n: usize) -> Self { - Self { - map: IndexMap::with_capacity(n), - } - } - - /// Return the number of exports in the `Exports` map. - pub fn len(&self) -> usize { - self.map.len() - } - - /// Return whether or not there are no exports - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Insert a new export into this `Exports` map. - pub fn insert(&mut self, name: S, value: E) - where - S: Into, - E: Into, - { - self.map.insert(name.into(), value.into()); - } - - /// Get an export given a `name`. - /// - /// The `get` method is specifically made for usage inside of - /// Rust APIs, as we can detect what's the desired type easily. - /// - /// If you want to get an export dynamically with type checking - /// please use the following functions: `get_func`, `get_memory`, - /// `get_table` or `get_global` instead. - /// - /// If you want to get an export dynamically handling manually - /// type checking manually, please use `get_extern`. - pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> { - match self.map.get(name) { - None => Err(ExportError::Missing(name.to_string())), - Some(extern_) => T::get_self_from_extern(extern_), - } - } - - /// Get an export as a `Global`. - pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> { - self.get(name) - } - - /// Get an export as a `Memory`. - pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> { - self.get(name) - } - - /// Get an export as a `Table`. - pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> { - self.get(name) - } - - /// Get an export as a `Func`. - pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> { - self.get(name) - } - - #[deprecated( - since = "3.0.0", - note = "get_native_function() has been renamed to get_typed_function(), just like NativeFunc has been renamed to TypedFunction." - )] - /// Get an export as a `TypedFunction`. - pub fn get_native_function( - &self, - store: &impl AsStoreRef, - name: &str, - ) -> Result, ExportError> - where - Args: WasmTypeList, - Rets: WasmTypeList, - { - self.get_typed_function(store, name) - } - - /// Get an export as a `TypedFunction`. - pub fn get_typed_function( - &self, - store: &impl AsStoreRef, - name: &str, - ) -> Result, ExportError> - where - Args: WasmTypeList, - Rets: WasmTypeList, - { - self.get_function(name)? - .typed(store) - .map_err(|_| ExportError::IncompatibleType) - } - - /// Hack to get this working with nativefunc too - pub fn get_with_generics<'a, T, Args, Rets>( - &'a self, - store: &impl AsStoreRef, - name: &str, - ) -> Result - where - Args: WasmTypeList, - Rets: WasmTypeList, - T: ExportableWithGenerics<'a, Args, Rets>, - { - match self.map.get(name) { - None => Err(ExportError::Missing(name.to_string())), - Some(extern_) => T::get_self_from_extern_with_generics(store, extern_), - } - } - - /// Like `get_with_generics` but with a WeakReference to the `InstanceRef` internally. - /// This is useful for passing data into Context data, for example. - pub fn get_with_generics_weak<'a, T, Args, Rets>( - &'a self, - store: &impl AsStoreRef, - name: &str, - ) -> Result - where - Args: WasmTypeList, - Rets: WasmTypeList, - T: ExportableWithGenerics<'a, Args, Rets>, - { - let out: T = self.get_with_generics(store, name)?; - Ok(out) - } - - /// Get an export as an `Extern`. - pub fn get_extern(&self, name: &str) -> Option<&Extern> { - self.map.get(name) - } - - /// Returns true if the `Exports` contains the given export name. - pub fn contains(&self, name: S) -> bool - where - S: Into, - { - self.map.contains_key(&name.into()) - } - - /// Get an iterator over the exports. - pub fn iter(&self) -> ExportsIterator> { - ExportsIterator { - iter: self.map.iter(), - } - } -} - -impl fmt::Debug for Exports { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_set().entries(self.iter()).finish() - } -} - -/// An iterator over exports. -pub struct ExportsIterator<'a, I> -where - I: Iterator + Sized, -{ - iter: I, -} - -impl<'a, I> Iterator for ExportsIterator<'a, I> -where - I: Iterator + Sized, -{ - type Item = (&'a String, &'a Extern); - - fn next(&mut self) -> Option { - self.iter.next() - } -} - -impl<'a, I> ExactSizeIterator for ExportsIterator<'a, I> -where - I: Iterator + ExactSizeIterator + Sized, -{ - fn len(&self) -> usize { - self.iter.len() - } -} - -impl<'a, I> ExportsIterator<'a, I> -where - I: Iterator + Sized, -{ - /// Get only the functions. - pub fn functions(self) -> impl Iterator + Sized { - self.iter.filter_map(|(name, export)| match export { - Extern::Function(function) => Some((name, function)), - _ => None, - }) - } - - /// Get only the memories. - pub fn memories(self) -> impl Iterator + Sized { - self.iter.filter_map(|(name, export)| match export { - Extern::Memory(memory) => Some((name, memory)), - _ => None, - }) - } - - /// Get only the globals. - pub fn globals(self) -> impl Iterator + Sized { - self.iter.filter_map(|(name, export)| match export { - Extern::Global(global) => Some((name, global)), - _ => None, - }) - } - - /// Get only the tables. - pub fn tables(self) -> impl Iterator + Sized { - self.iter.filter_map(|(name, export)| match export { - Extern::Table(table) => Some((name, table)), - _ => None, - }) - } -} - -impl FromIterator<(String, Extern)> for Exports { - fn from_iter>(iter: I) -> Self { - Self { - map: IndexMap::from_iter(iter), - } - } -} - -impl IntoIterator for Exports { - type IntoIter = indexmap::map::IntoIter; - type Item = (String, Extern); - - fn into_iter(self) -> Self::IntoIter { - self.map.clone().into_iter() - } -} - -impl<'a> IntoIterator for &'a Exports { - type IntoIter = indexmap::map::Iter<'a, String, Extern>; - type Item = (&'a String, &'a Extern); - - fn into_iter(self) -> Self::IntoIter { - self.map.iter() - } -} - -/// This trait is used to mark types as gettable from an [`Instance`]. -/// -/// [`Instance`]: crate::js::Instance -pub trait Exportable<'a>: Sized { - /// Implementation of how to get the export corresponding to the implementing type - /// from an [`Instance`] by name. - /// - /// [`Instance`]: crate::js::Instance - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>; -} - -/// A trait for accessing exports (like [`Exportable`]) but it takes generic -/// `Args` and `Rets` parameters so that `TypedFunction` can be accessed directly -/// as well. -pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized { - /// Get an export with the given generics. - fn get_self_from_extern_with_generics( - store: &impl AsStoreRef, - _extern: &'a Extern, - ) -> Result; -} - -/// We implement it for all concrete [`Exportable`] types (that are `Clone`) -/// with empty `Args` and `Rets`. -impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T { - fn get_self_from_extern_with_generics( - _store: &impl AsStoreRef, - _extern: &'a Extern, - ) -> Result { - T::get_self_from_extern(_extern).map(|i| i.clone()) - } -} diff --git a/lib/api/src/js/extern_ref.rs b/lib/api/src/js/extern_ref.rs new file mode 100644 index 00000000000..fc0771e6123 --- /dev/null +++ b/lib/api/src/js/extern_ref.rs @@ -0,0 +1,39 @@ +use std::any::Any; + +use crate::js::vm::VMExternRef; +use crate::store::{AsStoreMut, AsStoreRef}; + +#[derive(Debug, Clone)] +#[repr(transparent)] +pub struct ExternRef; + +impl ExternRef { + pub fn new(_store: &mut impl AsStoreMut, _value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + unimplemented!("ExternRef is not yet supported in Javascript"); + } + + pub fn downcast<'a, T>(&self, _store: &'a impl AsStoreRef) -> Option<&'a T> + where + T: Any + Send + Sync + 'static + Sized, + { + unimplemented!("ExternRef is not yet supported in Javascript"); + } + + pub(crate) fn vm_externref(&self) -> VMExternRef { + unimplemented!("ExternRef is not yet supported in Javascript"); + } + + pub(crate) unsafe fn from_vm_externref( + _store: &mut impl AsStoreMut, + _vm_externref: VMExternRef, + ) -> Self { + unimplemented!("ExternRef is not yet supported in Javascript"); + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 7b7115f473b..8ed64561849 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -1,24 +1,22 @@ -pub use self::inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; -use crate::js::exports::{ExportError, Exportable}; -use crate::js::externals::Extern; -use crate::js::function_env::FunctionEnvMut; -use crate::js::store::{AsStoreMut, AsStoreRef, StoreMut}; -use crate::js::types::{param_from_js, AsJs}; /* ValFuncRef */ -use crate::js::vm::VMExtern; -use crate::js::RuntimeError; -use crate::js::TypedFunction; -use crate::js::Value; -use crate::js::{FunctionEnv, FunctionType}; -use js_sys::{Array, Function as JSFunction}; +use crate::errors::RuntimeError; +use crate::externals::function::{HostFunction, HostFunctionKind, WithEnv, WithoutEnv}; +use crate::function_env::{FunctionEnv, FunctionEnvMut}; +use crate::js::as_js::{param_from_js, AsJs}; /* ValFuncRef */ +use crate::js::store::{InternalStoreHandle, StoreHandle}; +use crate::js::vm::{VMExtern, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionEnvironment}; +use crate::native_type::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList}; +use crate::store::{AsStoreMut, AsStoreRef, StoreMut}; +use crate::value::Value; +use std::fmt; use std::iter::FromIterator; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; +use std::marker::PhantomData; +use std::panic::{self, AssertUnwindSafe}; -use crate::js::vm::VMFunction; -use std::fmt; +use wasmer_types::{FunctionType, NativeWasmType, RawValue}; -#[repr(C)] -pub struct VMFunctionBody(u8); +use js_sys::{Array, Function as JSFunction}; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; #[inline] fn result_to_js(val: &Value) -> JsValue { @@ -40,28 +38,15 @@ fn results_to_js_array(values: &[Value]) -> Array { Array::from_iter(values.iter().map(result_to_js)) } -/// 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: -/// -/// # 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(Clone, PartialEq)] pub struct Function { pub(crate) handle: VMFunction, } +// Function can't be Send in js because it dosen't support `structuredClone` +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// unsafe impl Send for Function {} + impl From for Function { fn from(handle: VMFunction) -> Self { Self { handle } @@ -69,60 +54,11 @@ impl From for 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(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self - where - FT: Into, - F: Fn(&[Value]) -> Result, RuntimeError> + 'static + Send + Sync, - { - let env = FunctionEnv::new(&mut store.as_store_mut(), ()); - let wrapped_func = move |_env: FunctionEnvMut<()>, - args: &[Value]| - -> Result, RuntimeError> { func(args) }; - Self::new_with_env(store, &env, ty, wrapped_func) - } - /// To `VMExtern`. pub fn to_vm_extern(&self) -> VMExtern { VMExtern::Function(self.handle.clone()) } - /// 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`] or [`Function::new_typed_with_env`] - /// for less runtime overhead. - /// - /// # Examples - /// - /// ``` - /// # use wasmer::{Function, FunctionType, Type, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); - /// - /// let f = Function::new_with_env(&store, &signature, |args| { - /// let sum = args[0].unwrap_i32() + args[1].unwrap_i32(); - /// Ok(vec![Value::I32(sum)]) - /// }); - /// ``` - /// - /// With constant signature: - /// - /// ``` - /// # use wasmer::{Function, FunctionType, Type, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]); - /// - /// let f = Function::new_with_env(&store, I32_I32_TO_I32, |args| { - /// let sum = args[0].unwrap_i32() + args[1].unwrap_i32(); - /// Ok(vec![Value::I32(sum)]) - /// }); - /// ``` #[allow(clippy::cast_ptr_alignment)] pub fn new_with_env( store: &mut impl AsStoreMut, @@ -194,20 +130,6 @@ impl Function { Self::from_vm_extern(&mut store, vm_function) } - #[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(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(store: &mut impl AsStoreMut, func: F) -> Self where @@ -219,7 +141,7 @@ impl Function { if std::mem::size_of::() != 0 { Self::closures_unsupported_panic(); } - let function = inner::Function::::new(func); + let function = WasmFunction::::new(func); let address = function.address() as usize as u32; let ft = wasm_bindgen::function_table(); @@ -237,41 +159,6 @@ impl Function { } } - #[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 typed function. - pub fn new_native_with_env( - store: &mut impl AsStoreMut, - env: &FunctionEnv, - func: F, - ) -> Self - where - F: HostFunction + 'static + Send + Sync, - Args: WasmTypeList, - Rets: WasmTypeList, - { - Self::new_typed_with_env(store, env, func) - } - - /// Creates a new host `Function` from a typed function. - /// - /// The function signature is automatically retrieved using the - /// Rust typing system. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Store, Function}; - /// # let mut store = Store::default(); - /// # - /// fn sum(a: i32, b: i32) -> i32 { - /// a + b - /// } - /// - /// let f = Function::new_typed_with_env(&store, sum); - /// ``` pub fn new_typed_with_env( store: &mut impl AsStoreMut, env: &FunctionEnv, @@ -286,7 +173,7 @@ impl Function { if std::mem::size_of::() != 0 { Self::closures_unsupported_panic(); } - let function = inner::Function::::new(func); + let function = WasmFunction::::new(func); let address = function.address() as usize as u32; let ft = wasm_bindgen::function_table(); @@ -305,109 +192,27 @@ impl Function { } } - /// Returns the [`FunctionType`] of the `Function`. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Function, Store, Type}; - /// # let mut store = Store::default(); - /// # - /// fn sum(a: i32, b: i32) -> i32 { - /// a + b - /// } - /// - /// let f = Function::new_typed(&store, sum); - /// - /// assert_eq!(f.ty().params(), vec![Type::I32, Type::I32]); - /// assert_eq!(f.ty().results(), vec![Type::I32]); - /// ``` pub fn ty(&self, _store: &impl AsStoreRef) -> FunctionType { self.handle.ty.clone() } - /// Returns the number of parameters that this function takes. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type}; - /// # let mut store = Store::default(); - /// # - /// fn sum(a: i32, b: i32) -> i32 { - /// a + b - /// } - /// - /// let f = Function::new_typed(&store, sum); - /// - /// assert_eq!(f.param_arity(&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(); - /// # - /// fn sum(a: i32, b: i32) -> i32 { - /// a + b - /// } - /// - /// let f = Function::new_typed(&store, sum); - /// - /// assert_eq!(f.result_arity(&store), 1); - /// ``` - pub fn result_arity(&self, store: &impl AsStoreRef) -> usize { - self.ty(store).results().len() + pub fn call_raw( + &self, + _store: &mut impl AsStoreMut, + _params: Vec, + ) -> Result, RuntimeError> { + // There is no optimal call_raw in JS, so we just + // simply rely the call + // self.call(store, params) + unimplemented!(); } - /// 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}; - /// # let mut store = Store::default(); - /// # 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(&module, &import_object).unwrap(); - /// # - /// let sum = instance.exports.get_function("sum").unwrap(); - /// - /// assert_eq!(sum.call(&[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]); - /// ``` pub fn call( &self, store: &mut impl AsStoreMut, params: &[Value], ) -> Result, RuntimeError> { // Annotation is here to prevent spurious IDE warnings. - #[allow(unused_unsafe)] - let params: Vec<_> = unsafe { - params - .iter() - .map(|a| a.as_raw_value(&store.as_store_ref())) - .collect() - }; let arr = js_sys::Array::new_with_length(params.len() as u32); // let raw_env = env.as_raw() as *mut u8; @@ -470,132 +275,15 @@ impl Function { Self { handle: internal } } - #[deprecated(since = "3.0.0", note = "native() has been renamed to typed().")] - /// Transform this WebAssembly function into a typed function. - pub fn native( - &self, - store: &impl AsStoreRef, - ) -> Result, RuntimeError> - where - Args: WasmTypeList, - Rets: WasmTypeList, - { - self.typed(store) + pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef { + unimplemented!(); } - /// Transform this WebAssembly function into a typed function. - /// See [`TypedFunction`] to learn more. - /// - /// # Examples - /// - /// ``` - /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value}; - /// # let mut store = Store::default(); - /// # 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(&module, &import_object).unwrap(); - /// # - /// let sum = instance.exports.get_function("sum").unwrap(); - /// let sum_typed = sum.typed::<(i32, i32), i32>().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, Value}; - /// # let mut store = Store::default(); - /// # 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(&module, &import_object).unwrap(); - /// # - /// let sum = instance.exports.get_function("sum").unwrap(); - /// - /// // This results in an error: `RuntimeError` - /// let sum_typed = sum.typed::<(i64, i64), i32>(&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, Value}; - /// # let mut store = Store::default(); - /// # 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(&module, &import_object).unwrap(); - /// # - /// let sum = instance.exports.get_function("sum").unwrap(); - /// - /// // This results in an error: `RuntimeError` - /// let sum_typed = sum.typed::<(i32, i32), i64>(&mut store).unwrap(); - /// ``` - pub fn typed( - &self, - store: &impl AsStoreRef, - ) -> Result, 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::from_handle(self.clone())) + pub(crate) unsafe fn from_vm_funcref( + _store: &mut impl AsStoreMut, + _funcref: VMFuncRef, + ) -> Self { + unimplemented!(); } #[track_caller] @@ -609,530 +297,59 @@ impl Function { } } -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), - } - } -} - impl fmt::Debug for Function { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.debug_struct("Function").finish() } } -/// This private inner module contains the low-level implementation -/// for `Function` and its siblings. -mod inner { - use super::RuntimeError; - use super::VMFunctionBody; - use crate::js::function_env::{FunctionEnvMut, VMFunctionEnvironment}; - use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle, StoreHandle, StoreMut}; - use crate::js::FunctionEnv; - use crate::js::NativeWasmTypeInto; - 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::{raise_user_trap, resume_panic}; - - /// 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. - 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 this native type belongs to the given store - fn is_from_store(&self, _store: &impl AsStoreRef) -> bool; - } - - 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 - } - - #[inline] - fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { - true // Javascript only has one store - } - } - )* - }; - } - - 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)) - } - - #[inline] - fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { - true // Javascript only has one store - } - } - )* - }; - } - - 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::FromToNativeWasmType; - - #[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<[f64]>; - - /// 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: &[f64], - ) -> Result; - - /// 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 `f64`. - /// - /// # Safety - unsafe fn write_c_struct_to_ptr(c_struct: Self::CStruct, ptr: *mut f64); - - /// 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`. - /// - /// It is mostly used to turn result values of a Wasm function - /// call into a `Result`. - pub trait IntoResult - 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; - } - - impl IntoResult for T - where - T: WasmTypeList, - { - // `T` is not a `Result`, it's already a value, so no error - // can be built. - type Error = Infallible; +/// Represents a low-level Wasm static host function. See +/// `super::Function::new` and `super::Function::new_env` to learn +/// more. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct WasmFunction { + address: *const VMFunctionBody, + _phantom: PhantomData<(Args, Rets)>, +} - fn into_result(self) -> Result { - Ok(self) - } - } +unsafe impl Send for WasmFunction {} - impl IntoResult for Result +impl WasmFunction +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + /// Creates a new `WasmFunction`. + #[allow(dead_code)] + pub fn new(function: F) -> Self where - T: WasmTypeList, - E: Error + Sync + Send + 'static, + F: HostFunction, + T: Sized, { - 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 = x.into_result(); - - assert_eq!(result_of_x.unwrap(), x); - } - - #[test] - fn test_into_result_over_result() { - { - let x: Result = Ok(42); - let result_of_x: Result = 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 = Err(E); - let result_of_x: Result = x.into_result(); - - assert_eq!(result_of_x.unwrap_err(), E); - } + Self { + address: function.function_body_ptr(), + _phantom: PhantomData, } } - /// 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 - 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; + /// Get the function type of this `WasmFunction`. + #[allow(dead_code)] + pub fn ty(&self) -> FunctionType { + FunctionType::new(Args::wasm_types(), Rets::wasm_types()) } - /// 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 {} - } - - /// Represents a low-level Wasm static host function. See - /// `super::Function::new` and `super::Function::new_env` to learn - /// more. - #[derive(Clone, Debug, Hash, PartialEq, Eq)] - pub struct Function { - address: *const VMFunctionBody, - _phantom: PhantomData<(Args, Rets)>, - } - - unsafe impl Send for Function {} - - impl Function - where - Args: WasmTypeList, - Rets: WasmTypeList, - { - /// Creates a new `Function`. - #[allow(dead_code)] - pub fn new(function: F) -> Self - where - F: HostFunction, - T: Sized, - { - Self { - address: function.function_body_ptr(), - _phantom: PhantomData, - } - } - - /// Get the function type of this `Function`. - #[allow(dead_code)] - pub fn ty(&self) -> FunctionType { - FunctionType::new(Args::wasm_types(), Rets::wasm_types()) - } - - /// Get the address of this `Function`. - #[allow(dead_code)] - pub fn address(&self) -> *const VMFunctionBody { - self.address - } + /// Get the address of this `WasmFunction`. + #[allow(dead_code)] + pub fn address(&self) -> *const VMFunctionBody { + self.address } +} - macro_rules! impl_host_function { +macro_rules! impl_host_function { ( [$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { - /// A structure with a C-compatible representation that can hold a set of Wasm values. - /// This type is used by `WasmTypeList::CStruct`. - #[repr($c_struct_representation)] - pub struct $c_struct_name< $( $x ),* > ( $( <<$x as FromToNativeWasmType>::Native as NativeWasmType>::Abi ),* ) - where - $( $x: FromToNativeWasmType ),*; - - // Implement `WasmTypeList` for a specific tuple. - #[allow(unused_parens, dead_code)] - impl< $( $x ),* > - WasmTypeList - for - ( $( $x ),* ) - where - $( $x: FromToNativeWasmType ),* - { - type CStruct = $c_struct_name< $( $x ),* >; - - type Array = [f64; count_idents!( $( $x ),* )]; - - fn size() -> u32 { - count_idents!( $( $x ),* ) as _ - } - - #[allow(unused_mut)] - #[allow(clippy::unused_unit)] - #[allow(clippy::missing_safety_doc)] - unsafe fn from_array(mut _store: &mut impl AsStoreMut, array: Self::Array) -> Self { - // Unpack items of the array. - #[allow(non_snake_case)] - let [ $( $x ),* ] = array; - - // Build the tuple. - ( - $( - FromToNativeWasmType::from_native(NativeWasmTypeInto::from_raw(_store, $x)) - ),* - ) - } - - #[allow(clippy::missing_safety_doc)] - unsafe fn from_slice(store: &mut impl AsStoreMut, slice: &[f64]) -> Result { - Ok(Self::from_array(store, slice.try_into()?)) - } - - #[allow(unused_mut)] - #[allow(clippy::missing_safety_doc)] - unsafe fn into_array(self, mut _store: &mut impl AsStoreMut) -> Self::Array { - // Unpack items of the tuple. - #[allow(non_snake_case)] - let ( $( $x ),* ) = self; - - // Build the array. - [ - $( - FromToNativeWasmType::to_native($x).into_raw(_store) - ),* - ] - } - - fn empty_array() -> Self::Array { - // Build an array initialized with `0`. - [0_f64; count_idents!( $( $x ),* )] - } - - #[allow(unused_mut)] - #[allow(clippy::unused_unit)] - #[allow(clippy::missing_safety_doc)] - unsafe fn from_c_struct(mut _store: &mut impl AsStoreMut, c_struct: Self::CStruct) -> Self { - // Unpack items of the C structure. - #[allow(non_snake_case)] - let $c_struct_name( $( $x ),* ) = c_struct; - - ( - $( - FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(_store, $x)) - ),* - ) - } - - #[allow(unused_parens, non_snake_case, unused_mut)] - #[allow(clippy::missing_safety_doc)] - unsafe fn into_c_struct(self, mut _store: &mut impl AsStoreMut) -> Self::CStruct { - // Unpack items of the tuple. - let ( $( $x ),* ) = self; - - // Build the C structure. - $c_struct_name( - $( - FromToNativeWasmType::to_native($x).into_abi(_store) - ),* - ) - } - - #[allow(non_snake_case)] - unsafe fn write_c_struct_to_ptr(c_struct: Self::CStruct, _ptr: *mut f64) { - // Unpack items of the tuple. - let $c_struct_name( $( $x ),* ) = c_struct; - - let mut _n = 0; - $( - *_ptr.add(_n).cast() = $x; - _n += 1; - )* - } - - fn wasm_types() -> &'static [Type] { - &[ - $( - $x::Native::WASM_TYPE - ),* - ] - } - } - // Implement `HostFunction` for a function with a [`FunctionEnvMut`] that has the same // arity than the tuple. #[allow(unused_parens)] @@ -1239,218 +456,32 @@ mod inner { }; } - // Black-magic to count the number of identifiers at compile-time. - 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 - } - }; - } - - // Here we go! Let's generate all the C struct, `WasmTypeList` - // implementations and `HostFunction` implementations. - 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); - - // 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 = [f64; 0]; - - fn size() -> u32 { - 0 - } - - unsafe fn from_array(_: &mut impl AsStoreMut, _: Self::Array) -> Self { - unreachable!() - } - - unsafe fn from_slice( - _: &mut impl AsStoreMut, - _: &[f64], - ) -> Result { - 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 f64) {} - - fn wasm_types() -> &'static [Type] { - &[] - } - } - - /* - #[cfg(test)] - mod test_wasm_type_list { - use super::*; - use wasmer_types::Type; - - fn test_from_array() { - assert_eq!(<()>::from_array([]), ()); - assert_eq!(::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) - ); - } - - fn test_into_array() { - assert_eq!(().into_array(), [0; 0]); - 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()] - ); - } - - fn test_empty_array() { - assert_eq!(<()>::empty_array().len(), 0); - assert_eq!(::empty_array().len(), 1); - assert_eq!(<(i32, i64)>::empty_array().len(), 2); - } - - fn test_from_c_struct() { - assert_eq!(<()>::from_c_struct(S0()), ()); - assert_eq!(::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) - ); - } - - fn test_wasm_types_for_uni_values() { - assert_eq!(::wasm_types(), [Type::I32]); - assert_eq!(::wasm_types(), [Type::I64]); - assert_eq!(::wasm_types(), [Type::F32]); - assert_eq!(::wasm_types(), [Type::F64]); - } - - 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) - } - - 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]) - ); - } - - 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); - } - } - */ -} +// Here we go! Let's generate all the C struct, `WasmTypeList` +// implementations and `HostFunction` implementations. +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); diff --git a/lib/api/src/js/externals/global.rs b/lib/api/src/js/externals/global.rs index a01bb651e70..5e2f93b703d 100644 --- a/lib/api/src/js/externals/global.rs +++ b/lib/api/src/js/externals/global.rs @@ -1,67 +1,29 @@ -use crate::js::exports::{ExportError, Exportable}; -use crate::js::externals::Extern; -use crate::js::store::{AsStoreMut, AsStoreRef}; -use crate::js::value::Value; -use crate::js::vm::{VMExtern, VMGlobal}; +use crate::errors::RuntimeError; use crate::js::wasm_bindgen_polyfill::Global as JSGlobal; -use crate::js::GlobalType; -use crate::js::Mutability; -use crate::js::RuntimeError; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::value::Value; +use crate::vm::{VMExtern, VMGlobal}; +use crate::GlobalType; +use crate::Mutability; use wasm_bindgen::JsValue; +use wasmer_types::{RawValue, Type}; -/// A WebAssembly `global` instance. -/// -/// A global instance is the runtime representation of a global variable. -/// It consists of an individual value and a flag indicating whether it is mutable. -/// -/// Spec: #[derive(Debug, Clone, PartialEq)] pub struct Global { pub(crate) handle: VMGlobal, } -impl Global { - /// Create a new `Global` with the initial value [`Value`]. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Mutability, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new(&store, Value::I32(1)); - /// - /// assert_eq!(g.get(), Value::I32(1)); - /// assert_eq!(g.ty().mutability, Mutability::Const); - /// ``` - pub fn new(store: &mut impl AsStoreMut, val: Value) -> Self { - Self::from_value(store, val, Mutability::Const).unwrap() - } - - /// Create a mutable `Global` with the initial value [`Value`]. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Mutability, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new_mut(&store, Value::I32(1)); - /// - /// assert_eq!(g.get(), Value::I32(1)); - /// assert_eq!(g.ty().mutability, Mutability::Var); - /// ``` - pub fn new_mut(store: &mut impl AsStoreMut, val: Value) -> Self { - Self::from_value(store, val, Mutability::Var).unwrap() - } +// Global can't be Send in js because it dosen't support `structuredClone` +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// unsafe impl Send for Global {} - /// To `VMExtern`. +impl Global { pub(crate) fn to_vm_extern(&self) -> VMExtern { VMExtern::Global(self.handle.clone()) } /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. - fn from_value( + pub(crate) fn from_value( store: &mut impl AsStoreMut, val: Value, mutability: Mutability, @@ -98,89 +60,45 @@ impl Global { Ok(Self::from_vm_extern(store, vm_global)) } - /// Returns the [`GlobalType`] of the `Global`. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Mutability, Store, Type, Value, GlobalType}; - /// # let mut store = Store::default(); - /// # - /// let c = Global::new(&store, Value::I32(1)); - /// let v = Global::new_mut(&store, Value::I64(1)); - /// - /// assert_eq!(c.ty(), &GlobalType::new(Type::I32, Mutability::Const)); - /// assert_eq!(v.ty(), &GlobalType::new(Type::I64, Mutability::Var)); - /// ``` pub fn ty(&self, _store: &impl AsStoreRef) -> GlobalType { self.handle.ty } - /// Retrieves the current value [`Value`] that the Global has. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new(&store, Value::I32(1)); - /// - /// assert_eq!(g.get(), Value::I32(1)); - /// ``` - pub fn get(&self, store: &impl AsStoreRef) -> Value { + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { unsafe { - match self.handle.global.value().as_f64() { - Some(raw) => { - let ty = self.handle.ty; - Value::from_raw(store, ty.ty, raw) + let value = self.handle.global.value(); + let ty = self.handle.ty; + let raw = match ty.ty { + Type::I32 => RawValue { + i32: value.as_f64().unwrap() as _, + }, + Type::I64 => RawValue { + i64: value.as_f64().unwrap() as _, + }, + Type::F32 => RawValue { + f32: value.as_f64().unwrap() as _, + }, + Type::F64 => RawValue { + f64: value.as_f64().unwrap(), + }, + Type::V128 => RawValue { + u128: value.as_f64().unwrap() as _, + }, + Type::FuncRef => { + unimplemented!(); + // Self::FuncRef(VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(store, f))) } - None => Value::null(), - } + Type::ExternRef => { + unimplemented!(); + // Self::ExternRef( + // VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)), + // ) + } + }; + Value::from_raw(store, ty.ty, raw) } } - /// Sets a custom value [`Value`] to the runtime Global. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new_mut(&store, Value::I32(1)); - /// - /// assert_eq!(g.get(), Value::I32(1)); - /// - /// g.set(Value::I32(2)); - /// - /// assert_eq!(g.get(), Value::I32(2)); - /// ``` - /// - /// # Errors - /// - /// Trying to mutate a immutable global will raise an error: - /// - /// ```should_panic - /// # use wasmer::{Global, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new(&store, Value::I32(1)); - /// - /// g.set(Value::I32(2)).unwrap(); - /// ``` - /// - /// Trying to set a value of a incompatible type will raise an error: - /// - /// ```should_panic - /// # use wasmer::{Global, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new(&store, Value::I32(1)); - /// - /// // This results in an error: `RuntimeError`. - /// g.set(Value::I64(2)).unwrap(); - /// ``` pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { if !val.is_from_store(store) { return Err(RuntimeError::new( @@ -215,17 +133,7 @@ impl Global { Self { handle: vm_global } } - /// Checks whether this `Global` can be used with the given store. pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { true } } - -impl<'a> Exportable<'a> for Global { - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { - match _extern { - Extern::Global(global) => Ok(global), - _ => Err(ExportError::IncompatibleType), - } - } -} diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index 39d5b4732d1..3910000b03b 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -1,8 +1,7 @@ -use crate::js::exports::{ExportError, Exportable}; -use crate::js::externals::Extern; -use crate::js::store::{AsStoreMut, AsStoreRef, StoreObjects}; use crate::js::vm::{VMExtern, VMMemory}; -use crate::js::{MemoryAccessError, MemoryType}; +use crate::mem_access::MemoryAccessError; +use crate::store::{AsStoreMut, AsStoreRef, StoreObjects}; +use crate::MemoryType; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::slice; @@ -13,7 +12,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasmer_types::{Pages, WASM_PAGE_SIZE}; -use super::MemoryView; +use super::memory_view::MemoryView; pub use wasmer_types::MemoryError; @@ -49,48 +48,35 @@ extern "C" { pub fn grow(this: &JSMemory, pages: u32) -> Result; } -/// A WebAssembly `memory` instance. -/// -/// A memory instance is the runtime representation of a linear memory. -/// It consists of a vector of bytes and an optional maximum size. -/// -/// The length of the vector always is a multiple of the WebAssembly -/// page size, which is defined to be the constant 65536 – abbreviated 64Ki. -/// Like in a memory type, the maximum size in a memory instance is -/// given in units of this page size. -/// -/// A memory created by the host or in WebAssembly code will be accessible and -/// mutable from both host and WebAssembly. -/// -/// Spec: #[derive(Debug, Clone)] pub struct Memory { pub(crate) handle: VMMemory, } +// Only SharedMemories can be Send in js, becuase they support `structuredClone`. +// Normal memories will fail while doing structuredClone. +// In this case, we implement Send just in case as it can be a shared memory. +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// ```js +// const memory = new WebAssembly.Memory({ +// initial: 10, +// maximum: 100, +// shared: true // <--- It must be shared, otherwise structuredClone will fail +// }); +// structuredClone(memory) +// ``` unsafe impl Send for Memory {} unsafe impl Sync for Memory {} impl Memory { - /// Creates a new host `Memory` from the provided [`MemoryType`]. - /// - /// This function will construct the `Memory` using the store - /// [`BaseTunables`][crate::js::tunables::BaseTunables]. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; - /// # let mut store = Store::default(); - /// # - /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); - /// ``` pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { - let vm_memory = VMMemory::new(Self::new_internal(ty.clone())?, ty); + let vm_memory = VMMemory::new(Self::js_memory_from_type(&ty)?, ty); Ok(Self::from_vm_extern(store, vm_memory)) } - pub(crate) fn new_internal(ty: MemoryType) -> Result { + pub(crate) fn js_memory_from_type( + ty: &MemoryType, + ) -> Result { let descriptor = js_sys::Object::new(); // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] @@ -108,79 +94,22 @@ impl Memory { Ok(js_memory) } - /// Creates a new host `Memory` from provided JavaScript memory. - pub fn new_raw( - store: &mut impl AsStoreMut, - js_memory: js_sys::WebAssembly::Memory, - ty: MemoryType, - ) -> Result { - let vm_memory = VMMemory::new(js_memory, ty); - Ok(Self::from_vm_extern(store, vm_memory)) - } - - /// Create a memory object from an existing memory and attaches it to the store pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { Self::from_vm_extern(new_store, memory) } - /// To `VMExtern`. pub(crate) fn to_vm_extern(&self) -> VMExtern { VMExtern::Memory(self.handle.clone()) } - /// Returns the [`MemoryType`] of the `Memory`. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; - /// # let mut store = Store::default(); - /// # - /// let mt = MemoryType::new(1, None, false); - /// let m = Memory::new(&store, mt).unwrap(); - /// - /// assert_eq!(m.ty(), mt); - /// ``` pub fn ty(&self, _store: &impl AsStoreRef) -> MemoryType { self.handle.ty } - /// Creates a view into the memory that then allows for - /// read and write pub fn view(&self, store: &impl AsStoreRef) -> MemoryView { MemoryView::new(self, store) } - /// Grow memory by the specified amount of WebAssembly [`Pages`] and return - /// the previous memory size. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; - /// # let mut store = Store::default(); - /// # - /// let m = Memory::new(&store, MemoryType::new(1, Some(3), false)).unwrap(); - /// let p = m.grow(2).unwrap(); - /// - /// assert_eq!(p, Pages(1)); - /// assert_eq!(m.size(), Pages(3)); - /// ``` - /// - /// # Errors - /// - /// Returns an error if memory can't be grown by the specified amount - /// of pages. - /// - /// ```should_panic - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; - /// # let mut store = Store::default(); - /// # - /// let m = Memory::new(&store, MemoryType::new(1, Some(1), false)).unwrap(); - /// - /// // This results in an error: `MemoryError::CouldNotGrow`. - /// let s = m.grow(1).unwrap(); - /// ``` pub fn grow( &self, store: &mut impl AsStoreMut, @@ -205,7 +134,6 @@ impl Memory { Ok(Pages(new_pages)) } - /// Copies the memory to a new store and returns a memory reference to it pub fn copy_to_store( &self, store: &impl AsStoreRef, @@ -238,17 +166,15 @@ impl Memory { Self { handle: internal } } - /// Attempts to clone this memory (if its clonable) pub fn try_clone(&self, _store: &impl AsStoreRef) -> Option { self.handle.try_clone() } - /// Checks whether this `Global` can be used with the given context. pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { true } - /// Copies this memory to a new memory + #[allow(unused)] pub fn duplicate(&mut self, _store: &impl AsStoreRef) -> Result { self.handle.duplicate() } @@ -260,17 +186,8 @@ impl std::cmp::PartialEq for Memory { } } -impl<'a> Exportable<'a> for Memory { - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { - match _extern { - Extern::Memory(memory) => Ok(memory), - _ => Err(ExportError::IncompatibleType), - } - } -} - /// Underlying buffer for a memory. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) struct MemoryBuffer<'a> { pub(crate) base: *mut js_sys::Uint8Array, pub(crate) marker: PhantomData<(&'a Memory, &'a StoreObjects)>, diff --git a/lib/api/src/js/externals/memory_view.rs b/lib/api/src/js/externals/memory_view.rs index 78b29247eb6..a5e6c4df5fa 100644 --- a/lib/api/src/js/externals/memory_view.rs +++ b/lib/api/src/js/externals/memory_view.rs @@ -1,5 +1,5 @@ -use crate::js::store::AsStoreRef; -use crate::js::MemoryAccessError; +use crate::mem_access::MemoryAccessError; +use crate::store::AsStoreRef; use std::convert::TryInto; use std::marker::PhantomData; use std::mem::MaybeUninit; @@ -10,8 +10,7 @@ use wasm_bindgen::JsCast; use wasmer_types::{Bytes, Pages}; -use super::memory::MemoryBuffer; -use super::Memory; +use super::memory::{Memory, MemoryBuffer}; /// A WebAssembly `memory` view. /// @@ -105,6 +104,7 @@ impl<'a> MemoryView<'a> { Bytes(self.size as usize).try_into().unwrap() } + #[inline] pub(crate) fn buffer(&self) -> MemoryBuffer<'a> { MemoryBuffer { base: &self.view as *const _ as *mut _, @@ -170,11 +170,11 @@ impl<'a> MemoryView<'a> { /// /// This method is guaranteed to be safe (from the host side) in the face of /// concurrent writes. - pub fn read_uninit( + pub fn read_uninit<'b>( &self, offset: u64, - buf: &'a mut [MaybeUninit], - ) -> Result<&'a mut [u8], MemoryAccessError> { + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { let view = &self.view; let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; let len: u32 = buf @@ -254,6 +254,7 @@ impl<'a> MemoryView<'a> { } /// Copies the memory and returns it as a vector of bytes + #[allow(unused)] pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { let mut new_memory = Vec::new(); let mut offset = 0; diff --git a/lib/api/src/js/externals/mod.rs b/lib/api/src/js/externals/mod.rs index 6dd72eb6ff7..3575fe23bb5 100644 --- a/lib/api/src/js/externals/mod.rs +++ b/lib/api/src/js/externals/mod.rs @@ -1,134 +1,5 @@ pub(crate) mod function; -mod global; +pub(crate) mod global; pub(crate) mod memory; pub(crate) mod memory_view; -mod table; - -pub use self::function::{FromToNativeWasmType, Function, HostFunction, WasmTypeList}; -pub use self::global::Global; -pub use self::memory::{Memory, MemoryError}; -pub use self::memory_view::MemoryView; -pub use self::table::Table; - -use crate::js::exports::{ExportError, Exportable}; -use crate::js::store::{AsStoreMut, AsStoreRef}; -use crate::js::types::AsJs; -use crate::js::vm::VMExtern; -use std::fmt; -use wasmer_types::ExternType; - -/// An `Extern` is the runtime representation of an entity that -/// can be imported or exported. -/// -/// Spec: -#[derive(Clone)] -pub enum Extern { - /// A external [`Function`]. - Function(Function), - /// A external [`Global`]. - Global(Global), - /// A external [`Table`]. - Table(Table), - /// A external [`Memory`]. - Memory(Memory), -} - -impl Extern { - /// Return the underlying type of the inner `Extern`. - pub fn ty(&self, store: &impl AsStoreRef) -> ExternType { - match self { - Self::Function(ft) => ExternType::Function(ft.ty(store).clone()), - Self::Memory(ft) => ExternType::Memory(ft.ty(store)), - Self::Table(tt) => ExternType::Table(tt.ty(store)), - Self::Global(gt) => ExternType::Global(gt.ty(store)), - } - } - - /// Create an `Extern` from an `wasmer_engine::Export`. - pub fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExtern) -> Self { - match vm_extern { - VMExtern::Function(f) => Self::Function(Function::from_vm_extern(store, f)), - VMExtern::Memory(m) => Self::Memory(Memory::from_vm_extern(store, m)), - VMExtern::Global(g) => Self::Global(Global::from_vm_extern(store, g)), - VMExtern::Table(t) => Self::Table(Table::from_vm_extern(store, t)), - } - } - - /// To `VMExtern`. - pub fn to_vm_extern(&self) -> VMExtern { - match self { - Self::Function(f) => f.to_vm_extern(), - Self::Global(g) => g.to_vm_extern(), - Self::Memory(m) => m.to_vm_extern(), - Self::Table(t) => t.to_vm_extern(), - } - } - - /// Checks whether this `Extern` can be used with the given context. - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { - match self { - Self::Function(val) => val.is_from_store(store), - Self::Memory(val) => val.is_from_store(store), - Self::Global(val) => val.is_from_store(store), - Self::Table(val) => val.is_from_store(store), - } - } -} - -impl AsJs for Extern { - fn as_jsvalue(&self, store: &impl AsStoreRef) -> wasm_bindgen::JsValue { - match self { - Self::Function(_) => self.to_vm_extern().as_jsvalue(store), - Self::Global(_) => self.to_vm_extern().as_jsvalue(store), - Self::Table(_) => self.to_vm_extern().as_jsvalue(store), - Self::Memory(_) => self.to_vm_extern().as_jsvalue(store), - } - .clone() - } -} - -impl<'a> Exportable<'a> for Extern { - fn get_self_from_extern(_extern: &'a Self) -> Result<&'a Self, ExportError> { - // Since this is already an extern, we can just return it. - Ok(_extern) - } -} - -impl fmt::Debug for Extern { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::Function(_) => "Function(...)", - Self::Global(_) => "Global(...)", - Self::Memory(_) => "Memory(...)", - Self::Table(_) => "Table(...)", - } - ) - } -} - -impl From for Extern { - fn from(r: Function) -> Self { - Self::Function(r) - } -} - -impl From for Extern { - fn from(r: Global) -> Self { - Self::Global(r) - } -} - -impl From for Extern { - fn from(r: Memory) -> Self { - Self::Memory(r) - } -} - -impl From
for Extern { - fn from(r: Table) -> Self { - Self::Table(r) - } -} +pub(crate) mod table; diff --git a/lib/api/src/js/externals/table.rs b/lib/api/src/js/externals/table.rs index 6e9860042f4..c16fee03230 100644 --- a/lib/api/src/js/externals/table.rs +++ b/lib/api/src/js/externals/table.rs @@ -1,26 +1,20 @@ -use crate::js::exports::{ExportError, Exportable}; -use crate::js::externals::Extern; -use crate::js::store::{AsStoreMut, AsStoreRef}; -use crate::js::value::Value; -use crate::js::vm::{VMExtern, VMFunction, VMTable}; -use crate::js::RuntimeError; -use crate::js::{FunctionType, TableType}; +use crate::errors::RuntimeError; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::value::Value; +use crate::vm::VMExternTable; +use crate::vm::{VMExtern, VMFunction, VMTable}; +use crate::{FunctionType, TableType}; use js_sys::Function; -/// A WebAssembly `table` instance. -/// -/// The `Table` struct is an array-like structure representing a WebAssembly Table, -/// which stores function references. -/// -/// A table created by the host or in WebAssembly code will be accessible and -/// mutable from both host and WebAssembly. -/// -/// Spec: #[derive(Debug, Clone, PartialEq)] pub struct Table { pub(crate) handle: VMTable, } +// Table can't be Send in js because it dosen't support `structuredClone` +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// unsafe impl Send for Table {} + fn set_table_item(table: &VMTable, item_index: u32, item: &Function) -> Result<(), RuntimeError> { table.table.set(item_index, item).map_err(|e| e.into()) } @@ -30,19 +24,13 @@ fn get_function(store: &mut impl AsStoreMut, val: Value) -> Result 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!(), + _ => unimplemented!("The {val:?} is not yet supported"), } } impl Table { - /// Creates a new `Table` with the provided [`TableType`] definition. - /// - /// All the elements in the table will be set to the `init` value. - /// - /// This function will construct the `Table` using the store - /// [`BaseTunables`][crate::js::tunables::BaseTunables]. pub fn new( store: &mut impl AsStoreMut, ty: TableType, @@ -68,29 +56,25 @@ impl Table { Ok(Self { handle: table }) } - /// To `VMExtern`. pub fn to_vm_extern(&self) -> VMExtern { VMExtern::Table(self.handle.clone()) } - /// Returns the [`TableType`] of the `Table`. pub fn ty(&self, _store: &impl AsStoreRef) -> TableType { self.handle.ty } - /// Retrieves an element of the table at the provided `index`. pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { if let Some(func) = self.handle.table.get(index).ok() { let ty = FunctionType::new(vec![], vec![]); let vm_function = VMFunction::new(func, ty); - let function = crate::js::externals::Function::from_vm_extern(store, vm_function); + let function = crate::Function::from_vm_extern(store, vm_function); Some(Value::FuncRef(Some(function))) } else { None } } - /// Sets an element `val` in the Table at the provided `index`. pub fn set( &self, store: &mut impl AsStoreMut, @@ -101,20 +85,10 @@ impl Table { set_table_item(&self.handle, index, &item) } - /// Retrieves the size of the `Table` (in elements) pub fn size(&self, _store: &impl AsStoreRef) -> u32 { self.handle.table.length() } - /// Grows the size of the `Table` by `delta`, initializating - /// the elements with the provided `init` value. - /// - /// It returns the previous size of the `Table` in case is able - /// to grow the Table successfully. - /// - /// # Errors - /// - /// Returns an error if the `delta` is out of bounds for the table. pub fn grow( &self, _store: &mut impl AsStoreMut, @@ -124,13 +98,6 @@ impl Table { unimplemented!(); } - /// Copies the `len` elements of `src_table` starting at `src_index` - /// to the destination table `dst_table` at index `dst_index`. - /// - /// # Errors - /// - /// Returns an error if the range is out of bounds of either the source or - /// destination tables. pub fn copy( _store: &mut impl AsStoreMut, _dst_table: &Self, @@ -142,36 +109,11 @@ impl Table { unimplemented!("Table.copy is not natively supported in Javascript"); } - pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMTable) -> Self { - Self { handle: internal } + pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { + Self { handle: vm_extern } } - /// Checks whether this `Table` can be used with the given context. pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { true } - - /// Get access to the backing VM value for this extern. This function is for - /// tests it should not be called by users of the Wasmer API. - /// - /// # Safety - /// This function is unsafe to call outside of tests for the wasmer crate - /// because there is no stability guarantee for the returned type and we may - /// make breaking changes to it at any time or remove this method. - #[doc(hidden)] - pub unsafe fn get_vm_table<'context>( - &'context self, - _store: &'context impl AsStoreRef, - ) -> &'context VMTable { - &self.handle - } -} - -impl<'a> Exportable<'a> for Table { - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { - match _extern { - Extern::Table(table) => Ok(table), - _ => Err(ExportError::IncompatibleType), - } - } } diff --git a/lib/api/src/js/function_env.rs b/lib/api/src/js/function_env.rs deleted file mode 100644 index 8b3e0a926a7..00000000000 --- a/lib/api/src/js/function_env.rs +++ /dev/null @@ -1,184 +0,0 @@ -use std::{any::Any, marker::PhantomData}; - -use crate::js::{StoreHandle, StoreObjects}; - -use crate::js::{AsStoreMut, AsStoreRef, StoreMut, StoreRef}; - -#[derive(Debug)] -#[repr(transparent)] -/// An opaque reference to a function environment. -/// The function environment data is owned by the `Store`. -pub struct FunctionEnv { - pub(crate) handle: StoreHandle, - marker: PhantomData, -} - -impl FunctionEnv { - /// Make a new extern reference - pub fn new(store: &mut impl AsStoreMut, value: T) -> Self - where - T: Any + Send + 'static + Sized, - { - Self { - handle: StoreHandle::new( - store.as_store_mut().objects_mut(), - VMFunctionEnvironment::new(value), - ), - marker: PhantomData, - } - } - - pub(crate) fn from_handle(handle: StoreHandle) -> Self { - Self { - handle, - marker: PhantomData, - } - } - - /// Get the data as reference - pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T - where - T: Any + Send + 'static + Sized, - { - self.handle - .get(store.as_store_ref().objects()) - .as_ref() - .downcast_ref::() - .unwrap() - } - - /// Get the data as mutable - pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T - where - T: Any + Send + 'static + Sized, - { - self.handle - .get_mut(store.objects_mut()) - .as_mut() - .downcast_mut::() - .unwrap() - } - - /// Convert it into a `FunctionEnvMut` - pub fn into_mut(self, store: &mut impl AsStoreMut) -> FunctionEnvMut - where - T: Any + Send + 'static + Sized, - { - FunctionEnvMut { - store_mut: store.as_store_mut(), - func_env: self, - } - } -} - -impl PartialEq for FunctionEnv { - fn eq(&self, other: &Self) -> bool { - self.handle == other.handle - } -} - -impl Eq for FunctionEnv {} - -impl std::hash::Hash for FunctionEnv { - fn hash(&self, state: &mut H) { - self.handle.hash(state); - self.marker.hash(state); - } -} - -impl Clone for FunctionEnv { - fn clone(&self) -> Self { - Self { - handle: self.handle.clone(), - marker: self.marker, - } - } -} - -/// A temporary handle to a [`Context`]. -pub struct FunctionEnvMut<'a, T: 'a> { - pub(crate) store_mut: StoreMut<'a>, - pub(crate) func_env: FunctionEnv, -} - -impl FunctionEnvMut<'_, T> { - /// Returns a reference to the host state in this context. - pub fn data(&self) -> &T { - self.func_env.as_ref(&self.store_mut) - } - - /// Returns a mutable- reference to the host state in this context. - pub fn data_mut<'a>(&'a mut self) -> &'a mut T { - self.func_env.as_mut(&mut self.store_mut) - } - - /// Borrows a new immmutable reference - pub fn as_ref(&self) -> FunctionEnv { - self.func_env.clone() - } - - /// Borrows a new mutable reference - pub fn as_mut<'a>(&'a mut self) -> FunctionEnvMut<'a, T> { - FunctionEnvMut { - store_mut: self.store_mut.as_store_mut(), - func_env: self.func_env.clone(), - } - } - - /// Borrows a new mutable reference of both the attached Store and host state - pub fn data_and_store_mut(&mut self) -> (&mut T, StoreMut) { - let data = self.func_env.as_mut(&mut self.store_mut) as *mut T; - // telling the borrow check to close his eyes here - // this is still relatively safe to do as func_env are - // stored in a specific vec of Store, separate from the other objects - // and not really directly accessible with the StoreMut - let data = unsafe { &mut *data }; - (data, self.store_mut.as_store_mut()) - } -} - -impl AsStoreRef for FunctionEnvMut<'_, T> { - fn as_store_ref(&self) -> StoreRef<'_> { - StoreRef { - inner: self.store_mut.inner, - } - } -} - -impl AsStoreMut for FunctionEnvMut<'_, T> { - fn as_store_mut(&mut self) -> StoreMut<'_> { - StoreMut { - inner: self.store_mut.inner, - } - } - #[inline] - fn objects_mut(&mut self) -> &mut StoreObjects { - &mut self.store_mut.inner.objects - } -} - -/// Underlying FunctionEnvironment used by a `VMFunction`. -pub struct VMFunctionEnvironment { - contents: Box, -} - -impl VMFunctionEnvironment { - /// Wraps the given value to expose it to Wasm code as a function context. - pub fn new(val: impl Any + Send + 'static) -> Self { - Self { - contents: Box::new(val), - } - } - - #[allow(clippy::should_implement_trait)] - /// Returns a reference to the underlying value. - pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { - &*self.contents - } - - #[allow(clippy::should_implement_trait)] - /// Returns a mutable reference to the underlying value. - pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { - &mut *self.contents - } -} diff --git a/lib/api/src/js/imports.rs b/lib/api/src/js/imports.rs deleted file mode 100644 index ae76b7e0ace..00000000000 --- a/lib/api/src/js/imports.rs +++ /dev/null @@ -1,460 +0,0 @@ -//! The import module contains the implementation data structures and helper functions used to -//! manipulate and access a wasm module's imports including memories, tables, globals, and -//! functions. -use crate::js::error::{LinkError, WasmError}; -use crate::js::exports::Exports; -use crate::js::module::Module; -use crate::js::store::{AsStoreMut, AsStoreRef}; -use crate::js::types::AsJs; -use crate::js::vm::VMExtern; -use crate::js::ExternType; -use crate::Extern; -use std::collections::HashMap; -use std::fmt; -use wasmer_types::ImportError; - -/// All of the import data used when instantiating. -/// -/// It's suggested that you use the [`imports!`] macro -/// instead of creating an `Imports` by hand. -/// -/// [`imports!`]: macro.imports.html -/// -/// # Usage: -/// ```no_run -/// use wasmer::{Exports, Module, Store, Instance, imports, Imports, Function}; -/// # fn foo_test(module: Module, store: Store) { -/// -/// let host_fn = Function::new_typed(foo); -/// let import_object: Imports = imports! { -/// "env" => { -/// "foo" => host_fn, -/// }, -/// }; -/// -/// let instance = Instance::new(&module, &import_object).expect("Could not instantiate module."); -/// -/// fn foo(n: i32) -> i32 { -/// n -/// } -/// -/// # } -/// ``` -#[derive(Clone, Default)] -pub struct Imports { - map: HashMap<(String, String), Extern>, -} - -impl Imports { - /// Create a new `Imports`. - pub fn new() -> Self { - Default::default() - } - - /// Gets an export given a ns and a name - /// - /// # Usage - /// ```no_run - /// # use wasmer::Imports; - /// let mut import_object = Imports::new(); - /// import_object.get_export("ns", "name"); - /// ``` - pub fn get_export(&self, ns: &str, name: &str) -> Option { - if self.map.contains_key(&(ns.to_string(), name.to_string())) { - let ext = &self.map[&(ns.to_string(), name.to_string())]; - return Some(ext.clone()); - } - None - } - - /// Returns true if the Imports contains namespace with the provided name. - pub fn contains_namespace(&self, name: &str) -> bool { - self.map.keys().any(|(k, _)| (k == name)) - } - - /// Register a list of externs into a namespace. - /// - /// # Usage: - /// ```no_run - /// # use wasmer::{Imports, Exports, Memory}; - /// # fn foo_test(memory: Memory) { - /// let mut exports = Exports::new() - /// exports.insert("memory", memory); - /// - /// let mut import_object = Imports::new(); - /// import_object.register_namespace("env", exports); - /// // ... - /// # } - /// ``` - pub fn register_namespace( - &mut self, - ns: &str, - contents: impl IntoIterator, - ) { - for (name, extern_) in contents.into_iter() { - self.map.insert((ns.to_string(), name.clone()), extern_); - } - } - - /// Add a single import with a namespace `ns` and name `name`. - /// - /// # Usage - /// ```no_run - /// # let mut store = Default::default(); - /// use wasmer::{Imports, Function}; - /// fn foo(n: i32) -> i32 { - /// n - /// } - /// let mut import_object = Imports::new(); - /// import_object.define("env", "foo", Function::new_typed(&store, foo)); - /// ``` - pub fn define(&mut self, ns: &str, name: &str, val: impl Into) { - self.map - .insert((ns.to_string(), name.to_string()), val.into()); - } - - /// Returns the contents of a namespace as an `Exports`. - /// - /// Returns `None` if the namespace doesn't exist. - pub fn get_namespace_exports(&self, name: &str) -> Option { - let ret: Exports = self - .map - .iter() - .filter(|((ns, _), _)| ns == name) - .map(|((_, name), e)| (name.clone(), e.clone())) - .collect(); - if ret.is_empty() { - None - } else { - Some(ret) - } - } - - /// Resolve and return a vector of imports in the order they are defined in the `module`'s source code. - /// - /// This means the returned `Vec` might be a subset of the imports contained in `self`. - pub fn imports_for_module( - &self, - module: &Module, - _store: &mut impl AsStoreMut, - ) -> Result, LinkError> { - let mut ret = vec![]; - for import in module.imports() { - if let Some(imp) = self - .map - .get(&(import.module().to_string(), import.name().to_string())) - { - ret.push(imp.clone()); - } else { - return Err(LinkError::Import( - import.module().to_string(), - import.name().to_string(), - ImportError::UnknownImport(import.ty().clone()), - )); - } - } - Ok(ret) - } - - /// Returns the `Imports` as a Javascript `Object` - pub fn as_jsobject(&self, store: &impl AsStoreRef) -> js_sys::Object { - let imports = js_sys::Object::new(); - let namespaces: HashMap<&str, Vec<(&str, &Extern)>> = - self.map - .iter() - .fold(HashMap::default(), |mut acc, ((ns, name), ext)| { - acc.entry(ns.as_str()) - .or_default() - .push((name.as_str(), ext)); - acc - }); - - for (ns, exports) in namespaces.into_iter() { - let import_namespace = js_sys::Object::new(); - for (name, ext) in exports { - // Annotation is here to prevent spurious IDE warnings. - #[allow(unused_unsafe)] - unsafe { - js_sys::Reflect::set(&import_namespace, &name.into(), &ext.as_jsvalue(store)) - .expect("Error while setting into the js namespace object"); - } - } - // Annotation is here to prevent spurious IDE warnings. - #[allow(unused_unsafe)] - unsafe { - js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into()) - .expect("Error while setting into the js imports object"); - } - } - imports - } - - /// Iterates through all the imports in this structure - pub fn iter<'a>(&'a self) -> ImportsIterator<'a> { - ImportsIterator::new(self) - } - - /// Create a new `Imports` from a JS Object, it receives a reference to a `Module` to - /// map and assign the types of each import and the JS Object - /// that contains the values of imports. - /// - /// # Usage - /// ```ignore - /// let import_object = Imports::new_from_js_object(&mut store, &module, js_object); - /// ``` - pub fn new_from_js_object( - store: &mut impl AsStoreMut, - module: &Module, - object: js_sys::Object, - ) -> Result { - let module_imports: HashMap<(String, String), ExternType> = module - .imports() - .map(|import| { - ( - (import.module().to_string(), import.name().to_string()), - import.ty().clone(), - ) - }) - .collect::>(); - - let mut map: HashMap<(String, String), Extern> = HashMap::new(); - - for module_entry in js_sys::Object::entries(&object).iter() { - let module_entry: js_sys::Array = module_entry.into(); - let module_name = module_entry.get(0).as_string().unwrap().to_string(); - let module_import_object: js_sys::Object = module_entry.get(1).into(); - for import_entry in js_sys::Object::entries(&module_import_object).iter() { - let import_entry: js_sys::Array = import_entry.into(); - let import_name = import_entry.get(0).as_string().unwrap().to_string(); - let import_js: wasm_bindgen::JsValue = import_entry.get(1); - let key = (module_name.clone(), import_name); - let extern_type = module_imports.get(&key).unwrap(); - let export = VMExtern::from_js_value(import_js, store, extern_type.clone())?; - let extern_ = Extern::from_vm_extern(store, export); - map.insert(key, extern_); - } - } - - Ok(Self { map }) - } -} - -impl AsJs for Imports { - // Annotation is here to prevent spurious IDE warnings. - #[allow(unused_unsafe)] - fn as_jsvalue(&self, store: &impl AsStoreRef) -> wasm_bindgen::JsValue { - let imports_object = js_sys::Object::new(); - for (namespace, name, extern_) in self.iter() { - let val = unsafe { js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap() }; - if !val.is_undefined() { - // If the namespace is already set - - // Annotation is here to prevent spurious IDE warnings. - #[allow(unused_unsafe)] - unsafe { - js_sys::Reflect::set( - &val, - &name.into(), - &extern_.as_jsvalue(&store.as_store_ref()), - ) - .unwrap(); - } - } else { - // If the namespace doesn't exist - let import_namespace = js_sys::Object::new(); - #[allow(unused_unsafe)] - unsafe { - js_sys::Reflect::set( - &import_namespace, - &name.into(), - &extern_.as_jsvalue(&store.as_store_ref()), - ) - .unwrap(); - js_sys::Reflect::set( - &imports_object, - &namespace.into(), - &import_namespace.into(), - ) - .unwrap(); - } - } - } - imports_object.into() - } -} - -pub struct ImportsIterator<'a> { - iter: std::collections::hash_map::Iter<'a, (String, String), Extern>, -} - -impl<'a> ImportsIterator<'a> { - fn new(imports: &'a Imports) -> Self { - let iter = imports.map.iter(); - Self { iter } - } -} - -impl<'a> Iterator for ImportsIterator<'a> { - type Item = (&'a str, &'a str, &'a Extern); - - fn next(&mut self) -> Option { - self.iter - .next() - .map(|(k, v)| (k.0.as_str(), k.1.as_str(), v)) - } -} - -impl IntoIterator for &Imports { - type IntoIter = std::collections::hash_map::IntoIter<(String, String), Extern>; - type Item = ((String, String), Extern); - - fn into_iter(self) -> Self::IntoIter { - self.map.clone().into_iter() - } -} - -impl Extend<((String, String), Extern)> for Imports { - fn extend>(&mut self, iter: T) { - for ((ns, name), ext) in iter.into_iter() { - self.define(&ns, &name, ext); - } - } -} - -impl fmt::Debug for Imports { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - enum SecretMap { - Empty, - Some(usize), - } - - impl SecretMap { - fn new(len: usize) -> Self { - if len == 0 { - Self::Empty - } else { - Self::Some(len) - } - } - } - - impl fmt::Debug for SecretMap { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Empty => write!(f, "(empty)"), - Self::Some(len) => write!(f, "(... {} item(s) ...)", len), - } - } - } - - f.debug_struct("Imports") - .field("map", &SecretMap::new(self.map.len())) - .finish() - } -} - -// The import! macro for Imports - -/// Generate an [`Imports`] easily with the `imports!` macro. -/// -/// [`Imports`]: struct.Imports.html -/// -/// # Usage -/// -/// ``` -/// # use wasmer::{Function, Store}; -/// # let mut store = Store::default(); -/// use wasmer::imports; -/// -/// let import_object = imports! { -/// "env" => { -/// "foo" => Function::new_typed(&store, foo) -/// }, -/// }; -/// -/// fn foo(n: i32) -> i32 { -/// n -/// } -/// ``` -#[macro_export] -macro_rules! imports { - ( $( $ns_name:expr => $ns:tt ),* $(,)? ) => { - { - let mut import_object = $crate::Imports::new(); - - $({ - let namespace = $crate::import_namespace!($ns); - - import_object.register_namespace($ns_name, namespace); - })* - - import_object - } - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! namespace { - ($( $import_name:expr => $import_item:expr ),* $(,)? ) => { - $crate::import_namespace!( { $( $import_name => $import_item, )* } ) - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! import_namespace { - ( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {{ - let mut namespace = $crate::Exports::new(); - - $( - namespace.insert($import_name, $import_item); - )* - - namespace - }}; - - ( $namespace:ident ) => { - $namespace - }; -} - -#[cfg(test)] -mod test { - use crate::js::{Global, Store, Value}; - - // use wasm_bindgen::*; - use wasm_bindgen_test::*; - - #[wasm_bindgen_test] - fn chaining_works() { - let mut store = Store::default(); - - let g = Global::new(&mut store, Value::I32(0)); - - let mut imports1 = imports! { - "dog" => { - "happy" => g.clone() - } - }; - - let imports2 = imports! { - "dog" => { - "small" => g.clone() - }, - "cat" => { - "small" => g - } - }; - - imports1.extend(&imports2); - - let small_cat_export = imports1.get_export("cat", "small"); - assert!(small_cat_export.is_some()); - - let happy = imports1.get_export("dog", "happy"); - let small = imports1.get_export("dog", "small"); - assert!(happy.is_some()); - assert!(small.is_some()); - } -} diff --git a/lib/api/src/js/instance.rs b/lib/api/src/js/instance.rs index 04bcf49091c..30ff0b89d9b 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/js/instance.rs @@ -1,91 +1,41 @@ -use crate::js::error::InstantiationError; -use crate::js::exports::Exports; -use crate::js::externals::Extern; -use crate::js::imports::Imports; -use crate::js::module::Module; -use crate::js::store::{AsStoreMut, AsStoreRef}; -use crate::js::vm::{VMExtern, VMInstance}; +use crate::errors::InstantiationError; +use crate::exports::Exports; +use crate::imports::Imports; +use crate::js::as_js::AsJs; +use crate::js::vm::VMInstance; +use crate::module::Module; +use crate::store::AsStoreMut; +use crate::Extern; use js_sys::WebAssembly; -use std::fmt; -/// A WebAssembly Instance is a stateful, executable -/// instance of a WebAssembly [`Module`]. -/// -/// Instance objects contain all the exported WebAssembly -/// functions, memories, tables and globals that allow -/// interacting with WebAssembly. -/// -/// Spec: -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct Instance { - handle: VMInstance, - module: Module, - /// The exports for an instance. - pub exports: Exports, + pub(crate) _handle: VMInstance, } +// Instance can't be Send in js because it dosen't support `structuredClone` +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// unsafe impl Send for Instance {} + impl Instance { - /// Creates a new `Instance` from a WebAssembly [`Module`] and a - /// set of imports resolved by the [`Resolver`]. - /// - /// The resolver can be anything that implements the [`Resolver`] trait, - /// so you can plug custom resolution for the imports, if you wish not - /// to use [`ImportObject`]. - /// - /// The [`ImportObject`] is the easiest way to provide imports to the instance. - /// - /// [`ImportObject`]: crate::js::ImportObject - /// - /// ``` - /// # use wasmer::{imports, Store, Module, Global, Value, Instance}; - /// # fn main() -> anyhow::Result<()> { - /// let mut store = Store::default(); - /// let module = Module::new(&store, "(module)")?; - /// let imports = imports!{ - /// "host" => { - /// "var" => Global::new(&store, Value::I32(2)) - /// } - /// }; - /// let instance = Instance::new(&module, &imports)?; - /// # Ok(()) - /// # } - /// ``` - /// - /// ## Errors - /// - /// The function can return [`InstantiationError`]s. - /// - /// Those are, as defined by the spec: - /// * Link errors that happen when plugging the imports into the instance - /// * Runtime errors that happen when running the module `start` function. - pub fn new( + pub(crate) fn new( mut store: &mut impl AsStoreMut, module: &Module, imports: &Imports, - ) -> Result { + ) -> Result<(Self, Exports), InstantiationError> { let instance = module + .0 .instantiate(&mut store, imports) .map_err(|e| InstantiationError::Start(e))?; - let self_instance = Self::from_module_and_instance(store, module, instance)?; - Ok(self_instance) + Self::from_module_and_instance(store, &module, instance) } - /// Creates a new `Instance` from a WebAssembly [`Module`] and a - /// vector of imports. - /// - /// ## Errors - /// - /// The function can return [`InstantiationError`]s. - /// - /// Those are, as defined by the spec: - /// * Link errors that happen when plugging the imports into the instance - /// * Runtime errors that happen when running the module `start` function. - pub fn new_by_index( + pub(crate) fn new_by_index( store: &mut impl AsStoreMut, module: &Module, externs: &[Extern], - ) -> Result { + ) -> Result<(Self, Exports), InstantiationError> { let mut imports = Imports::new(); for (import_ty, extern_ty) in module.imports().zip(externs.iter()) { imports.define(import_ty.module(), import_ty.name(), extern_ty.clone()); @@ -94,65 +44,29 @@ impl Instance { } /// Creates a Wasmer `Instance` from a Wasmer `Module` and a WebAssembly Instance - /// - /// # Important - /// - /// Is expected that the function [`Instance::init_envs`] is run manually - /// by the user in case the instance has any Wasmer imports, so the function - /// environments are properly initiated. - /// - /// *This method is only available when targeting JS environments* - pub fn from_module_and_instance( + pub(crate) fn from_module_and_instance( mut store: &mut impl AsStoreMut, module: &Module, instance: WebAssembly::Instance, - ) -> Result { + ) -> Result<(Self, Exports), InstantiationError> { let instance_exports = instance.exports(); let exports = module .exports() .map(|export_type| { let name = export_type.name(); - let extern_type = export_type.ty().clone(); + let extern_type = export_type.ty(); // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] - let js_export = unsafe { - js_sys::Reflect::get(&instance_exports, &name.into()) - .map_err(|_e| InstantiationError::NotInExports(name.to_string()))? - }; - let export: VMExtern = - VMExtern::from_js_value(js_export, &mut store, extern_type)?.into(); - let extern_ = Extern::from_vm_extern(&mut store, export); + let js_export = + unsafe { js_sys::Reflect::get(&instance_exports, &name.into()).unwrap() }; + let extern_ = Extern::from_jsvalue(&mut store, extern_type, &js_export) + .map_err(|e| wasm_bindgen::JsValue::from(e)) + .unwrap(); Ok((name.to_string(), extern_)) }) .collect::>()?; - Ok(Self { - handle: instance, - module: module.clone(), - exports, - }) - } - - /// Gets the [`Module`] associated with this instance. - pub fn module(&self) -> &Module { - &self.module - } - - /// Returns the inner WebAssembly Instance - #[doc(hidden)] - pub fn raw<'context>( - &'context self, - _store: &'context impl AsStoreRef, - ) -> &'context WebAssembly::Instance { - &self.handle - } -} - -impl fmt::Debug for Instance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Instance") - .field("exports", &self.exports) - .finish() + Ok((Self { _handle: instance }, exports)) } } diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index 5c7aeaa7831..f8a822d1325 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -1,430 +1,13 @@ -use crate::access::{RefCow, SliceCow, WasmRefAccess}; -use crate::js::externals::memory::MemoryBuffer; -use crate::js::RuntimeError; -use crate::js::{Memory32, Memory64, MemoryView, WasmPtr}; -use crate::WasmSliceAccess; -use std::{ - convert::TryInto, - fmt, - marker::PhantomData, - mem::{self, MaybeUninit}, - ops::Range, - slice, - string::FromUtf8Error, -}; -use thiserror::Error; -use wasmer_types::{MemorySize, ValueType}; - -/// Error for invalid [`Memory`] access. -#[derive(Clone, Copy, Debug, Error)] -#[non_exhaustive] -pub enum MemoryAccessError { - /// Memory access is outside heap bounds. - #[error("memory access out of bounds")] - HeapOutOfBounds, - /// Address calculation overflow. - #[error("address calculation overflow")] - Overflow, - /// String is not valid UTF-8. - #[error("string is not valid utf-8")] - NonUtf8String, -} - -impl From for RuntimeError { - fn from(err: MemoryAccessError) -> Self { - RuntimeError::new(err.to_string()) - } -} -impl From for MemoryAccessError { - fn from(_err: FromUtf8Error) -> Self { - MemoryAccessError::NonUtf8String - } -} - -/// Reference to a value in Wasm memory. -/// -/// The type of the value must satisfy the requirements of the `ValueType` -/// trait which guarantees that reading and writing such a value to untrusted -/// memory is safe. -/// -/// The address is not required to be aligned: unaligned accesses are fully -/// supported. -/// -/// This wrapper safely handles concurrent modifications of the data by another -/// thread. -#[derive(Clone, Copy)] -pub struct WasmRef<'a, T: ValueType> { - buffer: MemoryBuffer<'a>, - offset: u64, - marker: PhantomData<*mut T>, -} - -impl<'a, T: ValueType> WasmRef<'a, T> { - /// Creates a new `WasmRef` at the given offset in a memory. - #[inline] - pub fn new(view: &'a MemoryView, offset: u64) -> Self { - Self { - buffer: view.buffer(), - offset, - marker: PhantomData, - } - } - - /// Get the offset into Wasm linear memory for this `WasmRef`. - #[inline] - pub fn offset(self) -> u64 { - self.offset - } - - /// Get a `WasmPtr` for this `WasmRef`. - #[inline] - pub fn as_ptr32(self) -> WasmPtr { - WasmPtr::new(self.offset as u32) - } - - /// Get a 64-bit `WasmPtr` for this `WasmRef`. - #[inline] - pub fn as_ptr64(self) -> WasmPtr { - WasmPtr::new(self.offset) - } - - /// Get a `WasmPtr` fror this `WasmRef`. - #[inline] - pub fn as_ptr(self) -> WasmPtr { - let offset: M::Offset = self - .offset - .try_into() - .map_err(|_| "invalid offset into memory") - .unwrap(); - WasmPtr::::new(offset) - } - - /// Reads the location pointed to by this `WasmRef`. - #[inline] - pub fn read(self) -> Result { - let mut out = MaybeUninit::uninit(); - let buf = - unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::()) }; - self.buffer.read(self.offset, buf)?; - Ok(unsafe { out.assume_init() }) - } - - /// Writes to the location pointed to by this `WasmRef`. - #[inline] - pub fn write(self, val: T) -> Result<(), MemoryAccessError> { - let mut data = MaybeUninit::new(val); - let data = unsafe { - slice::from_raw_parts_mut( - data.as_mut_ptr() as *mut MaybeUninit, - mem::size_of::(), - ) - }; - val.zero_padding_bytes(data); - let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) }; - self.buffer.write(self.offset, data) - } - - /// Gains direct access to the memory of this slice - #[inline] - pub fn access(self) -> Result, MemoryAccessError> { - WasmRefAccess::new(self) - } -} - -impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "WasmRef(offset: {}, pointer: {:#x})", - self.offset, self.offset - ) - } -} - -/// Reference to an array of values in Wasm memory. -/// -/// The type of the value must satisfy the requirements of the `ValueType` -/// trait which guarantees that reading and writing such a value to untrusted -/// memory is safe. -/// -/// The address is not required to be aligned: unaligned accesses are fully -/// supported. -/// -/// This wrapper safely handles concurrent modifications of the data by another -/// thread. -#[derive(Clone, Copy)] -pub struct WasmSlice<'a, T: ValueType> { - buffer: MemoryBuffer<'a>, - offset: u64, - len: u64, - marker: PhantomData<*mut T>, -} - -impl<'a, T: ValueType> WasmSlice<'a, T> { - /// Creates a new `WasmSlice` starting at the given offset in memory and - /// with the given number of elements. - /// - /// Returns a `MemoryAccessError` if the slice length overflows. - #[inline] - pub fn new(memory: &'a MemoryView, offset: u64, len: u64) -> Result { - let total_len = len - .checked_mul(mem::size_of::() as u64) - .ok_or(MemoryAccessError::Overflow)?; - offset - .checked_add(total_len) - .ok_or(MemoryAccessError::Overflow)?; - Ok(Self { - buffer: memory.buffer(), - offset, - len, - marker: PhantomData, - }) - } - - /// Get the offset into Wasm linear memory for this `WasmSlice`. - #[inline] - pub fn offset(self) -> u64 { - self.offset - } - - /// Get a 32-bit `WasmPtr` for this `WasmRef`. - #[inline] - pub fn as_ptr32(self) -> WasmPtr { - WasmPtr::new(self.offset as u32) - } - - /// Get a 64-bit `WasmPtr` for this `WasmRef`. - #[inline] - pub fn as_ptr64(self) -> WasmPtr { - WasmPtr::new(self.offset) - } - - /// Get the number of elements in this slice. - #[inline] - pub fn len(self) -> u64 { - self.len - } - - /// Return if the slice is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - /// Get a `WasmRef` to an element in the slice. - #[inline] - pub fn index(self, idx: u64) -> WasmRef<'a, T> { - if idx >= self.len { - panic!("WasmSlice out of bounds"); - } - let offset = self.offset + idx * mem::size_of::() as u64; - WasmRef { - buffer: self.buffer, - offset, - marker: PhantomData, - } - } - - /// Get a `WasmSlice` for a subslice of this slice. - #[inline] - pub fn subslice(self, range: Range) -> WasmSlice<'a, T> { - if range.start > range.end || range.end > self.len { - panic!("WasmSlice out of bounds"); - } - let offset = self.offset + range.start * mem::size_of::() as u64; - Self { - buffer: self.buffer, - offset, - len: range.end - range.start, - marker: PhantomData, - } - } - - /// Get an iterator over the elements in this slice. - #[inline] - pub fn iter(self) -> WasmSliceIter<'a, T> { - WasmSliceIter { slice: self } - } - - /// Reads an element of this slice. - #[inline] - pub fn read(self, idx: u64) -> Result { - self.index(idx).read() - } - - /// Writes to an element of this slice. - #[inline] - pub fn write(self, idx: u64, val: T) -> Result<(), MemoryAccessError> { - self.index(idx).write(val) - } - - /// Gains direct access to the memory of this slice - #[inline] - pub fn access(self) -> Result, MemoryAccessError> { - WasmSliceAccess::new(self) - } - - /// Reads the entire slice into the given buffer. - /// - /// The length of the buffer must match the length of the slice. - #[inline] - pub fn read_slice(self, buf: &mut [T]) -> Result<(), MemoryAccessError> { - assert_eq!( - buf.len() as u64, - self.len, - "slice length doesn't match WasmSlice length" - ); - let bytes = unsafe { - slice::from_raw_parts_mut( - buf.as_mut_ptr() as *mut MaybeUninit, - buf.len() * mem::size_of::(), - ) - }; - self.buffer.read_uninit(self.offset, bytes)?; - Ok(()) - } - - /// Reads the entire slice into the given uninitialized buffer. - /// - /// The length of the buffer must match the length of the slice. - /// - /// This method returns an initialized view of the buffer. - #[inline] - pub fn read_slice_uninit( - self, - buf: &mut [MaybeUninit], - ) -> Result<&mut [T], MemoryAccessError> { - assert_eq!( - buf.len() as u64, - self.len, - "slice length doesn't match WasmSlice length" - ); - let bytes = unsafe { - slice::from_raw_parts_mut( - buf.as_mut_ptr() as *mut MaybeUninit, - buf.len() * mem::size_of::(), - ) - }; - self.buffer.read_uninit(self.offset, bytes)?; - Ok(unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut T, buf.len()) }) - } - - /// Write the given slice into this `WasmSlice`. - /// - /// The length of the slice must match the length of the `WasmSlice`. - #[inline] - pub fn write_slice(self, data: &[T]) -> Result<(), MemoryAccessError> { - assert_eq!( - data.len() as u64, - self.len, - "slice length doesn't match WasmSlice length" - ); - let bytes = unsafe { - slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * mem::size_of::()) - }; - self.buffer.write(self.offset, bytes) - } - - /// Reads this `WasmSlice` into a `slice`. - #[inline] - pub fn read_to_slice<'b>( - self, - buf: &'b mut [MaybeUninit], - ) -> Result { - let len = self.len.try_into().expect("WasmSlice length overflow"); - self.buffer.read_uninit(self.offset, buf)?; - Ok(len) - } - - /// Reads this `WasmSlice` into a `Vec`. - #[inline] - pub fn read_to_vec(self) -> Result, MemoryAccessError> { - let len = self.len.try_into().expect("WasmSlice length overflow"); - let mut vec = Vec::with_capacity(len); - let bytes = unsafe { - slice::from_raw_parts_mut( - vec.as_mut_ptr() as *mut MaybeUninit, - len * mem::size_of::(), - ) - }; - self.buffer.read_uninit(self.offset, bytes)?; - unsafe { - vec.set_len(len); - } - Ok(vec) - } - - /// Reads this `WasmSlice` into a `BytesMut` - #[inline] - pub fn read_to_bytes(self) -> Result { - let len = self.len.try_into().expect("WasmSlice length overflow"); - let mut ret = bytes::BytesMut::with_capacity(len); - let bytes = unsafe { - slice::from_raw_parts_mut( - ret.as_mut_ptr() as *mut MaybeUninit, - len * mem::size_of::(), - ) - }; - self.buffer.read_uninit(self.offset, bytes)?; - unsafe { - ret.set_len(len); - } - Ok(ret) - } -} - -impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "WasmSlice(offset: {}, len: {}, pointer: {:#x})", - self.offset, self.len, self.offset - ) - } -} - -/// Iterator over the elements of a `WasmSlice`. -pub struct WasmSliceIter<'a, T: ValueType> { - slice: WasmSlice<'a, T>, -} - -impl<'a, T: ValueType> Iterator for WasmSliceIter<'a, T> { - type Item = WasmRef<'a, T>; - - fn next(&mut self) -> Option { - if self.slice.len() != 0 { - let elem = self.slice.index(0); - self.slice = self.slice.subslice(1..self.slice.len()); - Some(elem) - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option) { - (0..self.slice.len()).size_hint() - } -} - -impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> { - fn next_back(&mut self) -> Option { - if self.slice.len() != 0 { - let elem = self.slice.index(self.slice.len() - 1); - self.slice = self.slice.subslice(0..self.slice.len() - 1); - Some(elem) - } else { - None - } - } -} - -impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {} +use crate::access::{RefCow, SliceCow, WasmRefAccess, WasmSliceAccess}; +use crate::{MemoryAccessError, WasmRef, WasmSlice}; +use std::mem::{self, MaybeUninit}; +use std::slice; impl<'a, T> WasmSliceAccess<'a, T> where T: wasmer_types::ValueType, { - fn new(slice: WasmSlice<'a, T>) -> Result { + pub(crate) fn new(slice: WasmSlice<'a, T>) -> Result { let buf = slice.read_to_vec()?; Ok(Self { slice, @@ -437,11 +20,46 @@ impl<'a, T> WasmRefAccess<'a, T> where T: wasmer_types::ValueType, { - fn new(ptr: WasmRef<'a, T>) -> Result { - let val = ptr.read()?; + pub(crate) fn new(ptr: WasmRef<'a, T>) -> Result { + let mut out = MaybeUninit::uninit(); + let buf = + unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::()) }; + ptr.buffer.read(ptr.offset, buf)?; + let val = unsafe { out.assume_init() }; + Ok(Self { ptr, buf: RefCow::Owned(val, false), }) } } + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Reads the address pointed to by this `WasmPtr` in a memory. + #[inline] + #[allow(clippy::clone_on_copy)] + pub fn read(&self) -> T + where + T: Clone, + { + self.as_ref().clone() + } + + /// Writes to the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn write(&mut self, val: T) { + let mut data = MaybeUninit::new(val); + let data = unsafe { + slice::from_raw_parts_mut( + data.as_mut_ptr() as *mut MaybeUninit, + mem::size_of::(), + ) + }; + val.zero_padding_bytes(data); + let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) }; + self.ptr.buffer.write(self.ptr.offset, data).unwrap() + } +} diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 18e2f6b0f26..d8c82b7d28b 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -23,73 +23,21 @@ mod lib { } } -pub(crate) mod error; -mod exports; -mod externals; -mod function_env; -mod imports; -mod instance; -mod mem_access; -mod module; +mod as_js; +pub(crate) mod engine; +pub(crate) mod errors; +pub(crate) mod extern_ref; +pub(crate) mod externals; +pub(crate) mod instance; +pub(crate) mod mem_access; +pub(crate) mod module; #[cfg(feature = "wasm-types-polyfill")] mod module_info_polyfill; -mod native; -mod native_type; -mod ptr; -mod store; +pub(crate) mod store; mod trap; -mod types; -mod value; -mod vm; +pub(crate) mod typed_function; +pub(crate) mod vm; mod wasm_bindgen_polyfill; -pub use crate::js::error::{DeserializeError, InstantiationError, SerializeError}; -pub use crate::js::exports::{ExportError, Exportable, Exports, ExportsIterator}; -pub use crate::js::externals::{ - Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, MemoryError, MemoryView, - Table, WasmTypeList, -}; -pub use crate::js::function_env::{FunctionEnv, FunctionEnvMut}; -pub use crate::js::imports::Imports; -pub use crate::js::instance::Instance; -pub use crate::js::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter}; -pub use crate::js::module::{IoCompileError, Module, ModuleTypeHints}; -pub use crate::js::native::TypedFunction; -pub use crate::js::native_type::NativeWasmTypeInto; -pub use crate::js::ptr::{Memory32, Memory64, MemorySize, WasmPtr, WasmPtr64}; -pub use crate::js::trap::RuntimeError; -pub use crate::js::vm::VMMemory; - -pub use crate::js::store::{ - AsStoreMut, AsStoreRef, Store, StoreHandle, StoreMut, StoreObjects, StoreRef, -}; -pub use crate::js::types::ValType as Type; -pub use crate::js::types::{ - ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, - TableType, ValType, -}; -pub use crate::js::value::Value; -pub use crate::js::value::Value as Val; - -pub use wasmer_types::is_wasm; -// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 -pub use wasmer_types::{ - Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, OnCalledAction, Pages, ValueType, - WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, -}; - -#[cfg(feature = "wat")] -pub use wat::parse_bytes as wat2wasm; - -#[cfg(feature = "wasm-types-polyfill")] -pub use wasmparser; - -/// Version number of this crate. -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); - -/// This type is deprecated, it has been replaced by TypedFunction. -#[deprecated( - since = "3.0.0", - note = "NativeFunc has been replaced by TypedFunction" -)] -pub type NativeFunc = TypedFunction; +pub use crate::js::as_js::AsJs; +pub use crate::js::module::ModuleTypeHints; diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 4c75811fe66..7fa896a0916 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -1,44 +1,23 @@ -#[cfg(feature = "wat")] -use crate::js::error::WasmError; -use crate::js::error::{CompileError, InstantiationError}; -#[cfg(feature = "js-serializable-module")] -use crate::js::error::{DeserializeError, SerializeError}; -use crate::js::externals::Extern; -use crate::js::imports::Imports; -use crate::js::store::AsStoreMut; -use crate::js::types::{AsJs, ExportType, ImportType}; -use crate::js::vm::VMInstance; -use crate::js::RuntimeError; -use crate::AsStoreRef; +use crate::errors::InstantiationError; +use crate::errors::RuntimeError; +use crate::imports::Imports; +use crate::js::AsJs; +use crate::store::AsStoreMut; +use crate::vm::VMInstance; +use crate::Extern; use crate::IntoBytes; -#[cfg(feature = "js-serializable-module")] +use crate::{AsEngineRef, ExportType, ImportType}; use bytes::Bytes; use js_sys::{Reflect, Uint8Array, WebAssembly}; -use std::fmt; -use std::io; use std::path::Path; -#[cfg(feature = "std")] -use thiserror::Error; #[cfg(feature = "tracing")] use tracing::{debug, warn}; use wasm_bindgen::JsValue; use wasmer_types::{ - ExportsIterator, ExternType, FunctionType, GlobalType, ImportsIterator, MemoryType, Mutability, - Pages, TableType, Type, + CompileError, DeserializeError, ExportsIterator, ExternType, FunctionType, GlobalType, + ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, SerializeError, TableType, Type, }; -/// IO Error on a Module Compilation -#[derive(Debug)] -#[cfg_attr(feature = "std", derive(Error))] -pub enum IoCompileError { - /// An IO error - #[cfg_attr(feature = "std", error(transparent))] - Io(io::Error), - /// A compilation error - #[cfg_attr(feature = "std", error(transparent))] - Compile(CompileError), -} - /// WebAssembly in the browser doesn't yet output the descriptor/types /// corresponding to each extern (import and export). /// @@ -48,7 +27,7 @@ pub enum IoCompileError { /// /// Until that happens, we annotate the module with the expected /// types so we can built on top of them at runtime. -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct ModuleTypeHints { /// The type hints for the imported types pub imports: Vec, @@ -56,15 +35,7 @@ pub struct ModuleTypeHints { pub exports: Vec, } -/// A WebAssembly Module contains stateless WebAssembly -/// code that has already been compiled and can be instantiated -/// multiple times. -/// -/// ## Cloning a module -/// -/// Cloning a module is cheap: it does a shallow copy of the compiled -/// contents rather than a deep copy. -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct Module { module: WebAssembly::Module, name: Option, @@ -74,123 +45,40 @@ pub struct Module { raw_bytes: Option, } -impl Module { - /// Creates a new WebAssembly Module given the configuration - /// in the store. - /// - /// If the provided bytes are not WebAssembly-like (start with `b"\0asm"`), - /// and the "wat" feature is enabled for this crate, this function will try to - /// to convert the bytes assuming they correspond to the WebAssembly text - /// format. - /// - /// ## Security - /// - /// Before the code is compiled, it will be validated using the store - /// features. - /// - /// ## Errors - /// - /// Creating a WebAssembly module from bytecode can result in a - /// [`CompileError`] since this operation requires to transorm the Wasm - /// bytecode into code the machine can easily execute. - /// - /// ## Example - /// - /// Reading from a WAT file. - /// - /// ``` - /// use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = "(module)"; - /// let module = Module::new(&store, wat)?; - /// # Ok(()) - /// # } - /// ``` - /// - /// Reading from bytes: - /// - /// ``` - /// use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// // The following is the same as: - /// // (module - /// // (type $t0 (func (param i32) (result i32))) - /// // (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) - /// // get_local $p0 - /// // i32.const 1 - /// // i32.add) - /// // ) - /// let bytes: Vec = vec![ - /// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, - /// 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07, - /// 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, - /// 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, 0x00, 0x1a, 0x04, 0x6e, - /// 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, - /// 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30, - /// ]; - /// let module = Module::new(&store, bytes)?; - /// # Ok(()) - /// # } - /// ``` - #[allow(unreachable_code)] - pub fn new(_store: &impl AsStoreRef, bytes: impl AsRef<[u8]>) -> Result { - let bytes = bytes.as_ref(); - #[cfg(feature = "wat")] - if bytes.starts_with(b"\0asm") == false { - let parsed_bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { - CompileError::Wasm(WasmError::Generic(format!( - "Error when converting wat: {}", - e - ))) - })?; - return Self::from_binary(_store, parsed_bytes.as_ref()); - } - Self::from_binary(_store, bytes) - } - - /// Creates a new WebAssembly module from a file path. - pub fn from_file( - _store: &impl AsStoreRef, - _file: impl AsRef, - ) -> Result { - unimplemented!(); - } +// Module implements `structuredClone` in js, so it's safe it to make it Send. +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// ```js +// const module = new WebAssembly.Module(new Uint8Array([ +// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00 +// ])); +// structuredClone(module) +// ``` +unsafe impl Send for Module {} +unsafe impl Sync for Module {} - /// Creates a new WebAssembly module from a binary. - /// - /// Opposed to [`Module::new`], this function is not compatible with - /// the WebAssembly text format (if the "wat" feature is enabled for - /// this crate). - pub fn from_binary(_store: &impl AsStoreRef, binary: &[u8]) -> Result { - // Self::validate(store, binary)?; - unsafe { Self::from_binary_unchecked(_store, binary) } +impl Module { + pub(crate) fn from_binary( + _engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + unsafe { Self::from_binary_unchecked(_engine, binary) } } - /// Creates a new WebAssembly module skipping any kind of validation. - /// - /// # Safety - /// - /// This is safe since the JS vm should be safe already. - /// We maintain the `unsafe` to preserve the same API as Wasmer - pub unsafe fn from_binary_unchecked( - store: &impl AsStoreRef, + pub(crate) unsafe fn from_binary_unchecked( + _engine: &impl AsEngineRef, binary: &[u8], ) -> Result { let js_bytes = Uint8Array::view(binary); let module = WebAssembly::Module::new(&js_bytes.into()).unwrap(); - - Self::from_js_module(store, module, binary) + Ok(Self::from_js_module(module, binary)) } /// Creates a new WebAssembly module skipping any kind of validation from a javascript module /// - pub unsafe fn from_js_module( - _store: &impl AsStoreRef, + pub(crate) unsafe fn from_js_module( module: WebAssembly::Module, binary: impl IntoBytes, - ) -> Result { + ) -> Self { let binary = binary.into_bytes(); // The module is now validated, so we can safely parse it's types #[cfg(feature = "wasm-types-polyfill")] @@ -216,22 +104,16 @@ impl Module { #[cfg(not(feature = "wasm-types-polyfill"))] let (type_hints, name) = (None, None); - Ok(Self { + Self { module, type_hints, name, #[cfg(feature = "js-serializable-module")] raw_bytes: Some(binary.into_bytes()), - }) + } } - /// Validates a new WebAssembly Module given the configuration - /// in the Store. - /// - /// This validation is normally pretty fast and checks the enabled - /// WebAssembly features in the Store Engine to assure deterministic - /// validation of the Module. - pub fn validate(_store: &impl AsStoreRef, binary: &[u8]) -> Result<(), CompileError> { + pub fn validate(_engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { let js_bytes = unsafe { Uint8Array::view(binary) }; // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] @@ -328,98 +210,43 @@ impl Module { .map_err(|e: JsValue| -> RuntimeError { e.into() })?) } - /// Returns the name of the current module. - /// - /// This name is normally set in the WebAssembly bytecode by some - /// compilers, but can be also overwritten using the [`Module::set_name`] method. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = "(module $moduleName)"; - /// let module = Module::new(&store, wat)?; - /// assert_eq!(module.name(), Some("moduleName")); - /// # Ok(()) - /// # } - /// ``` pub fn name(&self) -> Option<&str> { self.name.as_ref().map(|s| s.as_ref()) - // self.artifact.module_ref().name.as_deref() } - /// Serializes a module into a binary representation that the `Engine` - /// can later process via [`Module::deserialize`]. - /// - #[cfg(feature = "js-serializable-module")] pub fn serialize(&self) -> Result { - self.raw_bytes.clone().ok_or(SerializeError::Generic( + #[cfg(feature = "js-serializable-module")] + return self.raw_bytes.clone().ok_or(SerializeError::Generic( "Not able to serialize module".to_string(), - )) + )); + + #[cfg(not(feature = "js-serializable-module"))] + return Err(SerializeError::Generic( + "You need to enable the `js-serializable-module` feature flag to serialize a `Module`" + .to_string(), + )); } - /// Deserializes a serialized Module binary into a `Module`. - /// - /// This is safe since deserialization under `js` is essentially same as reconstructing `Module`. - /// We maintain the `unsafe` to preserve the same API as Wasmer - #[cfg(feature = "js-serializable-module")] pub unsafe fn deserialize( - _store: &impl AsStoreRef, - bytes: impl IntoBytes, + _engine: &impl AsEngineRef, + _bytes: impl IntoBytes, ) -> Result { - let bytes = bytes.into_bytes(); - Self::new(_store, bytes).map_err(|e| DeserializeError::Compiler(e)) + #[cfg(feature = "js-serializable-module")] + return Self::from_binary(_engine, &_bytes.into_bytes()) + .map_err(|e| DeserializeError::Compiler(e)); + + #[cfg(not(feature = "js-serializable-module"))] + return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string())); } - #[cfg(feature = "compiler")] - /// Deserializes a a serialized Module located in a `Path` into a `Module`. - /// > Note: the module has to be serialized before with the `serialize` method. - /// - /// # Safety - /// - /// Please check [`Module::deserialize`]. - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # let mut store = Store::default(); - /// # fn main() -> anyhow::Result<()> { - /// let module = Module::deserialize_from_file(&store, path)?; - /// # Ok(()) - /// # } - /// ``` pub unsafe fn deserialize_from_file( - store: &impl AsStoreRef, + engine: &impl AsEngineRef, path: impl AsRef, ) -> Result { - let artifact = std::fs::read(path.as_ref())?; - Ok(Self::new(store, bytes).map_err(|e| DeserializeError::Compiler(e))) + let bytes = std::fs::read(path.as_ref())?; + Self::deserialize(engine, bytes) } - /// Sets the name of the current module. - /// This is normally useful for stacktraces and debugging. - /// - /// It will return `true` if the module name was changed successfully, - /// and return `false` otherwise (in case the module is already - /// instantiated). - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = "(module)"; - /// let mut module = Module::new(&store, wat)?; - /// assert_eq!(module.name(), None); - /// module.set_name("foo"); - /// assert_eq!(module.name(), Some("foo")); - /// # Ok(()) - /// # } - /// ``` pub fn set_name(&mut self, name: &str) -> bool { self.name = Some(name.to_string()); true @@ -436,30 +263,6 @@ impl Module { // .unwrap_or(false) } - /// Returns an iterator over the imported types in the Module. - /// - /// The order of the imports is guaranteed to be the same as in the - /// WebAssembly bytecode. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = r#"(module - /// (import "host" "func1" (func)) - /// (import "host" "func2" (func)) - /// )"#; - /// let module = Module::new(&store, wat)?; - /// for import in module.imports() { - /// assert_eq!(import.module(), "host"); - /// assert!(import.name().contains("func")); - /// import.ty(); - /// } - /// # Ok(()) - /// # } - /// ``` pub fn imports<'a>(&'a self) -> ImportsIterator + 'a> { let imports = WebAssembly::Module::imports(&self.module); let iter = imports @@ -522,6 +325,7 @@ impl Module { /// /// Returns an error if the hints doesn't match the shape of /// import or export types of the module. + #[allow(unused)] pub fn set_type_hints(&mut self, type_hints: ModuleTypeHints) -> Result<(), String> { let exports = WebAssembly::Module::exports(&self.module); // Check exports @@ -553,29 +357,6 @@ impl Module { Ok(()) } - /// Returns an iterator over the exported types in the Module. - /// - /// The order of the exports is guaranteed to be the same as in the - /// WebAssembly bytecode. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = r#"(module - /// (func (export "namedfunc")) - /// (memory (export "namedmemory") 1) - /// )"#; - /// let module = Module::new(&store, wat)?; - /// for export_ in module.exports() { - /// assert!(export_.name().contains("named")); - /// export_.ty(); - /// } - /// # Ok(()) - /// # } - /// ``` pub fn exports<'a>(&'a self) -> ExportsIterator + 'a> { let exports = WebAssembly::Module::exports(&self.module); let iter = exports @@ -633,13 +414,6 @@ impl Module { ExportsIterator::new(iter, exports.length() as usize) } - /// Get the custom sections of the module given a `name`. - /// - /// # Important - /// - /// Following the WebAssembly spec, one name can have multiple - /// custom sections. That's why an iterator (rather than one element) - /// is returned. pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { WebAssembly::Module::custom_sections(&self.module, name) .iter() @@ -650,13 +424,9 @@ impl Module { .collect::>>() .into_iter() } -} -impl fmt::Debug for Module { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Module") - .field("name", &self.name()) - .finish() + pub(crate) fn info(&self) -> &ModuleInfo { + unimplemented!() } } @@ -671,3 +441,15 @@ impl From for Module { } } } + +impl From<(WebAssembly::Module, T)> for crate::module::Module { + fn from((module, binary): (WebAssembly::Module, T)) -> crate::module::Module { + unsafe { crate::module::Module(Module::from_js_module(module, binary.into_bytes())) } + } +} + +impl From for crate::module::Module { + fn from(module: WebAssembly::Module) -> crate::module::Module { + crate::module::Module(module.into()) + } +} diff --git a/lib/api/src/js/native_type.rs b/lib/api/src/js/native_type.rs deleted file mode 100644 index a12a186ce48..00000000000 --- a/lib/api/src/js/native_type.rs +++ /dev/null @@ -1,182 +0,0 @@ -//! This module permits to create native functions -//! easily in Rust, thanks to its advanced typing system. - -use wasmer_types::{NativeWasmType, Type}; - -use crate::js::Function; - -use super::store::AsStoreMut; - -/// `NativeWasmTypeInto` performs conversions from and into `NativeWasmType` -/// types with a context. -pub trait NativeWasmTypeInto: NativeWasmType + Sized { - #[doc(hidden)] - fn into_abi(self, store: &mut impl AsStoreMut) -> Self::Abi; - - #[doc(hidden)] - unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self; - - /// Convert self to raw value representation. - fn into_raw(self, store: &mut impl AsStoreMut) -> f64; - - /// Convert to self from raw value representation. - /// - /// # Safety - /// - unsafe fn from_raw(store: &mut impl AsStoreMut, raw: f64) -> Self; -} - -impl NativeWasmTypeInto for i32 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> f64 { - self.into() - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: f64) -> Self { - raw as _ - } -} - -impl NativeWasmTypeInto for i64 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> f64 { - self as _ - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: f64) -> Self { - raw as _ - } -} - -impl NativeWasmTypeInto for f32 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> f64 { - self as _ - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: f64) -> Self { - raw as _ - } -} - -impl NativeWasmTypeInto for f64 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> f64 { - self - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: f64) -> Self { - raw - } -} - -impl NativeWasmTypeInto for u128 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> f64 { - self as _ - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: f64) -> Self { - raw as _ - } -} - -impl NativeWasmType for Function { - const WASM_TYPE: Type = Type::FuncRef; - type Abi = f64; -} - -/* -mod test_native_type { - use super::*; - use wasmer_types::Type; - - fn test_wasm_types() { - assert_eq!(i32::WASM_TYPE, Type::I32); - assert_eq!(i64::WASM_TYPE, Type::I64); - assert_eq!(f32::WASM_TYPE, Type::F32); - assert_eq!(f64::WASM_TYPE, Type::F64); - } - - fn test_roundtrip() { - unsafe { - assert_eq!(i32::from_raw(42i32.into_raw()), 42i32); - assert_eq!(i64::from_raw(42i64.into_raw()), 42i64); - assert_eq!(f32::from_raw(42f32.into_raw()), 42f32); - assert_eq!(f64::from_raw(42f64.into_raw()), 42f64); - } - } -} - */ - -// pub trait IntegerAtomic -// where -// Self: Sized -// { -// type Primitive; - -// fn add(&self, other: Self::Primitive) -> Self::Primitive; -// fn sub(&self, other: Self::Primitive) -> Self::Primitive; -// fn and(&self, other: Self::Primitive) -> Self::Primitive; -// fn or(&self, other: Self::Primitive) -> Self::Primitive; -// fn xor(&self, other: Self::Primitive) -> Self::Primitive; -// fn load(&self) -> Self::Primitive; -// fn store(&self, other: Self::Primitive) -> Self::Primitive; -// fn compare_exchange(&self, expected: Self::Primitive, new: Self::Primitive) -> Self::Primitive; -// fn swap(&self, other: Self::Primitive) -> Self::Primitive; -// } diff --git a/lib/api/src/js/ptr.rs b/lib/api/src/js/ptr.rs deleted file mode 100644 index 7d78109790b..00000000000 --- a/lib/api/src/js/ptr.rs +++ /dev/null @@ -1,275 +0,0 @@ -use crate::js::{externals::MemoryView, FromToNativeWasmType}; -use crate::js::{MemoryAccessError, WasmRef, WasmSlice}; -use crate::{js::NativeWasmTypeInto, AsStoreRef}; -use std::convert::TryFrom; -use std::{fmt, marker::PhantomData, mem}; -pub use wasmer_types::Memory32; -pub use wasmer_types::Memory64; -pub use wasmer_types::MemorySize; -use wasmer_types::ValueType; - -/// Alias for `WasmPtr. -pub type WasmPtr64 = WasmPtr; - -/// A zero-cost type that represents a pointer to something in Wasm linear -/// memory. -/// -/// This type can be used directly in the host function arguments: -/// ``` -/// # use wasmer::Memory; -/// # use wasmer::WasmPtr; -/// pub fn host_import(memory: Memory, ptr: WasmPtr) { -/// let derefed_ptr = ptr.deref(&memory); -/// let inner_val: u32 = derefed_ptr.read().expect("pointer in bounds"); -/// println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); -/// // update the value being pointed to -/// derefed_ptr.write(inner_val + 1).expect("pointer in bounds"); -/// } -/// ``` -/// -/// This type can also be used with primitive-filled structs, but be careful of -/// guarantees required by `ValueType`. -/// ``` -/// # use wasmer::Memory; -/// # use wasmer::WasmPtr; -/// # use wasmer::ValueType; -/// -/// // This is safe as the 12 bytes represented by this struct -/// // are valid for all bit combinations. -/// #[derive(Copy, Clone, Debug, ValueType)] -/// #[repr(C)] -/// struct V3 { -/// x: f32, -/// y: f32, -/// z: f32 -/// } -/// -/// fn update_vector_3(memory: Memory, ptr: WasmPtr) { -/// let derefed_ptr = ptr.deref(&memory); -/// let mut inner_val: V3 = derefed_ptr.read().expect("pointer in bounds"); -/// println!("Got {:?} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); -/// // update the value being pointed to -/// inner_val.x = 10.4; -/// derefed_ptr.write(inner_val).expect("pointer in bounds"); -/// } -/// ``` -#[repr(transparent)] -pub struct WasmPtr { - offset: M::Offset, - _phantom: PhantomData<*mut T>, -} - -impl WasmPtr { - /// Create a new `WasmPtr` at the given offset. - #[inline] - pub fn new(offset: M::Offset) -> Self { - Self { - offset, - _phantom: PhantomData, - } - } - - /// Get the offset into Wasm linear memory for this `WasmPtr`. - #[inline] - pub fn offset(&self) -> M::Offset { - self.offset - } - - /// Casts this `WasmPtr` to a `WasmPtr` of a different type. - #[inline] - pub fn cast(self) -> WasmPtr { - WasmPtr { - offset: self.offset, - _phantom: PhantomData, - } - } - - /// Returns a null `UserPtr`. - #[inline] - pub fn null() -> Self { - WasmPtr::new(M::ZERO) - } - - /// Checks whether the `WasmPtr` is null. - #[inline] - pub fn is_null(self) -> bool { - self.offset.into() == 0 - } - - /// Calculates an offset from the current pointer address. The argument is - /// in units of `T`. - /// - /// This method returns an error if an address overflow occurs. - #[inline] - pub fn add_offset(self, offset: M::Offset) -> Result { - let base = self.offset.into(); - let index = offset.into(); - let offset = index - .checked_mul(mem::size_of::() as u64) - .ok_or(MemoryAccessError::Overflow)?; - let address = base - .checked_add(offset) - .ok_or(MemoryAccessError::Overflow)?; - let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?; - Ok(WasmPtr::new(address)) - } - - /// Calculates an offset from the current pointer address. The argument is - /// in units of `T`. - /// - /// This method returns an error if an address overflow occurs. - #[inline] - pub fn sub_offset(self, offset: M::Offset) -> Result { - let base = self.offset.into(); - let index = offset.into(); - let offset = index - .checked_mul(mem::size_of::() as u64) - .ok_or(MemoryAccessError::Overflow)?; - let address = base - .checked_sub(offset) - .ok_or(MemoryAccessError::Overflow)?; - let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?; - Ok(WasmPtr::new(address)) - } -} - -impl WasmPtr { - /// Creates a `WasmRef` from this `WasmPtr` which allows reading and - /// mutating of the value being pointed to. - #[inline] - pub fn deref<'a>(self, view: &'a MemoryView) -> WasmRef<'a, T> { - WasmRef::new(view, self.offset.into()) - } - - /// Reads the address pointed to by this `WasmPtr` in a memory. - #[inline] - pub fn read(self, view: &MemoryView) -> Result { - self.deref(view).read() - } - - /// Writes to the address pointed to by this `WasmPtr` in a memory. - #[inline] - pub fn write(self, view: &MemoryView, val: T) -> Result<(), MemoryAccessError> { - self.deref(view).write(val) - } - - /// Creates a `WasmSlice` starting at this `WasmPtr` which allows reading - /// and mutating of an array of value being pointed to. - /// - /// Returns a `MemoryAccessError` if the slice length overflows a 64-bit - /// address. - #[inline] - pub fn slice<'a>( - self, - view: &'a MemoryView, - len: M::Offset, - ) -> Result, MemoryAccessError> { - WasmSlice::new(view, self.offset.into(), len.into()) - } - - /// Reads a sequence of values from this `WasmPtr` until a value that - /// matches the given condition is found. - /// - /// This last value is not included in the returned vector. - #[inline] - pub fn read_until<'a>( - self, - view: &'a MemoryView, - mut end: impl FnMut(&T) -> bool, - ) -> Result, MemoryAccessError> { - let mut vec = Vec::new(); - for i in 0u64.. { - let i = M::Offset::try_from(i).map_err(|_| MemoryAccessError::Overflow)?; - let val = self.add_offset(i)?.deref(view).read()?; - if end(&val) { - break; - } - vec.push(val); - } - Ok(vec) - } -} - -impl WasmPtr { - /// Reads a UTF-8 string from the `WasmPtr` with the given length. - /// - /// This method is safe to call even if the memory is being concurrently - /// modified. - #[inline] - pub fn read_utf8_string<'a>( - self, - view: &'a MemoryView, - len: M::Offset, - ) -> Result { - let vec = self.slice(view, len)?.read_to_vec()?; - Ok(String::from_utf8(vec)?) - } - - /// Reads a null-terminated UTF-8 string from the `WasmPtr`. - /// - /// This method is safe to call even if the memory is being concurrently - /// modified. - #[inline] - pub fn read_utf8_string_with_nul<'a>( - self, - view: &'a MemoryView, - ) -> Result { - let vec = self.read_until(view, |&byte| byte == 0)?; - Ok(String::from_utf8(vec)?) - } -} - -unsafe impl FromToNativeWasmType for WasmPtr -where - ::Native: NativeWasmTypeInto, -{ - type Native = M::Native; - - fn to_native(self) -> Self::Native { - M::offset_to_native(self.offset) - } - fn from_native(n: Self::Native) -> Self { - Self { - offset: M::native_to_offset(n), - _phantom: PhantomData, - } - } - #[inline] - fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { - true // in Javascript there are no different stores - } -} - -unsafe impl ValueType for WasmPtr { - fn zero_padding_bytes(&self, _bytes: &mut [mem::MaybeUninit]) {} -} - -impl Clone for WasmPtr { - fn clone(&self) -> Self { - Self { - offset: self.offset, - _phantom: PhantomData, - } - } -} - -impl Copy for WasmPtr {} - -impl PartialEq for WasmPtr { - fn eq(&self, other: &Self) -> bool { - self.offset.into() == other.offset.into() - } -} - -impl Eq for WasmPtr {} - -impl fmt::Debug for WasmPtr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "WasmPtr(offset: {}, pointer: {:#x})", - self.offset.into(), - self.offset.into() - ) - } -} diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs index 8b66fd890f9..86147f0e0f8 100644 --- a/lib/api/src/js/store.rs +++ b/lib/api/src/js/store.rs @@ -1,235 +1,12 @@ -use std::fmt; -use wasmer_types::OnCalledAction; - -/// Call handler for a store. -// TODO: better documentation! -// TODO: this type is duplicated in sys/store.rs. -// Maybe want to move it to wasmer_types... -pub type OnCalledHandler = Box< - dyn FnOnce(StoreMut<'_>) -> Result>, ->; - -/// We require the context to have a fixed memory address for its lifetime since -/// various bits of the VM have raw pointers that point back to it. Hence we -/// wrap the actual context in a box. -pub(crate) struct StoreInner { - pub(crate) objects: StoreObjects, - pub(crate) on_called: Option, -} - -/// The store represents all global state that can be manipulated by -/// WebAssembly programs. It consists of the runtime representation -/// of all instances of functions, tables, memories, and globals that -/// have been allocated during the lifetime of the abstract machine. -/// -/// The `Store` holds the engine (that is —amongst many things— used to compile -/// the Wasm bytes into a valid module artifact), in addition to the -/// [`Tunables`] (that are used to create the memories, tables and globals). -/// -/// Spec: -pub struct Store { - pub(crate) inner: Box, -} - -impl Store { - /// Creates a new `Store`. - pub fn new() -> Self { - Self { - inner: Box::new(StoreInner { - objects: Default::default(), - on_called: None, - }), - } - } - - /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. The - /// tunables are excluded from the logic. - pub fn same(_a: &Self, _b: &Self) -> bool { - true - } - - /// Returns the ID of this store - pub fn id(&self) -> StoreId { - self.inner.objects.id() - } -} - -impl PartialEq for Store { - fn eq(&self, other: &Self) -> bool { - Self::same(self, other) - } -} - -// This is required to be able to set the trap_handler in the -// Store. -unsafe impl Send for Store {} -unsafe impl Sync for Store {} - -impl Default for Store { - fn default() -> Self { - Self::new() - } -} - -impl fmt::Debug for Store { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Store").finish() - } -} - -impl AsStoreRef for Store { - fn as_store_ref(&self) -> StoreRef<'_> { - StoreRef { inner: &self.inner } - } -} -impl AsStoreMut for Store { - fn as_store_mut(&mut self) -> StoreMut<'_> { - StoreMut { - inner: &mut self.inner, - } - } - fn objects_mut(&mut self) -> &mut StoreObjects { - &mut self.inner.objects - } -} - -/// A temporary handle to a [`Context`]. -pub struct StoreRef<'a> { - pub(crate) inner: &'a StoreInner, -} - -impl<'a> StoreRef<'a> { - pub(crate) fn objects(&self) -> &'a StoreObjects { - &self.inner.objects - } - - /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. The - /// tunables are excluded from the logic. - pub fn same(a: &Self, b: &Self) -> bool { - a.inner.objects.id() == b.inner.objects.id() - } -} - -/// A temporary handle to a [`Context`]. -pub struct StoreMut<'a> { - pub(crate) inner: &'a mut StoreInner, -} - -impl<'a> StoreMut<'a> { - /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. The - /// tunables are excluded from the logic. - pub fn same(a: &Self, b: &Self) -> bool { - a.inner.objects.id() == b.inner.objects.id() - } - - pub(crate) fn as_raw(&self) -> *mut StoreInner { - self.inner as *const StoreInner as *mut StoreInner - } - - pub(crate) unsafe fn from_raw(raw: *mut StoreInner) -> Self { - Self { inner: &mut *raw } - } - - /// Sets the unwind callback which will be invoked when the call finishes - pub fn on_called(&mut self, callback: F) - where - F: FnOnce(StoreMut<'_>) -> Result> - + Send - + Sync - + 'static, - { - self.inner.on_called.replace(Box::new(callback)); - } -} - -/// Helper trait for a value that is convertible to a [`StoreRef`]. -pub trait AsStoreRef { - /// Returns a `StoreRef` pointing to the underlying context. - fn as_store_ref(&self) -> StoreRef<'_>; -} - -/// Helper trait for a value that is convertible to a [`StoreMut`]. -pub trait AsStoreMut: AsStoreRef { - /// Returns a `StoreMut` pointing to the underlying context. - fn as_store_mut(&mut self) -> StoreMut<'_>; - - /// Returns the ObjectMutable - fn objects_mut(&mut self) -> &mut StoreObjects; -} - -impl AsStoreRef for StoreRef<'_> { - fn as_store_ref(&self) -> StoreRef<'_> { - StoreRef { inner: self.inner } - } -} - -impl AsStoreRef for StoreMut<'_> { - fn as_store_ref(&self) -> StoreRef<'_> { - StoreRef { inner: self.inner } - } -} -impl AsStoreMut for StoreMut<'_> { - fn as_store_mut(&mut self) -> StoreMut<'_> { - StoreMut { inner: self.inner } - } - fn objects_mut(&mut self) -> &mut StoreObjects { - &mut self.inner.objects - } -} - -impl AsStoreRef for &'_ T { - fn as_store_ref(&self) -> StoreRef<'_> { - T::as_store_ref(*self) - } -} -impl AsStoreRef for &'_ mut T { - fn as_store_ref(&self) -> StoreRef<'_> { - T::as_store_ref(*self) - } -} -impl AsStoreMut for &'_ mut T { - fn as_store_mut(&mut self) -> StoreMut<'_> { - T::as_store_mut(*self) - } - fn objects_mut(&mut self) -> &mut StoreObjects { - T::objects_mut(*self) - } -} - pub(crate) use objects::{InternalStoreHandle, StoreObject}; -pub use objects::{StoreHandle, StoreId, StoreObjects}; +pub use objects::{StoreHandle, StoreObjects}; mod objects { use wasm_bindgen::JsValue; - use crate::js::{function_env::VMFunctionEnvironment, vm::VMGlobal}; - use std::{ - fmt, - marker::PhantomData, - num::{NonZeroU64, NonZeroUsize}, - sync::atomic::{AtomicU64, Ordering}, - }; - - /// Unique ID to identify a context. - /// - /// Every handle to an object managed by a context also contains the ID of the - /// context. This is used to check that a handle is always used with the - /// correct context. - #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] - pub struct StoreId(NonZeroU64); - - impl Default for StoreId { - // Allocates a unique ID for a new context. - fn default() -> Self { - // No overflow checking is needed here: overflowing this would take - // thousands of years. - static NEXT_ID: AtomicU64 = AtomicU64::new(1); - Self(NonZeroU64::new(NEXT_ID.fetch_add(1, Ordering::Relaxed)).unwrap()) - } - } + use crate::js::vm::{VMFunctionEnvironment, VMGlobal}; + use std::{fmt, marker::PhantomData, num::NonZeroUsize}; + pub use wasmer_types::StoreId; /// Trait to represent an object managed by a context. This is implemented on /// the VM types managed by the context. @@ -267,7 +44,7 @@ mod objects { } /// Set of objects managed by a context. - #[derive(Default)] + #[derive(Default, Debug)] pub struct StoreObjects { id: StoreId, globals: Vec, @@ -399,11 +176,13 @@ mod objects { } /// Returns the ID of the context associated with the handle. + #[allow(unused)] pub fn store_id(&self) -> StoreId { self.id } /// Overrides the store id with a new ID + #[allow(unused)] pub fn set_store_id(&mut self, id: StoreId) { self.id = id; } diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index 4d7dd887443..e6221109609 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -1,4 +1,3 @@ -// use super::frame_info::{FrameInfo, GlobalFrameInfo, FRAME_INFO}; use std::error::Error; use std::fmt; use std::sync::Arc; diff --git a/lib/api/src/js/native.rs b/lib/api/src/js/typed_function.rs similarity index 66% rename from lib/api/src/js/native.rs rename to lib/api/src/js/typed_function.rs index 436eff84d2e..22cd2bf9c58 100644 --- a/lib/api/src/js/native.rs +++ b/lib/api/src/js/typed_function.rs @@ -7,52 +7,17 @@ //! let add_one = instance.exports.get_function("function_name")?; //! let add_one_native: TypedFunction = add_one.typed().unwrap(); //! ``` -use std::marker::PhantomData; - -use crate::js::externals::Function; -use crate::js::store::{AsStoreMut, AsStoreRef}; -use crate::js::{FromToNativeWasmType, RuntimeError, WasmTypeList}; +use crate::native_type::NativeWasmTypeInto; +use crate::Value; +use crate::{AsStoreMut, TypedFunction}; +use crate::{FromToNativeWasmType, RuntimeError, WasmTypeList}; // use std::panic::{catch_unwind, AssertUnwindSafe}; -use crate::js::types::param_from_js; -use crate::js::types::AsJs; -use crate::js::vm::VMFunction; +use crate::js::as_js::{param_from_js, AsJs}; use js_sys::Array; use std::iter::FromIterator; use wasm_bindgen::JsValue; use wasmer_types::RawValue; -/// A WebAssembly function that can be called natively -/// (using the Native ABI). -#[derive(Clone)] -pub struct TypedFunction { - pub(crate) handle: VMFunction, - _phantom: PhantomData<(Args, Rets)>, -} - -unsafe impl Send for TypedFunction {} -unsafe impl Sync for TypedFunction {} - -impl TypedFunction -where - Args: WasmTypeList, - Rets: WasmTypeList, -{ - #[allow(dead_code)] - pub(crate) fn new(_store: &mut impl AsStoreMut, vm_function: VMFunction) -> Self { - Self { - handle: vm_function, - _phantom: PhantomData, - } - } - - pub(crate) fn from_handle(f: Function) -> Self { - Self { - handle: f.handle, - _phantom: PhantomData, - } - } -} - macro_rules! impl_native_traits { ( $( $x:ident ),* ) => { #[allow(unused_parens, non_snake_case)] @@ -64,23 +29,23 @@ macro_rules! impl_native_traits { /// Call the typed func and return results. #[allow(clippy::too_many_arguments)] pub fn call(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where - $( $x: FromToNativeWasmType + crate::js::NativeWasmTypeInto, )* + $( $x: FromToNativeWasmType + NativeWasmTypeInto, )* { #[allow(unused_unsafe)] - let params_list: Vec = unsafe { - vec![ $( RawValue { f64: $x.into_raw(store) } ),* ] + let params_list: Vec<_> = unsafe { + vec![ $( ($x::WASM_TYPE, $x.into_raw(store) ) ),* ] }; - let params_list: Vec = params_list - .into_iter() - .map(|a| a.as_jsvalue(&store.as_store_ref())) - .collect(); let results = { 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.handle.function.apply( + r = self.func.0.handle.function.apply( &JsValue::UNDEFINED, - &Array::from_iter(params_list.iter()) + unsafe { + &Array::from_iter(params_list.clone() + .into_iter() + .map(|(b, a)| Value::from_raw(store, b, a).as_jsvalue(store))) + } ); let store_mut = store.as_store_mut(); if let Some(callback) = store_mut.inner.on_called.take() { @@ -96,7 +61,7 @@ macro_rules! impl_native_traits { r? }; let mut rets_list_array = Rets::empty_array(); - let mut_rets = rets_list_array.as_mut() as *mut [f64] as *mut f64; + let mut_rets = rets_list_array.as_mut() as *mut [RawValue] as *mut RawValue; match Rets::size() { 0 => {}, 1 => unsafe { @@ -119,18 +84,6 @@ macro_rules! impl_native_traits { Ok(unsafe { Rets::from_array(store, rets_list_array) }) } } - - #[allow(unused_parens)] - impl<'a, $( $x, )* Rets> crate::js::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for TypedFunction<( $( $x ),* ), Rets> - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - { - fn get_self_from_extern_with_generics(store: &impl AsStoreRef, _extern: &crate::js::externals::Extern) -> Result { - use crate::js::exports::Exportable; - crate::js::Function::get_self_from_extern(_extern)?.typed(store).map_err(|_| crate::js::exports::ExportError::IncompatibleType) - } - } }; } diff --git a/lib/api/src/js/types.rs b/lib/api/src/js/types.rs deleted file mode 100644 index 4dd2133f523..00000000000 --- a/lib/api/src/js/types.rs +++ /dev/null @@ -1,57 +0,0 @@ -//use crate::js::externals::Function; -// use crate::js::store::{Store, StoreObject}; -// use crate::js::RuntimeError; -use crate::js::store::AsStoreRef; -use crate::js::value::Value; -use wasm_bindgen::JsValue; -pub use wasmer_types::{ - ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, - TableType, Type as ValType, -}; - -/// WebAssembly computations manipulate values of basic value types: -/// * Integers (32 or 64 bit width) -/// * Floating-point (32 or 64 bit width) -/// * Vectors (128 bits, with 32 or 64 bit lanes) -/// -/// Spec: -// pub type Value = (); -//pub type Value = Value; - -pub trait AsJs { - fn as_jsvalue(&self, store: &impl AsStoreRef) -> JsValue; -} - -#[inline] -pub fn param_from_js(ty: &ValType, js_val: &JsValue) -> Value { - match ty { - ValType::I32 => Value::I32(js_val.as_f64().unwrap() as _), - ValType::I64 => Value::I64(js_val.as_f64().unwrap() as _), - ValType::F32 => Value::F32(js_val.as_f64().unwrap() as _), - ValType::F64 => Value::F64(js_val.as_f64().unwrap()), - t => unimplemented!( - "The type `{:?}` is not yet supported in the JS Function API", - t - ), - } -} - -impl AsJs for Value { - fn as_jsvalue(&self, _store: &impl AsStoreRef) -> JsValue { - match self { - Self::I32(i) => JsValue::from_f64(*i as f64), - Self::I64(i) => JsValue::from_f64(*i as f64), - 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(None) => JsValue::null(), - } - } -} - -impl AsJs for wasmer_types::RawValue { - fn as_jsvalue(&self, _store: &impl AsStoreRef) -> JsValue { - unsafe { JsValue::from_f64(self.f64) } - } -} diff --git a/lib/api/src/js/value.rs b/lib/api/src/js/value.rs deleted file mode 100644 index 4a432c9ce2e..00000000000 --- a/lib/api/src/js/value.rs +++ /dev/null @@ -1,450 +0,0 @@ -use std::convert::TryFrom; -use std::fmt; -use std::string::{String, ToString}; - -use wasmer_types::RawValue; -use wasmer_types::Type; - -//use crate::ExternRef; -use crate::js::externals::function::Function; - -use super::store::AsStoreRef; - -/// WebAssembly computations manipulate values of basic value types: -/// * Integers (32 or 64 bit width) -/// * Floating-point (32 or 64 bit width) -/// -/// Spec: -#[derive(Clone, PartialEq)] -pub enum Value { - /// A 32-bit integer. - /// - /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned. - I32(i32), - - /// A 64-bit integer. - /// - /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned. - I64(i64), - - /// A 32-bit float. - F32(f32), - - /// A 64-bit float. - F64(f64), - - /// An `externref` value which can hold opaque data to the wasm instance itself. - //ExternRef(Option), - - /// A first-class reference to a WebAssembly function. - FuncRef(Option), - - /// A 128-bit number - V128(u128), -} - -macro_rules! accessors { - ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($( - /// Attempt to access the underlying value of this `Value`, returning - /// `None` if it is not the correct type. - pub fn $get(&self) -> Option<$ty> { - if let Self::$variant($bind) = self { - Some($cvt) - } else { - None - } - } - - /// Returns the underlying value of this `Value`, panicking if it's the - /// wrong type. - /// - /// # Panics - /// - /// Panics if `self` is not of the right type. - pub fn $unwrap(&self) -> $ty { - self.$get().expect(concat!("expected ", stringify!($ty))) - } - )*) -} - -impl Value { - /// Returns a null `externref` value. - pub fn null() -> Self { - Self::FuncRef(None) - } - - /// Returns the corresponding [`Type`] for this `Value`. - pub fn ty(&self) -> Type { - match self { - Self::I32(_) => Type::I32, - Self::I64(_) => Type::I64, - Self::F32(_) => Type::F32, - Self::F64(_) => Type::F64, - Self::V128(_) => Type::V128, - //Self::ExternRef(_) => Type::ExternRef, - Self::FuncRef(_) => Type::FuncRef, - } - } - - /// Converts the `Value` into a `f64`. - pub fn as_raw(&self, _store: &impl AsStoreRef) -> f64 { - match *self { - Self::I32(v) => v as f64, - Self::I64(v) => v as f64, - Self::F32(v) => v as f64, - Self::F64(v) => v, - Self::V128(v) => v as f64, - Self::FuncRef(Some(ref f)) => f.handle.function.as_f64().unwrap_or(0_f64), //TODO is this correct? - - Self::FuncRef(None) => 0_f64, - //Self::ExternRef(Some(ref e)) => unsafe { *e.address().0 } as .into_raw(), - //Self::ExternRef(None) => externref: 0 }, - } - } - - /// Converts the `Value` into a `RawValue`. - pub unsafe fn as_raw_value(&self, store: &impl AsStoreRef) -> RawValue { - RawValue { - f64: self.as_raw(store), - } - } - - /// Converts a `f64` to a `Value`. - /// - /// # Safety - /// - pub unsafe fn from_raw(_store: &impl AsStoreRef, ty: Type, raw: f64) -> Self { - match ty { - Type::I32 => Self::I32(raw as _), - Type::I64 => Self::I64(raw as _), - Type::F32 => Self::F32(raw as _), - Type::F64 => Self::F64(raw), - Type::V128 => Self::V128(raw as _), - Type::FuncRef => todo!(), - Type::ExternRef => todo!(), - //Self::ExternRef( - //{ - //VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)), - //), - } - } - - /// Checks whether a value can be used with the given context. - /// - /// Primitive (`i32`, `i64`, etc) and null funcref/externref values are not - /// tied to a context and can be freely shared between contexts. - /// - /// Externref and funcref values are tied to a context and can only be used - /// with that context. - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { - match self { - Self::I32(_) - | Self::I64(_) - | Self::F32(_) - | Self::F64(_) - | Self::V128(_) - //| Self::ExternRef(None) - | Self::FuncRef(None) => true, - //Self::ExternRef(Some(e)) => e.is_from_store(store), - Self::FuncRef(Some(f)) => f.is_from_store(store), - } - } - - accessors! { - e - (I32(i32) i32 unwrap_i32 *e) - (I64(i64) i64 unwrap_i64 *e) - (F32(f32) f32 unwrap_f32 *e) - (F64(f64) f64 unwrap_f64 *e) - (V128(u128) v128 unwrap_v128 *e) - //(ExternRef(&Option) externref unwrap_externref e) - (FuncRef(&Option) funcref unwrap_funcref e) - } -} - -impl fmt::Debug for Value { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::I32(v) => write!(f, "I32({:?})", v), - Self::I64(v) => write!(f, "I64({:?})", v), - Self::F32(v) => write!(f, "F32({:?})", v), - Self::F64(v) => write!(f, "F64({:?})", v), - Self::V128(v) => write!(f, "V128({:?})", v), - //Self::ExternRef(None) => write!(f, "Null ExternRef"), - //Self::ExternRef(Some(v)) => write!(f, "ExternRef({:?})", v), - Self::FuncRef(None) => write!(f, "Null FuncRef"), - Self::FuncRef(Some(v)) => write!(f, "FuncRef({:?})", v), - } - } -} - -impl ToString for Value { - fn to_string(&self) -> String { - match self { - Self::I32(v) => v.to_string(), - Self::I64(v) => v.to_string(), - Self::F32(v) => v.to_string(), - Self::F64(v) => v.to_string(), - Self::V128(v) => v.to_string(), - //Self::ExternRef(_) => "externref".to_string(), - Self::FuncRef(_) => "funcref".to_string(), - } - } -} - -impl From for Value { - fn from(val: u128) -> Self { - Self::V128(val) - } -} - -impl From for Value { - fn from(val: i32) -> Self { - Self::I32(val) - } -} - -impl From for Value { - fn from(val: u32) -> Self { - // In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers. - Self::I32(val as i32) - } -} - -impl From for Value { - fn from(val: i64) -> Self { - Self::I64(val) - } -} - -impl From for Value { - fn from(val: u64) -> Self { - // In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers. - Self::I64(val as i64) - } -} - -impl From for Value { - fn from(val: f32) -> Self { - Self::F32(val) - } -} - -impl From for Value { - fn from(val: f64) -> Self { - Self::F64(val) - } -} - -impl From for Value { - fn from(val: Function) -> Self { - Self::FuncRef(Some(val)) - } -} - -impl From> for Value { - fn from(val: Option) -> Self { - Self::FuncRef(val) - } -} - -//impl From for Value { -// fn from(val: ExternRef) -> Self { -// Self::ExternRef(Some(val)) -// } -//} -// -//impl From> for Value { -// fn from(val: Option) -> Self { -// Self::ExternRef(val) -// } -//} - -const NOT_I32: &str = "Value is not of Wasm type i32"; -const NOT_I64: &str = "Value is not of Wasm type i64"; -const NOT_F32: &str = "Value is not of Wasm type f32"; -const NOT_F64: &str = "Value is not of Wasm type f64"; -const NOT_V128: &str = "Value is not of Wasm type u128"; -const NOT_FUNCREF: &str = "Value is not of Wasm type funcref"; -//const NOT_EXTERNREF: &str = "Value is not of Wasm type externref"; - -impl TryFrom for u128 { - type Error = &'static str; - - fn try_from(value: Value) -> Result { - value.v128().ok_or(NOT_V128) - } -} - -impl TryFrom for i32 { - type Error = &'static str; - - fn try_from(value: Value) -> Result { - value.i32().ok_or(NOT_I32) - } -} - -impl TryFrom for u32 { - type Error = &'static str; - - fn try_from(value: Value) -> Result { - value.i32().ok_or(NOT_I32).map(|int| int as Self) - } -} - -impl TryFrom for i64 { - type Error = &'static str; - - fn try_from(value: Value) -> Result { - value.i64().ok_or(NOT_I64) - } -} - -impl TryFrom for u64 { - type Error = &'static str; - - fn try_from(value: Value) -> Result { - value.i64().ok_or(NOT_I64).map(|int| int as Self) - } -} - -impl TryFrom for f32 { - type Error = &'static str; - - fn try_from(value: Value) -> Result { - value.f32().ok_or(NOT_F32) - } -} - -impl TryFrom for f64 { - type Error = &'static str; - - fn try_from(value: Value) -> Result { - value.f64().ok_or(NOT_F64) - } -} - -impl TryFrom for Option { - type Error = &'static str; - - fn try_from(value: Value) -> Result { - match value { - Value::FuncRef(f) => Ok(f), - _ => Err(NOT_FUNCREF), - } - } -} - -//impl TryFrom for Option { -// type Error = &'static str; -// -// fn try_from(value: Value) -> Result { -// match value { -// Value::ExternRef(e) => Ok(e), -// _ => Err(NOT_EXTERNREF), -// } -// } -//} - -#[cfg(tests)] -mod tests { - use super::*; - /* - - fn test_value_i32_from_u32() { - let bytes = [0x00, 0x00, 0x00, 0x00]; - let v = Value::<()>::from(u32::from_be_bytes(bytes)); - assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); - - let bytes = [0x00, 0x00, 0x00, 0x01]; - let v = Value::<()>::from(u32::from_be_bytes(bytes)); - assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); - - let bytes = [0xAA, 0xBB, 0xCC, 0xDD]; - let v = Value::<()>::from(u32::from_be_bytes(bytes)); - assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); - - let bytes = [0xFF, 0xFF, 0xFF, 0xFF]; - let v = Value::<()>::from(u32::from_be_bytes(bytes)); - assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); - } - - fn test_value_i64_from_u64() { - let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let v = Value::<()>::from(u64::from_be_bytes(bytes)); - assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); - - let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - let v = Value::<()>::from(u64::from_be_bytes(bytes)); - assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); - - let bytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11]; - let v = Value::<()>::from(u64::from_be_bytes(bytes)); - assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); - - let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; - let v = Value::<()>::from(u64::from_be_bytes(bytes)); - assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); - } - - fn convert_value_to_i32() { - let value = Value::<()>::I32(5678); - let result = i32::try_from(value); - assert_eq!(result.unwrap(), 5678); - - let value = Value::<()>::from(u32::MAX); - let result = i32::try_from(value); - assert_eq!(result.unwrap(), -1); - } - - fn convert_value_to_u32() { - let value = Value::<()>::from(u32::MAX); - let result = u32::try_from(value); - assert_eq!(result.unwrap(), u32::MAX); - - let value = Value::<()>::I32(-1); - let result = u32::try_from(value); - assert_eq!(result.unwrap(), u32::MAX); - } - - fn convert_value_to_i64() { - let value = Value::<()>::I64(5678); - let result = i64::try_from(value); - assert_eq!(result.unwrap(), 5678); - - let value = Value::<()>::from(u64::MAX); - let result = i64::try_from(value); - assert_eq!(result.unwrap(), -1); - } - - fn convert_value_to_u64() { - let value = Value::<()>::from(u64::MAX); - let result = u64::try_from(value); - assert_eq!(result.unwrap(), u64::MAX); - - let value = Value::<()>::I64(-1); - let result = u64::try_from(value); - assert_eq!(result.unwrap(), u64::MAX); - } - - fn convert_value_to_f32() { - let value = Value::<()>::F32(1.234); - let result = f32::try_from(value); - assert_eq!(result.unwrap(), 1.234); - - let value = Value::<()>::F64(1.234); - let result = f32::try_from(value); - assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32"); - } - - fn convert_value_to_f64() { - let value = Value::<()>::F64(1.234); - let result = f64::try_from(value); - assert_eq!(result.unwrap(), 1.234); - - let value = Value::<()>::F32(1.234); - let result = f64::try_from(value); - assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64"); - } - */ -} diff --git a/lib/api/src/js/vm.rs b/lib/api/src/js/vm.rs index 349663b00fa..10ebd67dd6e 100644 --- a/lib/api/src/js/vm.rs +++ b/lib/api/src/js/vm.rs @@ -4,29 +4,25 @@ /// This module should not be needed any longer (with the exception of the memory) /// once the type reflection is added to the WebAssembly JS API. /// https://github.com/WebAssembly/js-types/ -use crate::js::error::WasmError; -use crate::js::store::{AsStoreMut, AsStoreRef}; -use crate::js::wasm_bindgen_polyfill::Global; use crate::js::wasm_bindgen_polyfill::Global as JsGlobal; -use crate::MemoryView; -use js_sys::Function; use js_sys::Function as JsFunction; use js_sys::WebAssembly; -use js_sys::WebAssembly::{Memory, Table}; use js_sys::WebAssembly::{Memory as JsMemory, Table as JsTable}; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::fmt; #[cfg(feature = "tracing")] use tracing::trace; use wasm_bindgen::{JsCast, JsValue}; +use wasmer_types::RawValue; use wasmer_types::{ - ExternType, FunctionType, GlobalType, MemoryError, MemoryType, Pages, TableType, WASM_PAGE_SIZE, + FunctionType, GlobalType, MemoryError, MemoryType, Pages, TableType, WASM_PAGE_SIZE, }; /// Represents linear memory that is managed by the javascript runtime #[derive(Clone, Debug, PartialEq)] pub struct VMMemory { - pub(crate) memory: Memory, + pub(crate) memory: JsMemory, pub(crate) ty: MemoryType, } @@ -41,7 +37,7 @@ struct DummyBuffer { impl VMMemory { /// Creates a new memory directly from a WebAssembly javascript object - pub fn new(memory: Memory, ty: MemoryType) -> Self { + pub fn new(memory: JsMemory, ty: MemoryType) -> Self { Self { memory, ty } } @@ -64,14 +60,14 @@ impl VMMemory { /// Copies this memory to a new memory pub fn duplicate(&self) -> Result { - let new_memory = crate::Memory::new_internal(self.ty.clone())?; + let new_memory = crate::js::externals::memory::Memory::js_memory_from_type(&self.ty)?; #[cfg(feature = "tracing")] trace!("memory copy started"); - let src = MemoryView::new_raw(&self.memory); + let src = crate::js::externals::memory_view::MemoryView::new_raw(&self.memory); let amount = src.data_size() as usize; - let mut dst = MemoryView::new_raw(&new_memory); + let mut dst = crate::js::externals::memory_view::MemoryView::new_raw(&new_memory); let dst_size = dst.data_size() as usize; if amount > dst_size { @@ -92,7 +88,7 @@ impl VMMemory { } })?; - dst = MemoryView::new_raw(&new_memory); + dst = crate::js::externals::memory_view::MemoryView::new_raw(&new_memory); } src.copy_to_memory(amount as u64, &dst).map_err(|err| { @@ -115,14 +111,18 @@ impl From for JsValue { } } +/// The shared memory is the same as the normal memory +pub type VMSharedMemory = VMMemory; + +/// The VM Global type #[derive(Clone, Debug, PartialEq)] pub struct VMGlobal { - pub(crate) global: Global, + pub(crate) global: JsGlobal, pub(crate) ty: GlobalType, } impl VMGlobal { - pub(crate) fn new(global: Global, ty: GlobalType) -> Self { + pub(crate) fn new(global: JsGlobal, ty: GlobalType) -> Self { Self { global, ty } } } @@ -130,9 +130,10 @@ impl VMGlobal { unsafe impl Send for VMGlobal {} unsafe impl Sync for VMGlobal {} +/// The VM Table type #[derive(Clone, Debug, PartialEq)] pub struct VMTable { - pub(crate) table: Table, + pub(crate) table: JsTable, pub(crate) ty: TableType, } @@ -140,17 +141,20 @@ unsafe impl Send for VMTable {} unsafe impl Sync for VMTable {} impl VMTable { - pub(crate) fn new(table: Table, ty: TableType) -> Self { + pub(crate) fn new(table: JsTable, ty: TableType) -> Self { Self { table, ty } } + + /// Get the table size at runtime pub fn get_runtime_size(&self) -> u32 { self.table.length() } } +/// The VM Function type #[derive(Clone)] pub struct VMFunction { - pub(crate) function: Function, + pub(crate) function: JsFunction, pub(crate) ty: FunctionType, } @@ -158,7 +162,7 @@ unsafe impl Send for VMFunction {} unsafe impl Sync for VMFunction {} impl VMFunction { - pub(crate) fn new(function: Function, ty: FunctionType) -> Self { + pub(crate) fn new(function: JsFunction, ty: FunctionType) -> Self { Self { function, ty } } } @@ -192,72 +196,77 @@ pub enum VMExtern { Global(VMGlobal), } -impl VMExtern { - /// Return the export as a `JSValue`. - pub fn as_jsvalue<'context>(&self, _store: &'context impl AsStoreRef) -> JsValue { - match self { - Self::Memory(js_wasm_memory) => js_wasm_memory.memory.clone().into(), - Self::Function(js_func) => js_func.function.clone().into(), - Self::Table(js_wasm_table) => js_wasm_table.table.clone().into(), - Self::Global(js_wasm_global) => js_wasm_global.global.clone().into(), +pub type VMInstance = WebAssembly::Instance; + +/// Underlying FunctionEnvironment used by a `VMFunction`. +#[derive(Debug)] +pub struct VMFunctionEnvironment { + contents: Box, +} + +impl VMFunctionEnvironment { + /// Wraps the given value to expose it to Wasm code as a function context. + pub fn new(val: impl Any + Send + 'static) -> Self { + Self { + contents: Box::new(val), } } - /// Convert a `JsValue` into an `Export` within a given `Context`. - pub fn from_js_value( - val: JsValue, - _store: &mut impl AsStoreMut, - extern_type: ExternType, - ) -> Result { - match extern_type { - ExternType::Memory(memory_type) => { - if val.is_instance_of::() { - Ok(Self::Memory(VMMemory::new( - val.unchecked_into::(), - memory_type, - ))) - } else { - Err(WasmError::TypeMismatch( - val.js_typeof() - .as_string() - .map(Into::into) - .unwrap_or("unknown".into()), - "Memory".into(), - )) - } - } - ExternType::Global(global_type) => { - if val.is_instance_of::() { - Ok(Self::Global(VMGlobal::new( - val.unchecked_into::(), - global_type, - ))) - } else { - panic!("Extern type doesn't match js value type"); - } - } - ExternType::Function(function_type) => { - if val.is_instance_of::() { - Ok(Self::Function(VMFunction::new( - val.unchecked_into::(), - function_type, - ))) - } else { - panic!("Extern type doesn't match js value type"); - } - } - ExternType::Table(table_type) => { - if val.is_instance_of::() { - Ok(Self::Table(VMTable::new( - val.unchecked_into::(), - table_type, - ))) - } else { - panic!("Extern type doesn't match js value type"); - } - } - } + #[allow(clippy::should_implement_trait)] + /// Returns a reference to the underlying value. + pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { + &*self.contents + } + + #[allow(clippy::should_implement_trait)] + /// Returns a mutable reference to the underlying value. + pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { + &mut *self.contents } } -pub type VMInstance = WebAssembly::Instance; +pub(crate) struct VMExternRef; + +impl VMExternRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!(); + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} + +#[repr(C)] +pub struct VMFunctionBody(u8); + +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub(crate) struct VMFuncRef; + +impl VMFuncRef { + /// Converts the `VMFuncRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!(); + } + + /// Extracts a `VMFuncRef` from a `RawValue`. + /// + /// # Safety + /// `raw.funcref` must be a valid pointer. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} + +pub struct VMTrampoline; + +pub(crate) type VMExternTable = VMTable; +pub(crate) type VMExternMemory = VMMemory; +pub(crate) type VMExternGlobal = VMGlobal; +pub(crate) type VMExternFunction = VMFunction; diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 73dea73bd48..748d2227bf1 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -428,6 +428,25 @@ compile_error!( "The `js` feature must be enabled only for the `wasm32` target (either `wasm32-unknown-unknown` or `wasm32-wasi`)." ); +mod access; +mod engine; +mod errors; +mod exports; +mod extern_ref; +mod externals; +mod function_env; +mod imports; +mod instance; +mod into_bytes; +mod mem_access; +mod module; +mod native_type; +mod ptr; +mod store; +mod typed_function; +mod value; +pub mod vm; + #[cfg(feature = "sys")] mod sys; @@ -440,7 +459,51 @@ mod js; #[cfg(feature = "js")] pub use js::*; -mod access; -mod into_bytes; +pub use crate::externals::{Extern, Function, Global, HostFunction, Memory, MemoryView, Table}; pub use access::WasmSliceAccess; +pub use engine::{AsEngineRef, Engine}; +pub use errors::{InstantiationError, LinkError, RuntimeError}; +pub use exports::{ExportError, Exportable, Exports, ExportsIterator}; +pub use extern_ref::ExternRef; +pub use function_env::{FunctionEnv, FunctionEnvMut}; +pub use imports::Imports; +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::{FromToNativeWasmType, NativeWasmTypeInto, WasmTypeList}; +pub use ptr::{Memory32, Memory64, MemorySize, WasmPtr, WasmPtr64}; +pub use store::{AsStoreMut, AsStoreRef, OnCalledHandler, Store, StoreId, StoreMut, StoreRef}; +#[cfg(feature = "sys")] +pub use store::{TrapHandlerFn, Tunables}; +pub use typed_function::TypedFunction; +pub use value::Value; + +// Reexport from other modules + +pub use wasmer_derive::ValueType; +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 +pub use wasmer_types::{ + is_wasm, Bytes, CompileError, CpuFeature, DeserializeError, ExportIndex, ExportType, + ExternType, FunctionType, GlobalInit, GlobalType, ImportType, LocalFunctionIndex, MemoryError, + MemoryType, MiddlewareError, Mutability, OnCalledAction, Pages, ParseCpuFeatureError, + SerializeError, TableType, Target, Type, ValueType, WasmError, WasmResult, WASM_MAX_PAGES, + WASM_MIN_PAGES, WASM_PAGE_SIZE, +}; +#[cfg(feature = "wat")] +pub use wat::parse_bytes as wat2wasm; + +#[cfg(feature = "wasmparser")] +pub use wasmparser; + +// Deprecated types + +/// This type is deprecated, it has been replaced by TypedFunction. +#[deprecated( + since = "3.0.0", + note = "NativeFunc has been replaced by TypedFunction" +)] +pub type NativeFunc = TypedFunction; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/api/src/mem_access.rs b/lib/api/src/mem_access.rs new file mode 100644 index 00000000000..b5c01df8f97 --- /dev/null +++ b/lib/api/src/mem_access.rs @@ -0,0 +1,409 @@ +use crate::access::WasmRefAccess; +use crate::externals::memory::MemoryBuffer; +use crate::{Memory32, Memory64, MemorySize, MemoryView, WasmPtr}; +use crate::{RuntimeError, WasmSliceAccess}; +use std::convert::TryInto; +use std::fmt; +use std::marker::PhantomData; +use std::mem::{self, MaybeUninit}; +use std::ops::Range; +use std::slice; +use std::string::FromUtf8Error; +use thiserror::Error; +use wasmer_types::ValueType; + +/// Error for invalid [`Memory`] access. +#[derive(Clone, Copy, Debug, Error)] +#[non_exhaustive] +pub enum MemoryAccessError { + /// Memory access is outside heap bounds. + #[error("memory access out of bounds")] + HeapOutOfBounds, + /// Address calculation overflow. + #[error("address calculation overflow")] + Overflow, + /// String is not valid UTF-8. + #[error("string is not valid utf-8")] + NonUtf8String, +} + +impl From for RuntimeError { + fn from(err: MemoryAccessError) -> Self { + Self::new(err.to_string()) + } +} +impl From for MemoryAccessError { + fn from(_err: FromUtf8Error) -> Self { + Self::NonUtf8String + } +} + +/// Reference to a value in Wasm memory. +/// +/// The type of the value must satisfy the requirements of the `ValueType` +/// trait which guarantees that reading and writing such a value to untrusted +/// memory is safe. +/// +/// The address is not required to be aligned: unaligned accesses are fully +/// supported. +/// +/// This wrapper safely handles concurrent modifications of the data by another +/// thread. +#[derive(Clone, Copy)] +pub struct WasmRef<'a, T: ValueType> { + #[allow(unused)] + pub(crate) buffer: MemoryBuffer<'a>, + pub(crate) offset: u64, + marker: PhantomData<*mut T>, +} + +impl<'a, T: ValueType> WasmRef<'a, T> { + /// Creates a new `WasmRef` at the given offset in a memory. + #[inline] + pub fn new(view: &'a MemoryView, offset: u64) -> Self { + Self { + buffer: view.buffer(), + offset, + marker: PhantomData, + } + } + + /// Get the offset into Wasm linear memory for this `WasmRef`. + #[inline] + pub fn offset(self) -> u64 { + self.offset + } + + /// Get a `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr32(self) -> WasmPtr { + WasmPtr::new(self.offset as u32) + } + + /// Get a 64-bit `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr64(self) -> WasmPtr { + WasmPtr::new(self.offset) + } + + /// Get a `WasmPtr` fror this `WasmRef`. + #[inline] + pub fn as_ptr(self) -> WasmPtr { + let offset: M::Offset = self + .offset + .try_into() + .map_err(|_| "invalid offset into memory") + .unwrap(); + WasmPtr::::new(offset) + } + + /// Reads the location pointed to by this `WasmRef`. + #[inline] + pub fn read(self) -> Result { + let mut out = MaybeUninit::uninit(); + let buf = + unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::()) }; + self.buffer.read(self.offset, buf)?; + Ok(unsafe { out.assume_init() }) + // Ok(self.access()?.read()) + } + + /// Writes to the location pointed to by this `WasmRef`. + #[inline] + pub fn write(self, val: T) -> Result<(), MemoryAccessError> { + self.access()?.write(val); + Ok(()) + } + + /// Gains direct access to the memory of this slice + #[inline] + pub fn access(self) -> Result, MemoryAccessError> { + WasmRefAccess::new(self) + } +} + +impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "WasmRef(offset: {}, pointer: {:#x})", + self.offset, self.offset + ) + } +} + +/// Reference to an array of values in Wasm memory. +/// +/// The type of the value must satisfy the requirements of the `ValueType` +/// trait which guarantees that reading and writing such a value to untrusted +/// memory is safe. +/// +/// The address is not required to be aligned: unaligned accesses are fully +/// supported. +/// +/// This wrapper safely handles concurrent modifications of the data by another +/// thread. +#[derive(Clone, Copy)] +pub struct WasmSlice<'a, T: ValueType> { + pub(crate) buffer: MemoryBuffer<'a>, + pub(crate) offset: u64, + pub(crate) len: u64, + marker: PhantomData<*mut T>, +} + +impl<'a, T: ValueType> WasmSlice<'a, T> { + /// Creates a new `WasmSlice` starting at the given offset in memory and + /// with the given number of elements. + /// + /// Returns a `MemoryAccessError` if the slice length overflows. + #[inline] + pub fn new(view: &'a MemoryView, offset: u64, len: u64) -> Result { + let total_len = len + .checked_mul(mem::size_of::() as u64) + .ok_or(MemoryAccessError::Overflow)?; + offset + .checked_add(total_len) + .ok_or(MemoryAccessError::Overflow)?; + Ok(Self { + buffer: view.buffer(), + offset, + len, + marker: PhantomData, + }) + } + + /// Get the offset into Wasm linear memory for this `WasmSlice`. + #[inline] + pub fn offset(self) -> u64 { + self.offset + } + + /// Get a 32-bit `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr32(self) -> WasmPtr { + WasmPtr::new(self.offset as u32) + } + + /// Get a 64-bit `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr64(self) -> WasmPtr { + WasmPtr::new(self.offset) + } + + /// Get the number of elements in this slice. + #[inline] + pub fn len(self) -> u64 { + self.len + } + + /// Returns `true` if the number of elements is 0. + #[inline] + pub fn is_empty(self) -> bool { + self.len == 0 + } + + /// Get a `WasmRef` to an element in the slice. + #[inline] + pub fn index(self, idx: u64) -> WasmRef<'a, T> { + if idx >= self.len { + panic!("WasmSlice out of bounds"); + } + let offset = self.offset + idx * mem::size_of::() as u64; + WasmRef { + buffer: self.buffer, + offset, + marker: PhantomData, + } + } + + /// Get a `WasmSlice` for a subslice of this slice. + #[inline] + pub fn subslice(self, range: Range) -> WasmSlice<'a, T> { + if range.start > range.end || range.end > self.len { + panic!("WasmSlice out of bounds"); + } + let offset = self.offset + range.start * mem::size_of::() as u64; + Self { + buffer: self.buffer, + offset, + len: range.end - range.start, + marker: PhantomData, + } + } + + /// Get an iterator over the elements in this slice. + #[inline] + pub fn iter(self) -> WasmSliceIter<'a, T> { + WasmSliceIter { slice: self } + } + + /// Gains direct access to the memory of this slice + #[inline] + pub fn access(self) -> Result, MemoryAccessError> { + WasmSliceAccess::new(self) + } + + /// Reads an element of this slice. + #[inline] + pub fn read(self, idx: u64) -> Result { + self.index(idx).read() + } + + /// Writes to an element of this slice. + #[inline] + pub fn write(self, idx: u64, val: T) -> Result<(), MemoryAccessError> { + self.index(idx).write(val) + } + + /// Reads the entire slice into the given buffer. + /// + /// The length of the buffer must match the length of the slice. + #[inline] + pub fn read_slice(self, buf: &mut [T]) -> Result<(), MemoryAccessError> { + assert_eq!( + buf.len() as u64, + self.len, + "slice length doesn't match WasmSlice length" + ); + let bytes = unsafe { + slice::from_raw_parts_mut( + buf.as_mut_ptr() as *mut MaybeUninit, + buf.len() * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + Ok(()) + } + + /// Reads the entire slice into the given uninitialized buffer. + /// + /// The length of the buffer must match the length of the slice. + /// + /// This method returns an initialized view of the buffer. + #[inline] + pub fn read_slice_uninit( + self, + buf: &mut [MaybeUninit], + ) -> Result<&mut [T], MemoryAccessError> { + assert_eq!( + buf.len() as u64, + self.len, + "slice length doesn't match WasmSlice length" + ); + let bytes = unsafe { + slice::from_raw_parts_mut( + buf.as_mut_ptr() as *mut MaybeUninit, + buf.len() * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + Ok(unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut T, buf.len()) }) + } + + /// Write the given slice into this `WasmSlice`. + /// + /// The length of the slice must match the length of the `WasmSlice`. + #[inline] + pub fn write_slice(self, data: &[T]) -> Result<(), MemoryAccessError> { + assert_eq!( + data.len() as u64, + self.len, + "slice length doesn't match WasmSlice length" + ); + let bytes = unsafe { + slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * mem::size_of::()) + }; + self.buffer.write(self.offset, bytes) + } + + /// Reads this `WasmSlice` into a `slice`. + #[inline] + pub fn read_to_slice(self, buf: &mut [MaybeUninit]) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + self.buffer.read_uninit(self.offset, buf)?; + Ok(len) + } + + /// Reads this `WasmSlice` into a `Vec`. + #[inline] + pub fn read_to_vec(self) -> Result, MemoryAccessError> { + let len = self.len.try_into().expect("WasmSlice length overflow"); + let mut vec = Vec::with_capacity(len); + let bytes = unsafe { + slice::from_raw_parts_mut( + vec.as_mut_ptr() as *mut MaybeUninit, + len * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + unsafe { + vec.set_len(len); + } + Ok(vec) + } + + /// Reads this `WasmSlice` into a `BytesMut` + #[inline] + pub fn read_to_bytes(self) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + let mut ret = bytes::BytesMut::with_capacity(len); + let bytes = unsafe { + slice::from_raw_parts_mut( + ret.as_mut_ptr() as *mut MaybeUninit, + len * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + unsafe { + ret.set_len(len); + } + Ok(ret) + } +} + +impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "WasmSlice(offset: {}, len: {}, pointer: {:#x})", + self.offset, self.len, self.offset + ) + } +} + +/// Iterator over the elements of a `WasmSlice`. +pub struct WasmSliceIter<'a, T: ValueType> { + slice: WasmSlice<'a, T>, +} + +impl<'a, T: ValueType> Iterator for WasmSliceIter<'a, T> { + type Item = WasmRef<'a, T>; + + fn next(&mut self) -> Option { + if !self.slice.is_empty() { + let elem = self.slice.index(0); + self.slice = self.slice.subslice(1..self.slice.len()); + Some(elem) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (0..self.slice.len()).size_hint() + } +} + +impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> { + fn next_back(&mut self) -> Option { + if !self.slice.is_empty() { + let elem = self.slice.index(self.slice.len() - 1); + self.slice = self.slice.subslice(0..self.slice.len() - 1); + Some(elem) + } else { + None + } + } +} + +impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {} diff --git a/lib/api/src/module.rs b/lib/api/src/module.rs new file mode 100644 index 00000000000..de4b3731cc3 --- /dev/null +++ b/lib/api/src/module.rs @@ -0,0 +1,399 @@ +use bytes::Bytes; +use std::fmt; +use std::fs; +use std::io; +use std::path::Path; + +use crate::engine::AsEngineRef; +use thiserror::Error; +#[cfg(feature = "wat")] +use wasmer_types::WasmError; +use wasmer_types::{ + CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError, +}; +use wasmer_types::{ExportType, ImportType}; + +use crate::into_bytes::IntoBytes; + +#[cfg(feature = "js")] +use crate::js::module as module_imp; +#[cfg(feature = "sys")] +use crate::sys::module as module_imp; + +/// IO Error on a Module Compilation +#[derive(Error, Debug)] +pub enum IoCompileError { + /// An IO error + #[error(transparent)] + Io(#[from] io::Error), + /// A compilation error + #[error(transparent)] + Compile(#[from] CompileError), +} + +/// A WebAssembly Module contains stateless WebAssembly +/// code that has already been compiled and can be instantiated +/// multiple times. +/// +/// ## Cloning a module +/// +/// Cloning a module is cheap: it does a shallow copy of the compiled +/// contents rather than a deep copy. +#[derive(Clone, PartialEq, Eq)] +pub struct Module(pub(crate) module_imp::Module); + +impl Module { + /// Creates a new WebAssembly Module given the configuration + /// in the store. + /// + /// If the provided bytes are not WebAssembly-like (start with `b"\0asm"`), + /// and the "wat" feature is enabled for this crate, this function will try to + /// to convert the bytes assuming they correspond to the WebAssembly text + /// format. + /// + /// ## Security + /// + /// Before the code is compiled, it will be validated using the store + /// features. + /// + /// ## Errors + /// + /// Creating a WebAssembly module from bytecode can result in a + /// [`CompileError`] since this operation requires to transorm the Wasm + /// bytecode into code the machine can easily execute. + /// + /// ## Example + /// + /// Reading from a WAT file. + /// + /// ``` + /// use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = "(module)"; + /// let module = Module::new(&store, wat)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// Reading from bytes: + /// + /// ``` + /// use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// // The following is the same as: + /// // (module + /// // (type $t0 (func (param i32) (result i32))) + /// // (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) + /// // get_local $p0 + /// // i32.const 1 + /// // i32.add) + /// // ) + /// let bytes: Vec = vec![ + /// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, + /// 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07, + /// 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, + /// 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, 0x00, 0x1a, 0x04, 0x6e, + /// 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, + /// 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30, + /// ]; + /// let module = Module::new(&store, bytes)?; + /// # Ok(()) + /// # } + /// ``` + /// # Example of loading a module using just an `Engine` and no `Store` + /// + /// ``` + /// # use wasmer::*; + /// # + /// # let engine: Engine = Cranelift::default().into(); + /// + /// let module = Module::from_file(&engine, "path/to/foo.wasm"); + /// ``` + pub fn new(engine: &impl AsEngineRef, bytes: impl AsRef<[u8]>) -> Result { + #[cfg(feature = "wat")] + let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { + CompileError::Wasm(WasmError::Generic(format!( + "Error when converting wat: {}", + e + ))) + })?; + Self::from_binary(engine, bytes.as_ref()) + } + + /// Creates a new WebAssembly module from a file path. + pub fn from_file( + engine: &impl AsEngineRef, + file: impl AsRef, + ) -> Result { + let file_ref = file.as_ref(); + let canonical = file_ref.canonicalize()?; + let wasm_bytes = std::fs::read(file_ref)?; + let mut module = Self::new(engine, &wasm_bytes)?; + // Set the module name to the absolute path of the filename. + // This is useful for debugging the stack traces. + let filename = canonical.as_path().to_str().unwrap(); + module.set_name(filename); + Ok(module) + } + + /// Creates a new WebAssembly module from a serialized binary. + /// + /// Opposed to [`Module::new`], this function is not compatible with + /// the WebAssembly text format (if the "wat" feature is enabled for + /// this crate). + pub fn from_binary(engine: &impl AsEngineRef, binary: &[u8]) -> Result { + Ok(Self(module_imp::Module::from_binary(engine, binary)?)) + } + + /// Creates a new WebAssembly module skipping any kind of validation. + /// + /// # Safety + /// + /// This can speed up compilation time a bit, but it should be only used + /// in environments where the WebAssembly modules are trusted and validated + /// beforehand. + pub unsafe fn from_binary_unchecked( + engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + Ok(Self(module_imp::Module::from_binary_unchecked( + engine, binary, + )?)) + } + + /// Validates a new WebAssembly Module given the configuration + /// in the Store. + /// + /// This validation is normally pretty fast and checks the enabled + /// WebAssembly features in the Store Engine to assure deterministic + /// validation of the Module. + pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { + module_imp::Module::validate(engine, binary) + } + + /// Serializes a module into a binary representation that the `Engine` + /// can later process via [`Module::deserialize`]. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// # let module = Module::from_file(&store, "path/to/foo.wasm")?; + /// let serialized = module.serialize()?; + /// # Ok(()) + /// # } + /// ``` + pub fn serialize(&self) -> Result { + self.0.serialize() + } + + /// Serializes a module into a file that the `Engine` + /// can later process via [`Module::deserialize_from_file`]. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// # let module = Module::from_file(&store, "path/to/foo.wasm")?; + /// module.serialize_to_file("path/to/foo.so")?; + /// # Ok(()) + /// # } + /// ``` + pub fn serialize_to_file(&self, path: impl AsRef) -> Result<(), SerializeError> { + let serialized = self.0.serialize()?; + fs::write(path, serialized)?; + Ok(()) + } + + /// Deserializes a serialized Module binary into a `Module`. + /// > Note: the module has to be serialized before with the `serialize` method. + /// + /// # Safety + /// + /// This function is inherently **unsafe** as the provided bytes: + /// 1. Are going to be deserialized directly into Rust objects. + /// 2. Contains the function assembly bodies and, if intercepted, + /// a malicious actor could inject code into executable + /// memory. + /// + /// And as such, the `deserialize` method is unsafe. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let module = Module::deserialize(&store, serialized_data)?; + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn deserialize( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + Ok(Self(module_imp::Module::deserialize(engine, bytes)?)) + } + + /// Deserializes a a serialized Module located in a `Path` into a `Module`. + /// > Note: the module has to be serialized before with the `serialize` method. + /// + /// # Safety + /// + /// Please check [`Module::deserialize`]. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # let mut store = Store::default(); + /// # fn main() -> anyhow::Result<()> { + /// let module = Module::deserialize_from_file(&store, path)?; + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn deserialize_from_file( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + Ok(Self(module_imp::Module::deserialize_from_file( + engine, path, + )?)) + } + + /// Returns the name of the current module. + /// + /// This name is normally set in the WebAssembly bytecode by some + /// compilers, but can be also overwritten using the [`Module::set_name`] method. + /// + /// # Example + /// + /// ``` + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = "(module $moduleName)"; + /// let module = Module::new(&store, wat)?; + /// assert_eq!(module.name(), Some("moduleName")); + /// # Ok(()) + /// # } + /// ``` + pub fn name(&self) -> Option<&str> { + self.0.name() + } + + /// Sets the name of the current module. + /// This is normally useful for stacktraces and debugging. + /// + /// It will return `true` if the module name was changed successfully, + /// and return `false` otherwise (in case the module is cloned or + /// already instantiated). + /// + /// # Example + /// + /// ``` + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = "(module)"; + /// let mut module = Module::new(&store, wat)?; + /// assert_eq!(module.name(), None); + /// module.set_name("foo"); + /// assert_eq!(module.name(), Some("foo")); + /// # Ok(()) + /// # } + /// ``` + pub fn set_name(&mut self, name: &str) -> bool { + self.0.set_name(name) + } + + /// Returns an iterator over the imported types in the Module. + /// + /// The order of the imports is guaranteed to be the same as in the + /// WebAssembly bytecode. + /// + /// # Example + /// + /// ``` + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = r#"(module + /// (import "host" "func1" (func)) + /// (import "host" "func2" (func)) + /// )"#; + /// let module = Module::new(&store, wat)?; + /// for import in module.imports() { + /// assert_eq!(import.module(), "host"); + /// assert!(import.name().contains("func")); + /// import.ty(); + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn imports(&self) -> ImportsIterator + '_> { + self.0.imports() + } + + /// Returns an iterator over the exported types in the Module. + /// + /// The order of the exports is guaranteed to be the same as in the + /// WebAssembly bytecode. + /// + /// # Example + /// + /// ``` + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = r#"(module + /// (func (export "namedfunc")) + /// (memory (export "namedmemory") 1) + /// )"#; + /// let module = Module::new(&store, wat)?; + /// for export_ in module.exports() { + /// assert!(export_.name().contains("named")); + /// export_.ty(); + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn exports(&self) -> ExportsIterator + '_> { + self.0.exports() + } + + /// Get the custom sections of the module given a `name`. + /// + /// # Important + /// + /// Following the WebAssembly spec, one name can have multiple + /// custom sections. That's why an iterator (rather than one element) + /// is returned. + pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { + self.0.custom_sections(name) + } + + /// The ABI of the [`ModuleInfo`] is very unstable, we refactor it very often. + /// This function is public because in some cases it can be useful to get some + /// extra information from the module. + /// + /// However, the usage is highly discouraged. + #[doc(hidden)] + pub fn info(&self) -> &ModuleInfo { + self.0.info() + } +} + +impl fmt::Debug for Module { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Module") + .field("name", &self.name()) + .finish() + } +} diff --git a/lib/api/src/native_type.rs b/lib/api/src/native_type.rs new file mode 100644 index 00000000000..8b6f08bd91a --- /dev/null +++ b/lib/api/src/native_type.rs @@ -0,0 +1,922 @@ +//! This module permits to create native functions +//! easily in Rust, thanks to its advanced typing system. + +use wasmer_types::{NativeWasmType, RawValue, Type}; + +use crate::store::AsStoreRef; +use crate::vm::{VMExternRef, VMFuncRef}; + +use crate::{ExternRef, Function, TypedFunction}; +use std::array::TryFromSliceError; +use std::convert::Infallible; +use std::convert::TryInto; +use std::error::Error; + +use crate::store::AsStoreMut; + +/// `NativeWasmTypeInto` performs conversions from and into `NativeWasmType` +/// types with a context. +pub trait NativeWasmTypeInto: NativeWasmType + Sized { + #[doc(hidden)] + fn into_abi(self, store: &mut impl AsStoreMut) -> Self::Abi; + + #[doc(hidden)] + unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self; + + /// Convert self to raw value representation. + fn into_raw(self, store: &mut impl AsStoreMut) -> RawValue; + + /// Convert to self from raw value representation. + /// + /// # Safety + /// + unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self; +} + +impl NativeWasmTypeInto for i32 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { i32: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.i32 + } +} + +impl NativeWasmTypeInto for i64 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { i64: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.i64 + } +} + +impl NativeWasmTypeInto for f32 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { f32: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.f32 + } +} + +impl NativeWasmTypeInto for f64 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { f64: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.f64 + } +} + +impl NativeWasmTypeInto for u128 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { u128: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.u128 + } +} + +impl NativeWasmType for ExternRef { + const WASM_TYPE: Type = Type::ExternRef; + type Abi = usize; +} + +impl NativeWasmTypeInto for Option { + #[inline] + unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + VMExternRef::from_raw(RawValue { externref: abi }) + .map(|e| ExternRef::from_vm_externref(store, e)) + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self.map_or(0, |e| unsafe { e.vm_externref().into_raw().externref }) + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + self.map_or(RawValue { externref: 0 }, |e| e.vm_externref().into_raw()) + } + + #[inline] + unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self { + VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)) + } +} + +impl From> for Function +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + fn from(other: TypedFunction) -> Self { + other.into_function() + } +} + +impl NativeWasmType for Function { + const WASM_TYPE: Type = Type::FuncRef; + type Abi = usize; +} + +impl NativeWasmTypeInto for Option { + #[inline] + unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + VMFuncRef::from_raw(RawValue { funcref: abi }).map(|f| Function::from_vm_funcref(store, f)) + } + + #[inline] + fn into_abi(self, store: &mut impl AsStoreMut) -> Self::Abi { + self.map_or(0, |f| unsafe { f.vm_funcref(store).into_raw().externref }) + } + + #[inline] + fn into_raw(self, store: &mut impl AsStoreMut) -> RawValue { + self.map_or(RawValue { externref: 0 }, |e| { + e.vm_funcref(store).into_raw() + }) + } + + #[inline] + unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self { + VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(store, f)) + } +} + +/// 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 { + 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 { + 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; + + /// 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`. +/// +/// It is mostly used to turn result values of a Wasm function +/// call into a `Result`. +pub trait IntoResult +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; +} + +impl IntoResult 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 { + Ok(self) + } +} + +impl IntoResult for Result +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 = x.into_result(); + + assert_eq!(result_of_x.unwrap(), x); + } + + #[test] + fn test_into_result_over_result() { + { + let x: Result = Ok(42); + let result_of_x: Result = 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 = Err(E); + let result_of_x: Result = 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 { + 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] { + &[] + } +} + +macro_rules! impl_wasmtypelist { + ( [$c_struct_representation:ident] + $c_struct_name:ident, + $( $x:ident ),* ) => { + + /// A structure with a C-compatible representation that can hold a set of Wasm values. + /// This type is used by `WasmTypeList::CStruct`. + #[repr($c_struct_representation)] + pub struct $c_struct_name< $( $x ),* > ( $( <<$x as FromToNativeWasmType>::Native as NativeWasmType>::Abi ),* ) + where + $( $x: FromToNativeWasmType ),*; + + // Implement `WasmTypeList` for a specific tuple. + #[allow(unused_parens, dead_code)] + impl< $( $x ),* > + WasmTypeList + for + ( $( $x ),* ) + where + $( $x: FromToNativeWasmType ),* + { + type CStruct = $c_struct_name< $( $x ),* >; + + type Array = [RawValue; count_idents!( $( $x ),* )]; + + fn size() -> u32 { + count_idents!( $( $x ),* ) as _ + } + + #[allow(unused_mut)] + #[allow(clippy::unused_unit)] + #[allow(clippy::missing_safety_doc)] + unsafe fn from_array(mut _store: &mut impl AsStoreMut, array: Self::Array) -> Self { + // Unpack items of the array. + #[allow(non_snake_case)] + let [ $( $x ),* ] = array; + + // Build the tuple. + ( + $( + FromToNativeWasmType::from_native(NativeWasmTypeInto::from_raw(_store, $x)) + ),* + ) + } + + #[allow(clippy::missing_safety_doc)] + unsafe fn from_slice(store: &mut impl AsStoreMut, slice: &[RawValue]) -> Result { + Ok(Self::from_array(store, slice.try_into()?)) + } + + #[allow(unused_mut)] + #[allow(clippy::missing_safety_doc)] + unsafe fn into_array(self, mut _store: &mut impl AsStoreMut) -> Self::Array { + // Unpack items of the tuple. + #[allow(non_snake_case)] + let ( $( $x ),* ) = self; + + // Build the array. + [ + $( + FromToNativeWasmType::to_native($x).into_raw(_store) + ),* + ] + } + + fn empty_array() -> Self::Array { + // Build an array initialized with `0`. + [RawValue { i32: 0 }; count_idents!( $( $x ),* )] + } + + #[allow(unused_mut)] + #[allow(clippy::unused_unit)] + #[allow(clippy::missing_safety_doc)] + unsafe fn from_c_struct(mut _store: &mut impl AsStoreMut, c_struct: Self::CStruct) -> Self { + // Unpack items of the C structure. + #[allow(non_snake_case)] + let $c_struct_name( $( $x ),* ) = c_struct; + + ( + $( + FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(_store, $x)) + ),* + ) + } + + #[allow(unused_parens, non_snake_case, unused_mut)] + #[allow(clippy::missing_safety_doc)] + unsafe fn into_c_struct(self, mut _store: &mut impl AsStoreMut) -> Self::CStruct { + // Unpack items of the tuple. + let ( $( $x ),* ) = self; + + // Build the C structure. + $c_struct_name( + $( + FromToNativeWasmType::to_native($x).into_abi(_store) + ),* + ) + } + + #[allow(non_snake_case)] + unsafe fn write_c_struct_to_ptr(c_struct: Self::CStruct, _ptr: *mut RawValue) { + // Unpack items of the tuple. + let $c_struct_name( $( $x ),* ) = c_struct; + + let mut _n = 0; + $( + *_ptr.add(_n).cast() = $x; + _n += 1; + )* + } + + fn wasm_types() -> &'static [Type] { + &[ + $( + $x::Native::WASM_TYPE + ),* + ] + } + } + + }; +} + +// Black-magic to count the number of identifiers at compile-time. +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 + } + }; +} + +// Here we go! Let's generate all the C struct and `WasmTypeList` +// implementations. +impl_wasmtypelist!([C] S0,); +impl_wasmtypelist!([transparent] S1, A1); +impl_wasmtypelist!([C] S2, A1, A2); +impl_wasmtypelist!([C] S3, A1, A2, A3); +impl_wasmtypelist!([C] S4, A1, A2, A3, A4); +impl_wasmtypelist!([C] S5, A1, A2, A3, A4, A5); +impl_wasmtypelist!([C] S6, A1, A2, A3, A4, A5, A6); +impl_wasmtypelist!([C] S7, A1, A2, A3, A4, A5, A6, A7); +impl_wasmtypelist!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); +impl_wasmtypelist!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_wasmtypelist!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_wasmtypelist!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_wasmtypelist!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_wasmtypelist!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_wasmtypelist!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_wasmtypelist!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_wasmtypelist!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_wasmtypelist!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_wasmtypelist!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); +impl_wasmtypelist!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); +impl_wasmtypelist!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); +impl_wasmtypelist!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); +impl_wasmtypelist!([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_wasmtypelist!([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_wasmtypelist!([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_wasmtypelist!([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_wasmtypelist!([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 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!(::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!(::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!(::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!(::wasm_types(), [Type::I32]); + assert_eq!(::wasm_types(), [Type::I64]); + assert_eq!(::wasm_types(), [Type::F32]); + assert_eq!(::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::*; + use wasmer_types::Type; + + #[test] + fn test_wasm_types() { + assert_eq!(i32::WASM_TYPE, Type::I32); + assert_eq!(i64::WASM_TYPE, Type::I64); + assert_eq!(f32::WASM_TYPE, Type::F32); + assert_eq!(f64::WASM_TYPE, Type::F64); + assert_eq!(u128::WASM_TYPE, Type::V128); + } + /* + #[test] + fn test_roundtrip() { + unsafe { + assert_eq!(i32::from_raw(42i32.into_raw()), 42i32); + assert_eq!(i64::from_raw(42i64.into_raw()), 42i64); + assert_eq!(f32::from_raw(42f32.into_raw()), 42f32); + assert_eq!(f64::from_raw(42f64.into_raw()), 42f64); + assert_eq!(u128::from_raw(42u128.into_raw()), 42u128); + } + } + */ +} + +// pub trait IntegerAtomic +// where +// Self: Sized +// { +// type Primitive; + +// fn add(&self, other: Self::Primitive) -> Self::Primitive; +// fn sub(&self, other: Self::Primitive) -> Self::Primitive; +// fn and(&self, other: Self::Primitive) -> Self::Primitive; +// fn or(&self, other: Self::Primitive) -> Self::Primitive; +// fn xor(&self, other: Self::Primitive) -> Self::Primitive; +// fn load(&self) -> Self::Primitive; +// fn store(&self, other: Self::Primitive) -> Self::Primitive; +// fn compare_exchange(&self, expected: Self::Primitive, new: Self::Primitive) -> Self::Primitive; +// fn swap(&self, other: Self::Primitive) -> Self::Primitive; +// } diff --git a/lib/api/src/sys/ptr.rs b/lib/api/src/ptr.rs similarity index 96% rename from lib/api/src/sys/ptr.rs rename to lib/api/src/ptr.rs index 371a9cc7eb0..079b82b7c84 100644 --- a/lib/api/src/sys/ptr.rs +++ b/lib/api/src/ptr.rs @@ -1,14 +1,12 @@ use crate::access::WasmRefAccess; -use crate::sys::{externals::MemoryView, FromToNativeWasmType}; -use crate::NativeWasmTypeInto; -use crate::{MemoryAccessError, WasmRef, WasmSlice}; +use crate::mem_access::{MemoryAccessError, WasmRef, WasmSlice}; +use crate::{AsStoreRef, FromToNativeWasmType, MemoryView, NativeWasmTypeInto}; use std::convert::TryFrom; use std::{fmt, marker::PhantomData, mem}; -use wasmer_types::ValueType; - pub use wasmer_types::Memory32; pub use wasmer_types::Memory64; pub use wasmer_types::MemorySize; +use wasmer_types::ValueType; /// Alias for `WasmPtr. pub type WasmPtr64 = WasmPtr; @@ -249,6 +247,10 @@ where _phantom: PhantomData, } } + #[inline] + fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true // in Javascript there are no different stores + } } unsafe impl ValueType for WasmPtr { diff --git a/lib/api/src/sys/store.rs b/lib/api/src/store.rs similarity index 64% rename from lib/api/src/sys/store.rs rename to lib/api/src/store.rs index 9636bfa1163..4e5e058c2f4 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/store.rs @@ -1,15 +1,24 @@ -use crate::sys::tunables::BaseTunables; +use crate::engine::{AsEngineRef, Engine, EngineRef}; use derivative::Derivative; use std::{ fmt, ops::{Deref, DerefMut}, }; -#[cfg(feature = "compiler")] -use wasmer_compiler::{AsEngineRef, Engine, EngineBuilder, EngineRef, Tunables}; -use wasmer_types::OnCalledAction; -use wasmer_vm::{init_traps, StoreId, TrapHandler, TrapHandlerFn}; - -use wasmer_vm::StoreObjects; +#[cfg(feature = "sys")] +pub use wasmer_compiler::Tunables; +pub use wasmer_types::{OnCalledAction, StoreId}; +#[cfg(feature = "sys")] +use wasmer_vm::init_traps; +#[cfg(feature = "sys")] +pub use wasmer_vm::TrapHandlerFn; + +#[cfg(feature = "sys")] +use crate::sys::NativeEngineExt; +#[cfg(feature = "sys")] +pub use wasmer_vm::{StoreHandle, StoreObjects}; + +#[cfg(feature = "js")] +pub use crate::js::store::{StoreHandle, StoreObjects}; /// Call handler for a store. // TODO: better documentation! @@ -25,8 +34,8 @@ pub type OnCalledHandler = Box< pub(crate) struct StoreInner { pub(crate) objects: StoreObjects, #[derivative(Debug = "ignore")] - #[cfg(feature = "compiler")] pub(crate) engine: Engine, + #[cfg(feature = "sys")] #[derivative(Debug = "ignore")] pub(crate) trap_handler: Option>>, #[derivative(Debug = "ignore")] @@ -44,32 +53,27 @@ pub(crate) struct StoreInner { /// Spec: pub struct Store { pub(crate) inner: Box, - #[cfg(feature = "compiler")] - engine: Engine, } impl Store { - #[cfg(feature = "compiler")] /// Creates a new `Store` with a specific [`Engine`]. pub fn new(engine: impl Into) -> Self { - let engine = engine.into(); - // Make sure the signal handlers are installed. // This is required for handling traps. + #[cfg(feature = "sys")] init_traps(); Self { inner: Box::new(StoreInner { objects: Default::default(), - engine: engine.cloned(), + engine: engine.into(), + #[cfg(feature = "sys")] trap_handler: None, on_called: None, }), - engine: engine.cloned(), } } - #[cfg(feature = "compiler")] #[deprecated( since = "3.0.0", note = "Store::new_with_engine has been deprecated in favor of Store::new" @@ -79,12 +83,17 @@ impl Store { Self::new(engine) } + #[cfg(feature = "sys")] /// Set the trap handler in this store. pub fn set_trap_handler(&mut self, handler: Option>>) { self.inner.trap_handler = handler; } - #[cfg(feature = "compiler")] + #[cfg(feature = "sys")] + #[deprecated( + since = "3.2.0", + note = "store.new_with_tunables() has been deprecated in favor of engine.set_tunables()" + )] /// Creates a new `Store` with a specific [`Engine`] and [`Tunables`]. pub fn new_with_tunables( engine: impl Into, @@ -96,24 +105,25 @@ impl Store { Self::new(engine) } - #[cfg(feature = "compiler")] + #[cfg(feature = "sys")] + #[deprecated( + since = "3.2.0", + note = "store.tunables() has been deprecated in favor of store.engine().tunables()" + )] /// Returns the [`Tunables`]. pub fn tunables(&self) -> &dyn Tunables { - self.engine.tunables() + self.inner.engine.tunables() } - #[cfg(feature = "compiler")] /// Returns the [`Engine`]. pub fn engine(&self) -> &Engine { - &self.engine + &self.inner.engine } - #[cfg(feature = "compiler")] /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. The - /// tunables are excluded from the logic. + /// equal to another store if both have the same engine. pub fn same(a: &Self, b: &Self) -> bool { - a.engine.id() == b.engine.id() + a.id() == b.id() } /// Returns the ID of this store @@ -122,81 +132,20 @@ impl Store { } } -#[cfg(feature = "compiler")] impl PartialEq for Store { fn eq(&self, other: &Self) -> bool { Self::same(self, other) } } -unsafe impl TrapHandler for Store { - fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool { - if let Some(handler) = self.inner.trap_handler.as_ref() { - call(handler.as_ref()) - } else { - false - } - } -} - -// impl PartialEq for Store { -// fn eq(&self, other: &Self) -> bool { -// Self::same(self, other) -// } -// } - // This is required to be able to set the trap_handler in the // Store. unsafe impl Send for Store {} unsafe impl Sync for Store {} -// We only implement default if we have assigned a default compiler and engine -#[cfg(feature = "compiler")] impl Default for Store { fn default() -> Self { - // We store them on a function that returns to make - // sure this function doesn't emit a compile error even if - // more than one compiler is enabled. - #[allow(unreachable_code)] - #[cfg(any(feature = "cranelift", feature = "llvm", feature = "singlepass"))] - fn get_config() -> impl wasmer_compiler::CompilerConfig + 'static { - cfg_if::cfg_if! { - if #[cfg(feature = "cranelift")] { - wasmer_compiler_cranelift::Cranelift::default() - } else if #[cfg(feature = "llvm")] { - wasmer_compiler_llvm::LLVM::default() - } else if #[cfg(feature = "singlepass")] { - wasmer_compiler_singlepass::Singlepass::default() - } else { - compile_error!("No default compiler chosen") - } - } - } - - #[allow(unreachable_code, unused_mut)] - fn get_engine() -> Engine { - cfg_if::cfg_if! { - if #[cfg(feature = "compiler")] { - cfg_if::cfg_if! { - if #[cfg(any(feature = "cranelift", feature = "llvm", feature = "singlepass"))] - { - let config = get_config(); - EngineBuilder::new(Box::new(config) as Box) - .engine() - } else { - EngineBuilder::headless() - .engine() - } - } - } else { - compile_error!("No default engine chosen") - } - } - } - - let engine = get_engine(); - let tunables = BaseTunables::for_target(engine.target()); - Self::new_with_tunables(&engine, tunables) + Self::new(Engine::default()) } } @@ -216,21 +165,18 @@ impl AsStoreMut for Store { } } -#[cfg(feature = "compiler")] impl AsEngineRef for Store { fn as_engine_ref(&self) -> EngineRef<'_> { - EngineRef::new(&self.engine) + EngineRef::new(&self.inner.engine) } } -#[cfg(feature = "compiler")] impl AsEngineRef for StoreRef<'_> { fn as_engine_ref(&self) -> EngineRef<'_> { EngineRef::new(&self.inner.engine) } } -#[cfg(feature = "compiler")] impl AsEngineRef for StoreMut<'_> { fn as_engine_ref(&self) -> EngineRef<'_> { EngineRef::new(&self.inner.engine) @@ -253,27 +199,29 @@ impl<'a> StoreRef<'a> { &self.inner.objects } - #[cfg(feature = "compiler")] + #[cfg(feature = "sys")] + #[deprecated( + since = "3.2.0", + note = "store.tunables() has been deprecated in favor of store.engine().tunables()" + )] /// Returns the [`Tunables`]. pub fn tunables(&self) -> &dyn Tunables { self.inner.engine.tunables() } - #[cfg(feature = "compiler")] /// Returns the [`Engine`]. pub fn engine(&self) -> &Engine { &self.inner.engine } - #[cfg(feature = "compiler")] /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. The - /// tunables are excluded from the logic. + /// equal to another store if both have the same engine. pub fn same(a: &Self, b: &Self) -> bool { - a.inner.engine.id() == b.inner.engine.id() + a.inner.objects.id() == b.inner.objects.id() } /// The signal handler + #[cfg(feature = "sys")] #[inline] pub fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { self.inner @@ -290,28 +238,29 @@ pub struct StoreMut<'a> { impl<'a> StoreMut<'a> { /// Returns the [`Tunables`]. - #[cfg(feature = "compiler")] + #[cfg(feature = "sys")] + #[deprecated( + since = "3.2.0", + note = "store.tunables() has been deprecated in favor of store.engine().tunables()" + )] pub fn tunables(&self) -> &dyn Tunables { self.inner.engine.tunables() } /// Returns the [`Engine`]. - #[cfg(feature = "compiler")] pub fn engine(&self) -> &Engine { &self.inner.engine } - #[cfg(feature = "compiler")] /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. The - /// tunables are excluded from the logic. + /// equal to another store if both have the same engine. pub fn same(a: &Self, b: &Self) -> bool { - a.inner.engine.id() == b.inner.engine.id() + a.inner.objects.id() == b.inner.objects.id() } - #[cfg(feature = "compiler")] - pub(crate) fn tunables_and_objects_mut(&mut self) -> (&dyn Tunables, &mut StoreObjects) { - (self.inner.engine.tunables(), &mut self.inner.objects) + #[allow(unused)] + pub(crate) fn engine_and_objects_mut(&mut self) -> (&Engine, &mut StoreObjects) { + (&self.inner.engine, &mut self.inner.objects) } pub(crate) fn as_raw(&self) -> *mut StoreInner { diff --git a/lib/api/src/sys/engine.rs b/lib/api/src/sys/engine.rs new file mode 100644 index 00000000000..4598bcc430a --- /dev/null +++ b/lib/api/src/sys/engine.rs @@ -0,0 +1,106 @@ +pub use wasmer_compiler::{ + Artifact, BaseTunables, CompilerConfig, Engine, EngineBuilder, Tunables, +}; +use wasmer_types::{Features, Target}; + +/// Returns the default engine for the Sys engine +pub(crate) fn default_engine() -> Engine { + // We store them on a function that returns to make + // sure this function doesn't emit a compile error even if + // more than one compiler is enabled. + #[allow(unreachable_code)] + #[cfg(any(feature = "cranelift", feature = "llvm", feature = "singlepass"))] + fn get_config() -> impl wasmer_compiler::CompilerConfig + 'static { + cfg_if::cfg_if! { + if #[cfg(feature = "cranelift")] { + wasmer_compiler_cranelift::Cranelift::default() + } else if #[cfg(feature = "llvm")] { + wasmer_compiler_llvm::LLVM::default() + } else if #[cfg(feature = "singlepass")] { + wasmer_compiler_singlepass::Singlepass::default() + } else { + compile_error!("No default compiler chosen") + } + } + } + + #[allow(unreachable_code, unused_mut)] + fn get_engine() -> Engine { + cfg_if::cfg_if! { + if #[cfg(feature = "compiler")] { + cfg_if::cfg_if! { + if #[cfg(any(feature = "cranelift", feature = "llvm", feature = "singlepass"))] + { + let config = get_config(); + EngineBuilder::new(Box::new(config) as Box) + .engine() + } else { + EngineBuilder::headless() + .engine() + } + } + } else { + compile_error!("No default engine chosen") + } + } + } + + let mut engine = get_engine(); + let tunables = BaseTunables::for_target(engine.target()); + engine.set_tunables(tunables); + engine +} + +/// The custom trait to access to all the `sys` function in the common +/// engine. +pub trait NativeEngineExt { + /// Create a new `Engine` with the given config + #[cfg(feature = "compiler")] + fn new(compiler_config: Box, target: Target, features: Features) -> Self; + + /// Create a headless `Engine` + /// + /// A headless engine is an engine without any compiler attached. + /// This is useful for assuring a minimal runtime for running + /// WebAssembly modules. + /// + /// For example, for running in IoT devices where compilers are very + /// expensive, or also to optimize startup speed. + /// + /// # Important + /// + /// Headless engines can't compile or validate any modules, + /// they just take already processed Modules (via `Module::serialize`). + fn headless() -> Self; + + /// Gets the target + fn target(&self) -> &Target; + + /// Attach a Tunable to this engine + fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static); + + /// Get a reference to attached Tunable of this engine + fn tunables(&self) -> &dyn Tunables; +} + +impl NativeEngineExt for crate::engine::Engine { + fn new(compiler_config: Box, target: Target, features: Features) -> Self { + Self(Engine::new(compiler_config, target, features)) + } + + fn headless() -> Self { + Self(Engine::headless()) + } + + fn target(&self) -> &Target { + self.0.target() + } + + fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) { + self.0.set_tunables(tunables) + } + + fn tunables(&self) -> &dyn Tunables { + self.0.tunables() + } +} diff --git a/lib/api/src/sys/extern_ref.rs b/lib/api/src/sys/extern_ref.rs index 4f0fcd9fa83..2f50751b6bb 100644 --- a/lib/api/src/sys/extern_ref.rs +++ b/lib/api/src/sys/extern_ref.rs @@ -1,18 +1,16 @@ use std::any::Any; +use wasmer_vm::VMExternRef; +use wasmer_vm::{StoreHandle, VMExternObj}; -use wasmer_vm::{StoreHandle, VMExternObj, VMExternRef}; - -use super::store::{AsStoreMut, AsStoreRef}; +use crate::store::{AsStoreMut, AsStoreRef}; #[derive(Debug, Clone)] #[repr(transparent)] -/// An opaque reference to some data. This reference can be passed through Wasm. pub struct ExternRef { handle: StoreHandle, } impl ExternRef { - /// Make a new extern reference pub fn new(store: &mut impl AsStoreMut, value: T) -> Self where T: Any + Send + Sync + 'static + Sized, @@ -22,7 +20,6 @@ impl ExternRef { } } - /// Try to downcast to the given value. pub fn downcast<'a, T>(&self, store: &'a impl AsStoreRef) -> Option<&'a T> where T: Any + Send + Sync + 'static + Sized, @@ -46,13 +43,6 @@ impl ExternRef { } } - /// Checks whether this `ExternRef` can be used with the given context. - /// - /// Primitive (`i32`, `i64`, etc) and null funcref/externref values are not - /// tied to a context and can be freely shared between contexts. - /// - /// Externref and funcref values are tied to a context and can only be used - /// with that context. pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() } diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 3c3d383113a..30a89a1411e 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -1,49 +1,18 @@ -use wasmer_types::RawValue; +use crate::externals::function::{HostFunction, WithEnv, WithoutEnv}; +use crate::native_type::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList}; +use crate::store::{AsStoreMut, AsStoreRef, StoreInner, StoreMut}; +use crate::vm::VMExternFunction; +use crate::{FunctionEnv, FunctionEnvMut, FunctionType, RuntimeError, Value}; +use std::panic::{self, AssertUnwindSafe}; +use std::{cell::UnsafeCell, cmp::max, ffi::c_void}; +use wasmer_types::{NativeWasmType, RawValue}; use wasmer_vm::{ - on_host_stack, raise_user_trap, resume_panic, InternalStoreHandle, StoreHandle, VMContext, - VMDynamicFunctionContext, VMExtern, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionKind, - VMTrampoline, + on_host_stack, raise_user_trap, resume_panic, wasmer_call_trampoline, MaybeInstanceOwned, + StoreHandle, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMExtern, VMFuncRef, + VMFunction, VMFunctionBody, VMFunctionContext, VMFunctionKind, VMTrampoline, }; -use crate::sys::exports::{ExportError, Exportable}; -use crate::sys::externals::Extern; -use crate::sys::store::{AsStoreMut, AsStoreRef}; -use crate::sys::{FunctionType, RuntimeError, TypedFunction}; -use crate::FunctionEnv; - -pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; - -#[cfg(feature = "compiler")] -use { - crate::{ - sys::store::{StoreInner, StoreMut}, - FunctionEnvMut, Value, - }, - inner::StaticFunction, - std::{cell::UnsafeCell, cmp::max, ffi::c_void}, - wasmer_vm::{ - wasmer_call_trampoline, MaybeInstanceOwned, VMCallerCheckedAnyfunc, VMFunctionContext, - }, -}; - -/// 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: -/// -/// # 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)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Function { pub(crate) handle: StoreHandle, } @@ -55,61 +24,6 @@ impl From> for 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. - #[cfg(feature = "compiler")] - pub fn new(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self - where - FT: Into, - F: Fn(&[Value]) -> Result, RuntimeError> + 'static + Send + Sync, - { - let env = FunctionEnv::new(&mut store.as_store_mut(), ()); - let wrapped_func = move |_env: FunctionEnvMut<()>, - args: &[Value]| - -> Result, RuntimeError> { func(args) }; - Self::new_with_env(store, &env, ty, wrapped_func) - } - - #[cfg(feature = "compiler")] - /// 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( store: &mut impl AsStoreMut, env: &FunctionEnv, @@ -170,6 +84,7 @@ impl Function { let type_index = store .as_store_mut() .engine() + .0 .register_signature(&function_type); let vmctx = VMFunctionContext { host_env: host_data.as_ref() as *const _ as *mut c_void, @@ -193,22 +108,6 @@ impl Function { } } - #[cfg(feature = "compiler")] - #[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(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) - } - - #[cfg(feature = "compiler")] /// Creates a new host `Function` from a native function. pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self where @@ -228,6 +127,7 @@ impl Function { let type_index = store .as_store_mut() .engine() + .0 .register_signature(&function_type); let vmctx = VMFunctionContext { host_env: host_data.as_ref() as *const _ as *mut c_void, @@ -252,44 +152,6 @@ impl Function { } } - #[cfg(feature = "compiler")] - #[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( - store: &mut impl AsStoreMut, - env: &FunctionEnv, - func: F, - ) -> Self - where - F: HostFunction + 'static + Send + Sync, - Args: WasmTypeList, - Rets: WasmTypeList, - { - Self::new_typed_with_env(store, env, func) - } - - #[cfg(feature = "compiler")] - /// 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( store: &mut impl AsStoreMut, env: &FunctionEnv, @@ -300,8 +162,6 @@ impl Function { Args: WasmTypeList, Rets: WasmTypeList, { - // println!("new native {:p}", &new_env); - let func_ptr = func.function_body_ptr(); let host_data = Box::new(StaticFunction { raw_store: store.as_store_mut().as_raw() as *mut u8, @@ -313,6 +173,7 @@ impl Function { let type_index = store .as_store_mut() .engine() + .0 .register_signature(&function_type); let vmctx = VMFunctionContext { host_env: host_data.as_ref() as *const _ as *mut c_void, @@ -337,40 +198,6 @@ impl Function { } } - #[allow(missing_docs)] - #[allow(unused_variables)] - #[cfg(not(feature = "compiler"))] - pub fn new_typed_with_env( - store: &mut impl AsStoreMut, - env: &FunctionEnv, - func: F, - ) -> Self - where - F: HostFunction + 'static + Send + Sync, - Args: WasmTypeList, - Rets: WasmTypeList, - { - unimplemented!("this platform does not support functions without the 'compiler' feature") - } - - /// 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.handle .get(store.as_store_ref().objects()) @@ -378,7 +205,6 @@ impl Function { .clone() } - #[cfg(feature = "compiler")] fn call_wasm( &self, store: &mut impl AsStoreMut, @@ -423,9 +249,7 @@ impl Function { ))); } if !arg.is_from_store(store) { - return Err(RuntimeError::new( - "cross-`Context` values are not supported", - )); + return Err(RuntimeError::new("cross-`Store` values are not supported")); } *slot = arg.as_raw(store); } @@ -435,7 +259,6 @@ impl Function { Ok(()) } - #[cfg(feature = "compiler")] fn call_wasm_raw( &self, store: &mut impl AsStoreMut, @@ -492,80 +315,10 @@ impl Function { Ok(()) } - /// 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() } - #[cfg(feature = "compiler")] - /// 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, @@ -586,7 +339,6 @@ impl Function { #[doc(hidden)] #[allow(missing_docs)] - #[cfg(feature = "compiler")] pub fn call_raw( &self, store: &mut impl AsStoreMut, @@ -613,11 +365,11 @@ impl Function { VMFuncRef(vm_function.anyfunc.as_ptr()) } - #[cfg(feature = "compiler")] pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self { let signature = store .as_store_ref() .engine() + .0 .lookup_signature(funcref.0.as_ref().type_index) .expect("Signature not found in store"); let vm_function = VMFunction { @@ -633,150 +385,10 @@ impl Function { } } - /// Transform this WebAssembly function into a native function. - /// See [`TypedFunction`] to learn more. - #[cfg(feature = "compiler")] - #[deprecated(since = "3.0.0", note = "native() has been renamed to typed().")] - pub fn native( - &self, - store: &impl AsStoreRef, - ) -> Result, 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( - &self, - store: &impl AsStoreRef, - ) -> Result, 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(self.clone())) - } - - pub(crate) fn from_vm_extern( - store: &mut impl AsStoreMut, - internal: InternalStoreHandle, - ) -> Self { + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self { Self { handle: unsafe { - StoreHandle::from_internal(store.as_store_ref().objects().id(), internal) + StoreHandle::from_internal(store.as_store_ref().objects().id(), vm_extern) }, } } @@ -791,15 +403,6 @@ impl Function { } } -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), - } - } -} - /// Host state for a dynamic function. pub(crate) struct DynamicFunction { func: F, @@ -815,8 +418,6 @@ where this: &mut VMDynamicFunctionContext, values_vec: *mut RawValue, ) { - use std::panic::{self, AssertUnwindSafe}; - let result = on_host_stack(|| panic::catch_unwind(AssertUnwindSafe(|| (this.ctx.func)(values_vec)))); @@ -847,503 +448,20 @@ where } } -/// This private inner module contains the low-level implementation -/// for `Function` and its siblings. -mod inner { - use std::array::TryFromSliceError; - use std::convert::{Infallible, TryInto}; - use std::error::Error; - use std::panic::{self, AssertUnwindSafe}; - use wasmer_vm::{on_host_stack, VMContext, VMTrampoline}; - - use crate::sys::function_env::FunctionEnvMut; - use wasmer_types::{NativeWasmType, RawValue, Type}; - use wasmer_vm::{raise_user_trap, resume_panic, VMFunctionBody}; - - use crate::sys::NativeWasmTypeInto; - use crate::{AsStoreMut, AsStoreRef, ExternRef, FunctionEnv, StoreMut}; - - use crate::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 { - 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)) - } - } - - #[cfg(feature = "compiler")] - unsafe impl FromToNativeWasmType for Option { - 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]>; - - /// 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; - - /// 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`. - /// - /// It is mostly used to turn result values of a Wasm function - /// call into a `Result`. - pub trait IntoResult - 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; - } - - impl IntoResult 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 { - Ok(self) - } - } - - impl IntoResult for Result - 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 = x.into_result(); - - assert_eq!(result_of_x.unwrap(), x); - } - - #[test] - fn test_into_result_over_result() { - { - let x: Result = Ok(42); - let result_of_x: Result = 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 = Err(E); - let result_of_x: Result = x.into_result(); - - assert_eq!(result_of_x.unwrap_err(), E); - } - } - } - - /// 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 - 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; - } - - /// 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 {} - } - - /// Represents a low-level Wasm static host function. See - /// [`super::Function::new_typed`] and - /// [`super::Function::new_typed_with_env`] to learn more. - pub(crate) struct StaticFunction { - pub(crate) raw_store: *mut u8, - pub(crate) env: FunctionEnv, - pub(crate) func: F, - } +/// Represents a low-level Wasm static host function. See +/// [`super::Function::new_typed`] and +/// [`super::Function::new_typed_with_env`] to learn more. +pub(crate) struct StaticFunction { + pub(crate) raw_store: *mut u8, + pub(crate) env: FunctionEnv, + pub(crate) func: F, +} - macro_rules! impl_host_function { +macro_rules! impl_host_function { ( [$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { - /// A structure with a C-compatible representation that can hold a set of Wasm values. - /// This type is used by `WasmTypeList::CStruct`. - #[repr($c_struct_representation)] - pub struct $c_struct_name< $( $x ),* > ( $( <<$x as FromToNativeWasmType>::Native as NativeWasmType>::Abi ),* ) - where - $( $x: FromToNativeWasmType ),*; - - // Implement `WasmTypeList` for a specific tuple. - #[allow(unused_parens, dead_code)] - impl< $( $x ),* > - WasmTypeList - for - ( $( $x ),* ) - where - $( $x: FromToNativeWasmType ),* - { - type CStruct = $c_struct_name< $( $x ),* >; - - type Array = [RawValue; count_idents!( $( $x ),* )]; - - #[allow(unused_mut)] - #[allow(clippy::unused_unit)] - #[allow(clippy::missing_safety_doc)] - unsafe fn from_array(mut _store: &mut impl AsStoreMut, array: Self::Array) -> Self { - // Unpack items of the array. - #[allow(non_snake_case)] - let [ $( $x ),* ] = array; - - // Build the tuple. - ( - $( - FromToNativeWasmType::from_native(NativeWasmTypeInto::from_raw(_store, $x)) - ),* - ) - } - - #[allow(clippy::missing_safety_doc)] - unsafe fn from_slice(store: &mut impl AsStoreMut, slice: &[RawValue]) -> Result { - Ok(Self::from_array(store, slice.try_into()?)) - } - - #[allow(unused_mut)] - #[allow(clippy::missing_safety_doc)] - unsafe fn into_array(self, mut _store: &mut impl AsStoreMut) -> Self::Array { - // Unpack items of the tuple. - #[allow(non_snake_case)] - let ( $( $x ),* ) = self; - - // Build the array. - [ - $( - FromToNativeWasmType::to_native($x).into_raw(_store) - ),* - ] - } - - fn empty_array() -> Self::Array { - // Build an array initialized with `0`. - [RawValue { i32: 0 }; count_idents!( $( $x ),* )] - } - - #[allow(unused_mut)] - #[allow(clippy::unused_unit)] - #[allow(clippy::missing_safety_doc)] - unsafe fn from_c_struct(mut _store: &mut impl AsStoreMut, c_struct: Self::CStruct) -> Self { - // Unpack items of the C structure. - #[allow(non_snake_case)] - let $c_struct_name( $( $x ),* ) = c_struct; - - ( - $( - FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(_store, $x)) - ),* - ) - } - - #[allow(unused_parens, non_snake_case, unused_mut)] - #[allow(clippy::missing_safety_doc)] - unsafe fn into_c_struct(self, mut _store: &mut impl AsStoreMut) -> Self::CStruct { - // Unpack items of the tuple. - let ( $( $x ),* ) = self; - - // Build the C structure. - $c_struct_name( - $( - FromToNativeWasmType::to_native($x).into_abi(_store) - ),* - ) - } - - #[allow(non_snake_case)] - unsafe fn write_c_struct_to_ptr(c_struct: Self::CStruct, _ptr: *mut RawValue) { - // Unpack items of the tuple. - let $c_struct_name( $( $x ),* ) = c_struct; - - let mut _n = 0; - $( - *_ptr.add(_n).cast() = $x; - _n += 1; - )* - } - - fn wasm_types() -> &'static [Type] { - &[ - $( - $x::Native::WASM_TYPE - ),* - ] - } - } - // Implement `HostFunction` for a function with a [`FunctionEnvMut`] that has the same // arity than the tuple. #[allow(unused_parens)] @@ -1508,235 +626,32 @@ mod inner { }; } - // Black-magic to count the number of identifiers at compile-time. - 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 - } - }; - } - - // Here we go! Let's generate all the C struct, `WasmTypeList` - // implementations and `HostFunction` implementations. - 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); - - // 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]; - - unsafe fn from_array(_: &mut impl AsStoreMut, _: Self::Array) -> Self { - unreachable!() - } - - unsafe fn from_slice( - _: &mut impl AsStoreMut, - _: &[RawValue], - ) -> Result { - 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!(::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!(::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!(::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!(::wasm_types(), [Type::I32]); - assert_eq!(::wasm_types(), [Type::I64]); - assert_eq!(::wasm_types(), [Type::F32]); - assert_eq!(::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); - } - } - */ -} +// Here we go! Let's generate all the C struct, `WasmTypeList` +// implementations and `HostFunction` implementations. +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); diff --git a/lib/api/src/sys/externals/global.rs b/lib/api/src/sys/externals/global.rs index 2dfc77ff06d..7b5accdbe24 100644 --- a/lib/api/src/sys/externals/global.rs +++ b/lib/api/src/sys/externals/global.rs @@ -1,68 +1,25 @@ -use crate::sys::exports::{ExportError, Exportable}; -use crate::sys::externals::Extern; -use crate::sys::store::{AsStoreMut, AsStoreRef}; -use crate::sys::value::Value; -use crate::sys::GlobalType; -use crate::sys::Mutability; -use crate::sys::RuntimeError; -use wasmer_vm::{InternalStoreHandle, StoreHandle, VMExtern, VMGlobal}; +use crate::errors::RuntimeError; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::value::Value; +use crate::vm::VMExternGlobal; +use crate::GlobalType; +use crate::Mutability; +use wasmer_vm::{StoreHandle, VMExtern, VMGlobal}; -/// A WebAssembly `global` instance. -/// -/// A global instance is the runtime representation of a global variable. -/// It consists of an individual value and a flag indicating whether it is mutable. -/// -/// Spec: #[derive(Debug, Clone)] pub struct Global { handle: StoreHandle, } impl Global { - /// Create a new `Global` with the initial value [`Value`]. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Mutability, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new(&mut store, Value::I32(1)); - /// - /// assert_eq!(g.get(&mut store), Value::I32(1)); - /// assert_eq!(g.ty(&mut store).mutability, Mutability::Const); - /// ``` - pub fn new(store: &mut impl AsStoreMut, val: Value) -> Self { - Self::from_value(store, val, Mutability::Const).unwrap() - } - - /// Create a mutable `Global` with the initial value [`Value`]. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Mutability, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new_mut(&mut store, Value::I32(1)); - /// - /// assert_eq!(g.get(&mut store), Value::I32(1)); - /// assert_eq!(g.ty(&mut store).mutability, Mutability::Var); - /// ``` - pub fn new_mut(store: &mut impl AsStoreMut, val: Value) -> Self { - Self::from_value(store, val, Mutability::Var).unwrap() - } - /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. - fn from_value( + pub(crate) fn from_value( store: &mut impl AsStoreMut, val: Value, mutability: Mutability, ) -> Result { if !val.is_from_store(store) { - return Err(RuntimeError::new( - "cross-`Context` values are not supported", - )); + return Err(RuntimeError::new("cross-`Store` values are not supported")); } let global = VMGlobal::new(GlobalType { mutability, @@ -77,37 +34,10 @@ impl Global { }) } - /// Returns the [`GlobalType`] of the `Global`. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Mutability, Store, Type, Value, GlobalType}; - /// # let mut store = Store::default(); - /// # - /// let c = Global::new(&mut store, Value::I32(1)); - /// let v = Global::new_mut(&mut store, Value::I64(1)); - /// - /// assert_eq!(c.ty(&mut store), GlobalType::new(Type::I32, Mutability::Const)); - /// assert_eq!(v.ty(&mut store), GlobalType::new(Type::I64, Mutability::Var)); - /// ``` pub fn ty(&self, store: &impl AsStoreRef) -> GlobalType { *self.handle.get(store.as_store_ref().objects()).ty() } - #[cfg(feature = "compiler")] - /// Retrieves the current value [`Value`] that the Global has. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new(&mut store, Value::I32(1)); - /// - /// assert_eq!(g.get(&mut store), Value::I32(1)); - /// ``` pub fn get(&self, store: &mut impl AsStoreMut) -> Value { unsafe { let raw = self @@ -121,52 +51,9 @@ impl Global { } } - /// Sets a custom value [`Value`] to the runtime Global. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new_mut(&mut store, Value::I32(1)); - /// - /// assert_eq!(g.get(&mut store), Value::I32(1)); - /// - /// g.set(&mut store, Value::I32(2)); - /// - /// assert_eq!(g.get(&mut store), Value::I32(2)); - /// ``` - /// - /// # Errors - /// - /// Trying to mutate a immutable global will raise an error: - /// - /// ```should_panic - /// # use wasmer::{Global, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new(&mut store, Value::I32(1)); - /// - /// g.set(&mut store, Value::I32(2)).unwrap(); - /// ``` - /// - /// Trying to set a value of a incompatible type will raise an error: - /// - /// ```should_panic - /// # use wasmer::{Global, Store, Value}; - /// # let mut store = Store::default(); - /// # - /// let g = Global::new(&mut store, Value::I32(1)); - /// - /// // This results in an error: `RuntimeError`. - /// g.set(&mut store, Value::I64(2)).unwrap(); - /// ``` pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { if !val.is_from_store(store) { - return Err(RuntimeError::new( - "cross-`Context` values are not supported", - )); + return Err(RuntimeError::new("cross-`Store` values are not supported")); } if self.ty(store).mutability != Mutability::Var { return Err(RuntimeError::new("Attempted to set an immutable global")); @@ -188,18 +75,14 @@ impl Global { Ok(()) } - pub(crate) fn from_vm_extern( - store: &mut impl AsStoreMut, - internal: InternalStoreHandle, - ) -> Self { + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternGlobal) -> Self { Self { handle: unsafe { - StoreHandle::from_internal(store.as_store_ref().objects().id(), internal) + StoreHandle::from_internal(store.as_store_ref().objects().id(), vm_extern) }, } } - /// Checks whether this `Global` can be used with the given context. pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() } @@ -216,12 +99,3 @@ impl std::cmp::PartialEq for Global { } impl std::cmp::Eq for Global {} - -impl<'a> Exportable<'a> for Global { - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { - match _extern { - Extern::Global(global) => Ok(global), - _ => Err(ExportError::IncompatibleType), - } - } -} diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index fddcb2995ab..e61c3aeeb6e 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -1,8 +1,9 @@ -use crate::sys::exports::{ExportError, Exportable}; -use crate::sys::externals::Extern; -use crate::sys::store::{AsStoreMut, AsStoreRef}; -use crate::sys::MemoryType; +use super::memory_view::MemoryView; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::sys::NativeEngineExt; +use crate::vm::VMExternMemory; use crate::MemoryAccessError; +use crate::MemoryType; use std::convert::TryInto; use std::marker::PhantomData; use std::mem; @@ -11,47 +12,17 @@ use std::slice; #[cfg(feature = "tracing")] use tracing::warn; use wasmer_types::Pages; -use wasmer_vm::{InternalStoreHandle, LinearMemory, MemoryError, StoreHandle, VMExtern, VMMemory}; +use wasmer_vm::{LinearMemory, MemoryError, StoreHandle, VMExtern, VMMemory}; -use super::MemoryView; - -/// A WebAssembly `memory` instance. -/// -/// A memory instance is the runtime representation of a linear memory. -/// It consists of a vector of bytes and an optional maximum size. -/// -/// The length of the vector always is a multiple of the WebAssembly -/// page size, which is defined to be the constant 65536 – abbreviated 64Ki. -/// Like in a memory type, the maximum size in a memory instance is -/// given in units of this page size. -/// -/// A memory created by the host or in WebAssembly code will be accessible and -/// mutable from both host and WebAssembly. -/// -/// Spec: #[derive(Debug, Clone)] pub struct Memory { pub(crate) handle: StoreHandle, } impl Memory { - #[cfg(feature = "compiler")] - /// Creates a new host `Memory` from the provided [`MemoryType`]. - /// - /// This function will construct the `Memory` using the store - /// [`BaseTunables`][crate::sys::BaseTunables]. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; - /// # let mut store = Store::default(); - /// # - /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); - /// ``` pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { let mut store = store.as_store_mut(); - let tunables = store.tunables(); + let tunables = store.engine().tunables(); let style = tunables.memory_style(&ty); let memory = tunables.create_host_memory(&ty, &style)?; @@ -60,67 +31,19 @@ impl Memory { }) } - /// Create a memory object from an existing memory and attaches it to the store pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { let handle = StoreHandle::new(new_store.objects_mut(), memory); Self::from_vm_extern(new_store, handle.internal_handle()) } - /// Returns the [`MemoryType`] of the `Memory`. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; - /// # let mut store = Store::default(); - /// # - /// let mt = MemoryType::new(1, None, false); - /// let m = Memory::new(&mut store, mt).unwrap(); - /// - /// assert_eq!(m.ty(&mut store), mt); - /// ``` pub fn ty(&self, store: &impl AsStoreRef) -> MemoryType { self.handle.get(store.as_store_ref().objects()).ty() } - /// Creates a view into the memory that then allows for - /// read and write pub fn view<'a>(&'a self, store: &impl AsStoreRef) -> MemoryView<'a> { MemoryView::new(self, store) } - /// Grow memory by the specified amount of WebAssembly [`Pages`] and return - /// the previous memory size. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; - /// # let mut store = Store::default(); - /// # - /// let m = Memory::new(&mut store, MemoryType::new(1, Some(3), false)).unwrap(); - /// let p = m.grow(&mut store, 2).unwrap(); - /// - /// assert_eq!(p, Pages(1)); - /// assert_eq!(m.view(&mut store).size(), Pages(3)); - /// ``` - /// - /// # Errors - /// - /// Returns an error if memory can't be grown by the specified amount - /// of pages. - /// - /// ```should_panic - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; - /// # use wasmer::FunctionEnv; - /// # let mut store = Store::default(); - /// # let env = FunctionEnv::new(&mut store, ()); - /// # - /// let m = Memory::new(&mut store, MemoryType::new(1, Some(1), false)).unwrap(); - /// - /// // This results in an error: `MemoryError::CouldNotGrow`. - /// let s = m.grow(&mut store, 1).unwrap(); - /// ``` pub fn grow( &self, store: &mut impl AsStoreMut, @@ -132,8 +55,6 @@ impl Memory { self.handle.get_mut(store.objects_mut()).grow(delta.into()) } - /// Copies the memory to a new store and returns a memory reference to it - #[cfg(feature = "compiler")] pub fn copy_to_store( &self, store: &impl AsStoreRef, @@ -162,13 +83,10 @@ impl Memory { Ok(new_memory) } - pub(crate) fn from_vm_extern( - store: &impl AsStoreRef, - internal: InternalStoreHandle, - ) -> Self { + pub(crate) fn from_vm_extern(store: &impl AsStoreRef, vm_extern: VMExternMemory) -> Self { Self { handle: unsafe { - StoreHandle::from_internal(store.as_store_ref().objects().id(), internal) + StoreHandle::from_internal(store.as_store_ref().objects().id(), vm_extern) }, } } @@ -178,7 +96,6 @@ impl Memory { self.handle.store_id() == store.as_store_ref().objects().id() } - /// Attempts to clone this memory (if its clonable) pub fn try_clone(&self, store: &impl AsStoreRef) -> Option { let mem = self.handle.get(store.as_store_ref().objects()); mem.try_clone().map(|mem| mem.into()) @@ -198,15 +115,6 @@ impl std::cmp::PartialEq for Memory { impl std::cmp::Eq for Memory {} -impl<'a> Exportable<'a> for Memory { - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { - match _extern { - Extern::Memory(memory) => Ok(memory), - _ => Err(ExportError::IncompatibleType), - } - } -} - /// Underlying buffer for a memory. #[derive(Debug, Copy, Clone)] pub(crate) struct MemoryBuffer<'a> { diff --git a/lib/api/src/sys/externals/memory_view.rs b/lib/api/src/sys/externals/memory_view.rs index ee1cbaea17c..05df80c0e38 100644 --- a/lib/api/src/sys/externals/memory_view.rs +++ b/lib/api/src/sys/externals/memory_view.rs @@ -1,4 +1,4 @@ -use crate::sys::store::AsStoreRef; +use crate::store::AsStoreRef; use crate::MemoryAccessError; use std::convert::TryInto; use std::marker::PhantomData; @@ -7,8 +7,7 @@ use std::slice; use wasmer_types::Pages; use wasmer_vm::LinearMemory; -use super::memory::MemoryBuffer; -use super::Memory; +use super::memory::{Memory, MemoryBuffer}; /// A WebAssembly `memory` view. /// @@ -96,6 +95,7 @@ impl<'a> MemoryView<'a> { self.size } + #[inline] pub(crate) fn buffer(&'a self) -> MemoryBuffer<'a> { self.buffer } @@ -160,6 +160,7 @@ impl<'a> MemoryView<'a> { Ok(()) } + #[allow(unused)] /// Copies the memory and returns it as a vector of bytes pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { let mut new_memory = Vec::new(); @@ -175,6 +176,7 @@ impl<'a> MemoryView<'a> { Ok(new_memory) } + #[allow(unused)] /// Copies the memory to another new memory object pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { let mut offset = 0; diff --git a/lib/api/src/sys/externals/mod.rs b/lib/api/src/sys/externals/mod.rs index e33518c8ccd..3575fe23bb5 100644 --- a/lib/api/src/sys/externals/mod.rs +++ b/lib/api/src/sys/externals/mod.rs @@ -3,121 +3,3 @@ pub(crate) mod global; pub(crate) mod memory; pub(crate) mod memory_view; pub(crate) mod table; - -pub use self::function::{FromToNativeWasmType, Function, HostFunction, WasmTypeList}; - -pub use self::global::Global; -pub use self::memory::Memory; -pub use self::memory_view::MemoryView; -pub use self::table::Table; - -use crate::sys::exports::{ExportError, Exportable}; -use crate::sys::ExternType; -use std::fmt; -use wasmer_vm::VMExtern; - -use super::store::{AsStoreMut, AsStoreRef}; - -/// An `Extern` is the runtime representation of an entity that -/// can be imported or exported. -/// -/// Spec: -#[derive(Clone)] -pub enum Extern { - /// A external [`Function`]. - Function(Function), - /// A external [`Global`]. - Global(Global), - /// A external [`Table`]. - Table(Table), - /// A external [`Memory`]. - Memory(Memory), -} - -impl Extern { - /// Return the underlying type of the inner `Extern`. - pub fn ty(&self, store: &impl AsStoreRef) -> ExternType { - match self { - Self::Function(ft) => ExternType::Function(ft.ty(store)), - Self::Memory(ft) => ExternType::Memory(ft.ty(store)), - Self::Table(tt) => ExternType::Table(tt.ty(store)), - Self::Global(gt) => ExternType::Global(gt.ty(store)), - } - } - - /// Create an `Extern` from an `wasmer_engine::Export`. - pub fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExtern) -> Self { - match vm_extern { - VMExtern::Function(f) => Self::Function(Function::from_vm_extern(store, f)), - VMExtern::Memory(m) => Self::Memory(Memory::from_vm_extern(store, m)), - VMExtern::Global(g) => Self::Global(Global::from_vm_extern(store, g)), - VMExtern::Table(t) => Self::Table(Table::from_vm_extern(store, t)), - } - } - - /// Checks whether this `Extern` can be used with the given context. - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { - match self { - Self::Function(f) => f.is_from_store(store), - Self::Global(g) => g.is_from_store(store), - Self::Memory(m) => m.is_from_store(store), - Self::Table(t) => t.is_from_store(store), - } - } - - /// To `VMExtern`. - pub fn to_vm_extern(&self) -> VMExtern { - match self { - Self::Function(f) => f.to_vm_extern(), - Self::Global(g) => g.to_vm_extern(), - Self::Memory(m) => m.to_vm_extern(), - Self::Table(t) => t.to_vm_extern(), - } - } -} - -impl<'a> Exportable<'a> for Extern { - fn get_self_from_extern(_extern: &'a Self) -> Result<&'a Self, ExportError> { - // Since this is already an extern, we can just return it. - Ok(_extern) - } -} - -impl fmt::Debug for Extern { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::Function(_) => "Function(...)", - Self::Global(_) => "Global(...)", - Self::Memory(_) => "Memory(...)", - Self::Table(_) => "Table(...)", - } - ) - } -} - -impl From for Extern { - fn from(r: Function) -> Self { - Self::Function(r) - } -} - -impl From for Extern { - fn from(r: Global) -> Self { - Self::Global(r) - } -} - -impl From for Extern { - fn from(r: Memory) -> Self { - Self::Memory(r) - } -} - -impl From
for Extern { - fn from(r: Table) -> Self { - Self::Table(r) - } -} diff --git a/lib/api/src/sys/externals/table.rs b/lib/api/src/sys/externals/table.rs index 52137330846..7fc7b08cd47 100644 --- a/lib/api/src/sys/externals/table.rs +++ b/lib/api/src/sys/externals/table.rs @@ -1,20 +1,10 @@ -use crate::sys::exports::{ExportError, Exportable}; -use crate::sys::externals::Extern; -use crate::sys::store::{AsStoreMut, AsStoreRef}; -use crate::sys::TableType; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::sys::NativeEngineExt; +use crate::TableType; use crate::Value; -use crate::{sys::RuntimeError, ExternRef, Function}; -use wasmer_vm::{InternalStoreHandle, StoreHandle, TableElement, VMExtern, VMTable}; - -/// A WebAssembly `table` instance. -/// -/// The `Table` struct is an array-like structure representing a WebAssembly Table, -/// which stores function references. -/// -/// A table created by the host or in WebAssembly code will be accessible and -/// mutable from both host and WebAssembly. -/// -/// Spec: +use crate::{vm::VMExternTable, ExternRef, Function, RuntimeError}; +use wasmer_vm::{StoreHandle, TableElement, VMExtern, VMTable}; + #[derive(Debug, Clone)] pub struct Table { handle: StoreHandle, @@ -46,7 +36,6 @@ fn value_to_table_element( }) } -#[cfg(feature = "compiler")] fn value_from_table_element(store: &mut impl AsStoreMut, item: wasmer_vm::TableElement) -> Value { match item { wasmer_vm::TableElement::FuncRef(funcref) => { @@ -59,13 +48,6 @@ fn value_from_table_element(store: &mut impl AsStoreMut, item: wasmer_vm::TableE } impl Table { - #[cfg(feature = "compiler")] - /// Creates a new `Table` with the provided [`TableType`] definition. - /// - /// All the elements in the table will be set to the `init` value. - /// - /// This function will construct the `Table` using the store - /// [`BaseTunables`][crate::sys::BaseTunables]. pub fn new( mut store: &mut impl AsStoreMut, ty: TableType, @@ -73,7 +55,7 @@ impl Table { ) -> Result { let item = value_to_table_element(&mut store, init)?; let mut store = store.as_store_mut(); - let tunables = store.tunables(); + let tunables = store.engine().tunables(); let style = tunables.table_style(&ty); let mut table = tunables .create_host_table(&ty, &style) @@ -89,19 +71,15 @@ impl Table { }) } - /// Returns the [`TableType`] of the `Table`. pub fn ty(&self, store: &impl AsStoreRef) -> TableType { *self.handle.get(store.as_store_ref().objects()).ty() } - #[cfg(feature = "compiler")] - /// Retrieves an element of the table at the provided `index`. pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { let item = self.handle.get(store.as_store_ref().objects()).get(index)?; Some(value_from_table_element(store, item)) } - /// Sets an element `val` in the Table at the provided `index`. pub fn set( &self, store: &mut impl AsStoreMut, @@ -112,20 +90,10 @@ impl Table { set_table_item(self.handle.get_mut(store.objects_mut()), index, item) } - /// Retrieves the size of the `Table` (in elements) pub fn size(&self, store: &impl AsStoreRef) -> u32 { self.handle.get(store.as_store_ref().objects()).size() } - /// Grows the size of the `Table` by `delta`, initializating - /// the elements with the provided `init` value. - /// - /// It returns the previous size of the `Table` in case is able - /// to grow the Table successfully. - /// - /// # Errors - /// - /// Returns an error if the `delta` is out of bounds for the table. pub fn grow( &self, store: &mut impl AsStoreMut, @@ -139,13 +107,6 @@ impl Table { .ok_or_else(|| RuntimeError::new(format!("failed to grow table by `{}`", delta))) } - /// Copies the `len` elements of `src_table` starting at `src_index` - /// to the destination table `dst_table` at index `dst_index`. - /// - /// # Errors - /// - /// Returns an error if the range is out of bounds of either the source or - /// destination tables. pub fn copy( store: &mut impl AsStoreMut, dst_table: &Self, @@ -156,7 +117,7 @@ impl Table { ) -> Result<(), RuntimeError> { if dst_table.handle.store_id() != src_table.handle.store_id() { return Err(RuntimeError::new( - "cross-`Context` table copies are not supported", + "cross-`Store` table copies are not supported", )); } let store = store; @@ -174,13 +135,10 @@ impl Table { Ok(()) } - pub(crate) fn from_vm_extern( - store: &mut impl AsStoreMut, - internal: InternalStoreHandle, - ) -> Self { + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { Self { handle: unsafe { - StoreHandle::from_internal(store.as_store_ref().objects().id(), internal) + StoreHandle::from_internal(store.as_store_ref().objects().id(), vm_extern) }, } } @@ -202,31 +160,3 @@ impl std::cmp::PartialEq for Table { } impl std::cmp::Eq for Table {} - -impl<'a> Exportable<'a> for Table { - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { - match _extern { - Extern::Table(table) => Ok(table), - _ => Err(ExportError::IncompatibleType), - } - } -} - -/// Check the example from . -#[test] -fn test_table_grow_issue_3197() { - use crate::{imports, Instance, Module, Store, Table, TableType, Type, Value}; - - const WAT: &str = r#"(module (table (import "env" "table") 100 funcref))"#; - - // Tests that the table type of `table` is compatible with the export in the WAT - // This tests that `wasmer_types::types::is_table_compatible` works as expected. - let mut store = Store::default(); - let module = Module::new(&store, WAT).unwrap(); - let ty = TableType::new(Type::FuncRef, 0, None); - let table = Table::new(&mut store, ty, Value::FuncRef(None)).unwrap(); - table.grow(&mut store, 100, Value::FuncRef(None)).unwrap(); - assert_eq!(table.ty(&store).minimum, 0); - let imports = imports! {"env" => {"table" => table}}; - let _instance = Instance::new(&mut store, &module, &imports).unwrap(); -} diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/sys/instance.rs index c139a24ce46..3b8396d11b4 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/sys/instance.rs @@ -1,29 +1,15 @@ -use crate::sys::exports::Exports; -use crate::sys::module::Module; -use crate::sys::{LinkError, RuntimeError}; -use std::fmt; -use thiserror::Error; +use crate::errors::InstantiationError; +use crate::exports::Exports; +use crate::module::Module; use wasmer_vm::{StoreHandle, VMInstance}; -#[cfg(feature = "compiler")] -use super::store::AsStoreMut; -#[cfg(feature = "compiler")] -use crate::sys::{externals::Extern, imports::Imports}; +use crate::imports::Imports; +use crate::store::AsStoreMut; +use crate::Extern; -/// A WebAssembly Instance is a stateful, executable -/// instance of a WebAssembly [`Module`]. -/// -/// Instance objects contain all the exported WebAssembly -/// functions, memories, tables and globals that allow -/// interacting with WebAssembly. -/// -/// Spec: -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct Instance { _handle: StoreHandle, - module: Module, - /// The exports for an instance. - pub exports: Exports, } #[cfg(test)] @@ -40,40 +26,6 @@ mod send_test { } } -/// An error while instantiating a module. -/// -/// This is not a common WebAssembly error, however -/// we need to differentiate from a `LinkError` (an error -/// that happens while linking, on instantiation), a -/// Trap that occurs when calling the WebAssembly module -/// start function, and an error when initializing the user's -/// host environments. -#[derive(Error, Debug)] -pub enum InstantiationError { - /// A linking ocurred during instantiation. - #[error(transparent)] - Link(LinkError), - - /// A runtime error occured while invoking the start function - #[error(transparent)] - Start(RuntimeError), - - /// The module was compiled with a CPU feature that is not available on - /// the current host. - #[error("missing required CPU features: {0:?}")] - CpuFeature(String), - - /// Import from a different Store. - /// This error occurs when an import from a different store is used. - #[error("cannot mix imports from different stores")] - DifferentStores, - - /// Import from a different Store. - /// This error occurs when an import from a different store is used. - #[error("incorrect OS or architecture")] - DifferentArchOS, -} - impl From for InstantiationError { fn from(other: wasmer_compiler::InstantiationError) -> Self { match other { @@ -85,92 +37,45 @@ impl From for InstantiationError { } impl Instance { - #[cfg(feature = "compiler")] - /// Creates a new `Instance` from a WebAssembly [`Module`] and a - /// set of imports using [`Imports`] or the [`imports`] macro helper. - /// - /// [`imports`]: crate::imports - /// [`Imports`]: crate::Imports - /// - /// ``` - /// # use wasmer::{imports, Store, Module, Global, Value, Instance}; - /// # use wasmer::FunctionEnv; - /// # fn main() -> anyhow::Result<()> { - /// let mut store = Store::default(); - /// let env = FunctionEnv::new(&mut store, ()); - /// let module = Module::new(&store, "(module)")?; - /// let imports = imports!{ - /// "host" => { - /// "var" => Global::new(&mut store, Value::I32(2)) - /// } - /// }; - /// let instance = Instance::new(&mut store, &module, &imports)?; - /// # Ok(()) - /// # } - /// ``` - /// - /// ## Errors - /// - /// The function can return [`InstantiationError`]s. - /// - /// Those are, as defined by the spec: - /// * Link errors that happen when plugging the imports into the instance - /// * Runtime errors that happen when running the module `start` function. - pub fn new( + pub(crate) fn new( store: &mut impl AsStoreMut, module: &Module, imports: &Imports, - ) -> Result { + ) -> Result<(Self, Exports), InstantiationError> { let externs = imports .imports_for_module(module) .map_err(InstantiationError::Link)?; - let mut handle = module.instantiate(store, &externs)?; - let mut exports = module - .exports() - .map(|export| { - let name = export.name().to_string(); - let export = handle.lookup(&name).expect("export"); - let extern_ = Extern::from_vm_extern(store, export); - (name, extern_) - }) - .collect::(); - - // If the memory is imported then also export it for backwards compatibility reasons - // (many will assume the memory is always exported) - later we can remove this - if exports.get_memory("memory").is_err() { - if let Some(memory) = externs.iter().find(|a| a.ty(store).memory().is_some()) { - exports.insert("memory", memory.clone()); - } - } + let mut handle = module.0.instantiate(store, &externs)?; + let exports = Self::get_exports(store, module, &mut handle); let instance = Self { _handle: StoreHandle::new(store.objects_mut(), handle), - module: module.clone(), - exports, }; - Ok(instance) + Ok((instance, exports)) } - #[cfg(feature = "compiler")] - /// Creates a new `Instance` from a WebAssembly [`Module`] and a - /// vector of imports. - /// - /// ## Errors - /// - /// The function can return [`InstantiationError`]s. - /// - /// Those are, as defined by the spec: - /// * Link errors that happen when plugging the imports into the instance - /// * Runtime errors that happen when running the module `start` function. - pub fn new_by_index( + pub(crate) fn new_by_index( store: &mut impl AsStoreMut, module: &Module, externs: &[Extern], - ) -> Result { + ) -> Result<(Self, Exports), InstantiationError> { let externs = externs.to_vec(); - let mut handle = module.instantiate(store, &externs)?; - let mut exports = module + let mut handle = module.0.instantiate(store, &externs)?; + let exports = Self::get_exports(store, module, &mut handle); + let instance = Self { + _handle: StoreHandle::new(store.objects_mut(), handle), + }; + + Ok((instance, exports)) + } + + fn get_exports( + store: &mut impl AsStoreMut, + module: &Module, + handle: &mut VMInstance, + ) -> Exports { + module .exports() .map(|export| { let name = export.name().to_string(); @@ -178,35 +83,6 @@ impl Instance { let extern_ = Extern::from_vm_extern(store, export); (name, extern_) }) - .collect::(); - - // If the memory is imported then also export it for backwards compatibility reasons - // (many will assume the memory is always exported) - later we can remove this - if exports.get_memory("memory").is_err() { - if let Some(memory) = externs.iter().find(|a| a.ty(store).memory().is_some()) { - exports.insert("memory", memory.clone()); - } - } - - let instance = Self { - _handle: StoreHandle::new(store.objects_mut(), handle), - module: module.clone(), - exports, - }; - - Ok(instance) - } - - /// Gets the [`Module`] associated with this instance. - pub fn module(&self) -> &Module { - &self.module - } -} - -impl fmt::Debug for Instance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Instance") - .field("exports", &self.exports) - .finish() + .collect::() } } diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs index 239597eb257..ad045eedc5d 100644 --- a/lib/api/src/sys/mem_access.rs +++ b/lib/api/src/sys/mem_access.rs @@ -1,418 +1,12 @@ -use crate::{ - access::{RefCow, SliceCow, WasmRefAccess}, - RuntimeError, WasmSliceAccess, -}; -#[allow(unused_imports)] -use crate::{Memory, Memory32, Memory64, MemorySize, MemoryView, WasmPtr}; -use std::{ - convert::TryInto, - fmt, - marker::PhantomData, - mem::{self, MaybeUninit}, - ops::Range, - slice, - string::FromUtf8Error, -}; -use thiserror::Error; -use wasmer_types::ValueType; - -use super::externals::memory::MemoryBuffer; - -/// Error for invalid [`Memory`] access. -#[derive(Clone, Copy, Debug, Error)] -#[non_exhaustive] -pub enum MemoryAccessError { - /// Memory access is outside heap bounds. - #[error("memory access out of bounds")] - HeapOutOfBounds, - /// Address calculation overflow. - #[error("address calculation overflow")] - Overflow, - /// String is not valid UTF-8. - #[error("string is not valid utf-8")] - NonUtf8String, -} - -impl From for RuntimeError { - fn from(err: MemoryAccessError) -> Self { - Self::new(err.to_string()) - } -} -impl From for MemoryAccessError { - fn from(_err: FromUtf8Error) -> Self { - Self::NonUtf8String - } -} - -/// Reference to a value in Wasm memory. -/// -/// The type of the value must satisfy the requirements of the `ValueType` -/// trait which guarantees that reading and writing such a value to untrusted -/// memory is safe. -/// -/// The address is not required to be aligned: unaligned accesses are fully -/// supported. -/// -/// This wrapper safely handles concurrent modifications of the data by another -/// thread. -#[derive(Clone, Copy)] -pub struct WasmRef<'a, T: ValueType> { - buffer: MemoryBuffer<'a>, - offset: u64, - marker: PhantomData<*mut T>, -} - -impl<'a, T: ValueType> WasmRef<'a, T> { - /// Creates a new `WasmRef` at the given offset in a memory. - #[inline] - pub fn new(view: &'a MemoryView, offset: u64) -> Self { - Self { - buffer: view.buffer, - offset, - marker: PhantomData, - } - } - - /// Get the offset into Wasm linear memory for this `WasmRef`. - #[inline] - pub fn offset(self) -> u64 { - self.offset - } - - /// Get a `WasmPtr` for this `WasmRef`. - #[inline] - pub fn as_ptr32(self) -> WasmPtr { - WasmPtr::new(self.offset as u32) - } - - /// Get a 64-bit `WasmPtr` for this `WasmRef`. - #[inline] - pub fn as_ptr64(self) -> WasmPtr { - WasmPtr::new(self.offset) - } - - /// Get a `WasmPtr` fror this `WasmRef`. - #[inline] - pub fn as_ptr(self) -> WasmPtr { - let offset: M::Offset = self - .offset - .try_into() - .map_err(|_| "invalid offset into memory") - .unwrap(); - WasmPtr::::new(offset) - } - - /// Reads the location pointed to by this `WasmRef`. - #[inline] - pub fn read(self) -> Result { - Ok(self.access()?.read()) - } - - /// Writes to the location pointed to by this `WasmRef`. - #[inline] - pub fn write(self, val: T) -> Result<(), MemoryAccessError> { - self.access()?.write(val); - Ok(()) - } - - /// Gains direct access to the memory of this slice - #[inline] - pub fn access(self) -> Result, MemoryAccessError> { - WasmRefAccess::new(self) - } -} - -impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "WasmRef(offset: {}, pointer: {:#x})", - self.offset, self.offset - ) - } -} - -/// Reference to an array of values in Wasm memory. -/// -/// The type of the value must satisfy the requirements of the `ValueType` -/// trait which guarantees that reading and writing such a value to untrusted -/// memory is safe. -/// -/// The address is not required to be aligned: unaligned accesses are fully -/// supported. -/// -/// This wrapper safely handles concurrent modifications of the data by another -/// thread. -#[derive(Clone, Copy)] -pub struct WasmSlice<'a, T: ValueType> { - buffer: MemoryBuffer<'a>, - offset: u64, - len: u64, - marker: PhantomData<*mut T>, -} - -impl<'a, T: ValueType> WasmSlice<'a, T> { - /// Creates a new `WasmSlice` starting at the given offset in memory and - /// with the given number of elements. - /// - /// Returns a `MemoryAccessError` if the slice length overflows. - #[inline] - pub fn new(view: &'a MemoryView, offset: u64, len: u64) -> Result { - let total_len = len - .checked_mul(mem::size_of::() as u64) - .ok_or(MemoryAccessError::Overflow)?; - offset - .checked_add(total_len) - .ok_or(MemoryAccessError::Overflow)?; - Ok(Self { - buffer: view.buffer(), - offset, - len, - marker: PhantomData, - }) - } - - /// Get the offset into Wasm linear memory for this `WasmSlice`. - #[inline] - pub fn offset(self) -> u64 { - self.offset - } - - /// Get a 32-bit `WasmPtr` for this `WasmRef`. - #[inline] - pub fn as_ptr32(self) -> WasmPtr { - WasmPtr::new(self.offset as u32) - } - - /// Get a 64-bit `WasmPtr` for this `WasmRef`. - #[inline] - pub fn as_ptr64(self) -> WasmPtr { - WasmPtr::new(self.offset) - } - - /// Get the number of elements in this slice. - #[inline] - pub fn len(self) -> u64 { - self.len - } - - /// Returns `true` if the number of elements is 0. - #[inline] - pub fn is_empty(self) -> bool { - self.len == 0 - } - - /// Get a `WasmRef` to an element in the slice. - #[inline] - pub fn index(self, idx: u64) -> WasmRef<'a, T> { - if idx >= self.len { - panic!("WasmSlice out of bounds"); - } - let offset = self.offset + idx * mem::size_of::() as u64; - WasmRef { - buffer: self.buffer, - offset, - marker: PhantomData, - } - } - - /// Get a `WasmSlice` for a subslice of this slice. - #[inline] - pub fn subslice(self, range: Range) -> WasmSlice<'a, T> { - if range.start > range.end || range.end > self.len { - panic!("WasmSlice out of bounds"); - } - let offset = self.offset + range.start * mem::size_of::() as u64; - Self { - buffer: self.buffer, - offset, - len: range.end - range.start, - marker: PhantomData, - } - } - - /// Get an iterator over the elements in this slice. - #[inline] - pub fn iter(self) -> WasmSliceIter<'a, T> { - WasmSliceIter { slice: self } - } - - /// Gains direct access to the memory of this slice - #[inline] - pub fn access(self) -> Result, MemoryAccessError> { - WasmSliceAccess::new(self) - } - - /// Reads an element of this slice. - #[inline] - pub fn read(self, idx: u64) -> Result { - self.index(idx).read() - } - - /// Writes to an element of this slice. - #[inline] - pub fn write(self, idx: u64, val: T) -> Result<(), MemoryAccessError> { - self.index(idx).write(val) - } - - /// Reads the entire slice into the given buffer. - /// - /// The length of the buffer must match the length of the slice. - #[inline] - pub fn read_slice(self, buf: &mut [T]) -> Result<(), MemoryAccessError> { - assert_eq!( - buf.len() as u64, - self.len, - "slice length doesn't match WasmSlice length" - ); - let bytes = unsafe { - slice::from_raw_parts_mut( - buf.as_mut_ptr() as *mut MaybeUninit, - buf.len() * mem::size_of::(), - ) - }; - self.buffer.read_uninit(self.offset, bytes)?; - Ok(()) - } - - /// Reads the entire slice into the given uninitialized buffer. - /// - /// The length of the buffer must match the length of the slice. - /// - /// This method returns an initialized view of the buffer. - #[inline] - pub fn read_slice_uninit( - self, - buf: &mut [MaybeUninit], - ) -> Result<&mut [T], MemoryAccessError> { - assert_eq!( - buf.len() as u64, - self.len, - "slice length doesn't match WasmSlice length" - ); - let bytes = unsafe { - slice::from_raw_parts_mut( - buf.as_mut_ptr() as *mut MaybeUninit, - buf.len() * mem::size_of::(), - ) - }; - self.buffer.read_uninit(self.offset, bytes)?; - Ok(unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut T, buf.len()) }) - } - - /// Write the given slice into this `WasmSlice`. - /// - /// The length of the slice must match the length of the `WasmSlice`. - #[inline] - pub fn write_slice(self, data: &[T]) -> Result<(), MemoryAccessError> { - assert_eq!( - data.len() as u64, - self.len, - "slice length doesn't match WasmSlice length" - ); - let bytes = unsafe { - slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * mem::size_of::()) - }; - self.buffer.write(self.offset, bytes) - } - - /// Reads this `WasmSlice` into a `slice`. - #[inline] - pub fn read_to_slice(self, buf: &mut [MaybeUninit]) -> Result { - let len = self.len.try_into().expect("WasmSlice length overflow"); - self.buffer.read_uninit(self.offset, buf)?; - Ok(len) - } - - /// Reads this `WasmSlice` into a `Vec`. - #[inline] - pub fn read_to_vec(self) -> Result, MemoryAccessError> { - let len = self.len.try_into().expect("WasmSlice length overflow"); - let mut vec = Vec::with_capacity(len); - let bytes = unsafe { - slice::from_raw_parts_mut( - vec.as_mut_ptr() as *mut MaybeUninit, - len * mem::size_of::(), - ) - }; - self.buffer.read_uninit(self.offset, bytes)?; - unsafe { - vec.set_len(len); - } - Ok(vec) - } - - /// Reads this `WasmSlice` into a `BytesMut` - #[inline] - pub fn read_to_bytes(self) -> Result { - let len = self.len.try_into().expect("WasmSlice length overflow"); - let mut ret = bytes::BytesMut::with_capacity(len); - let bytes = unsafe { - slice::from_raw_parts_mut( - ret.as_mut_ptr() as *mut MaybeUninit, - len * mem::size_of::(), - ) - }; - self.buffer.read_uninit(self.offset, bytes)?; - unsafe { - ret.set_len(len); - } - Ok(ret) - } -} - -impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "WasmSlice(offset: {}, len: {}, pointer: {:#x})", - self.offset, self.len, self.offset - ) - } -} - -/// Iterator over the elements of a `WasmSlice`. -pub struct WasmSliceIter<'a, T: ValueType> { - slice: WasmSlice<'a, T>, -} - -impl<'a, T: ValueType> Iterator for WasmSliceIter<'a, T> { - type Item = WasmRef<'a, T>; - - fn next(&mut self) -> Option { - if !self.slice.is_empty() { - let elem = self.slice.index(0); - self.slice = self.slice.subslice(1..self.slice.len()); - Some(elem) - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option) { - (0..self.slice.len()).size_hint() - } -} - -impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> { - fn next_back(&mut self) -> Option { - if !self.slice.is_empty() { - let elem = self.slice.index(self.slice.len() - 1); - self.slice = self.slice.subslice(0..self.slice.len() - 1); - Some(elem) - } else { - None - } - } -} - -impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {} +use crate::access::{RefCow, SliceCow, WasmRefAccess, WasmSliceAccess}; +use crate::{MemoryAccessError, WasmRef, WasmSlice}; +use std::mem; impl<'a, T> WasmSliceAccess<'a, T> where T: wasmer_types::ValueType, { - fn new(slice: WasmSlice<'a, T>) -> Result { + pub(crate) fn new(slice: WasmSlice<'a, T>) -> Result { let total_len = slice .len .checked_mul(mem::size_of::() as u64) @@ -421,16 +15,16 @@ where .offset .checked_add(total_len) .ok_or(MemoryAccessError::Overflow)?; - if end > slice.buffer.len as u64 { + if end > slice.buffer.0.len as u64 { #[cfg(feature = "tracing")] warn!( "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - total_len, end, slice.buffer.len + total_len, end, slice.buffer.0.len ); return Err(MemoryAccessError::HeapOutOfBounds); } let buf = unsafe { - let buf_ptr: *mut u8 = slice.buffer.base.add(slice.offset as usize); + let buf_ptr: *mut u8 = slice.buffer.0.base.add(slice.offset as usize); let buf_ptr: *mut T = std::mem::transmute(buf_ptr); std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize) }; @@ -445,22 +39,22 @@ impl<'a, T> WasmRefAccess<'a, T> where T: wasmer_types::ValueType, { - fn new(ptr: WasmRef<'a, T>) -> Result { + pub(crate) fn new(ptr: WasmRef<'a, T>) -> Result { let total_len = mem::size_of::() as u64; let end = ptr .offset .checked_add(total_len) .ok_or(MemoryAccessError::Overflow)?; - if end > ptr.buffer.len as u64 { + if end > ptr.buffer.0.len as u64 { #[cfg(feature = "tracing")] warn!( "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - total_len, end, ptr.buffer.len + total_len, end, ptr.buffer.0.len ); return Err(MemoryAccessError::HeapOutOfBounds); } let val = unsafe { - let val_ptr: *mut u8 = ptr.buffer.base.add(ptr.offset as usize); + let val_ptr: *mut u8 = ptr.buffer.0.base.add(ptr.offset as usize); let val_ptr: *mut T = std::mem::transmute(val_ptr); &mut *val_ptr }; @@ -470,3 +64,27 @@ where }) } } + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Reads the address pointed to by this `WasmPtr` in a memory. + #[inline] + #[allow(clippy::clone_on_copy)] + pub fn read(&self) -> T + where + T: Clone, + { + self.as_ref().clone() + } + + /// Writes to the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn write(&mut self, val: T) { + // Note: Zero padding is not required here as its a typed copy which does + // not leak the bytes into the memory + // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes + *(self.as_mut()) = val; + } +} diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 3989ec10f36..485b87f55b6 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -1,90 +1,37 @@ -mod exports; -mod extern_ref; -mod externals; -mod function_env; -mod imports; -mod instance; -mod mem_access; -mod module; -mod native; -mod native_type; -mod ptr; -mod store; +pub(crate) mod engine; +pub(crate) mod extern_ref; +pub(crate) mod externals; +pub(crate) mod instance; +pub(crate) mod mem_access; +pub(crate) mod module; mod tunables; -mod value; +pub(crate) mod typed_function; -pub use crate::sys::exports::{ExportError, Exportable, Exports, ExportsIterator}; -pub use crate::sys::extern_ref::ExternRef; -pub use crate::sys::externals::{ - Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, MemoryView, Table, - WasmTypeList, -}; -pub use crate::sys::function_env::{FunctionEnv, FunctionEnvMut}; -pub use crate::sys::imports::Imports; -pub use crate::sys::instance::{Instance, InstantiationError}; -pub use crate::sys::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter}; -pub use crate::sys::module::{IoCompileError, Module}; -pub use crate::sys::native::TypedFunction; -pub use crate::sys::native_type::NativeWasmTypeInto; -pub use crate::sys::store::{AsStoreMut, AsStoreRef, StoreMut, StoreRef}; - -pub use crate::sys::ptr::{Memory32, Memory64, MemorySize, WasmPtr, WasmPtr64}; -pub use crate::sys::store::Store; +pub use crate::sys::engine::NativeEngineExt; pub use crate::sys::tunables::BaseTunables; -pub use crate::sys::value::Value; pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST}; #[cfg(feature = "compiler")] pub use wasmer_compiler::{ wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareReaderState, ModuleMiddleware, }; -pub use wasmer_compiler::{Features, FrameInfo, LinkError, RuntimeError, Tunables}; -pub use wasmer_derive::ValueType; -pub use wasmer_types::is_wasm; -// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 -pub use wasmer_types::{ - CpuFeature, ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, - Mutability, OnCalledAction, TableType, Target, Type, -}; - -pub use wasmer_types::{ - Bytes, CompileError, DeserializeError, ExportIndex, GlobalInit, LocalFunctionIndex, - MiddlewareError, Pages, ParseCpuFeatureError, SerializeError, ValueType, WasmError, WasmResult, - WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, -}; - -// TODO: should those be moved into wasmer::vm as well? -pub use wasmer_vm::{raise_user_trap, MemoryError}; -pub mod vm { - //! The `vm` module re-exports wasmer-vm types. - - pub use wasmer_vm::{ - MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition, - VMOwnedMemory, VMSharedMemory, VMTable, VMTableDefinition, - }; -} - -#[cfg(feature = "wat")] -pub use wat::parse_bytes as wat2wasm; - -#[cfg(feature = "singlepass")] -pub use wasmer_compiler_singlepass::Singlepass; - +pub use wasmer_compiler::{Artifact, EngineBuilder, Features, FrameInfo, Tunables}; #[cfg(feature = "cranelift")] pub use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel}; - #[cfg(feature = "llvm")] pub use wasmer_compiler_llvm::{LLVMOptLevel, LLVM}; +#[cfg(feature = "singlepass")] +pub use wasmer_compiler_singlepass::Singlepass; -#[cfg(feature = "compiler")] -pub use wasmer_compiler::{Artifact, EngineBuilder}; -pub use wasmer_compiler::{AsEngineRef, Engine, EngineRef}; - -/// Version number of this crate. -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +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, VMFunctionBody, VMFunctionEnvironment, + VMGlobal, VMInstance, VMMemory, VMTable, VMTrampoline, + }; -/// This type is deprecated, it has been replaced by TypedFunction. -#[deprecated( - since = "3.0.0", - note = "NativeFunc has been replaced by TypedFunction" -)] -pub type NativeFunc = TypedFunction; + pub(crate) type VMExternTable = InternalStoreHandle; + pub(crate) type VMExternMemory = InternalStoreHandle; + pub(crate) type VMExternGlobal = InternalStoreHandle; + pub(crate) type VMExternFunction = InternalStoreHandle; +} diff --git a/lib/api/src/sys/module.rs b/lib/api/src/sys/module.rs index b284ded3655..1eca3fa1978 100644 --- a/lib/api/src/sys/module.rs +++ b/lib/api/src/sys/module.rs @@ -1,44 +1,19 @@ +use crate::engine::AsEngineRef; +use crate::sys::engine::NativeEngineExt; use bytes::Bytes; -use std::fmt; -use std::io; use std::path::Path; use std::sync::Arc; -use thiserror::Error; use wasmer_compiler::Artifact; use wasmer_compiler::ArtifactCreate; -use wasmer_compiler::AsEngineRef; -#[cfg(feature = "wat")] -use wasmer_types::WasmError; use wasmer_types::{ CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError, }; use wasmer_types::{ExportType, ImportType}; -#[cfg(feature = "compiler")] -use crate::{sys::InstantiationError, AsStoreMut, AsStoreRef, IntoBytes}; -#[cfg(feature = "compiler")] -use wasmer_vm::VMInstance; +use crate::vm::VMInstance; +use crate::{AsStoreMut, AsStoreRef, InstantiationError, IntoBytes}; -/// IO Error on a Module Compilation -#[derive(Error, Debug)] -pub enum IoCompileError { - /// An IO error - #[error(transparent)] - Io(#[from] io::Error), - /// A compilation error - #[error(transparent)] - Compile(#[from] CompileError), -} - -/// A WebAssembly Module contains stateless WebAssembly -/// code that has already been compiled and can be instantiated -/// multiple times. -/// -/// ## Cloning a module -/// -/// Cloning a module is cheap: it does a shallow copy of the compiled -/// contents rather than a deep copy. -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct Module { // The field ordering here is actually significant because of the drop // order: we want to drop the artifact before dropping the engine. @@ -55,129 +30,18 @@ pub struct Module { // In the future, this code should be refactored to properly describe the // ownership of the code and its metadata. artifact: Arc, - module_info: Arc, } impl Module { - #[cfg(feature = "compiler")] - /// Creates a new WebAssembly Module given the configuration - /// in the store. - /// - /// If the provided bytes are not WebAssembly-like (start with `b"\0asm"`), - /// and the "wat" feature is enabled for this crate, this function will try to - /// to convert the bytes assuming they correspond to the WebAssembly text - /// format. - /// - /// ## Security - /// - /// Before the code is compiled, it will be validated using the store - /// features. - /// - /// ## Errors - /// - /// Creating a WebAssembly module from bytecode can result in a - /// [`CompileError`] since this operation requires to transorm the Wasm - /// bytecode into code the machine can easily execute. - /// - /// ## Example - /// - /// Reading from a WAT file. - /// - /// ``` - /// use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = "(module)"; - /// let module = Module::new(&store, wat)?; - /// # Ok(()) - /// # } - /// ``` - /// - /// Reading from bytes: - /// - /// ``` - /// use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// // The following is the same as: - /// // (module - /// // (type $t0 (func (param i32) (result i32))) - /// // (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) - /// // get_local $p0 - /// // i32.const 1 - /// // i32.add) - /// // ) - /// let bytes: Vec = vec![ - /// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, - /// 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07, - /// 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, - /// 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, 0x00, 0x1a, 0x04, 0x6e, - /// 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, - /// 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30, - /// ]; - /// let module = Module::new(&store, bytes)?; - /// # Ok(()) - /// # } - /// ``` - /// # Example of loading a module using just an `Engine` and no `Store` - /// - /// ``` - /// # use wasmer::*; - /// # - /// # let compiler = Cranelift::default(); - /// # let engine = EngineBuilder::new(compiler).engine(); - /// - /// let module = Module::from_file(&engine, "path/to/foo.wasm"); - /// ``` - #[allow(unreachable_code)] - pub fn new(engine: &impl AsEngineRef, bytes: impl AsRef<[u8]>) -> Result { - #[cfg(feature = "wat")] - let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { - CompileError::Wasm(WasmError::Generic(format!( - "Error when converting wat: {}", - e - ))) - })?; - Self::from_binary(engine, bytes.as_ref()) - } - - #[cfg(feature = "compiler")] - /// Creates a new WebAssembly module from a file path. - pub fn from_file( + pub(crate) fn from_binary( engine: &impl AsEngineRef, - file: impl AsRef, - ) -> Result { - let file_ref = file.as_ref(); - let canonical = file_ref.canonicalize()?; - let wasm_bytes = std::fs::read(file_ref)?; - let mut module = Self::new(engine, &wasm_bytes)?; - // Set the module name to the absolute path of the filename. - // This is useful for debugging the stack traces. - let filename = canonical.as_path().to_str().unwrap(); - module.set_name(filename); - Ok(module) - } - - #[cfg(feature = "compiler")] - /// Creates a new WebAssembly module from a binary. - /// - /// Opposed to [`Module::new`], this function is not compatible with - /// the WebAssembly text format (if the "wat" feature is enabled for - /// this crate). - pub fn from_binary(engine: &impl AsEngineRef, binary: &[u8]) -> Result { + binary: &[u8], + ) -> Result { Self::validate(engine, binary)?; unsafe { Self::from_binary_unchecked(engine, binary) } } - #[cfg(feature = "compiler")] - /// Creates a new WebAssembly module skipping any kind of validation. - /// - /// # Safety - /// - /// This can speed up compilation time a bit, but it should be only used - /// in environments where the WebAssembly modules are trusted and validated - /// beforehand. - pub unsafe fn from_binary_unchecked( + pub(crate) unsafe fn from_binary_unchecked( engine: &impl AsEngineRef, binary: &[u8], ) -> Result { @@ -185,114 +49,36 @@ impl Module { Ok(module) } - #[cfg(feature = "compiler")] - /// Validates a new WebAssembly Module given the configuration - /// in the Store. - /// - /// This validation is normally pretty fast and checks the enabled - /// WebAssembly features in the Store Engine to assure deterministic - /// validation of the Module. - pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { - engine.as_engine_ref().engine().validate(binary) + pub(crate) fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { + engine.as_engine_ref().engine().0.validate(binary) } #[cfg(feature = "compiler")] fn compile(engine: &impl AsEngineRef, binary: &[u8]) -> Result { - let artifact = engine.as_engine_ref().engine().compile(binary)?; + let artifact = engine.as_engine_ref().engine().0.compile(binary)?; Ok(Self::from_artifact(artifact)) } - /// Serializes a module into a binary representation that the `Engine` - /// can later process via - #[cfg_attr(feature = "compiler", doc = "[`Module::deserialize`].")] - #[cfg_attr(not(feature = "compiler"), doc = "`Module::deserialize`.")] - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// # let module = Module::from_file(&store, "path/to/foo.wasm")?; - /// let serialized = module.serialize()?; - /// # Ok(()) - /// # } - /// ``` - pub fn serialize(&self) -> Result { - self.artifact.serialize().map(|bytes| bytes.into()) + #[cfg(not(feature = "compiler"))] + fn compile(engine: &impl AsEngineRef, binary: &[u8]) -> Result { + Err(CompileError::UnsupportedTarget( + "The compiler feature is not enabled, but is required to compile a Module", + )) } - /// Serializes a module into a file that the `Engine` - /// can later process via - #[cfg_attr(feature = "compiler", doc = "[`Module::deserialize_from_file`].")] - #[cfg_attr(not(feature = "compiler"), doc = "`Module::deserialize_from_file`.")] - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// # let module = Module::from_file(&store, "path/to/foo.wasm")?; - /// module.serialize_to_file("path/to/foo.so")?; - /// # Ok(()) - /// # } - /// ``` - pub fn serialize_to_file(&self, path: impl AsRef) -> Result<(), SerializeError> { - self.artifact.serialize_to_file(path.as_ref()) + pub(crate) fn serialize(&self) -> Result { + self.artifact.serialize().map(|bytes| bytes.into()) } - #[cfg(feature = "compiler")] - /// Deserializes a serialized Module binary into a `Module`. - /// > Note: the module has to be serialized before with the `serialize` method. - /// - /// # Safety - /// - /// This function is inherently **unsafe** as the provided bytes: - /// 1. Are going to be deserialized directly into Rust objects. - /// 2. Contains the function assembly bodies and, if intercepted, - /// a malicious actor could inject code into executable - /// memory. - /// - /// And as such, the `deserialize` method is unsafe. - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let module = Module::deserialize(&store, serialized_data)?; - /// # Ok(()) - /// # } - /// ``` pub unsafe fn deserialize( engine: &impl AsEngineRef, bytes: impl IntoBytes, ) -> Result { let bytes = bytes.into_bytes(); - let artifact = engine.as_engine_ref().engine().deserialize(&bytes)?; + let artifact = engine.as_engine_ref().engine().0.deserialize(&bytes)?; Ok(Self::from_artifact(artifact)) } - #[cfg(feature = "compiler")] - /// Deserializes a a serialized Module located in a `Path` into a `Module`. - /// > Note: the module has to be serialized before with the `serialize` method. - /// - /// # Safety - /// - /// Please check [`Module::deserialize`]. - /// - /// # Usage - /// - /// ```ignore - /// # use wasmer::*; - /// # let mut store = Store::default(); - /// # fn main() -> anyhow::Result<()> { - /// let module = Module::deserialize_from_file(&store, path)?; - /// # Ok(()) - /// # } - /// ``` pub unsafe fn deserialize_from_file( engine: &impl AsEngineRef, path: impl AsRef, @@ -300,18 +86,15 @@ impl Module { let artifact = engine .as_engine_ref() .engine() + .0 .deserialize_from_file(path.as_ref())?; Ok(Self::from_artifact(artifact)) } fn from_artifact(artifact: Arc) -> Self { - Self { - module_info: Arc::new(artifact.create_module_info()), - artifact, - } + Self { artifact } } - #[cfg(feature = "compiler")] pub(crate) fn instantiate( &self, store: &mut impl AsStoreMut, @@ -329,10 +112,10 @@ impl Module { } } let mut store_mut = store.as_store_mut(); - let (tunables, objects) = store_mut.tunables_and_objects_mut(); + let (engine, objects) = store_mut.engine_and_objects_mut(); unsafe { let mut instance_handle = self.artifact.instantiate( - tunables, + engine.tunables(), &imports .iter() .map(crate::Extern::to_vm_extern) @@ -354,136 +137,32 @@ impl Module { } } - /// Returns the name of the current module. - /// - /// This name is normally set in the WebAssembly bytecode by some - /// compilers, but can be also overwritten using the [`Module::set_name`] method. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = "(module $moduleName)"; - /// let module = Module::new(&store, wat)?; - /// assert_eq!(module.name(), Some("moduleName")); - /// # Ok(()) - /// # } - /// ``` - pub fn name(&self) -> Option<&str> { - self.module_info.name.as_deref() + pub(crate) fn name(&self) -> Option<&str> { + self.info().name.as_deref() } - /// Sets the name of the current module. - /// This is normally useful for stacktraces and debugging. - /// - /// It will return `true` if the module name was changed successfully, - /// and return `false` otherwise (in case the module is already - /// instantiated). - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = "(module)"; - /// let mut module = Module::new(&store, wat)?; - /// assert_eq!(module.name(), None); - /// module.set_name("foo"); - /// assert_eq!(module.name(), Some("foo")); - /// # Ok(()) - /// # } - /// ``` - pub fn set_name(&mut self, name: &str) -> bool { - Arc::get_mut(&mut self.module_info).map_or(false, |mut module_info| { - module_info.name = Some(name.to_string()); - true + pub(crate) fn set_name(&mut self, name: &str) -> bool { + Arc::get_mut(&mut self.artifact).map_or(false, |artifact| { + artifact.set_module_info_name(name.to_string()) }) } - /// Returns an iterator over the imported types in the Module. - /// - /// The order of the imports is guaranteed to be the same as in the - /// WebAssembly bytecode. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = r#"(module - /// (import "host" "func1" (func)) - /// (import "host" "func2" (func)) - /// )"#; - /// let module = Module::new(&store, wat)?; - /// for import in module.imports() { - /// assert_eq!(import.module(), "host"); - /// assert!(import.name().contains("func")); - /// import.ty(); - /// } - /// # Ok(()) - /// # } - /// ``` - pub fn imports(&self) -> ImportsIterator + '_> { - self.module_info.imports() + pub(crate) fn imports(&self) -> ImportsIterator + '_> { + self.info().imports() } - /// Returns an iterator over the exported types in the Module. - /// - /// The order of the exports is guaranteed to be the same as in the - /// WebAssembly bytecode. - /// - /// # Example - /// - /// ``` - /// # use wasmer::*; - /// # fn main() -> anyhow::Result<()> { - /// # let mut store = Store::default(); - /// let wat = r#"(module - /// (func (export "namedfunc")) - /// (memory (export "namedmemory") 1) - /// )"#; - /// let module = Module::new(&store, wat)?; - /// for export_ in module.exports() { - /// assert!(export_.name().contains("named")); - /// export_.ty(); - /// } - /// # Ok(()) - /// # } - /// ``` - pub fn exports(&self) -> ExportsIterator + '_> { - self.module_info.exports() + pub(crate) fn exports(&self) -> ExportsIterator + '_> { + self.info().exports() } - /// Get the custom sections of the module given a `name`. - /// - /// # Important - /// - /// Following the WebAssembly spec, one name can have multiple - /// custom sections. That's why an iterator (rather than one element) - /// is returned. - pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { - self.module_info.custom_sections(name) + pub(crate) fn custom_sections<'a>( + &'a self, + name: &'a str, + ) -> impl Iterator> + 'a { + self.info().custom_sections(name) } - /// The ABI of the ModuleInfo is very unstable, we refactor it very often. - /// This function is public because in some cases it can be useful to get some - /// extra information from the module. - /// - /// However, the usage is highly discouraged. - #[doc(hidden)] - pub fn info(&self) -> &ModuleInfo { - &self.module_info - } -} - -impl fmt::Debug for Module { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Module") - .field("name", &self.name()) - .finish() + pub(crate) fn info(&self) -> &ModuleInfo { + self.artifact.module_info() } } diff --git a/lib/api/src/sys/native_type.rs b/lib/api/src/sys/native_type.rs deleted file mode 100644 index 765b5f6b2c1..00000000000 --- a/lib/api/src/sys/native_type.rs +++ /dev/null @@ -1,252 +0,0 @@ -//! This module permits to create native functions -//! easily in Rust, thanks to its advanced typing system. - -use wasmer_types::{NativeWasmType, RawValue, Type}; -use wasmer_vm::VMExternRef; -#[cfg(feature = "compiler")] -use wasmer_vm::VMFuncRef; - -use crate::{ExternRef, Function, TypedFunction, WasmTypeList}; - -use super::store::AsStoreMut; - -/// `NativeWasmTypeInto` performs conversions from and into `NativeWasmType` -/// types with a context. -pub trait NativeWasmTypeInto: NativeWasmType + Sized { - #[doc(hidden)] - fn into_abi(self, store: &mut impl AsStoreMut) -> Self::Abi; - - #[doc(hidden)] - unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self; - - /// Convert self to raw value representation. - fn into_raw(self, store: &mut impl AsStoreMut) -> RawValue; - - /// Convert to self from raw value representation. - /// - /// # Safety - /// - unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self; -} - -impl NativeWasmTypeInto for i32 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { - RawValue { i32: self } - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { - raw.i32 - } -} - -impl NativeWasmTypeInto for i64 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { - RawValue { i64: self } - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { - raw.i64 - } -} - -impl NativeWasmTypeInto for f32 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { - RawValue { f32: self } - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { - raw.f32 - } -} - -impl NativeWasmTypeInto for f64 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { - RawValue { f64: self } - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { - raw.f64 - } -} - -impl NativeWasmTypeInto for u128 { - #[inline] - unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - abi - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { - RawValue { u128: self } - } - - #[inline] - unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { - raw.u128 - } -} - -impl NativeWasmType for ExternRef { - const WASM_TYPE: Type = Type::ExternRef; - type Abi = usize; -} - -impl NativeWasmTypeInto for Option { - #[inline] - unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - VMExternRef::from_raw(RawValue { externref: abi }) - .map(|e| ExternRef::from_vm_externref(store, e)) - } - - #[inline] - fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { - self.map_or(0, |e| unsafe { e.vm_externref().into_raw().externref }) - } - - #[inline] - fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { - self.map_or(RawValue { externref: 0 }, |e| e.vm_externref().into_raw()) - } - - #[inline] - unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self { - VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)) - } -} - -impl From> for Function -where - Args: WasmTypeList, - Rets: WasmTypeList, -{ - fn from(other: TypedFunction) -> Self { - other.func - } -} - -impl NativeWasmType for Function { - const WASM_TYPE: Type = Type::FuncRef; - type Abi = usize; -} - -#[cfg(feature = "compiler")] -impl NativeWasmTypeInto for Option { - #[inline] - unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - VMFuncRef::from_raw(RawValue { funcref: abi }).map(|f| Function::from_vm_funcref(store, f)) - } - - #[inline] - fn into_abi(self, store: &mut impl AsStoreMut) -> Self::Abi { - self.map_or(0, |f| unsafe { f.vm_funcref(store).into_raw().externref }) - } - - #[inline] - fn into_raw(self, store: &mut impl AsStoreMut) -> RawValue { - self.map_or(RawValue { externref: 0 }, |e| { - e.vm_funcref(store).into_raw() - }) - } - - #[inline] - unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self { - VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(store, f)) - } -} - -#[cfg(test)] -mod test_native_type { - use super::*; - use wasmer_types::Type; - - #[test] - fn test_wasm_types() { - assert_eq!(i32::WASM_TYPE, Type::I32); - assert_eq!(i64::WASM_TYPE, Type::I64); - assert_eq!(f32::WASM_TYPE, Type::F32); - assert_eq!(f64::WASM_TYPE, Type::F64); - assert_eq!(u128::WASM_TYPE, Type::V128); - } - /* - #[test] - fn test_roundtrip() { - unsafe { - assert_eq!(i32::from_raw(42i32.into_raw()), 42i32); - assert_eq!(i64::from_raw(42i64.into_raw()), 42i64); - assert_eq!(f32::from_raw(42f32.into_raw()), 42f32); - assert_eq!(f64::from_raw(42f64.into_raw()), 42f64); - assert_eq!(u128::from_raw(42u128.into_raw()), 42u128); - } - } - */ -} - -// pub trait IntegerAtomic -// where -// Self: Sized -// { -// type Primitive; - -// fn add(&self, other: Self::Primitive) -> Self::Primitive; -// fn sub(&self, other: Self::Primitive) -> Self::Primitive; -// fn and(&self, other: Self::Primitive) -> Self::Primitive; -// fn or(&self, other: Self::Primitive) -> Self::Primitive; -// fn xor(&self, other: Self::Primitive) -> Self::Primitive; -// fn load(&self) -> Self::Primitive; -// fn store(&self, other: Self::Primitive) -> Self::Primitive; -// fn compare_exchange(&self, expected: Self::Primitive, new: Self::Primitive) -> Self::Primitive; -// fn swap(&self, other: Self::Primitive) -> Self::Primitive; -// } diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index 51d2f9ffda2..80111464ff8 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -6,7 +6,8 @@ pub use wasmer_compiler::BaseTunables; #[cfg(test)] mod tests { use super::*; - use crate::sys::TableType; + use crate::sys::NativeEngineExt; + use crate::TableType; use std::cell::UnsafeCell; use std::ptr::NonNull; use wasmer_compiler::Tunables; @@ -228,7 +229,7 @@ mod tests { #[test] fn check_customtunables() -> Result<(), Box> { - use crate::{imports, wat2wasm, Instance, Memory, Module, Store}; + use crate::{imports, wat2wasm, Engine, Instance, Memory, Module, Store}; use wasmer_compiler_cranelift::Cranelift; let wasm_bytes = wat2wasm( @@ -242,7 +243,9 @@ mod tests { let compiler = Cranelift::default(); let tunables = TinyTunables {}; - let mut store = Store::new_with_tunables(compiler, tunables); + let mut engine = Engine::new(compiler.into(), Default::default(), Default::default()); + engine.set_tunables(tunables); + let mut store = Store::new(engine); //let mut store = Store::new(compiler); let module = Module::new(&store, wasm_bytes)?; let import_object = imports! {}; diff --git a/lib/api/src/sys/native.rs b/lib/api/src/sys/typed_function.rs similarity index 88% rename from lib/api/src/sys/native.rs rename to lib/api/src/sys/typed_function.rs index 54d20e56952..dd5f60e3378 100644 --- a/lib/api/src/sys/native.rs +++ b/lib/api/src/sys/typed_function.rs @@ -1,49 +1,8 @@ -//! Native Functions. -//! -//! This module creates the helper `TypedFunction` that let us call WebAssembly -//! functions with the native ABI, that is: -//! -//! ```ignore -//! let add_one = instance.exports.get_function("function_name")?; -//! let add_one_native: TypedFunction = add_one.native().unwrap(); -//! ``` -use std::marker::PhantomData; - -use crate::sys::{ - AsStoreMut, FromToNativeWasmType, Function, NativeWasmTypeInto, RuntimeError, WasmTypeList, -}; +use crate::{FromToNativeWasmType, RuntimeError, TypedFunction, WasmTypeList}; use wasmer_types::RawValue; -/// A WebAssembly function that can be called natively -/// (using the Native ABI). -pub struct TypedFunction { - pub(crate) func: Function, - _phantom: PhantomData Rets>, -} - -unsafe impl Send for TypedFunction {} - -impl TypedFunction -where - Args: WasmTypeList, - Rets: WasmTypeList, -{ - pub(crate) fn new(func: Function) -> Self { - Self { - func, - _phantom: PhantomData, - } - } -} - -impl Clone for TypedFunction { - fn clone(&self) -> Self { - Self { - func: self.func.clone(), - _phantom: PhantomData, - } - } -} +use crate::native_type::NativeWasmTypeInto; +use crate::store::{AsStoreMut, AsStoreRef}; macro_rules! impl_native_traits { ( $( $x:ident ),* ) => { @@ -58,7 +17,7 @@ macro_rules! impl_native_traits { #[allow(clippy::too_many_arguments)] pub fn call(&self, store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result { let anyfunc = unsafe { - *self.func + *self.func.0 .handle .get(store.as_store_ref().objects()) .anyfunc @@ -68,7 +27,7 @@ macro_rules! impl_native_traits { // Ensure all parameters come from the same context. if $(!FromToNativeWasmType::is_from_store(&$x, store) ||)* false { return Err(RuntimeError::new( - "cross-`Context` values are not supported", + "cross-`Store` values are not supported", )); } // TODO: when `const fn` related features mature more, we can declare a single array @@ -144,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 ) -> Result { let anyfunc = unsafe { - *self.func + *self.func.0 .handle .get(store.as_store_ref().objects()) .anyfunc diff --git a/lib/api/src/typed_function.rs b/lib/api/src/typed_function.rs new file mode 100644 index 00000000000..ccaadcfd4e5 --- /dev/null +++ b/lib/api/src/typed_function.rs @@ -0,0 +1,42 @@ +//! Native Functions. +//! +//! This module creates the helper `TypedFunction` that let us call WebAssembly +//! functions with the native ABI, that is: +//! +//! ```ignore +//! let add_one = instance.exports.get_function("function_name")?; +//! let add_one_native: TypedFunction = add_one.native().unwrap(); +//! ``` +use crate::{Function, WasmTypeList}; +use std::marker::PhantomData; + +use crate::store::AsStoreRef; + +/// A WebAssembly function that can be called natively +/// (using the Native ABI). +#[derive(Clone)] +pub struct TypedFunction { + pub(crate) func: Function, + _phantom: PhantomData Rets>, +} + +unsafe impl Send for TypedFunction {} +unsafe impl Sync for TypedFunction {} + +impl TypedFunction +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + #[allow(dead_code)] + pub(crate) fn new(_store: &impl AsStoreRef, func: Function) -> Self { + Self { + func, + _phantom: PhantomData, + } + } + + pub(crate) fn into_function(self) -> Function { + self.func + } +} diff --git a/lib/api/src/sys/value.rs b/lib/api/src/value.rs similarity index 99% rename from lib/api/src/sys/value.rs rename to lib/api/src/value.rs index fe4fb5c4b5f..15ec16edeaa 100644 --- a/lib/api/src/sys/value.rs +++ b/lib/api/src/value.rs @@ -3,13 +3,13 @@ use std::fmt; use std::string::{String, ToString}; use wasmer_types::Type; -#[cfg(feature = "compiler")] -use wasmer_vm::{VMExternRef, VMFuncRef}; + +use crate::vm::{VMExternRef, VMFuncRef}; use crate::ExternRef; use crate::Function; -use super::store::AsStoreRef; +use crate::store::AsStoreRef; pub use wasmer_types::RawValue; @@ -106,7 +106,6 @@ impl Value { } } - #[cfg(feature = "compiler")] /// Converts a `RawValue` to a `Value`. /// /// # Safety diff --git a/lib/api/src/vm.rs b/lib/api/src/vm.rs new file mode 100644 index 00000000000..8fc058d64af --- /dev/null +++ b/lib/api/src/vm.rs @@ -0,0 +1,31 @@ +//! The `vm` module re-exports wasmer-vm types. + +#[cfg(feature = "js")] +pub(crate) use crate::js::vm::{ + VMExtern, VMExternFunction, VMExternGlobal, VMExternMemory, VMExternRef, VMExternTable, + VMFuncRef, VMFunctionBody, VMFunctionEnvironment, VMInstance, VMTrampoline, +}; + +#[cfg(feature = "sys")] +pub(crate) use crate::sys::vm::{ + VMExtern, VMExternFunction, VMExternGlobal, VMExternMemory, VMExternRef, VMExternTable, + VMFuncRef, VMFunctionBody, VMFunctionEnvironment, VMInstance, VMTrampoline, +}; + +#[cfg(feature = "js")] +pub use crate::js::vm::{VMFunction, VMGlobal, VMMemory, VMSharedMemory, VMTable}; + +#[cfg(feature = "sys")] +pub use wasmer_vm::{VMFunction, VMGlobal, VMMemory, VMSharedMemory, VMTable}; + +// Needed for tunables customization (those are public types now) +#[cfg(feature = "sys")] +pub use wasmer_vm::{ + // An extra one for VMMemory implementors + LinearMemory, + VMMemoryDefinition, + VMTableDefinition, +}; + +// Deprecated exports +pub use wasmer_types::{MemoryError, MemoryStyle, TableStyle}; diff --git a/lib/cli-compiler/src/commands/compile.rs b/lib/cli-compiler/src/commands/compile.rs index bcffabff54f..6efc44c9a5e 100644 --- a/lib/cli-compiler/src/commands/compile.rs +++ b/lib/cli-compiler/src/commands/compile.rs @@ -2,6 +2,7 @@ use crate::store::StoreOptions; use crate::warning; use anyhow::{Context, Result}; use clap::Parser; +use std::fs; use std::path::{Path, PathBuf}; use wasmer_compiler::{ArtifactBuild, ArtifactCreate, ModuleEnvironment}; use wasmer_types::entity::PrimaryMap; @@ -105,7 +106,8 @@ impl Compile { memory_styles, table_styles, )?; - artifact.serialize_to_file(self.output.as_ref())?; + let serialized = artifact.serialize()?; + fs::write(output_filename, serialized)?; eprintln!( "✔ File compiled successfully to `{}`.", self.output.display(), diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index c13d06abb6f..2075775fed6 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -209,7 +209,7 @@ impl CreateExe { return Err(anyhow::anyhow!("input path cannot be a directory")); } - let (_, compiler_type) = self.compiler.get_store_for_target(target.clone())?; + let (store, compiler_type) = self.compiler.get_store_for_target(target.clone())?; println!("Compiler: {}", compiler_type.to_string()); println!("Target: {}", target.triple()); @@ -255,7 +255,7 @@ impl CreateExe { ) }?; - get_module_infos(&tempdir, &atoms)?; + get_module_infos(&store, &tempdir, &atoms)?; let mut entrypoint = get_entrypoint(&tempdir)?; create_header_files_in_dir(&tempdir, &mut entrypoint, &atoms, &self.precompiled_atom)?; link_exe_from_dir( @@ -724,12 +724,11 @@ fn compile_atoms( } continue; } - let (store, _) = compiler.get_store_for_target(target.clone())?; - let engine = store.engine(); + let (engine, _) = compiler.get_engine_for_target(target.clone())?; let engine_inner = engine.inner(); let compiler = engine_inner.compiler()?; let features = engine_inner.features(); - let tunables = store.tunables(); + let tunables = engine.tunables(); let (module_info, obj, _, _) = Artifact::generate_object( compiler, data, @@ -916,6 +915,7 @@ pub(super) fn prepare_directory_from_single_wasm_file( // reads the module info from the wasm module and writes the ModuleInfo for each file // into the entrypoint.json file fn get_module_infos( + store: &Store, directory: &Path, atoms: &[(String, Vec)], ) -> Result, anyhow::Error> { @@ -924,9 +924,8 @@ fn get_module_infos( let mut module_infos = BTreeMap::new(); for (atom_name, atom_bytes) in atoms { - let module_info = Engine::get_module_info(atom_bytes.as_slice()) - .map_err(|e| anyhow::anyhow!("could not get module info for atom {atom_name}: {e}"))?; - + let module = Module::new(&store, atom_bytes.as_slice())?; + let module_info = module.info(); if let Some(s) = entrypoint .atoms .iter_mut() diff --git a/lib/cli/src/commands/gen_c_header.rs b/lib/cli/src/commands/gen_c_header.rs index 15da738d364..44348d9c2ba 100644 --- a/lib/cli/src/commands/gen_c_header.rs +++ b/lib/cli/src/commands/gen_c_header.rs @@ -91,12 +91,11 @@ impl GenCHeader { &target_triple, &self.cpu_features, ); - let (store, _) = CompilerOptions::default().get_store_for_target(target.clone())?; - let engine = store.engine(); + let (engine, _) = CompilerOptions::default().get_engine_for_target(target.clone())?; let engine_inner = engine.inner(); let compiler = engine_inner.compiler()?; let features = engine_inner.features(); - let tunables = store.tunables(); + let tunables = engine.tunables(); let (metadata, _, _) = Artifact::metadata( compiler, &file, diff --git a/lib/cli/src/store.rs b/lib/cli/src/store.rs index 49b897c7bc3..c78390c03a6 100644 --- a/lib/cli/src/store.rs +++ b/lib/cli/src/store.rs @@ -109,6 +109,13 @@ impl CompilerOptions { Ok((store, compiler_type)) } + /// Gets the Engine for a given target. + pub fn get_engine_for_target(&self, target: Target) -> Result<(Engine, CompilerType)> { + let (compiler_config, compiler_type) = self.get_compiler_config()?; + let engine = self.get_engine(target, compiler_config)?; + Ok((engine, compiler_type)) + } + #[cfg(feature = "compiler")] fn get_engine( &self, diff --git a/lib/compiler-singlepass/src/compiler.rs b/lib/compiler-singlepass/src/compiler.rs index 5cee5d4ec5b..961712556f5 100644 --- a/lib/compiler-singlepass/src/compiler.rs +++ b/lib/compiler-singlepass/src/compiler.rs @@ -302,7 +302,7 @@ mod tests { ) { let compile_info = CompileModuleInfo { features: Features::new(), - module: ModuleInfo::new(), + module: Arc::new(ModuleInfo::new()), memory_styles: PrimaryMap::::new(), table_styles: PrimaryMap::::new(), }; diff --git a/lib/compiler/src/artifact_builders/artifact_builder.rs b/lib/compiler/src/artifact_builders/artifact_builder.rs index f1940bdc1e6..cc79feb7b0d 100644 --- a/lib/compiler/src/artifact_builders/artifact_builder.rs +++ b/lib/compiler/src/artifact_builders/artifact_builder.rs @@ -8,6 +8,7 @@ use crate::EngineInner; use crate::Features; use crate::{ModuleEnvironment, ModuleMiddlewareChain}; use enumset::EnumSet; +use std::sync::Arc; use wasmer_types::entity::PrimaryMap; #[cfg(feature = "compiler")] use wasmer_types::CompileModuleInfo; @@ -57,7 +58,7 @@ impl ArtifactBuild { middlewares.apply_on_module_info(&mut module); let compile_info = CompileModuleInfo { - module, + module: Arc::new(module), features, memory_styles, table_styles, @@ -195,10 +196,21 @@ impl ArtifactBuild { } impl ArtifactCreate for ArtifactBuild { - fn create_module_info(&self) -> ModuleInfo { + fn create_module_info(&self) -> Arc { self.serializable.compile_info.module.clone() } + fn set_module_info_name(&mut self, name: String) -> bool { + Arc::get_mut(&mut self.serializable.compile_info.module).map_or(false, |mut module_info| { + module_info.name = Some(name.to_string()); + true + }) + } + + fn module_info(&self) -> &ModuleInfo { + &self.serializable.compile_info.module + } + fn features(&self) -> &Features { &self.serializable.compile_info.features } diff --git a/lib/compiler/src/engine/artifact.rs b/lib/compiler/src/engine/artifact.rs index a9f95b8873d..6505ed3dde2 100644 --- a/lib/compiler/src/engine/artifact.rs +++ b/lib/compiler/src/engine/artifact.rs @@ -16,6 +16,7 @@ use crate::{Engine, EngineInner}; use enumset::EnumSet; #[cfg(any(feature = "static-artifact-create", feature = "static-artifact-load"))] use std::mem; +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::Arc; use std::sync::Mutex; #[cfg(feature = "static-artifact-create")] @@ -46,8 +47,38 @@ pub struct AllocatedArtifact { finished_function_lengths: BoxedSlice, } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +/// A unique identifier for an Artifact. +pub struct ArtifactId { + id: usize, +} + +impl ArtifactId { + /// Format this identifier as a string. + pub fn id(&self) -> String { + format!("{}", &self.id) + } +} + +impl Clone for ArtifactId { + fn clone(&self) -> Self { + Self::default() + } +} + +impl Default for ArtifactId { + fn default() -> Self { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + Self { + id: NEXT_ID.fetch_add(1, SeqCst), + } + } +} + /// A compiled wasm module, ready to be instantiated. pub struct Artifact { + id: ArtifactId, artifact: ArtifactBuild, // The artifact will only be allocated in memory in case we can execute it // (that means, if the target != host then this will be None). @@ -95,6 +126,15 @@ impl Artifact { self.allocated.is_some() } + /// A unique identifier for this object. + /// + /// This exists to allow us to compare two Artifacts for equality. Otherwise, + /// comparing two trait objects unsafely relies on implementation details + /// of trait representation. + pub fn id(&self) -> &ArtifactId { + &self.id + } + /// Compile a data buffer into a `ArtifactBuild`, which may then be instantiated. #[cfg(not(feature = "compiler"))] pub fn new(_engine: &Engine, _data: &[u8]) -> Result { @@ -145,18 +185,19 @@ impl Artifact { ) -> Result { if !target.is_native() { return Ok(Self { + id: Default::default(), artifact, allocated: None, }); } - let module_info = artifact.create_module_info(); + let module_info = artifact.module_info(); let ( finished_functions, finished_function_call_trampolines, finished_dynamic_function_trampolines, custom_sections, ) = engine_inner.allocate( - &module_info, + module_info, artifact.get_function_bodies_ref(), artifact.get_function_call_trampolines_ref(), artifact.get_dynamic_function_trampolines_ref(), @@ -164,7 +205,7 @@ impl Artifact { )?; link_module( - &module_info, + module_info, &finished_functions, artifact.get_function_relocations(), &custom_sections, @@ -218,6 +259,7 @@ impl Artifact { let signatures = signatures.into_boxed_slice(); Ok(Self { + id: Default::default(), artifact, allocated: Some(AllocatedArtifact { finished_functions, @@ -236,11 +278,35 @@ impl Artifact { } } +impl PartialEq for Artifact { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} +impl Eq for Artifact {} + +impl std::fmt::Debug for Artifact { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Artifact") + .field("artifact_id", &self.id) + .field("module_info", &self.module_info()) + .finish() + } +} + impl ArtifactCreate for Artifact { - fn create_module_info(&self) -> ModuleInfo { + fn set_module_info_name(&mut self, name: String) -> bool { + self.artifact.set_module_info_name(name) + } + + fn create_module_info(&self) -> Arc { self.artifact.create_module_info() } + fn module_info(&self) -> &ModuleInfo { + self.artifact.module_info() + } + fn features(&self) -> &Features { self.artifact.features() } @@ -381,7 +447,7 @@ impl Artifact { self.preinstantiate()?; - let module = Arc::new(self.create_module_info()); + let module = self.create_module_info(); let imports = resolve_imports( &module, imports, @@ -499,7 +565,7 @@ impl Artifact { .collect(); let compile_info = CompileModuleInfo { - module, + module: Arc::new(module), features: features.clone(), memory_styles, table_styles, @@ -638,7 +704,7 @@ impl Artifact { emit_compilation(&mut obj, compilation, &symbol_registry, target_triple) .map_err(to_compile_error)?; Ok(( - metadata.compile_info.module, + Arc::try_unwrap(metadata.compile_info.module).unwrap(), obj, metadata_binary.len(), Box::new(symbol_registry), @@ -775,6 +841,7 @@ impl Artifact { .into_boxed_slice(); Ok(Self { + id: Default::default(), artifact, allocated: Some(AllocatedArtifact { finished_functions: finished_functions.into_boxed_slice(), diff --git a/lib/compiler/src/engine/engineref.rs b/lib/compiler/src/engine/engineref.rs deleted file mode 100644 index f5fa32aa551..00000000000 --- a/lib/compiler/src/engine/engineref.rs +++ /dev/null @@ -1,51 +0,0 @@ -use core::ops::Deref; - -use super::Engine; -use crate::Tunables; - -/// A temporary handle to an [`Engine`] -/// EngineRef can be used to build a [`Module`][wasmer::Module] -/// It can be created directly with an [`Engine`] -/// Or from anything implementing [`AsEngineRef`] -/// like from [`Store`][wasmer::Store] typicaly -pub struct EngineRef<'a> { - /// The inner engine - pub(crate) inner: &'a Engine, -} - -impl<'a> EngineRef<'a> { - /// Get inner [`Engine`] - pub fn engine(&self) -> &Engine { - self.inner - } - /// Get the [`Tunables`] - pub fn tunables(&self) -> &dyn Tunables { - self.inner.tunables() - } - /// Create an EngineRef from an Engine and Tunables - pub fn new(engine: &'a Engine) -> Self { - EngineRef { inner: engine } - } -} - -/// Helper trait for a value that is convertible to a [`EngineRef`]. -pub trait AsEngineRef { - /// Returns a `EngineRef` pointing to the underlying context. - fn as_engine_ref(&self) -> EngineRef<'_>; -} - -impl AsEngineRef for EngineRef<'_> { - fn as_engine_ref(&self) -> EngineRef<'_> { - EngineRef { inner: self.inner } - } -} - -impl

AsEngineRef for P -where - P: Deref, - P::Target: AsEngineRef, -{ - fn as_engine_ref(&self) -> EngineRef<'_> { - (**self).as_engine_ref() - } -} diff --git a/lib/compiler/src/engine/inner.rs b/lib/compiler/src/engine/inner.rs index 240ef19c87d..378d80daa9e 100644 --- a/lib/compiler/src/engine/inner.rs +++ b/lib/compiler/src/engine/inner.rs @@ -7,8 +7,6 @@ use crate::Artifact; use crate::BaseTunables; #[cfg(not(target_arch = "wasm32"))] use crate::CodeMemory; -#[cfg(not(target_arch = "wasm32"))] -use crate::{AsEngineRef, EngineRef}; #[cfg(feature = "compiler")] use crate::{Compiler, CompilerConfig}; #[cfg(not(target_arch = "wasm32"))] @@ -74,13 +72,13 @@ impl Engine { } } - /// Returns only the `ModuleInfo` given a `wasm` byte slice - #[cfg(feature = "compiler")] - pub fn get_module_info(data: &[u8]) -> Result { - // this is from `artifact_builder.rs` - let environ = crate::ModuleEnvironment::new(); - let translation = environ.translate(data).map_err(CompileError::Wasm)?; - Ok(translation.module) + #[cfg(not(feature = "compiler"))] + pub fn new( + compiler_config: Box, + target: Target, + features: Features, + ) -> Self { + panic!("The engine is not compiled with any compiler support") } /// Returns the name of this engine @@ -88,6 +86,15 @@ impl Engine { self.name.as_str() } + /// Returns the deterministic id of this engine + pub fn deterministic_id(&self) -> &str { + // TODO: add a `deterministic_id` to the Compiler, so two + // compilers can actually serialize into a different deterministic_id + // if their configuration is different (eg. LLVM with optimizations vs LLVM + // without optimizations) + self.name.as_str() + } + /// Create a headless `Engine` /// /// A headless engine is an engine without any compiler attached. @@ -235,13 +242,6 @@ impl Engine { } } -#[cfg(not(target_arch = "wasm32"))] -impl AsEngineRef for Engine { - fn as_engine_ref(&self) -> EngineRef { - EngineRef { inner: self } - } -} - impl std::fmt::Debug for Engine { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Engine") diff --git a/lib/compiler/src/engine/mod.rs b/lib/compiler/src/engine/mod.rs index bf54a791311..75b4db3b2f9 100644 --- a/lib/compiler/src/engine/mod.rs +++ b/lib/compiler/src/engine/mod.rs @@ -1,8 +1,5 @@ //! The Wasmer Engine. -#[cfg(feature = "translator")] -#[cfg(not(target_arch = "wasm32"))] -mod engineref; mod error; #[cfg(not(target_arch = "wasm32"))] mod resolver; @@ -28,9 +25,6 @@ mod link; #[cfg(not(target_arch = "wasm32"))] mod unwind; -#[cfg(feature = "translator")] -#[cfg(not(target_arch = "wasm32"))] -pub use self::engineref::{AsEngineRef, EngineRef}; pub use self::error::{InstantiationError, LinkError}; #[cfg(not(target_arch = "wasm32"))] pub use self::resolver::resolve_imports; diff --git a/lib/compiler/src/engine/trap/frame_info.rs b/lib/compiler/src/engine/trap/frame_info.rs index c7bf7034d73..a23f7607f94 100644 --- a/lib/compiler/src/engine/trap/frame_info.rs +++ b/lib/compiler/src/engine/trap/frame_info.rs @@ -13,7 +13,7 @@ //! ``` use std::cmp; use std::collections::BTreeMap; -use std::sync::RwLock; +use std::sync::{Arc, RwLock}; use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{CompiledFunctionFrameInfo, SourceLoc, TrapInformation}; use wasmer_types::{LocalFunctionIndex, ModuleInfo}; @@ -54,7 +54,7 @@ pub struct GlobalFrameInfoRegistration { struct ModuleInfoFrameInfo { start: usize, functions: BTreeMap, - module: ModuleInfo, + module: Arc, frame_infos: PrimaryMap, } @@ -187,7 +187,7 @@ pub struct FunctionExtent { /// then `None` will be returned. Otherwise the returned object, when /// dropped, will be used to unregister all name information from this map. pub fn register( - module: ModuleInfo, + module: Arc, finished_functions: &BoxedSlice, frame_infos: PrimaryMap, ) -> Option { diff --git a/lib/compiler/src/traits.rs b/lib/compiler/src/traits.rs index d6ce67eda09..b33426ad09f 100644 --- a/lib/compiler/src/traits.rs +++ b/lib/compiler/src/traits.rs @@ -3,8 +3,7 @@ use crate::Features; use enumset::EnumSet; use std::any::Any; -use std::fs; -use std::path::Path; +use std::sync::Arc; use wasmer_types::entity::PrimaryMap; use wasmer_types::SerializeError; use wasmer_types::{ @@ -19,7 +18,13 @@ use wasmer_types::{ /// module at runtime, such as [`ModuleInfo`] and [`Features`]. pub trait ArtifactCreate: Send + Sync + Upcastable { /// Create a `ModuleInfo` for instantiation - fn create_module_info(&self) -> ModuleInfo; + fn create_module_info(&self) -> Arc; + + /// Sets the `ModuleInfo` name + fn set_module_info_name(&mut self, name: String) -> bool; + + /// Returns the `ModuleInfo` for instantiation + fn module_info(&self) -> &ModuleInfo; /// Returns the features for this Artifact fn features(&self) -> &Features; @@ -38,13 +43,6 @@ pub trait ArtifactCreate: Send + Sync + Upcastable { /// Serializes an artifact into bytes fn serialize(&self) -> Result, SerializeError>; - - /// Serializes an artifact into a file path - fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { - let serialized = self.serialize()?; - fs::write(path, serialized)?; - Ok(()) - } } // Implementation of `Upcastable` taken from https://users.rust-lang.org/t/why-does-downcasting-not-work-for-subtraits/33286/7 . diff --git a/lib/types/src/compilation/module.rs b/lib/types/src/compilation/module.rs index 461511de404..9869e6cfce3 100644 --- a/lib/types/src/compilation/module.rs +++ b/lib/types/src/compilation/module.rs @@ -4,6 +4,7 @@ use crate::{Features, MemoryIndex, MemoryStyle, ModuleInfo, TableIndex, TableSty use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; +use std::sync::Arc; /// The required info for compiling a module. /// @@ -16,7 +17,7 @@ pub struct CompileModuleInfo { /// The features used for compiling the module pub features: Features, /// The module information - pub module: ModuleInfo, + pub module: Arc, /// The memory styles used for compiling. /// /// The compiler will emit the most optimal code based diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 1c84890dcc1..268c59b5ce4 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -62,6 +62,7 @@ mod libcalls; mod memory; mod module; mod serialize; +mod store_id; mod table; mod trapcode; mod types; @@ -129,6 +130,8 @@ pub use crate::compilation::symbols::{Symbol, SymbolRegistry}; pub use crate::compilation::trap::TrapInformation; pub use crate::compilation::unwind::CompiledFunctionUnwindInfo; +pub use crate::store_id::StoreId; + /// Offset in bytes from the beginning of the function. pub type CodeOffset = u32; diff --git a/lib/types/src/serialize.rs b/lib/types/src/serialize.rs index 4d1fe7f229a..1d92854ddfd 100644 --- a/lib/types/src/serialize.rs +++ b/lib/types/src/serialize.rs @@ -12,8 +12,7 @@ use rkyv::{ Serialize as RkyvSerialize, }; use std::convert::TryInto; -use std::path::Path; -use std::{fs, mem}; +use std::mem; /// The compilation related data for a serialized modules #[derive(Archive, Default, RkyvDeserialize, RkyvSerialize)] @@ -129,7 +128,12 @@ impl SerializableModule { /// Create a `ModuleInfo` for instantiation pub fn create_module_info(&self) -> ModuleInfo { - self.compile_info.module.clone() + self.compile_info.module.as_ref().clone() + } + + /// Returns the `ModuleInfo` for instantiation + pub fn module_info(&self) -> &ModuleInfo { + &self.compile_info.module } /// Returns the features for this Artifact @@ -156,13 +160,6 @@ impl SerializableModule { pub fn table_styles(&self) -> &PrimaryMap { &self.compile_info.table_styles } - - /// Serializes an artifact into a file path - pub fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { - let serialized = self.serialize()?; - fs::write(path, serialized)?; - Ok(()) - } } /// Metadata header which holds an ABI version and the length of the remaining diff --git a/lib/types/src/store_id.rs b/lib/types/src/store_id.rs new file mode 100644 index 00000000000..cd25a8ef3b0 --- /dev/null +++ b/lib/types/src/store_id.rs @@ -0,0 +1,22 @@ +use std::{ + num::NonZeroUsize, + sync::atomic::{AtomicUsize, Ordering}, +}; + +/// Unique ID to identify a context. +/// +/// Every handle to an object managed by a context also contains the ID of the +/// context. This is used to check that a handle is always used with the +/// correct context. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct StoreId(NonZeroUsize); + +impl Default for StoreId { + // Allocates a unique ID for a new context. + fn default() -> Self { + // No overflow checking is needed here: overflowing this would take + // thousands of years. + static NEXT_ID: AtomicUsize = AtomicUsize::new(1); + Self(NonZeroUsize::new(NEXT_ID.fetch_add(1, Ordering::Relaxed)).unwrap()) + } +} diff --git a/lib/types/src/value.rs b/lib/types/src/value.rs index e07d2d5231e..045594673a0 100644 --- a/lib/types/src/value.rs +++ b/lib/types/src/value.rs @@ -20,6 +20,30 @@ pub union RawValue { pub bytes: [u8; 16], } +impl From for RawValue { + fn from(value: i32) -> Self { + Self { i32: value } + } +} + +impl From for RawValue { + fn from(value: i64) -> Self { + Self { i64: value } + } +} + +impl From for RawValue { + fn from(value: f32) -> Self { + Self { f32: value } + } +} + +impl From for RawValue { + fn from(value: f64) -> Self { + Self { f64: value } + } +} + impl Default for RawValue { fn default() -> Self { Self { bytes: [0; 16] } @@ -43,6 +67,7 @@ macro_rules! partial_eq { } )*) } + partial_eq! { i32 => i32, u32 => u32, diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index f4fe1a18ea2..b2d69b8616d 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -52,9 +52,7 @@ pub use crate::memory::{ pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; -pub use crate::store::{ - InternalStoreHandle, MaybeInstanceOwned, StoreHandle, StoreId, StoreObjects, -}; +pub use crate::store::{InternalStoreHandle, MaybeInstanceOwned, StoreHandle, StoreObjects}; pub use crate::table::{TableElement, VMTable}; pub use crate::trap::*; pub use crate::vmcontext::{ @@ -67,7 +65,7 @@ pub use wasmer_types::MemoryError; pub use wasmer_types::MemoryStyle; use wasmer_types::RawValue; pub use wasmer_types::TableStyle; -pub use wasmer_types::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; +pub use wasmer_types::{StoreId, TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; #[deprecated( since = "2.1.0", diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 8b2633929d9..8813ee26c15 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -1,34 +1,9 @@ -use core::slice::Iter; -use std::{ - cell::UnsafeCell, - fmt, - marker::PhantomData, - num::{NonZeroU64, NonZeroUsize}, - ptr::NonNull, - sync::atomic::{AtomicU64, Ordering}, -}; - use crate::{ VMExternObj, VMFunction, VMFunctionEnvironment, VMGlobal, VMInstance, VMMemory, VMTable, }; - -/// Unique ID to identify a context. -/// -/// Every handle to an object managed by a context also contains the ID of the -/// context. This is used to check that a handle is always used with the -/// correct context. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct StoreId(NonZeroU64); - -impl Default for StoreId { - // Allocates a unique ID for a new context. - fn default() -> Self { - // No overflow checking is needed here: overflowing this would take - // thousands of years. - static NEXT_ID: AtomicU64 = AtomicU64::new(1); - Self(NonZeroU64::new(NEXT_ID.fetch_add(1, Ordering::Relaxed)).unwrap()) - } -} +use core::slice::Iter; +use std::{cell::UnsafeCell, fmt, marker::PhantomData, num::NonZeroUsize, ptr::NonNull}; +use wasmer_types::StoreId; /// Trait to represent an object managed by a context. This is implemented on /// the VM types managed by the context. diff --git a/lib/vm/src/trap/mod.rs b/lib/vm/src/trap/mod.rs index afa81642a45..6a467c2d438 100644 --- a/lib/vm/src/trap/mod.rs +++ b/lib/vm/src/trap/mod.rs @@ -11,7 +11,7 @@ mod traphandlers; pub use trap::Trap; pub use traphandlers::{ catch_traps, on_host_stack, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, - TrapHandler, TrapHandlerFn, + TrapHandlerFn, }; pub use traphandlers::{init_traps, resume_panic}; pub use wasmer_types::TrapCode; diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index 117833bd859..89b9a141da5 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -86,21 +86,6 @@ unsafe fn process_illegal_op(addr: usize) -> Option { } } -/// A package of functionality needed by `catch_traps` to figure out what to do -/// when handling a trap. -/// -/// # Safety -/// -/// Note that this is an `unsafe` trait at least because it's being run in the -/// context of a synchronous signal handler, so it needs to be careful to not -/// access too much state in answering these queries. -pub unsafe trait TrapHandler { - /// Uses `call` to call a custom signal handler, if one is specified. - /// - /// Returns `true` if `call` returns true, otherwise returns `false`. - fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool; -} - cfg_if::cfg_if! { if #[cfg(unix)] { static mut PREV_SIGSEGV: MaybeUninit = MaybeUninit::uninit(); diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index d6463ea9836..791d9a727d1 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -6,6 +6,8 @@ use crate::{ }; use futures::Future; use tracing::*; +#[cfg(feature = "sys")] +use wasmer::NativeEngineExt; use wasmer::{FunctionEnvMut, Instance, Memory, Module, Store}; use wasmer_wasi_types::wasi::{Errno, ExitCode}; @@ -23,11 +25,8 @@ pub fn spawn_exec( runtime: &Arc, compiled_modules: &ModuleCache, ) -> Result { - // Load the module - #[cfg(feature = "sys")] - let compiler = store.engine().name(); - #[cfg(not(feature = "sys"))] - let compiler = "generic"; + // The deterministic id for this engine + let compiler = store.engine().deterministic_id(); let module = compiled_modules.get_compiled_module(&store, binary.hash().as_str(), compiler); let module = match (module, binary.entry.as_ref()) { @@ -85,7 +84,7 @@ pub fn spawn_exec_module( let memory_spawn = match shared_memory { Some(ty) => { #[cfg(feature = "sys")] - let style = store.tunables().memory_style(&ty); + let style = store.engine().tunables().memory_style(&ty); SpawnType::CreateWithType(SpawnedMemory { ty, #[cfg(feature = "sys")] diff --git a/lib/wasi/src/bin_factory/module_cache.rs b/lib/wasi/src/bin_factory/module_cache.rs index ffc55e26b13..e87e111703b 100644 --- a/lib/wasi/src/bin_factory/module_cache.rs +++ b/lib/wasi/src/bin_factory/module_cache.rs @@ -186,8 +186,7 @@ impl ModuleCache { pub fn get_compiled_module( &self, - #[cfg(feature = "sys")] engine: &impl wasmer::AsEngineRef, - #[cfg(feature = "js")] engine: &impl wasmer::AsStoreRef, + engine: &impl wasmer::AsEngineRef, data_hash: &str, compiler: &str, ) -> Option { diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 28ad07cd286..927a0f24a6b 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -840,5 +840,5 @@ fn mem_error_to_bus(err: MemoryAccessError) -> BusErrno { #[cfg(all(feature = "sys"))] pub fn build_test_engine(features: Option) -> wasmer::Engine { let _ = features; - wasmer::Store::default().engine().cloned() + wasmer::Store::default().engine().clone() } diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 372a351fc9d..d11d7efae12 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -6,13 +6,8 @@ use std::{pin::Pin, time::Duration}; use ::tokio::runtime::Handle; use futures::Future; -use wasmer::{MemoryType, Module, Store}; - -#[cfg(feature = "js")] -use wasmer::VMMemory; - -#[cfg(not(target_family = "wasm"))] use wasmer::vm::VMMemory; +use wasmer::{MemoryType, Module, Store}; #[cfg(feature = "sys")] use wasmer_types::MemoryStyle; diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index 670339095ac..d39873f4775 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -3,6 +3,8 @@ use std::{collections::HashMap, ops::Deref, path::PathBuf, sync::Arc, time::Dura use derivative::Derivative; use rand::Rng; use tracing::{trace, warn}; +#[cfg(feature = "sys")] +use wasmer::NativeEngineExt; use wasmer::{ AsStoreMut, AsStoreRef, FunctionEnvMut, Global, Instance, Memory, MemoryView, Module, TypedFunction, @@ -436,7 +438,7 @@ impl WasiEnv { match shared_memory { Some(ty) => { #[cfg(feature = "sys")] - let style = store.tunables().memory_style(&ty); + let style = store.engine().tunables().memory_style(&ty); SpawnType::CreateWithType(SpawnedMemory { ty, #[cfg(feature = "sys")] diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index 3f8aea7b88e..e0b64cb1474 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -1,10 +1,7 @@ use super::*; use crate::{os::task::OwnedTaskStatus, syscalls::*}; -#[cfg(feature = "sys")] use wasmer::vm::VMMemory; -#[cfg(feature = "js")] -use wasmer::VMMemory; /// ### `proc_fork()` /// Forks the current process into a new subprocess. If the function diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index fb650f9ea85..abc22e541d3 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -1,10 +1,7 @@ use super::*; use crate::syscalls::*; -#[cfg(feature = "sys")] use wasmer::vm::VMMemory; -#[cfg(feature = "js")] -use wasmer::VMMemory; /// ### `thread_spawn()` /// Creates a new thread by spawning that shares the same diff --git a/tests/compilers/serialize.rs b/tests/compilers/serialize.rs index 48997f0e484..fce0451402a 100644 --- a/tests/compilers/serialize.rs +++ b/tests/compilers/serialize.rs @@ -4,7 +4,7 @@ use wasmer::*; #[test] fn sanity_test_artifact_deserialize() { let engine = Engine::headless(); - let result = unsafe { Artifact::deserialize(&engine, &[]) }; + let result = unsafe { Module::deserialize(&engine, &[]) }; assert!(result.is_err()); }