diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 5883ca598fa..2ff837c00f9 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -56,6 +56,7 @@ anyhow = "1.0" wasmer-types = { path = "../types", version = "=2.3.0", default-features = false, features = ["std"] } wasm-bindgen = "0.2.74" js-sys = "0.3.51" +#web-sys = { version = "0.3.51", features = [ "console" ] } wasmer-derive = { path = "../derive", version = "=2.3.0" } # - Optional dependencies for `js`. wasmparser = { version = "0.83", default-features = false, optional = true } diff --git a/lib/api/src/js/context.rs b/lib/api/src/js/context.rs new file mode 100644 index 00000000000..9823abbe7cb --- /dev/null +++ b/lib/api/src/js/context.rs @@ -0,0 +1,449 @@ +#![allow(dead_code)] +use crate::Store; + +/// 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 ContextInner { + pub(crate) objects: ContextObjects, + pub(crate) store: Store, + pub(crate) data: T, +} + +/// A context containing a set of WebAssembly instances, along with host state. +/// +/// All WebAssembly instances must exist within a context. In the majority of +/// cases each instance will have its own context, but it is possible to have +/// multiple instances in a context when these instances need to interact with +/// each other, for example sharing a memory between instances or calling +/// functions in another instance. +/// +/// The lifetimes of run-time WebAssembly objects, notably [`Instance`], +/// [`Memory`], [`Global`], [`Table`] and [`Function`] is tied to a context: +/// the backing memory for these objects is only freed when the context is +/// freed. +/// +/// The `T` generic parameter allows arbitrary data to be attached to a context. +/// This data can be accessed using the [`Context::data`] and +/// [`Context::data_mut`] methods. Host functions defined using +/// [`Function::new`] and [`Function::new_native`] receive +/// a reference to the context when they are called. +pub struct Context { + pub(crate) inner: Box>, +} + +impl Context { + /// Creates a new context with the given host state. + // TODO: Eliminate the Store type and move its functionality into Engine. + pub fn new(store: &Store, data: T) -> Self { + Self { + inner: Box::new(ContextInner { + objects: Default::default(), + store: store.clone(), + data, + }), + } + } + + /// Returns a reference to the host state in this context. + pub fn data(&self) -> &T { + &self.inner.data + } + + /// Returns a mutable- reference to the host state in this context. + pub fn data_mut(&mut self) -> &mut T { + &mut self.inner.data + } + + /// Drops the context and returns the host state that was stored in it. + pub fn into_data(self) -> T { + self.inner.data + } + + /// Returns a reference to the `Store` of this context. + pub fn store(&self) -> &Store { + &self.inner.store + } +} + +/// A temporary handle to a [`Context`]. +pub struct ContextRef<'a, T: 'a> { + inner: &'a ContextInner, +} + +impl<'a, T> ContextRef<'a, T> { + /// Returns a reference to the host state in this context. + pub fn data(&self) -> &'a T { + &self.inner.data + } + + /// Returns a reference to the `Store` of this context. + pub fn store(&self) -> &Store { + &self.inner.store + } + + pub(crate) fn objects(&self) -> &'a ContextObjects { + &self.inner.objects + } +} + +/// A temporary handle to a [`Context`]. +pub struct ContextMut<'a, T: 'a> { + inner: &'a mut ContextInner, +} + +impl ContextMut<'_, T> { + /// Returns a reference to the host state in this context. + pub fn data(&self) -> &T { + &self.inner.data + } + + /// Returns a mutable- reference to the host state in this context. + pub fn data_mut(&mut self) -> &mut T { + &mut self.inner.data + } + + pub(crate) fn objects_mut(&mut self) -> &mut ContextObjects { + &mut self.inner.objects + } + + /// Returns a reference to the `Store` of this context. + pub fn store(&self) -> &Store { + &self.inner.store + } + + /// Returns the raw pointer of the context + pub(crate) fn as_raw(&self) -> *mut ContextInner { + self.inner as *const ContextInner as *mut ContextInner + } + + /// Constructs the context from the raw pointer + pub(crate) unsafe fn from_raw(raw: *mut ContextInner) -> Self { + Self { inner: &mut *raw } + } +} + +/// Helper trait for a value that is convertible to a [`ContextRef`]. +pub trait AsContextRef { + /// Host state associated with the [`Context`]. + type Data; + + /// Returns a `ContextRef` pointing to the underlying context. + fn as_context_ref(&self) -> ContextRef<'_, Self::Data>; +} + +/// Helper trait for a value that is convertible to a [`ContextMut`]. +pub trait AsContextMut: AsContextRef { + /// Returns a `ContextMut` pointing to the underlying context. + fn as_context_mut(&mut self) -> ContextMut<'_, Self::Data>; +} + +impl AsContextRef for Context { + type Data = T; + + fn as_context_ref(&self) -> ContextRef<'_, Self::Data> { + ContextRef { inner: &self.inner } + } +} +impl AsContextMut for Context { + fn as_context_mut(&mut self) -> ContextMut<'_, Self::Data> { + ContextMut { + inner: &mut self.inner, + } + } +} +impl AsContextRef for ContextRef<'_, T> { + type Data = T; + + fn as_context_ref(&self) -> ContextRef<'_, Self::Data> { + ContextRef { inner: self.inner } + } +} +impl AsContextRef for ContextMut<'_, T> { + type Data = T; + + fn as_context_ref(&self) -> ContextRef<'_, Self::Data> { + ContextRef { inner: self.inner } + } +} +impl AsContextMut for ContextMut<'_, T> { + fn as_context_mut(&mut self) -> ContextMut<'_, Self::Data> { + ContextMut { inner: self.inner } + } +} +impl AsContextRef for &'_ T { + type Data = T::Data; + + fn as_context_ref(&self) -> ContextRef<'_, Self::Data> { + T::as_context_ref(*self) + } +} +impl AsContextRef for &'_ mut T { + type Data = T::Data; + + fn as_context_ref(&self) -> ContextRef<'_, Self::Data> { + T::as_context_ref(*self) + } +} +impl AsContextMut for &'_ mut T { + fn as_context_mut(&mut self) -> ContextMut<'_, Self::Data> { + T::as_context_mut(*self) + } +} + +pub use objects::*; +mod objects { + use crate::js::export::{VMFunction, VMGlobal, VMMemory, VMTable}; + use std::{ + cell::UnsafeCell, + fmt, + marker::PhantomData, + num::{NonZeroU64, NonZeroUsize}, + ptr::NonNull, + 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)] + pub struct ContextId(NonZeroU64); + + impl Default for ContextId { + // 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()) + } + } + + /// Trait to represent an object managed by a context. This is implemented on + /// the VM types managed by the context. + pub trait ContextObject: Sized { + fn list(ctx: &ContextObjects) -> &Vec; + fn list_mut(ctx: &mut ContextObjects) -> &mut Vec; + } + + macro_rules! impl_context_object { + ($($field:ident => $ty:ty,)*) => { + $( + impl ContextObject for $ty { + fn list(ctx: &ContextObjects) -> &Vec { + &ctx.$field + } + fn list_mut(ctx: &mut ContextObjects) -> &mut Vec { + &mut ctx.$field + } + } + )* + }; +} + + impl_context_object! { + functions => VMFunction, + tables => VMTable, + globals => VMGlobal, + memories => VMMemory, + instances => js_sys::WebAssembly::Instance, + } + + /// Set of objects managed by a context. + #[derive(Default)] + pub struct ContextObjects { + id: ContextId, + memories: Vec, + tables: Vec, + globals: Vec, + functions: Vec, + instances: Vec, + } + + impl ContextObjects { + /// Returns the ID of this context. + pub fn id(&self) -> ContextId { + self.id + } + + /// Returns a pair of mutable references from two handles. + /// + /// Panics if both handles point to the same object. + pub fn get_2_mut( + &mut self, + a: InternalContextHandle, + b: InternalContextHandle, + ) -> (&mut T, &mut T) { + assert_ne!(a.index(), b.index()); + let list = T::list_mut(self); + if a.index() < b.index() { + let (low, high) = list.split_at_mut(b.index()); + (&mut low[a.index()], &mut high[0]) + } else { + let (low, high) = list.split_at_mut(a.index()); + (&mut high[0], &mut low[a.index()]) + } + } + } + + /// Handle to an object managed by a context. + /// + /// Internally this is just an integer index into a context. A reference to the + /// context must be passed in separately to access the actual object. + pub struct ContextHandle { + id: ContextId, + internal: InternalContextHandle, + } + + impl core::cmp::PartialEq for ContextHandle { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } + } + impl Clone for ContextHandle { + fn clone(&self) -> Self { + Self { + id: self.id, + internal: self.internal, + } + } + } + + impl fmt::Debug for ContextHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ContextHandle") + .field("id", &self.id) + .field("internal", &self.internal.index()) + .finish() + } + } + + impl ContextHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(ctx: &mut ContextObjects, val: T) -> Self { + Self { + id: ctx.id, + internal: InternalContextHandle::new(ctx, val), + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, ctx: &'a ContextObjects) -> &'a T { + assert_eq!(self.id, ctx.id, "object used with the wrong context"); + self.internal.get(ctx) + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, ctx: &'a mut ContextObjects) -> &'a mut T { + assert_eq!(self.id, ctx.id, "object used with the wrong context"); + self.internal.get_mut(ctx) + } + + /// Returns the internal handle contains within this handle. + pub fn internal_handle(&self) -> InternalContextHandle { + self.internal + } + + /// Returns the ID of the context associated with the handle. + pub fn context_id(&self) -> ContextId { + self.id + } + + /// Constructs a `ContextHandle` from a `ContextId` and an `InternalContextHandle`. + /// + /// # Safety + /// Handling `InternalContextHandle` values is unsafe because they do not track context ID. + pub unsafe fn from_internal(id: ContextId, internal: InternalContextHandle) -> Self { + Self { id, internal } + } + } + + /// Internal handle to an object owned by the current context. + /// + /// Unlike `ContextHandle` this does not track the context ID: it is only + /// intended to be used within objects already owned by a context. + #[repr(transparent)] + pub struct InternalContextHandle { + // Use a NonZero here to reduce the size of Option. + idx: NonZeroUsize, + marker: PhantomData T>, + } + + impl Clone for InternalContextHandle { + fn clone(&self) -> Self { + *self + } + } + impl Copy for InternalContextHandle {} + + impl fmt::Debug for InternalContextHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InternalContextHandle") + .field("idx", &self.idx) + .finish() + } + } + impl PartialEq for InternalContextHandle { + fn eq(&self, other: &Self) -> bool { + self.idx == other.idx + } + } + impl Eq for InternalContextHandle {} + + impl InternalContextHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(ctx: &mut ContextObjects, val: T) -> Self { + let list = T::list_mut(ctx); + let idx = NonZeroUsize::new(list.len() + 1).unwrap(); + list.push(val); + Self { + idx, + marker: PhantomData, + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, ctx: &'a ContextObjects) -> &'a T { + &T::list(ctx)[self.idx.get() - 1] + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, ctx: &'a mut ContextObjects) -> &'a mut T { + &mut T::list_mut(ctx)[self.idx.get() - 1] + } + + pub(crate) fn index(&self) -> usize { + self.idx.get() + } + + pub(crate) fn from_index(idx: usize) -> Option { + NonZeroUsize::new(idx).map(|idx| Self { + idx, + marker: PhantomData, + }) + } + } + + /// Data used by the generated code is generally located inline within the + /// `VMContext` for items defined in an instance. Host-defined objects are + /// allocated separately and owned directly by the context. + pub enum MaybeInstanceOwned { + /// The data is owned here. + Host(Box>), + + /// The data is stored inline in the `VMContext` of an instance. + Instance(NonNull), + } + + impl MaybeInstanceOwned { + /// Returns underlying pointer to the VM data. + pub fn as_ptr(&self) -> NonNull { + match self { + MaybeInstanceOwned::Host(p) => unsafe { NonNull::new_unchecked(p.get()) }, + MaybeInstanceOwned::Instance(p) => *p, + } + } + } +} diff --git a/lib/api/src/js/env.rs b/lib/api/src/js/env.rs deleted file mode 100644 index 20921c10560..00000000000 --- a/lib/api/src/js/env.rs +++ /dev/null @@ -1,219 +0,0 @@ -use crate::js::{ExportError, Instance}; -use thiserror::Error; - -/// An error while initializing the user supplied host env with the `WasmerEnv` trait. -#[derive(Error, Debug)] -#[error("Host env initialization error: {0}")] -pub enum HostEnvInitError { - /// An error occurred when accessing an export - Export(ExportError), -} - -impl From for HostEnvInitError { - fn from(other: ExportError) -> Self { - Self::Export(other) - } -} - -/// Trait for initializing the environments passed to host functions after -/// instantiation but before execution. -/// -/// This is useful for filling an environment with data that can only be accesed -/// after instantiation. For example, exported items such as memories and -/// functions which don't exist prior to instantiation can be accessed here so -/// that host functions can use them. -/// -/// # Examples -/// -/// This trait can be derived like so: -/// -/// ``` -/// use wasmer::{WasmerEnv, LazyInit, Memory, TypedFunction}; -/// -/// #[derive(WasmerEnv, Clone)] -/// pub struct MyEnvWithNoInstanceData { -/// non_instance_data: u8, -/// } -/// -/// #[derive(WasmerEnv, Clone)] -/// pub struct MyEnvWithInstanceData { -/// non_instance_data: u8, -/// #[wasmer(export)] -/// memory: LazyInit, -/// #[wasmer(export(name = "real_name"))] -/// func: LazyInit>, -/// #[wasmer(export(optional = true, alias = "memory2", alias = "_memory2"))] -/// optional_memory: LazyInit, -/// } -/// ``` -/// -/// When deriving `WasmerEnv`, you must wrap your types to be initialized in -/// [`LazyInit`]. The derive macro will also generate helper methods of the form -/// `_ref` and `_ref_unchecked` for easy access to the -/// data. -/// -/// The valid arguments to `export` are: -/// - `name = "string"`: specify the name of this item in the Wasm module. If this is not specified, it will default to the name of the field. -/// - `optional = true`: specify whether this export is optional. Defaults to -/// `false`. Being optional means that if the export can't be found, the -/// [`LazyInit`] will be left uninitialized. -/// - `alias = "string"`: specify additional names to look for in the Wasm module. -/// `alias` may be specified multiple times to search for multiple aliases. -/// ------- -/// -/// This trait may also be implemented manually: -/// ``` -/// # use wasmer::{WasmerEnv, LazyInit, Memory, Instance, HostEnvInitError}; -/// #[derive(Clone)] -/// pub struct MyEnv { -/// memory: LazyInit, -/// } -/// -/// impl WasmerEnv for MyEnv { -/// fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { -/// let memory: Memory = instance.exports.get_with_generics_weak("memory").unwrap(); -/// self.memory.initialize(memory.clone()); -/// Ok(()) -/// } -/// } -/// ``` -pub trait WasmerEnv { - /// The function that Wasmer will call on your type to let it finish - /// setting up the environment with data from the `Instance`. - /// - /// This function is called after `Instance` is created but before it is - /// returned to the user via `Instance::new`. - fn init_with_instance(&mut self, _instance: &Instance) -> Result<(), HostEnvInitError> { - Ok(()) - } -} - -impl WasmerEnv for u8 {} -impl WasmerEnv for i8 {} -impl WasmerEnv for u16 {} -impl WasmerEnv for i16 {} -impl WasmerEnv for u32 {} -impl WasmerEnv for i32 {} -impl WasmerEnv for u64 {} -impl WasmerEnv for i64 {} -impl WasmerEnv for u128 {} -impl WasmerEnv for i128 {} -impl WasmerEnv for f32 {} -impl WasmerEnv for f64 {} -impl WasmerEnv for usize {} -impl WasmerEnv for isize {} -impl WasmerEnv for char {} -impl WasmerEnv for bool {} -impl WasmerEnv for String {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicBool {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI8 {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU8 {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI16 {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU16 {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI32 {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU32 {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI64 {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicUsize {} -impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicIsize {} -impl WasmerEnv for Box { - fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { - (&mut **self).init_with_instance(instance) - } -} - -impl WasmerEnv for ::std::sync::Arc<::std::sync::Mutex> { - fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { - let mut guard = self.lock().unwrap(); - guard.init_with_instance(instance) - } -} - -/// Lazily init an item -pub struct LazyInit { - /// The data to be initialized - data: std::mem::MaybeUninit, - /// Whether or not the data has been initialized - initialized: bool, -} - -impl LazyInit { - /// Creates an unitialized value. - pub fn new() -> Self { - Self { - data: std::mem::MaybeUninit::uninit(), - initialized: false, - } - } - - /// # Safety - /// - The data must be initialized first - pub unsafe fn get_unchecked(&self) -> &T { - &*self.data.as_ptr() - } - - /// Get the inner data. - pub fn get_ref(&self) -> Option<&T> { - if !self.initialized { - None - } else { - Some(unsafe { self.get_unchecked() }) - } - } - - /// Sets a value and marks the data as initialized. - pub fn initialize(&mut self, value: T) -> bool { - if self.initialized { - return false; - } - unsafe { - self.data.as_mut_ptr().write(value); - } - self.initialized = true; - true - } -} - -impl std::fmt::Debug for LazyInit { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("LazyInit") - .field("data", &self.get_ref()) - .finish() - } -} - -impl Clone for LazyInit { - fn clone(&self) -> Self { - if let Some(inner) = self.get_ref() { - Self { - data: std::mem::MaybeUninit::new(inner.clone()), - initialized: true, - } - } else { - Self { - data: std::mem::MaybeUninit::uninit(), - initialized: false, - } - } - } -} - -impl Drop for LazyInit { - fn drop(&mut self) { - if self.initialized { - unsafe { - let ptr = self.data.as_mut_ptr(); - std::ptr::drop_in_place(ptr); - }; - } - } -} - -impl Default for LazyInit { - fn default() -> Self { - Self::new() - } -} - -unsafe impl Send for LazyInit {} -// I thought we could opt out of sync..., look into this -// unsafe impl !Sync for InitWithInstance {} diff --git a/lib/api/src/js/error.rs b/lib/api/src/js/error.rs index 8931d0fd6ab..5a25c99d88e 100644 --- a/lib/api/src/js/error.rs +++ b/lib/api/src/js/error.rs @@ -1,4 +1,7 @@ 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; @@ -83,11 +86,27 @@ pub enum WasmError { #[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 @@ -124,3 +143,45 @@ pub enum DeserializeError { #[cfg_attr(feature = "std", error(transparent))] Compiler(CompileError), } + +/// 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("Link error: {0}"))] + Link(String), + + /// A runtime error occured while invoking the start function + #[cfg_attr(feature = "std", error(transparent))] + Start(RuntimeError), + + /// Import from a different [`Context`]. + /// This error occurs when an import from a different context is used. + #[cfg_attr(feature = "std", error("cannot mix imports from different contexts"))] + BadContext, + + /// A generic error occured while invoking API functions + #[cfg_attr(feature = "std", error(transparent))] + Wasm(WasmError), +} + +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/export.rs b/lib/api/src/js/export.rs index fc5f638c442..41d72145d01 100644 --- a/lib/api/src/js/export.rs +++ b/lib/api/src/js/export.rs @@ -1,12 +1,9 @@ -use crate::js::instance::Instance; +use crate::js::context::{AsContextMut, AsContextRef, InternalContextHandle}; +use crate::js::error::WasmError; use crate::js::wasm_bindgen_polyfill::Global; -use crate::js::HostEnvInitError; -use crate::js::WasmerEnv; use js_sys::Function; use js_sys::WebAssembly::{Memory, Table}; -use std::cell::RefCell; use std::fmt; -use std::sync::Arc; use wasm_bindgen::{JsCast, JsValue}; use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType}; @@ -59,31 +56,14 @@ impl VMTable { pub struct VMFunction { pub(crate) function: Function, pub(crate) ty: FunctionType, - pub(crate) environment: Option>>>, } unsafe impl Send for VMFunction {} unsafe impl Sync for VMFunction {} impl VMFunction { - pub(crate) fn new( - function: Function, - ty: FunctionType, - environment: Option>, - ) -> Self { - Self { - function, - ty, - environment: environment.map(|env| Arc::new(RefCell::new(env))), - } - } - - pub(crate) fn init_envs(&self, instance: &Instance) -> Result<(), HostEnvInitError> { - if let Some(env) = &self.environment { - let mut borrowed_env = env.borrow_mut(); - borrowed_env.init_with_instance(instance)?; - } - Ok(()) + pub(crate) fn new(function: Function, ty: FunctionType) -> Self { + Self { function, ty } } } @@ -105,67 +85,90 @@ impl fmt::Debug for VMFunction { #[derive(Debug, Clone)] pub enum Export { /// A function export value. - Function(VMFunction), + Function(InternalContextHandle), /// A table export value. - Table(VMTable), + Table(InternalContextHandle), /// A memory export value. - Memory(VMMemory), + Memory(InternalContextHandle), /// A global export value. - Global(VMGlobal), + Global(InternalContextHandle), } impl Export { /// Return the export as a `JSValue`. - pub fn as_jsvalue(&self) -> &JsValue { + pub fn as_jsvalue<'context>(&self, ctx: &'context impl AsContextRef) -> &'context JsValue { match self { - Export::Memory(js_wasm_memory) => js_wasm_memory.memory.as_ref(), - Export::Function(js_func) => js_func.function.as_ref(), - Export::Table(js_wasm_table) => js_wasm_table.table.as_ref(), - Export::Global(js_wasm_global) => js_wasm_global.global.as_ref(), + Self::Memory(js_wasm_memory) => js_wasm_memory + .get(ctx.as_context_ref().objects()) + .memory + .as_ref(), + Self::Function(js_func) => js_func + .get(ctx.as_context_ref().objects()) + .function + .as_ref(), + Self::Table(js_wasm_table) => js_wasm_table + .get(ctx.as_context_ref().objects()) + .table + .as_ref(), + Self::Global(js_wasm_global) => js_wasm_global + .get(ctx.as_context_ref().objects()) + .global + .as_ref(), } } -} -impl From<(JsValue, ExternType)> for Export { - fn from((val, extern_type): (JsValue, ExternType)) -> Export { + /// Convert a `JsValue` into an `Export` within a given `Context`. + pub fn from_js_value( + val: JsValue, + ctx: &mut impl AsContextMut, + extern_type: ExternType, + ) -> Result { match extern_type { ExternType::Memory(memory_type) => { if val.is_instance_of::() { - return Export::Memory(VMMemory::new( - val.unchecked_into::(), - memory_type, - )); + Ok(Self::Memory(InternalContextHandle::new( + &mut ctx.as_context_mut().objects_mut(), + VMMemory::new(val.unchecked_into::(), memory_type), + ))) } else { - panic!("Extern type doesn't match js value type"); + 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::() { - return Export::Global(VMGlobal::new( - val.unchecked_into::(), - global_type, - )); + Ok(Self::Global(InternalContextHandle::new( + &mut ctx.as_context_mut().objects_mut(), + 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::() { - return Export::Function(VMFunction::new( - val.unchecked_into::(), - function_type, - None, - )); + Ok(Self::Function(InternalContextHandle::new( + &mut ctx.as_context_mut().objects_mut(), + 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::() { - return Export::Table(VMTable::new(val.unchecked_into::
(), table_type)); + Ok(Self::Table(InternalContextHandle::new( + &mut ctx.as_context_mut().objects_mut(), + VMTable::new(val.unchecked_into::
(), table_type), + ))) } else { panic!("Extern type doesn't match js value type"); } diff --git a/lib/api/src/js/exports.rs b/lib/api/src/js/exports.rs index 5837820caaf..c25e947eb91 100644 --- a/lib/api/src/js/exports.rs +++ b/lib/api/src/js/exports.rs @@ -1,4 +1,4 @@ -use crate::js::export::Export; +use crate::js::context::AsContextRef; use crate::js::externals::{Extern, Function, Global, Memory, Table}; use crate::js::native::TypedFunction; use crate::js::WasmTypeList; @@ -141,18 +141,20 @@ impl Exports { /// Get an export as a `TypedFunction`. pub fn get_native_function( &self, + ctx: &impl AsContextRef, name: &str, ) -> Result, ExportError> where Args: WasmTypeList, Rets: WasmTypeList, { - self.get_typed_function(name) + self.get_typed_function(ctx, name) } /// Get an export as a `TypedFunction`. pub fn get_typed_function( &self, + ctx: &impl AsContextRef, name: &str, ) -> Result, ExportError> where @@ -160,12 +162,16 @@ impl Exports { Rets: WasmTypeList, { self.get_function(name)? - .native() + .native(ctx) .map_err(|_| ExportError::IncompatibleType) } /// Hack to get this working with nativefunc too - pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result + pub fn get_with_generics<'a, T, Args, Rets>( + &'a self, + ctx: &impl AsContextRef, + name: &str, + ) -> Result where Args: WasmTypeList, Rets: WasmTypeList, @@ -173,19 +179,23 @@ impl Exports { { match self.map.get(name) { None => Err(ExportError::Missing(name.to_string())), - Some(extern_) => T::get_self_from_extern_with_generics(extern_), + Some(extern_) => T::get_self_from_extern_with_generics(ctx, extern_), } } /// Like `get_with_generics` but with a WeakReference to the `InstanceRef` internally. - /// This is useful for passing data into `WasmerEnv`, for example. - pub fn get_with_generics_weak<'a, T, Args, Rets>(&'a self, name: &str) -> Result + /// This is useful for passing data into Context data, for example. + pub fn get_with_generics_weak<'a, T, Args, Rets>( + &'a self, + ctx: &impl AsContextRef, + name: &str, + ) -> Result where Args: WasmTypeList, Rets: WasmTypeList, T: ExportableWithGenerics<'a, Args, Rets>, { - let out: T = self.get_with_generics(name)?; + let out: T = self.get_with_generics(ctx, name)?; Ok(out) } @@ -311,12 +321,6 @@ impl<'a> IntoIterator for &'a Exports { /// /// [`Instance`]: crate::js::Instance pub trait Exportable<'a>: Sized { - /// This function is used when providedd the [`Extern`] as exportable, so it - /// can be used while instantiating the [`Module`]. - /// - /// [`Module`]: crate::js::Module - fn to_export(&self) -> Export; - /// Implementation of how to get the export corresponding to the implementing type /// from an [`Instance`] by name. /// @@ -329,13 +333,19 @@ pub trait Exportable<'a>: Sized { /// 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(_extern: &'a Extern) -> Result; + fn get_self_from_extern_with_generics( + ctx: &impl AsContextRef, + _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(_extern: &'a Extern) -> Result { + fn get_self_from_extern_with_generics( + _ctx: &impl AsContextRef, + _extern: &'a Extern, + ) -> Result { T::get_self_from_extern(_extern).map(|i| i.clone()) } } diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 58981dcf150..17d6cd56b7a 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -1,30 +1,32 @@ +pub use self::inner::{FromToNativeWasmType, HostFunction, WasmTypeList}; +use crate::js::context::{ + AsContextMut, AsContextRef, ContextHandle, ContextMut, InternalContextHandle, +}; use crate::js::exports::{ExportError, Exportable}; use crate::js::externals::Extern; -use crate::js::store::Store; -use crate::js::types::{param_from_js, AsJs /* ValFuncRef */, Val}; +use crate::js::types::{param_from_js, AsJs}; /* ValFuncRef */ use crate::js::FunctionType; use crate::js::RuntimeError; use crate::js::TypedFunction; -use crate::js::WasmerEnv; -pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; +use crate::js::Value; use js_sys::{Array, Function as JSFunction}; use std::iter::FromIterator; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; -use crate::js::export::{Export, VMFunction}; +use crate::js::export::VMFunction; use std::fmt; #[repr(C)] pub struct VMFunctionBody(u8); #[inline] -fn result_to_js(val: &Val) -> JsValue { +fn result_to_js(val: &Value) -> JsValue { match val { - Val::I32(i) => JsValue::from_f64(*i as _), - Val::I64(i) => JsValue::from_f64(*i as _), - Val::F32(f) => JsValue::from_f64(*f as _), - Val::F64(f) => JsValue::from_f64(*f), + Value::I32(i) => JsValue::from_f64(*i as _), + Value::I64(i) => JsValue::from_f64(*i as _), + Value::F32(f) => JsValue::from_f64(*f as _), + Value::F64(f) => JsValue::from_f64(*f), val => unimplemented!( "The value `{:?}` is not yet supported in the JS Function API", val @@ -33,7 +35,7 @@ fn result_to_js(val: &Val) -> JsValue { } #[inline] -fn results_to_js_array(values: &[Val]) -> Array { +fn results_to_js_array(values: &[Value]) -> Array { Array::from_iter(values.iter().map(result_to_js)) } @@ -56,12 +58,9 @@ fn results_to_js_array(values: &[Val]) -> Array { /// [Closures as host functions tracking issue](https://github.com/wasmerio/wasmer/issues/1840) #[derive(Clone, PartialEq)] pub struct Function { - pub(crate) store: Store, - pub(crate) exported: VMFunction, + pub(crate) handle: ContextHandle, } -impl WasmerEnv for WithoutEnv {} - impl Function { /// Creates a new host `Function` (dynamic) with the provided signature. /// @@ -96,47 +95,55 @@ impl Function { /// }); /// ``` #[allow(clippy::cast_ptr_alignment)] - pub fn new(store: &Store, ty: FT, func: F) -> Self + pub fn new(ctx: &mut impl AsContextMut, ty: FT, func: F) -> Self where FT: Into, - F: Fn(&[Val]) -> Result, RuntimeError> + 'static + Send + Sync, + F: Fn(ContextMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, { - let ty = ty.into(); - let new_ty = ty.clone(); + let mut ctx = ctx.as_context_mut(); + let function_type = ty.into(); + let func_ty = function_type.clone(); + let raw_ctx = ctx.as_raw() as *mut u8; - let wrapped_func: JsValue = match ty.results().len() { + let wrapped_func: JsValue = match function_type.results().len() { 0 => Closure::wrap(Box::new(move |args: &Array| { - let wasm_arguments = new_ty + let mut ctx: ContextMut = unsafe { ContextMut::from_raw(raw_ctx as _) }; + let wasm_arguments = function_type .params() .iter() .enumerate() .map(|(i, param)| param_from_js(param, &args.get(i as u32))) .collect::>(); - let _results = func(&wasm_arguments)?; + let _results = func(ctx.as_context_mut(), &wasm_arguments)?; Ok(()) }) as Box Result<(), JsValue>>) .into_js_value(), 1 => Closure::wrap(Box::new(move |args: &Array| { - let wasm_arguments = new_ty + let mut ctx: ContextMut = unsafe { ContextMut::from_raw(raw_ctx as _) }; + let wasm_arguments = function_type .params() .iter() .enumerate() .map(|(i, param)| param_from_js(param, &args.get(i as u32))) .collect::>(); - let results = func(&wasm_arguments)?; + let results = func(ctx.as_context_mut(), &wasm_arguments)?; return Ok(result_to_js(&results[0])); }) as Box Result>) .into_js_value(), _n => Closure::wrap(Box::new(move |args: &Array| { - let wasm_arguments = new_ty + let mut ctx: ContextMut = unsafe { ContextMut::from_raw(raw_ctx as _) }; + let wasm_arguments = function_type .params() .iter() .enumerate() .map(|(i, param)| param_from_js(param, &args.get(i as u32))) .collect::>(); - let results = func(&wasm_arguments)?; + let results = func(ctx.as_context_mut(), &wasm_arguments)?; return Ok(results_to_js_array(&results)); }) as Box Result>) @@ -146,124 +153,8 @@ impl Function { let dyn_func = JSFunction::new_with_args("f", "return f(Array.prototype.slice.call(arguments, 1))"); let binded_func = dyn_func.bind1(&JsValue::UNDEFINED, &wrapped_func); - Self { - store: store.clone(), - exported: VMFunction::new(binded_func, ty, None), - } - } - - /// Creates a new host `Function` (dynamic) with the provided signature and environment. - /// - /// If you know the signature of the host function at compile time, - /// consider using [`Function::new_native_with_env`] for less runtime - /// overhead. - /// - /// # Examples - /// - /// ``` - /// # use wasmer::{Function, FunctionType, Type, Store, Value, WasmerEnv}; - /// # let store = Store::default(); - /// # - /// #[derive(WasmerEnv, Clone)] - /// struct Env { - /// multiplier: i32, - /// }; - /// let env = Env { multiplier: 2 }; - /// - /// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); - /// - /// let f = Function::new_with_env(&store, &signature, env, |env, args| { - /// let result = env.multiplier * (args[0].unwrap_i32() + args[1].unwrap_i32()); - /// Ok(vec![Value::I32(result)]) - /// }); - /// ``` - /// - /// With constant signature: - /// - /// ``` - /// # use wasmer::{Function, FunctionType, Type, Store, Value, WasmerEnv}; - /// # let store = Store::default(); - /// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]); - /// - /// #[derive(WasmerEnv, Clone)] - /// struct Env { - /// multiplier: i32, - /// }; - /// let env = Env { multiplier: 2 }; - /// - /// let f = Function::new_with_env(&store, I32_I32_TO_I32, env, |env, args| { - /// let result = env.multiplier * (args[0].unwrap_i32() + args[1].unwrap_i32()); - /// Ok(vec![Value::I32(result)]) - /// }); - /// ``` - #[allow(clippy::cast_ptr_alignment)] - pub fn new_with_env(store: &Store, ty: FT, env: Env, func: F) -> Self - where - FT: Into, - F: Fn(&Env, &[Val]) -> Result, RuntimeError> + 'static + Send + Sync, - Env: Sized + WasmerEnv + 'static, - { - let ty = ty.into(); - let new_ty = ty.clone(); - - let wrapped_func: JsValue = match ty.results().len() { - 0 => Closure::wrap(Box::new(move |args: &Array| { - let wasm_arguments = new_ty - .params() - .iter() - .enumerate() - .map(|(i, param)| param_from_js(param, &args.get(i as u32 + 1))) - .collect::>(); - let env_ptr = args.get(0).as_f64().unwrap() as usize; - let env: &Env = unsafe { &*(env_ptr as *const u8 as *const Env) }; - let _results = func(env, &wasm_arguments)?; - Ok(()) - }) - as Box Result<(), JsValue>>) - .into_js_value(), - 1 => Closure::wrap(Box::new(move |args: &Array| { - let wasm_arguments = new_ty - .params() - .iter() - .enumerate() - .map(|(i, param)| param_from_js(param, &args.get(i as u32 + 1))) - .collect::>(); - let env_ptr = args.get(0).as_f64().unwrap() as usize; - let env: &Env = unsafe { &*(env_ptr as *const u8 as *const Env) }; - let results = func(env, &wasm_arguments)?; - return Ok(result_to_js(&results[0])); - }) - as Box Result>) - .into_js_value(), - _n => Closure::wrap(Box::new(move |args: &Array| { - let wasm_arguments = new_ty - .params() - .iter() - .enumerate() - .map(|(i, param)| param_from_js(param, &args.get(i as u32 + 1))) - .collect::>(); - let env_ptr = args.get(0).as_f64().unwrap() as usize; - let env: &Env = unsafe { &*(env_ptr as *const u8 as *const Env) }; - let results = func(env, &wasm_arguments)?; - return Ok(results_to_js_array(&results)); - }) - as Box Result>) - .into_js_value(), - }; - - let dyn_func = - JSFunction::new_with_args("f", "return f(Array.prototype.slice.call(arguments, 1))"); - - let environment = Box::new(env); - let binded_func = dyn_func.bind2( - &JsValue::UNDEFINED, - &wrapped_func, - &JsValue::from_f64(&*environment as *const Env as *const u8 as usize as f64), - ); - Self { - store: store.clone(), - exported: VMFunction::new(binded_func, ty, Some(environment)), - } + let vm_function = VMFunction::new(binded_func, func_ty); + Self::from_vm_export(&mut ctx, vm_function) } /// Creates a new host `Function` from a native function. @@ -283,13 +174,13 @@ impl Function { /// /// let f = Function::new_native(&store, sum); /// ``` - pub fn new_native(store: &Store, func: F) -> Self + pub fn new_native(ctx: &mut impl AsContextMut, func: F) -> Self where - F: HostFunction, + F: HostFunction, Args: WasmTypeList, Rets: WasmTypeList, - Env: Sized + 'static, { + let mut ctx = ctx.as_context_mut(); if std::mem::size_of::() != 0 { Self::closures_unsupported_panic(); } @@ -299,63 +190,15 @@ impl Function { let ft = wasm_bindgen::function_table(); let as_table = ft.unchecked_ref::(); let func = as_table.get(address).unwrap(); - let binded_func = func.bind1(&JsValue::UNDEFINED, &JsValue::UNDEFINED); - let ty = function.ty(); - Self { - store: store.clone(), - exported: VMFunction::new(binded_func, ty, None), - } - } - - /// Creates a new host `Function` from a native function and a provided environment. - /// - /// The function signature is automatically retrieved using the - /// Rust typing system. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Store, Function, WasmerEnv}; - /// # let store = Store::default(); - /// # - /// #[derive(WasmerEnv, Clone)] - /// struct Env { - /// multiplier: i32, - /// }; - /// let env = Env { multiplier: 2 }; - /// - /// fn sum_and_multiply(env: &Env, a: i32, b: i32) -> i32 { - /// (a + b) * env.multiplier - /// } - /// - /// let f = Function::new_native_with_env(&store, env, sum_and_multiply); - /// ``` - pub fn new_native_with_env(store: &Store, env: Env, func: F) -> Self - where - F: HostFunction, - Args: WasmTypeList, - Rets: WasmTypeList, - Env: Sized + WasmerEnv + 'static, - { - if std::mem::size_of::() != 0 { - Self::closures_unsupported_panic(); - } - let function = inner::Function::::new(func); - let address = function.address() as usize as u32; - let ft = wasm_bindgen::function_table(); - let as_table = ft.unchecked_ref::(); - let func = as_table.get(address).unwrap(); - let ty = function.ty(); - let environment = Box::new(env); let binded_func = func.bind1( &JsValue::UNDEFINED, - &JsValue::from_f64(&*environment as *const Env as *const u8 as usize as f64), + &JsValue::from_f64(ctx.as_raw() as *mut u8 as usize as f64), ); - // panic!("Function env {:?}", environment.type_id()); + let ty = function.ty(); + let vm_function = VMFunction::new(binded_func, ty); Self { - store: store.clone(), - exported: VMFunction::new(binded_func, ty, Some(environment)), + handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), vm_function), } } @@ -376,13 +219,8 @@ impl Function { /// assert_eq!(f.ty().params(), vec![Type::I32, Type::I32]); /// assert_eq!(f.ty().results(), vec![Type::I32]); /// ``` - pub fn ty(&self) -> &FunctionType { - &self.exported.ty - } - - /// Returns the [`Store`] where the `Function` belongs. - pub fn store(&self) -> &Store { - &self.store + pub fn ty<'context>(&self, ctx: &'context impl AsContextRef) -> &'context FunctionType { + &self.handle.get(ctx.as_context_ref().objects()).ty } /// Returns the number of parameters that this function takes. @@ -401,8 +239,8 @@ impl Function { /// /// assert_eq!(f.param_arity(), 2); /// ``` - pub fn param_arity(&self) -> usize { - self.ty().params().len() + pub fn param_arity(&self, ctx: &impl AsContextRef) -> usize { + self.ty(ctx).params().len() } /// Returns the number of results this function produces. @@ -421,8 +259,8 @@ impl Function { /// /// assert_eq!(f.result_arity(), 1); /// ``` - pub fn result_arity(&self) -> usize { - self.ty().results().len() + pub fn result_arity(&self, ctx: &impl AsContextRef) -> usize { + self.ty(ctx).results().len() } /// Call the `Function` function. @@ -454,16 +292,27 @@ impl Function { /// /// assert_eq!(sum.call(&[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]); /// ``` - pub fn call(&self, params: &[Val]) -> Result, RuntimeError> { + pub fn call( + &self, + ctx: &mut impl AsContextMut, + params: &[Value], + ) -> Result, RuntimeError> { let arr = js_sys::Array::new_with_length(params.len() as u32); + + // let raw_ctx = ctx.as_context_mut().as_raw() as *mut u8; + // let mut ctx = unsafe { ContextMut::from_raw(raw_ctx as *mut ContextInner<()>) }; + for (i, param) in params.iter().enumerate() { - let js_value = param.as_jsvalue(); + let js_value = param.as_jsvalue(&ctx.as_context_ref()); arr.set(i as u32, js_value); } - let result = - js_sys::Reflect::apply(&self.exported.function, &wasm_bindgen::JsValue::NULL, &arr)?; + let result = js_sys::Reflect::apply( + &self.handle.get(ctx.as_context_ref().objects()).function, + &wasm_bindgen::JsValue::NULL, + &arr, + )?; - let result_types = self.exported.ty.results(); + let result_types = self.handle.get(ctx.as_context_ref().objects()).ty.results(); match result_types.len() { 0 => Ok(Box::new([])), 1 => { @@ -482,10 +331,20 @@ impl Function { } } - pub(crate) fn from_vm_export(store: &Store, wasmer_export: VMFunction) -> Self { + pub(crate) fn from_vm_export(ctx: &mut impl AsContextMut, vm_function: VMFunction) -> Self { Self { - store: store.clone(), - exported: wasmer_export, + handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), vm_function), + } + } + + pub(crate) fn from_vm_extern( + ctx: &mut impl AsContextMut, + internal: InternalContextHandle, + ) -> Self { + Self { + handle: unsafe { + ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal) + }, } } @@ -564,14 +423,19 @@ impl Function { /// // This results in an error: `RuntimeError` /// let sum_native = sum.native::<(i32, i32), i64>().unwrap(); /// ``` - pub fn native(&self) -> Result, RuntimeError> + pub fn native( + &self, + ctx: &impl AsContextRef, + ) -> Result, RuntimeError> where Args: WasmTypeList, Rets: WasmTypeList, { + let vm_function = self.handle.get(ctx.as_context_ref().objects()); + // type check { - let expected = self.exported.ty.params(); + let expected = vm_function.ty.params(); let given = Args::wasm_types(); if expected != given { @@ -584,7 +448,7 @@ impl Function { } { - let expected = self.exported.ty.results(); + let expected = vm_function.ty.results(); let given = Rets::wasm_types(); if expected != given { @@ -597,23 +461,21 @@ impl Function { } } - Ok(TypedFunction::new( - self.store.clone(), - self.exported.clone(), - )) + Ok(TypedFunction::from_handle(self.clone())) } #[track_caller] fn closures_unsupported_panic() -> ! { unimplemented!("Closures (functions with captured environments) are currently unsupported with native functions. See: https://github.com/wasmerio/wasmer/issues/1840") } -} -impl<'a> Exportable<'a> for Function { - fn to_export(&self) -> Export { - Export::Function(self.exported.clone()) + /// Checks whether this `Function` can be used with the given context. + pub fn is_from_context(&self, ctx: &impl AsContextRef) -> bool { + self.handle.context_id() == ctx.as_context_ref().objects().id() } +} +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), @@ -624,10 +486,7 @@ impl<'a> Exportable<'a> for Function { impl fmt::Debug for Function { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter - .debug_struct("Function") - .field("ty", &self.ty()) - .finish() + formatter.debug_struct("Function").finish() } } @@ -636,6 +495,8 @@ impl fmt::Debug for Function { mod inner { use super::RuntimeError; use super::VMFunctionBody; + use crate::js::context::{AsContextMut, ContextInner, ContextMut}; + use crate::js::NativeWasmTypeInto; use std::array::TryFromSliceError; use std::convert::{Infallible, TryInto}; use std::error::Error; @@ -657,7 +518,7 @@ mod inner { Self: Sized, { /// Native Wasm type. - type Native: NativeWasmType; + type Native: NativeWasmTypeInto; /// Convert a value of kind `Self::Native` to `Self`. /// @@ -736,7 +597,7 @@ mod inner { #[cfg(test)] mod test_from_to_native_wasm_type { - use super::*; + use super::FromToNativeWasmType; #[test] fn test_to_native() { @@ -772,13 +633,15 @@ mod inner { /// The array type that can hold all the represented values. /// /// Note that all values are stored in their binary form. - type Array: AsMut<[i128]>; + type Array: AsMut<[f64]>; /// The size of the array fn size() -> u32; /// Constructs `Self` based on an array of values. - fn from_array(array: Self::Array) -> Self; + /// + /// # Safety + unsafe fn from_array(ctx: &mut impl AsContextMut, array: Self::Array) -> Self; /// Constructs `Self` based on a slice of values. /// @@ -786,11 +649,18 @@ mod inner { /// that the slice doesn't have the same size than /// `Self::Array`, in which circumstance an error of kind /// `TryFromSliceError` will be returned. - fn from_slice(slice: &[i128]) -> Result; + /// + /// # Safety + unsafe fn from_slice( + ctx: &mut impl AsContextMut, + slice: &[f64], + ) -> Result; /// Builds and returns an array of type `Array` from a tuple /// (list) of values. - fn into_array(self) -> Self::Array; + /// + /// # Safety + unsafe fn into_array(self, ctx: &mut impl AsContextMut) -> Self::Array; /// Allocates and return an empty array of type `Array` that /// will hold a tuple (list) of values, usually to hold the @@ -799,11 +669,20 @@ mod inner { /// Builds a tuple (list) of values from a C struct of type /// `CStruct`. - fn from_c_struct(c_struct: Self::CStruct) -> Self; + /// + /// # Safety + unsafe fn from_c_struct(ctx: &mut impl AsContextMut, c_struct: Self::CStruct) -> Self; /// Builds and returns a C struct of type `CStruct` from a /// tuple (list) of values. - fn into_c_struct(self) -> Self::CStruct; + /// + /// # Safety + unsafe fn into_c_struct(self, ctx: &mut impl AsContextMut) -> 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. @@ -899,11 +778,10 @@ mod inner { /// 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 + pub trait HostFunction where Args: WasmTypeList, Rets: WasmTypeList, - Kind: HostFunctionKind, T: Sized, Self: Sized, { @@ -911,28 +789,6 @@ mod inner { fn function_body_ptr(self) -> *const VMFunctionBody; } - /// 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 {} - - /// 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. - #[derive(Clone)] - pub struct WithoutEnv; - - impl HostFunctionKind for WithoutEnv {} - /// Represents a low-level Wasm static host function. See /// `super::Function::new` and `super::Function::new_env` to learn /// more. @@ -950,11 +806,11 @@ mod inner { Rets: WasmTypeList, { /// Creates a new `Function`. - pub fn new(function: F) -> Self + #[allow(dead_code)] + pub fn new(function: F) -> Self where - F: HostFunction, - T: HostFunctionKind, - E: Sized, + F: HostFunction, + T: Sized, { Self { address: function.function_body_ptr(), @@ -963,11 +819,13 @@ mod inner { } /// 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 } @@ -981,7 +839,7 @@ mod inner { /// 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 ),* ) + pub struct $c_struct_name< $( $x ),* > ( $( <<$x as FromToNativeWasmType>::Native as NativeWasmType>::Abi ),* ) where $( $x: FromToNativeWasmType ),*; @@ -996,13 +854,16 @@ mod inner { { type CStruct = $c_struct_name< $( $x ),* >; - type Array = [i128; count_idents!( $( $x ),* )]; + type Array = [f64; count_idents!( $( $x ),* )]; fn size() -> u32 { count_idents!( $( $x ),* ) as _ } - fn from_array(array: Self::Array) -> Self { + #[allow(unused_mut)] + #[allow(clippy::unused_unit)] + #[allow(clippy::missing_safety_doc)] + unsafe fn from_array(mut _ctx: &mut impl AsContextMut, array: Self::Array) -> Self { // Unpack items of the array. #[allow(non_snake_case)] let [ $( $x ),* ] = array; @@ -1010,16 +871,19 @@ mod inner { // Build the tuple. ( $( - FromToNativeWasmType::from_native(NativeWasmType::from_binary($x)) + FromToNativeWasmType::from_native(NativeWasmTypeInto::from_raw(_ctx, $x)) ),* ) } - fn from_slice(slice: &[i128]) -> Result { - Ok(Self::from_array(slice.try_into()?)) + #[allow(clippy::missing_safety_doc)] + unsafe fn from_slice(ctx: &mut impl AsContextMut, slice: &[f64]) -> Result { + Ok(Self::from_array(ctx, slice.try_into()?)) } - fn into_array(self) -> Self::Array { + #[allow(unused_mut)] + #[allow(clippy::missing_safety_doc)] + unsafe fn into_array(self, mut _ctx: &mut impl AsContextMut) -> Self::Array { // Unpack items of the tuple. #[allow(non_snake_case)] let ( $( $x ),* ) = self; @@ -1027,41 +891,57 @@ mod inner { // Build the array. [ $( - FromToNativeWasmType::to_native($x).to_binary() + FromToNativeWasmType::to_native($x).into_raw(_ctx) ),* ] } fn empty_array() -> Self::Array { // Build an array initialized with `0`. - [0; count_idents!( $( $x ),* )] + [0_f64; count_idents!( $( $x ),* )] } - fn from_c_struct(c_struct: Self::CStruct) -> Self { + #[allow(unused_mut)] + #[allow(clippy::unused_unit)] + #[allow(clippy::missing_safety_doc)] + unsafe fn from_c_struct(mut _ctx: &mut impl AsContextMut, 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($x) + FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(_ctx, $x)) ),* ) } - #[allow(unused_parens, non_snake_case)] - fn into_c_struct(self) -> Self::CStruct { + #[allow(unused_parens, non_snake_case, unused_mut)] + #[allow(clippy::missing_safety_doc)] + unsafe fn into_c_struct(self, mut _ctx: &mut impl AsContextMut) -> Self::CStruct { // Unpack items of the tuple. let ( $( $x ),* ) = self; // Build the C structure. $c_struct_name( $( - FromToNativeWasmType::to_native($x) + FromToNativeWasmType::to_native($x).into_abi(_ctx) ),* ) } + #[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] { &[ $( @@ -1072,90 +952,46 @@ mod inner { } // Implement `HostFunction` for a function that has the same arity than the tuple. - // This specific function has no environment. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, Func > - HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()> - for - Func - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static + Send, - { - #[allow(non_snake_case)] - fn function_body_ptr(self) -> *const VMFunctionBody { - /// This is a function that wraps the real host - /// function. Its address will be used inside the - /// runtime. - extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( _: usize, $( $x: $x::Native, )* ) -> Rets::CStruct - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn( $( $x ),* ) -> RetsAsResult + 'static - { - let func: &Func = unsafe { &*(&() as *const () as *const Func) }; - let result = panic::catch_unwind(AssertUnwindSafe(|| { - func( $( FromToNativeWasmType::from_native($x) ),* ).into_result() - })); - match result { - Ok(Ok(result)) => return result.into_c_struct(), - Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)), - _ => unimplemented!(), - // Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) }, - // Err(panic) => unsafe { resume_panic(panic) }, - } - } - - func_wrapper::< $( $x, )* Rets, RetsAsResult, Self > as *const VMFunctionBody - } - } - - // Implement `HostFunction` for a function that has the same arity than the tuple. - // This specific function has an environment. #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, Env, Func > - HostFunction<( $( $x ),* ), Rets, WithEnv, Env> + impl< $( $x, )* Rets, RetsAsResult, T, Func > + HostFunction for Func where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, - Env: Sized, - Func: Fn(&Env, $( $x , )*) -> RetsAsResult + Send + 'static, + Func: Fn(ContextMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, { #[allow(non_snake_case)] fn function_body_ptr(self) -> *const VMFunctionBody { /// This is a function that wraps the real host /// function. Its address will be used inside the /// runtime. - extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Env, Func>( ptr: usize, $( $x: $x::Native, )* ) -> Rets::CStruct + unsafe extern "C" fn func_wrapper( ctx_ptr: usize, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, - Env: Sized, - Func: Fn(&Env, $( $x ),* ) -> RetsAsResult + 'static + Func: Fn(ContextMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, { - let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; - let func: &Func = unsafe { &*(&() as *const () as *const Func) }; + let func: &Func = &*(&() as *const () as *const Func); + let mut ctx = ContextMut::from_raw(ctx_ptr as *mut ContextInner); + let mut ctx2 = ContextMut::from_raw(ctx_ptr as *mut ContextInner); let result = panic::catch_unwind(AssertUnwindSafe(|| { - func(env, $( FromToNativeWasmType::from_native($x) ),* ).into_result() + func(ctx2.as_context_mut(), $( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut ctx, $x)) ),* ).into_result() })); + match result { - Ok(Ok(result)) => return result.into_c_struct(), + Ok(Ok(result)) => return result.into_c_struct(&mut ctx), + #[allow(deprecated)] Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)), - _ => unimplemented!(), - // Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) }, - // Err(panic) => unsafe { resume_panic(panic) }, + Err(_panic) => unimplemented!(), } } - func_wrapper::< $( $x, )* Rets, RetsAsResult, Env, Self > as *const VMFunctionBody + func_wrapper::< T, $( $x, )* Rets, RetsAsResult, Self > as *const VMFunctionBody } } }; @@ -1209,171 +1045,170 @@ mod inner { // fail (with `Result<_, Infallible>`). impl WasmTypeList for Infallible { type CStruct = Self; - type Array = [i128; 0]; + type Array = [f64; 0]; fn size() -> u32 { 0 } - fn from_array(_: Self::Array) -> Self { + unsafe fn from_array(_: &mut impl AsContextMut, _: Self::Array) -> Self { unreachable!() } - fn from_slice(_: &[i128]) -> Result { + unsafe fn from_slice( + _: &mut impl AsContextMut, + _: &[f64], + ) -> Result { unreachable!() } - fn into_array(self) -> Self::Array { + unsafe fn into_array(self, _: &mut impl AsContextMut) -> Self::Array { [] } fn empty_array() -> Self::Array { - unreachable!() + [] } - fn from_c_struct(_: Self::CStruct) -> Self { - unreachable!() + unsafe fn from_c_struct(_: &mut impl AsContextMut, self_: Self::CStruct) -> Self { + self_ } - fn into_c_struct(self) -> Self::CStruct { - unreachable!() + unsafe fn into_c_struct(self, _: &mut impl AsContextMut) -> 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; + /* + #[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) + ); + } - #[test] - 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()] + ); + } - #[test] - fn test_into_array() { - assert_eq!(().into_array(), [0i128; 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); + } - #[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); - } + 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) + ); + } - #[test] - 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]); + } - #[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]); - } + 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]); - #[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] - ); + 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; + #[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 func() {} + fn func__i32() -> i32 { + 0 + } + fn func_i32(_a: i32) {} + fn func_i32__i32(a: i32) -> i32 { + a * 2 + } + fn func_i32_i32__i32(a: i32, b: i32) -> i32 { + a + b + } + fn func_i32_i32__i32_i32(a: i32, b: i32) -> (i32, i32) { + (a, b) + } + fn func_f32_i32__i32_f32(a: f32, b: i32) -> (i32, f32) { + (b, a) + } - #[test] - fn test_function_types() { - assert_eq!(Function::new(func).ty(), FunctionType::new(vec![], vec![])); - assert_eq!( - Function::new(func__i32).ty(), - FunctionType::new(vec![], vec![Type::I32]) - ); - assert_eq!( - Function::new(func_i32).ty(), - FunctionType::new(vec![Type::I32], vec![]) - ); - assert_eq!( - Function::new(func_i32__i32).ty(), - FunctionType::new(vec![Type::I32], vec![Type::I32]) - ); - assert_eq!( - Function::new(func_i32_i32__i32).ty(), - FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]) - ); - assert_eq!( - Function::new(func_i32_i32__i32_i32).ty(), - FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]) - ); - assert_eq!( - Function::new(func_f32_i32__i32_f32).ty(), - FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32]) - ); - } + fn test_function_types() { + assert_eq!(Function::new(func).ty(), FunctionType::new(vec![], vec![])); + assert_eq!( + Function::new(func__i32).ty(), + FunctionType::new(vec![], vec![Type::I32]) + ); + assert_eq!( + Function::new(func_i32).ty(), + FunctionType::new(vec![Type::I32], vec![]) + ); + assert_eq!( + Function::new(func_i32__i32).ty(), + FunctionType::new(vec![Type::I32], vec![Type::I32]) + ); + assert_eq!( + Function::new(func_i32_i32__i32).ty(), + FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]) + ); + assert_eq!( + Function::new(func_i32_i32__i32_i32).ty(), + FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]) + ); + assert_eq!( + Function::new(func_f32_i32__i32_f32).ty(), + FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32]) + ); + } - #[test] - fn test_function_pointer() { - let f = Function::new(func_i32__i32); - let function = unsafe { std::mem::transmute::<_, fn(usize, i32) -> i32>(f.address) }; - assert_eq!(function(0, 3), 6); + 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); + } } - } + */ } diff --git a/lib/api/src/js/externals/global.rs b/lib/api/src/js/externals/global.rs index 431547129a2..c570c7b27d9 100644 --- a/lib/api/src/js/externals/global.rs +++ b/lib/api/src/js/externals/global.rs @@ -1,9 +1,8 @@ -use crate::js::export::Export; +use crate::js::context::{AsContextMut, AsContextRef, ContextHandle, InternalContextHandle}; use crate::js::export::VMGlobal; use crate::js::exports::{ExportError, Exportable}; use crate::js::externals::Extern; -use crate::js::store::Store; -use crate::js::types::{Val, ValType}; +use crate::js::value::Value; use crate::js::wasm_bindgen_polyfill::Global as JSGlobal; use crate::js::GlobalType; use crate::js::Mutability; @@ -18,12 +17,11 @@ use wasm_bindgen::JsValue; /// Spec: #[derive(Debug, Clone, PartialEq)] pub struct Global { - store: Store, - vm_global: VMGlobal, + pub(crate) handle: ContextHandle, } impl Global { - /// Create a new `Global` with the initial value [`Val`]. + /// Create a new `Global` with the initial value [`Value`]. /// /// # Example /// @@ -36,11 +34,11 @@ impl Global { /// assert_eq!(g.get(), Value::I32(1)); /// assert_eq!(g.ty().mutability, Mutability::Const); /// ``` - pub fn new(store: &Store, val: Val) -> Self { - Self::from_value(store, val, Mutability::Const).unwrap() + pub fn new(ctx: &mut impl AsContextMut, val: Value) -> Self { + Self::from_value(ctx, val, Mutability::Const).unwrap() } - /// Create a mutable `Global` with the initial value [`Val`]. + /// Create a mutable `Global` with the initial value [`Value`]. /// /// # Example /// @@ -53,22 +51,31 @@ impl Global { /// assert_eq!(g.get(), Value::I32(1)); /// assert_eq!(g.ty().mutability, Mutability::Var); /// ``` - pub fn new_mut(store: &Store, val: Val) -> Self { - Self::from_value(store, val, Mutability::Var).unwrap() + pub fn new_mut(ctx: &mut impl AsContextMut, val: Value) -> Self { + Self::from_value(ctx, val, Mutability::Var).unwrap() } - /// Create a `Global` with the initial value [`Val`] and the provided [`Mutability`]. - fn from_value(store: &Store, val: Val, mutability: Mutability) -> Result { + /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. + fn from_value( + ctx: &mut impl AsContextMut, + val: Value, + mutability: Mutability, + ) -> Result { + if !val.is_from_context(ctx) { + return Err(RuntimeError::new( + "cross-`Context` values are not supported", + )); + } let global_ty = GlobalType { mutability, ty: val.ty(), }; let descriptor = js_sys::Object::new(); let (type_str, value) = match val { - Val::I32(i) => ("i32", JsValue::from_f64(i as _)), - Val::I64(i) => ("i64", JsValue::from_f64(i as _)), - Val::F32(f) => ("f32", JsValue::from_f64(f as _)), - Val::F64(f) => ("f64", JsValue::from_f64(f)), + Value::I32(i) => ("i32", JsValue::from_f64(i as _)), + Value::I64(i) => ("i64", JsValue::from_f64(i as _)), + Value::F32(f) => ("f32", JsValue::from_f64(f as _)), + Value::F64(f) => ("f64", JsValue::from_f64(f)), _ => unimplemented!("The type is not yet supported in the JS Global API"), }; // This is the value type as string, even though is incorrectly called "value" @@ -81,12 +88,9 @@ impl Global { )?; let js_global = JSGlobal::new(&descriptor, &value).unwrap(); - let global = VMGlobal::new(js_global, global_ty); + let vm_global = VMGlobal::new(js_global, global_ty); - Ok(Self { - store: store.clone(), - vm_global: global, - }) + Ok(Self::from_vm_export(ctx, vm_global)) } /// Returns the [`GlobalType`] of the `Global`. @@ -103,27 +107,11 @@ impl Global { /// assert_eq!(c.ty(), &GlobalType::new(Type::I32, Mutability::Const)); /// assert_eq!(v.ty(), &GlobalType::new(Type::I64, Mutability::Var)); /// ``` - pub fn ty(&self) -> &GlobalType { - &self.vm_global.ty - } - - /// Returns the [`Store`] where the `Global` belongs. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Store, Value}; - /// # let store = Store::default(); - /// # - /// let g = Global::new(&store, Value::I32(1)); - /// - /// assert_eq!(g.store(), &store); - /// ``` - pub fn store(&self) -> &Store { - &self.store + pub fn ty(&self, ctx: &impl AsContextRef) -> GlobalType { + self.handle.get(ctx.as_context_ref().objects()).ty } - /// Retrieves the current value [`Val`] that the Global has. + /// Retrieves the current value [`Value`] that the Global has. /// /// # Example /// @@ -135,17 +123,30 @@ impl Global { /// /// assert_eq!(g.get(), Value::I32(1)); /// ``` - pub fn get(&self) -> Val { + pub fn get(&self, ctx: &impl AsContextRef) -> Value { + unsafe { + let raw = self + .handle + .get(ctx.as_context_ref().objects()) + .global + .value() + .as_f64() + .unwrap(); + let ty = self.handle.get(ctx.as_context_ref().objects()).ty; + Value::from_raw(ctx, ty.ty, raw) + } + /* match self.vm_global.ty.ty { - ValType::I32 => Val::I32(self.vm_global.global.value().as_f64().unwrap() as _), - ValType::I64 => Val::I64(self.vm_global.global.value().as_f64().unwrap() as _), - ValType::F32 => Val::F32(self.vm_global.global.value().as_f64().unwrap() as _), - ValType::F64 => Val::F64(self.vm_global.global.value().as_f64().unwrap()), + ValType::I32 => Value::I32(self.vm_global.global.value().as_f64().unwrap() as _), + ValType::I64 => Value::I64(self.vm_global.global.value().as_f64().unwrap() as _), + ValType::F32 => Value::F32(self.vm_global.global.value().as_f64().unwrap() as _), + ValType::F64 => Value::F64(self.vm_global.global.value().as_f64().unwrap()), _ => unimplemented!("The type is not yet supported in the JS Global API"), } + */ } - /// Sets a custom value [`Val`] to the runtime Global. + /// Sets a custom value [`Value`] to the runtime Global. /// /// # Example /// @@ -186,53 +187,61 @@ impl Global { /// // This results in an error: `RuntimeError`. /// g.set(Value::I64(2)).unwrap(); /// ``` - pub fn set(&self, val: Val) -> Result<(), RuntimeError> { - if self.vm_global.ty.mutability == Mutability::Const { + pub fn set(&self, ctx: &mut impl AsContextMut, val: Value) -> Result<(), RuntimeError> { + if !val.is_from_context(ctx) { + return Err(RuntimeError::new( + "cross-`Context` values are not supported", + )); + } + let global_ty = self.ty(&ctx); + if global_ty.mutability == Mutability::Const { return Err(RuntimeError::new("The global is immutable".to_owned())); } - if val.ty() != self.vm_global.ty.ty { + if val.ty() != global_ty.ty { return Err(RuntimeError::new("The types don't match".to_owned())); } let new_value = match val { - Val::I32(i) => JsValue::from_f64(i as _), - Val::I64(i) => JsValue::from_f64(i as _), - Val::F32(f) => JsValue::from_f64(f as _), - Val::F64(f) => JsValue::from_f64(f), - _ => unimplemented!("The type is not yet supported in the JS Global API"), + Value::I32(i) => JsValue::from_f64(i as _), + Value::I64(i) => JsValue::from_f64(i as _), + Value::F32(f) => JsValue::from_f64(f as _), + Value::F64(f) => JsValue::from_f64(f), + _ => { + return Err(RuntimeError::new( + "The type is not yet supported in the JS Global API".to_owned(), + )) + } }; - self.vm_global.global.set_value(&new_value); + self.handle + .get_mut(ctx.as_context_mut().objects_mut()) + .global + .set_value(&new_value); Ok(()) } - pub(crate) fn from_vm_export(store: &Store, vm_global: VMGlobal) -> Self { + pub(crate) fn from_vm_export(ctx: &mut impl AsContextMut, vm_global: VMGlobal) -> Self { Self { - store: store.clone(), - vm_global, + handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), vm_global), } } - /// Returns whether or not these two globals refer to the same data. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Global, Store, Value}; - /// # let store = Store::default(); - /// # - /// let g = Global::new(&store, Value::I32(1)); - /// - /// assert!(g.same(&g)); - /// ``` - pub fn same(&self, other: &Self) -> bool { - self.vm_global == other.vm_global + pub(crate) fn from_vm_extern( + ctx: &mut impl AsContextMut, + internal: InternalContextHandle, + ) -> Self { + Self { + handle: unsafe { + ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal) + }, + } } -} -impl<'a> Exportable<'a> for Global { - fn to_export(&self) -> Export { - Export::Global(self.vm_global.clone()) + /// Checks whether this `Global` can be used with the given context. + pub fn is_from_context(&self, ctx: &impl AsContextRef) -> bool { + self.handle.context_id() == ctx.as_context_ref().objects().id() } +} +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), diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index 2de3d044db4..d100208c715 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -1,9 +1,12 @@ -use crate::js::export::{Export, VMMemory}; +use crate::js::context::{ + AsContextMut, AsContextRef, ContextHandle, ContextObjects, InternalContextHandle, +}; +use crate::js::export::VMMemory; use crate::js::exports::{ExportError, Exportable}; use crate::js::externals::Extern; -use crate::js::store::Store; use crate::js::{MemoryAccessError, MemoryType}; use std::convert::TryInto; +use std::marker::PhantomData; use std::mem::MaybeUninit; use std::slice; use thiserror::Error; @@ -77,8 +80,8 @@ extern "C" { /// Spec: #[derive(Debug, Clone)] pub struct Memory { - store: Store, - vm_memory: VMMemory, + pub(crate) handle: ContextHandle, + #[allow(dead_code)] view: js_sys::Uint8Array, } @@ -99,7 +102,7 @@ impl Memory { /// # /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); /// ``` - pub fn new(store: &Store, ty: MemoryType) -> Result { + pub fn new(ctx: &mut impl AsContextMut, ty: MemoryType) -> Result { let descriptor = js_sys::Object::new(); js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap(); if let Some(max) = ty.maximum { @@ -110,13 +113,8 @@ impl Memory { let js_memory = js_sys::WebAssembly::Memory::new(&descriptor) .map_err(|_e| MemoryError::Generic("Error while creating the memory".to_owned()))?; - let memory = VMMemory::new(js_memory, ty); - let view = js_sys::Uint8Array::new(&memory.memory.buffer()); - Ok(Self { - store: store.clone(), - vm_memory: memory, - view, - }) + let vm_memory = VMMemory::new(js_memory, ty); + Ok(Self::from_vm_export(ctx, vm_memory)) } /// Returns the [`MemoryType`] of the `Memory`. @@ -132,26 +130,8 @@ impl Memory { /// /// assert_eq!(m.ty(), mt); /// ``` - pub fn ty(&self) -> MemoryType { - let mut ty = self.vm_memory.ty.clone(); - ty.minimum = self.size(); - ty - } - - /// Returns the [`Store`] where the `Memory` belongs. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; - /// # let store = Store::default(); - /// # - /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); - /// - /// assert_eq!(m.store(), &store); - /// ``` - pub fn store(&self) -> &Store { - &self.store + pub fn ty(&self, ctx: &impl AsContextRef) -> MemoryType { + self.handle.get(ctx.as_context_ref().objects()).ty } /// Returns the pointer to the raw bytes of the `Memory`. @@ -161,11 +141,18 @@ impl Memory { } /// Returns the size (in bytes) of the `Memory`. - pub fn data_size(&self) -> u64 { - js_sys::Reflect::get(&self.vm_memory.memory.buffer(), &"byteLength".into()) - .unwrap() - .as_f64() - .unwrap() as _ + pub fn data_size(&self, ctx: &impl AsContextRef) -> u64 { + js_sys::Reflect::get( + &self + .handle + .get(ctx.as_context_ref().objects()) + .memory + .buffer(), + &"byteLength".into(), + ) + .unwrap() + .as_f64() + .unwrap() as _ } /// Returns the size (in [`Pages`]) of the `Memory`. @@ -180,11 +167,18 @@ impl Memory { /// /// assert_eq!(m.size(), Pages(1)); /// ``` - pub fn size(&self) -> Pages { - let bytes = js_sys::Reflect::get(&self.vm_memory.memory.buffer(), &"byteLength".into()) - .unwrap() - .as_f64() - .unwrap() as u64; + pub fn size(&self, ctx: &impl AsContextRef) -> Pages { + let bytes = js_sys::Reflect::get( + &self + .handle + .get(ctx.as_context_ref().objects()) + .memory + .buffer(), + &"byteLength".into(), + ) + .unwrap() + .as_f64() + .unwrap() as u64; Bytes(bytes as usize).try_into().unwrap() } @@ -218,16 +212,22 @@ impl Memory { /// // This results in an error: `MemoryError::CouldNotGrow`. /// let s = m.grow(1).unwrap(); /// ``` - pub fn grow(&self, delta: IntoPages) -> Result + pub fn grow( + &self, + ctx: &mut impl AsContextMut, + delta: IntoPages, + ) -> Result where IntoPages: Into, { let pages = delta.into(); - let js_memory = self.vm_memory.memory.clone().unchecked_into::(); - let new_pages = js_memory.grow(pages.0).map_err(|err| { + let mut ctx_mut = ctx.as_context_mut(); + let js_memory = &self.handle.get_mut(ctx_mut.objects_mut()).memory; + let our_js_memory: &JSMemory = JsCast::unchecked_from_js_ref(js_memory); + let new_pages = our_js_memory.grow(pages.0).map_err(|err| { if err.is_instance_of::() { MemoryError::CouldNotGrow { - current: self.size(), + current: self.size(&ctx.as_context_ref()), attempted_delta: pages, } } else { @@ -239,33 +239,43 @@ impl Memory { /// Used by tests #[doc(hidden)] - pub fn uint8view(&self) -> js_sys::Uint8Array { - js_sys::Uint8Array::new(&self.vm_memory.memory.buffer()) + pub fn uint8view(&self, ctx: &impl AsContextRef) -> js_sys::Uint8Array { + js_sys::Uint8Array::new( + &self + .handle + .get(ctx.as_context_ref().objects()) + .memory + .buffer(), + ) } - pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self { + pub(crate) fn buffer<'a>(&'a self, _ctx: &'a impl AsContextRef) -> MemoryBuffer<'a> { + MemoryBuffer { + base: &self.view as *const _ as *mut _, + marker: PhantomData, + } + } + + pub(crate) fn from_vm_export(ctx: &mut impl AsContextMut, vm_memory: VMMemory) -> Self { let view = js_sys::Uint8Array::new(&vm_memory.memory.buffer()); Self { - store: store.clone(), - vm_memory, + handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), vm_memory), view, } } - /// Returns whether or not these two memories refer to the same data. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Store, Value}; - /// # let store = Store::default(); - /// # - /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); - /// - /// assert!(m.same(&m)); - /// ``` - pub fn same(&self, other: &Self) -> bool { - self.vm_memory == other.vm_memory + pub(crate) fn from_vm_extern( + ctx: &mut impl AsContextMut, + internal: InternalContextHandle, + ) -> Self { + let view = + js_sys::Uint8Array::new(&internal.get(ctx.as_context_ref().objects()).memory.buffer()); + Self { + handle: unsafe { + ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal) + }, + view, + } } /// Safely reads bytes from the memory at the given offset. @@ -275,10 +285,15 @@ impl Memory { /// /// 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> { - let view = self.uint8view(); + pub fn read( + &self, + _ctx: &impl AsContextRef, + offset: u64, + data: &mut [u8], + ) -> Result<(), MemoryAccessError> { + let view = &self.view; let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; - let len: u32 = buf + let len: u32 = data .len() .try_into() .map_err(|_| MemoryAccessError::Overflow)?; @@ -286,7 +301,7 @@ impl Memory { if end > view.length() { Err(MemoryAccessError::HeapOutOfBounds)?; } - view.subarray(offset, end).copy_to(buf); + view.subarray(offset, end).copy_to(data); Ok(()) } @@ -302,10 +317,11 @@ impl Memory { /// concurrent writes. pub fn read_uninit<'a>( &self, + _ctx: &impl AsContextRef, offset: u64, buf: &'a mut [MaybeUninit], ) -> Result<&'a mut [u8], MemoryAccessError> { - let view = self.uint8view(); + let view = &self.view; let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; let len: u32 = buf .len() @@ -334,14 +350,18 @@ impl Memory { /// /// 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> { - let view = self.uint8view(); + pub fn write( + &self, + _ctx: &mut impl AsContextMut, + offset: u64, + data: &[u8], + ) -> Result<(), MemoryAccessError> { let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; let len: u32 = data .len() .try_into() .map_err(|_| MemoryAccessError::Overflow)?; - let view = self.uint8view(); + let view = &self.view; let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?; if end > view.length() { Err(MemoryAccessError::HeapOutOfBounds)?; @@ -349,13 +369,14 @@ impl Memory { view.subarray(offset, end).copy_from(data); Ok(()) } -} -impl<'a> Exportable<'a> for Memory { - fn to_export(&self) -> Export { - Export::Memory(self.vm_memory.clone()) + /// Checks whether this `Global` can be used with the given context. + pub fn is_from_context(&self, ctx: &impl AsContextRef) -> bool { + self.handle.context_id() == ctx.as_context_ref().objects().id() } +} +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), @@ -363,3 +384,57 @@ impl<'a> Exportable<'a> for Memory { } } } + +/// Underlying buffer for a memory. +#[derive(Copy, Clone)] +pub(crate) struct MemoryBuffer<'a> { + base: *mut js_sys::Uint8Array, + marker: PhantomData<(&'a Memory, &'a ContextObjects)>, +} + +impl<'a> MemoryBuffer<'a> { + pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + let end = offset + .checked_add(buf.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let view = unsafe { &*(self.base) }; + if end > view.length().into() { + return Err(MemoryAccessError::HeapOutOfBounds); + } + view.subarray(offset as _, end as _) + .copy_to(unsafe { &mut slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) }); + Ok(()) + } + + pub(crate) fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + let end = offset + .checked_add(buf.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let view = unsafe { &*(self.base) }; + if end > view.length().into() { + return Err(MemoryAccessError::HeapOutOfBounds); + } + let buf_ptr = buf.as_mut_ptr() as *mut u8; + view.subarray(offset as _, end as _) + .copy_to(unsafe { &mut slice::from_raw_parts_mut(buf_ptr, buf.len()) }); + + Ok(unsafe { slice::from_raw_parts_mut(buf_ptr, buf.len()) }) + } + + pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + let end = offset + .checked_add(data.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let view = unsafe { &mut *(self.base) }; + if end > view.length().into() { + return Err(MemoryAccessError::HeapOutOfBounds); + } + view.subarray(offset as _, end as _).copy_from(data); + + Ok(()) + } +} diff --git a/lib/api/src/js/externals/mod.rs b/lib/api/src/js/externals/mod.rs index 33a5bc25d2a..03c76ada645 100644 --- a/lib/api/src/js/externals/mod.rs +++ b/lib/api/src/js/externals/mod.rs @@ -1,19 +1,18 @@ pub(crate) mod function; mod global; -mod memory; +pub(crate) mod memory; mod table; -pub use self::function::{ - FromToNativeWasmType, Function, HostFunction, WasmTypeList, WithEnv, WithoutEnv, -}; - +pub use self::function::{FromToNativeWasmType, Function, HostFunction, WasmTypeList}; pub use self::global::Global; pub use self::memory::{Memory, MemoryError}; pub use self::table::Table; +use crate::js::context::{AsContextMut, AsContextRef}; use crate::js::export::Export; use crate::js::exports::{ExportError, Exportable}; -use crate::js::store::{Store, StoreObject}; +use crate::js::store::StoreObject; +use crate::js::types::AsJs; use crate::js::ExternType; use std::fmt; @@ -35,36 +34,58 @@ pub enum Extern { impl Extern { /// Return the underlying type of the inner `Extern`. - pub fn ty(&self) -> ExternType { + pub fn ty(&self, ctx: &impl AsContextRef) -> ExternType { match self { - Self::Function(ft) => ExternType::Function(ft.ty().clone()), - Self::Memory(ft) => ExternType::Memory(ft.ty()), - Self::Table(tt) => ExternType::Table(*tt.ty()), - Self::Global(gt) => ExternType::Global(*gt.ty()), + Self::Function(ft) => ExternType::Function(ft.ty(ctx).clone()), + Self::Memory(ft) => ExternType::Memory(ft.ty(ctx)), + Self::Table(tt) => ExternType::Table(tt.ty(ctx)), + Self::Global(gt) => ExternType::Global(gt.ty(ctx)), } } /// Create an `Extern` from an `wasmer_compiler::Export`. - pub fn from_vm_export(store: &Store, export: Export) -> Self { + pub fn from_vm_export(ctx: &mut impl AsContextMut, export: Export) -> Self { match export { - Export::Function(f) => Self::Function(Function::from_vm_export(store, f)), - Export::Memory(m) => Self::Memory(Memory::from_vm_export(store, m)), - Export::Global(g) => Self::Global(Global::from_vm_export(store, g)), - Export::Table(t) => Self::Table(Table::from_vm_export(store, t)), + Export::Function(f) => Self::Function(Function::from_vm_extern(ctx, f)), + Export::Memory(m) => Self::Memory(Memory::from_vm_extern(ctx, m)), + Export::Global(g) => Self::Global(Global::from_vm_extern(ctx, g)), + Export::Table(t) => Self::Table(Table::from_vm_extern(ctx, t)), + } + } + + /// Checks whether this `Extern` can be used with the given context. + pub fn is_from_context(&self, ctx: &impl AsContextRef) -> bool { + match self { + Self::Function(val) => val.is_from_context(ctx), + Self::Memory(val) => val.is_from_context(ctx), + Self::Global(val) => val.is_from_context(ctx), + Self::Table(val) => val.is_from_context(ctx), } } -} -impl<'a> Exportable<'a> for Extern { fn to_export(&self) -> Export { match self { - Self::Function(f) => f.to_export(), - Self::Global(g) => g.to_export(), - Self::Memory(m) => m.to_export(), - Self::Table(t) => t.to_export(), + Self::Function(val) => Export::Function(val.handle.internal_handle()), + Self::Memory(val) => Export::Memory(val.handle.internal_handle()), + Self::Global(val) => Export::Global(val.handle.internal_handle()), + Self::Table(val) => Export::Table(val.handle.internal_handle()), } } +} +impl AsJs for Extern { + fn as_jsvalue(&self, ctx: &impl AsContextRef) -> wasm_bindgen::JsValue { + match self { + Self::Function(_) => self.to_export().as_jsvalue(ctx), + Self::Global(_) => self.to_export().as_jsvalue(ctx), + Self::Table(_) => self.to_export().as_jsvalue(ctx), + Self::Memory(_) => self.to_export().as_jsvalue(ctx), + } + .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) diff --git a/lib/api/src/js/externals/table.rs b/lib/api/src/js/externals/table.rs index cc7608fee9b..f779b02d351 100644 --- a/lib/api/src/js/externals/table.rs +++ b/lib/api/src/js/externals/table.rs @@ -1,13 +1,11 @@ -use crate::js::export::VMFunction; -use crate::js::export::{Export, VMTable}; +use crate::js::context::{AsContextMut, AsContextRef, ContextHandle, InternalContextHandle}; +use crate::js::export::{VMFunction, VMTable}; use crate::js::exports::{ExportError, Exportable}; -use crate::js::externals::{Extern, Function as WasmerFunction}; -use crate::js::store::Store; -use crate::js::types::Val; +use crate::js::externals::Extern; +use crate::js::value::Value; use crate::js::RuntimeError; -use crate::js::TableType; +use crate::js::{FunctionType, TableType}; use js_sys::Function; -use wasmer_types::FunctionType; /// A WebAssembly `table` instance. /// @@ -20,17 +18,24 @@ use wasmer_types::FunctionType; /// Spec: #[derive(Debug, Clone, PartialEq)] pub struct Table { - store: Store, - vm_table: VMTable, + pub(crate) handle: ContextHandle, } fn set_table_item(table: &VMTable, item_index: u32, item: &Function) -> Result<(), RuntimeError> { table.table.set(item_index, item).map_err(|e| e.into()) } -fn get_function(val: Val) -> Result { +fn get_function(ctx: &mut impl AsContextMut, val: Value) -> Result { + if !val.is_from_context(ctx) { + return Err(RuntimeError::new("cannot pass Value across contexts")); + } match val { - Val::FuncRef(func) => Ok(func.as_ref().unwrap().exported.function.clone().into()), + Value::FuncRef(Some(ref func)) => Ok(func + .handle + .get(&ctx.as_context_ref().objects()) + .function + .clone() + .into()), // Only funcrefs is supported by the spec atm _ => unimplemented!(), } @@ -43,7 +48,12 @@ impl Table { /// /// This function will construct the `Table` using the store /// [`BaseTunables`][crate::js::tunables::BaseTunables]. - pub fn new(store: &Store, ty: TableType, init: Val) -> Result { + pub fn new( + ctx: &mut impl AsContextMut, + ty: TableType, + init: Value, + ) -> Result { + let mut ctx = ctx.as_context_mut(); let descriptor = js_sys::Object::new(); js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.into())?; if let Some(max) = ty.maximum { @@ -55,47 +65,60 @@ impl Table { let table = VMTable::new(js_table, ty); let num_elements = table.table.length(); - let func = get_function(init)?; + let func = get_function(&mut ctx, init)?; for i in 0..num_elements { set_table_item(&table, i, &func)?; } Ok(Self { - store: store.clone(), - vm_table: table, + handle: ContextHandle::new(ctx.objects_mut(), table), }) } /// Returns the [`TableType`] of the `Table`. - pub fn ty(&self) -> &TableType { - &self.vm_table.ty - } - - /// Returns the [`Store`] where the `Table` belongs. - pub fn store(&self) -> &Store { - &self.store + pub fn ty(&self, ctx: &impl AsContextRef) -> TableType { + self.handle.get(ctx.as_context_ref().objects()).ty } /// Retrieves an element of the table at the provided `index`. - pub fn get(&self, index: u32) -> Option { - let func = self.vm_table.table.get(index).ok()?; - let ty = FunctionType::new(vec![], vec![]); - Some(Val::FuncRef(Some(WasmerFunction::from_vm_export( - &self.store, - VMFunction::new(func, ty, None), - )))) + pub fn get(&self, ctx: &mut impl AsContextMut, index: u32) -> Option { + if let Some(func) = self + .handle + .get(ctx.as_context_ref().objects()) + .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_export(ctx, vm_function); + Some(Value::FuncRef(Some(function))) + } else { + None + } } /// Sets an element `val` in the Table at the provided `index`. - pub fn set(&self, index: u32, val: Val) -> Result<(), RuntimeError> { - let func = get_function(val)?; - set_table_item(&self.vm_table, index, &func)?; - Ok(()) + pub fn set( + &self, + ctx: &mut impl AsContextMut, + index: u32, + val: Value, + ) -> Result<(), RuntimeError> { + let item = get_function(ctx, val)?; + set_table_item( + self.handle.get_mut(ctx.as_context_mut().objects_mut()), + index, + &item, + ) } /// Retrieves the size of the `Table` (in elements) - pub fn size(&self) -> u32 { - self.vm_table.table.length() + pub fn size(&self, ctx: &impl AsContextRef) -> u32 { + self.handle + .get(ctx.as_context_ref().objects()) + .table + .length() } /// Grows the size of the `Table` by `delta`, initializating @@ -107,7 +130,7 @@ impl Table { /// # Errors /// /// Returns an error if the `delta` is out of bounds for the table. - pub fn grow(&self, _delta: u32, _init: Val) -> Result { + pub fn grow(&self, _delta: u32, _init: Value) -> Result { unimplemented!(); } @@ -128,16 +151,20 @@ impl Table { unimplemented!("Table.copy is not natively supported in Javascript"); } - pub(crate) fn from_vm_export(store: &Store, vm_table: VMTable) -> Self { + pub(crate) fn from_vm_extern( + ctx: &mut impl AsContextMut, + internal: InternalContextHandle, + ) -> Self { Self { - store: store.clone(), - vm_table, + handle: unsafe { + ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal) + }, } } - /// Returns whether or not these two tables refer to the same data. - pub fn same(&self, other: &Self) -> bool { - self.vm_table == other.vm_table + /// Checks whether this `Table` can be used with the given context. + pub fn is_from_context(&self, ctx: &impl AsContextRef) -> bool { + self.handle.context_id() == ctx.as_context_ref().objects().id() } /// Get access to the backing VM value for this extern. This function is for @@ -148,16 +175,15 @@ impl Table { /// 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(&self) -> &VMTable { - &self.vm_table + pub unsafe fn get_vm_table<'context>( + &self, + ctx: &'context impl AsContextRef, + ) -> &'context VMTable { + self.handle.get(ctx.as_context_ref().objects()) } } impl<'a> Exportable<'a> for Table { - fn to_export(&self) -> Export { - Export::Table(self.vm_table.clone()) - } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { match _extern { Extern::Table(table) => Ok(table), diff --git a/lib/api/src/js/imports.rs b/lib/api/src/js/imports.rs index e1543e66ea9..596336e62ef 100644 --- a/lib/api/src/js/imports.rs +++ b/lib/api/src/js/imports.rs @@ -1,9 +1,11 @@ //! 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::exports::{Exportable, Exports}; -use crate::js::instance::InstantiationError; +use crate::js::context::AsContextRef; +use crate::js::error::InstantiationError; +use crate::js::exports::Exports; use crate::js::module::Module; +use crate::js::types::AsJs; use crate::Extern; use std::collections::HashMap; use std::fmt; @@ -149,7 +151,7 @@ impl Imports { } /// Returns the `Imports` as a Javascript `Object` - pub fn as_jsobject(&self) -> js_sys::Object { + pub fn as_jsobject(&self, ctx: &impl AsContextRef) -> js_sys::Object { let imports = js_sys::Object::new(); let namespaces: HashMap<&str, Vec<(&str, &Extern)>> = self.map @@ -164,12 +166,8 @@ impl Imports { for (ns, exports) in namespaces.into_iter() { let import_namespace = js_sys::Object::new(); for (name, ext) in exports { - js_sys::Reflect::set( - &import_namespace, - &name.into(), - ext.to_export().as_jsvalue(), - ) - .expect("Error while setting into the js namespace object"); + js_sys::Reflect::set(&import_namespace, &name.into(), &ext.as_jsvalue(ctx)) + .expect("Error while setting into the js namespace object"); } js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into()) .expect("Error while setting into the js imports object"); @@ -178,12 +176,6 @@ impl Imports { } } -impl Into for Imports { - fn into(self) -> js_sys::Object { - self.as_jsobject() - } -} - impl IntoIterator for &Imports { type IntoIter = std::collections::hash_map::IntoIter<(String, String), Extern>; type Item = ((String, String), Extern); @@ -299,15 +291,14 @@ macro_rules! import_namespace { }; } -#[cfg(test)] +/* mod test { - use crate::js::export::Export; use crate::js::exports::Exportable; use crate::js::Type; use crate::js::{Global, Store, Val}; - use wasm_bindgen_test::*; - #[wasm_bindgen_test] + use crate::js::export::Export; + use wasm_bindgen_test::*; fn namespace() { let store = Store::default(); let g1 = Global::new(&store, Val::I32(0)); @@ -329,7 +320,6 @@ mod test { ); } - #[wasm_bindgen_test] fn imports_macro_allows_trailing_comma_and_none() { use crate::js::Function; @@ -381,7 +371,6 @@ mod test { }; } - #[wasm_bindgen_test] fn chaining_works() { let store = Store::default(); let g = Global::new(&store, Val::I32(0)); @@ -412,7 +401,6 @@ mod test { assert!(small.is_some()); } - #[wasm_bindgen_test] fn extending_conflict_overwrites() { let store = Store::default(); let g1 = Global::new(&store, Val::I32(0)); @@ -470,3 +458,4 @@ mod test { ); } } + */ diff --git a/lib/api/src/js/instance.rs b/lib/api/src/js/instance.rs index afbf23d11f8..ca2c830d7e3 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/js/instance.rs @@ -1,15 +1,12 @@ -use crate::js::env::HostEnvInitError; +use crate::js::context::{AsContextMut, AsContextRef, ContextHandle}; +use crate::js::error::InstantiationError; use crate::js::export::Export; -use crate::js::exports::{Exportable, Exports}; +use crate::js::exports::Exports; use crate::js::externals::Extern; use crate::js::imports::Imports; use crate::js::module::Module; -use crate::js::store::Store; -use crate::js::trap::RuntimeError; use js_sys::WebAssembly; use std::fmt; -#[cfg(feature = "std")] -use thiserror::Error; /// A WebAssembly Instance is a stateful, executable /// instance of a WebAssembly [`Module`]. @@ -21,7 +18,7 @@ use thiserror::Error; /// Spec: #[derive(Clone)] pub struct Instance { - instance: WebAssembly::Instance, + _handle: ContextHandle, module: Module, #[allow(dead_code)] imports: Imports, @@ -29,37 +26,6 @@ pub struct Instance { pub exports: Exports, } -/// 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("Link error: {0}"))] - Link(String), - - /// A runtime error occured while invoking the start function - #[cfg_attr(feature = "std", error(transparent))] - Start(RuntimeError), - - /// Error occurred when initializing the host environment. - #[cfg_attr(feature = "std", error(transparent))] - HostEnvInitialization(HostEnvInitError), -} - -#[cfg(feature = "core")] -impl std::fmt::Display for InstantiationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "InstantiationError") - } -} - impl Instance { /// Creates a new `Instance` from a WebAssembly [`Module`] and a /// set of imports resolved by the [`Resolver`]. @@ -94,14 +60,18 @@ impl Instance { /// 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(module: &Module, imports: &Imports) -> Result { + pub fn new( + ctx: &mut impl AsContextMut, + module: &Module, + imports: &Imports, + ) -> Result { let import_copy = imports.clone(); - let (instance, imports): (WebAssembly::Instance, Vec) = module - .instantiate(imports) + let (instance, _imports): (ContextHandle, Vec) = module + .instantiate(&mut ctx.as_context_mut(), imports) .map_err(|e| InstantiationError::Start(e))?; - let self_instance = Self::from_module_and_instance(module, instance, import_copy)?; - self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::>())?; + let self_instance = Self::from_module_and_instance(ctx, module, instance, import_copy)?; + //self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::>())?; Ok(self_instance) } @@ -115,12 +85,12 @@ impl Instance { /// /// *This method is only available when targeting JS environments* pub fn from_module_and_instance( + ctx: &mut impl AsContextMut, module: &Module, - instance: WebAssembly::Instance, + instance: ContextHandle, imports: Imports, ) -> Result { - let store = module.store(); - let instance_exports = instance.exports(); + let instance_exports = instance.get(ctx.as_context_ref().objects()).exports(); let exports = module .exports() .map(|export_type| { @@ -133,53 +103,34 @@ impl Instance { &name )) })?; - let export: Export = (js_export, extern_type).into(); - let extern_ = Extern::from_vm_export(store, export); + let export: Export = + Export::from_js_value(js_export, &mut ctx.as_context_mut(), extern_type)? + .into(); + let extern_ = Extern::from_vm_export(&mut ctx.as_context_mut(), export); Ok((name.to_string(), extern_)) }) .collect::>()?; Ok(Self { - instance, + _handle: instance, module: module.clone(), imports, exports, }) } - /// Initialize the given extern imports with the `Instance`. - /// - /// # Important - /// - /// This method should be called if the Wasmer `Instance` is initialized - /// from Javascript with an already existing `WebAssembly.Instance` but with - /// a imports from the Rust side. - /// - /// *This method is only available when targeting JS environments* - pub fn init_envs(&self, imports: &[Export]) -> Result<(), InstantiationError> { - for import in imports { - if let Export::Function(func) = import { - func.init_envs(&self) - .map_err(|e| InstantiationError::HostEnvInitialization(e))?; - } - } - Ok(()) - } - /// Gets the [`Module`] associated with this instance. pub fn module(&self) -> &Module { &self.module } - /// Returns the [`Store`] where the `Instance` belongs. - pub fn store(&self) -> &Store { - self.module.store() - } - /// Returns the inner WebAssembly Instance #[doc(hidden)] - pub fn raw(&self) -> &WebAssembly::Instance { - &self.instance + pub fn raw<'context>( + &self, + ctx: &'context impl AsContextRef, + ) -> &'context WebAssembly::Instance { + &self._handle.get(ctx.as_context_ref().objects()) } } diff --git a/lib/api/src/js/js_import_object.rs b/lib/api/src/js/js_import_object.rs index 98c0c33c85a..f1e58aec906 100644 --- a/lib/api/src/js/js_import_object.rs +++ b/lib/api/src/js/js_import_object.rs @@ -1,3 +1,5 @@ +use crate::js::context::AsContextMut; +use crate::js::error::WasmError; use crate::js::{Export, ExternType, Module}; use std::collections::HashMap; @@ -49,15 +51,23 @@ impl JsImportObject { /// let import_object = JsImportObject::new(&module, js_object); /// import_object.get_export("module", "name"); /// ``` - pub fn get_export(&self, module: &str, name: &str) -> Option { - let namespace = js_sys::Reflect::get(&self.object, &module.into()).ok()?; - let js_export = js_sys::Reflect::get(&namespace, &name.into()).ok()?; + pub fn get_export( + &self, + ctx: &mut impl AsContextMut, + module: &str, + name: &str, + ) -> Result { + let namespace = js_sys::Reflect::get(&self.object, &module.into())?; + let js_export = js_sys::Reflect::get(&namespace, &name.into())?; match self .module_imports .get(&(module.to_string(), name.to_string())) { - Some(extern_type) => Some((js_export, extern_type.clone()).into()), - None => None, + Some(extern_type) => Ok(Export::from_js_value(js_export, ctx, extern_type.clone())?), + None => Err(WasmError::Generic(format!( + "Name {} not found in module {}", + name, module + ))), } } } diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index 9c7cbef967c..e5eb45afb0a 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -1,3 +1,5 @@ +use crate::js::context::AsContextRef; +use crate::js::externals::memory::MemoryBuffer; use crate::RuntimeError; use crate::{Memory, Memory32, Memory64, WasmPtr}; use std::{ @@ -51,7 +53,7 @@ impl From for MemoryAccessError { /// thread. #[derive(Clone, Copy)] pub struct WasmRef<'a, T: ValueType> { - memory: &'a Memory, + buffer: MemoryBuffer<'a>, offset: u64, marker: PhantomData<*mut T>, } @@ -59,9 +61,9 @@ pub struct WasmRef<'a, T: ValueType> { impl<'a, T: ValueType> WasmRef<'a, T> { /// Creates a new `WasmRef` at the given offset in a memory. #[inline] - pub fn new(memory: &'a Memory, offset: u64) -> Self { + pub fn new(ctx: &'a impl AsContextRef, memory: &'a Memory, offset: u64) -> Self { Self { - memory, + buffer: memory.buffer(ctx), offset, marker: PhantomData, } @@ -96,19 +98,13 @@ impl<'a, T: ValueType> WasmRef<'a, T> { WasmPtr::::new(offset) } - /// Get a reference to the Wasm memory backing this reference. - #[inline] - pub fn memory(self) -> &'a Memory { - self.memory - } - /// 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.memory.read(self.offset, buf)?; + self.buffer.read(self.offset, buf)?; Ok(unsafe { out.assume_init() }) } @@ -124,7 +120,7 @@ impl<'a, T: ValueType> WasmRef<'a, T> { }; val.zero_padding_bytes(data); let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) }; - self.memory.write(self.offset, data) + self.buffer.write(self.offset, data) } } @@ -151,7 +147,7 @@ impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> { /// thread. #[derive(Clone, Copy)] pub struct WasmSlice<'a, T: ValueType> { - memory: &'a Memory, + buffer: MemoryBuffer<'a>, offset: u64, len: u64, marker: PhantomData<*mut T>, @@ -163,7 +159,12 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { /// /// Returns a `MemoryAccessError` if the slice length overflows. #[inline] - pub fn new(memory: &'a Memory, offset: u64, len: u64) -> Result { + pub fn new( + ctx: &'a impl AsContextRef, + memory: &'a Memory, + offset: u64, + len: u64, + ) -> Result { let total_len = len .checked_mul(mem::size_of::() as u64) .ok_or(MemoryAccessError::Overflow)?; @@ -171,7 +172,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { .checked_add(total_len) .ok_or(MemoryAccessError::Overflow)?; Ok(Self { - memory, + buffer: memory.buffer(ctx), offset, len, marker: PhantomData, @@ -202,12 +203,6 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { self.len } - /// Get a reference to the Wasm memory backing this reference. - #[inline] - pub fn memory(self) -> &'a Memory { - self.memory - } - /// Get a `WasmRef` to an element in the slice. #[inline] pub fn index(self, idx: u64) -> WasmRef<'a, T> { @@ -216,7 +211,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { } let offset = self.offset + idx * mem::size_of::() as u64; WasmRef { - memory: self.memory, + buffer: self.buffer, offset, marker: PhantomData, } @@ -230,7 +225,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { } let offset = self.offset + range.start * mem::size_of::() as u64; Self { - memory: self.memory, + buffer: self.buffer, offset, len: range.end - range.start, marker: PhantomData, @@ -271,7 +266,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { buf.len() * mem::size_of::(), ) }; - self.memory.read_uninit(self.offset, bytes)?; + self.buffer.read_uninit(self.offset, bytes)?; Ok(()) } @@ -296,7 +291,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { buf.len() * mem::size_of::(), ) }; - self.memory.read_uninit(self.offset, bytes)?; + self.buffer.read_uninit(self.offset, bytes)?; Ok(unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut T, buf.len()) }) } @@ -313,7 +308,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { let bytes = unsafe { slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * mem::size_of::()) }; - self.memory.write(self.offset, bytes) + self.buffer.write(self.offset, bytes) } /// Reads this `WasmSlice` into a `Vec`. @@ -327,7 +322,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { len * mem::size_of::(), ) }; - self.memory.read_uninit(self.offset, bytes)?; + self.buffer.read_uninit(self.offset, bytes)?; unsafe { vec.set_len(len); } diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 74c01f2060d..8f75409a87f 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -23,7 +23,7 @@ mod lib { } } -mod env; +mod context; mod error; mod export; mod exports; @@ -36,19 +36,16 @@ mod module; #[cfg(feature = "wasm-types-polyfill")] mod module_info_polyfill; mod native; +mod native_type; mod ptr; mod store; mod trap; mod types; +mod value; mod wasm_bindgen_polyfill; -/// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`. -/// -/// See the [`WasmerEnv`] trait for more information. -pub use wasmer_derive::WasmerEnv; - -pub use crate::js::env::{HostEnvInitError, LazyInit, WasmerEnv}; -pub use crate::js::error::{DeserializeError, SerializeError}; +pub use crate::js::context::{AsContextMut, AsContextRef, Context, ContextMut, ContextRef}; +pub use crate::js::error::{DeserializeError, InstantiationError, SerializeError}; pub use crate::js::export::Export; pub use crate::js::exports::{ExportError, Exportable, Exports, ExportsIterator}; pub use crate::js::externals::{ @@ -56,20 +53,23 @@ pub use crate::js::externals::{ WasmTypeList, }; pub use crate::js::imports::Imports; -pub use crate::js::instance::{Instance, InstantiationError}; +pub use crate::js::instance::Instance; pub use crate::js::js_import_object::JsImportObject; pub use crate::js::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter}; pub use crate::js::module::{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::store::{Store, StoreObject}; +pub use crate::js::types::ValType as Type; pub use crate::js::types::{ ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, - TableType, Val, ValType, + TableType, ValType, }; -pub use crate::js::types::{Val as Value, ValType as Type}; +pub use crate::js::value::Value; +pub use crate::js::value::Value as Val; pub use wasmer_types::is_wasm; pub use wasmer_types::{ diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 1828ca83945..67ef83b9aa1 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -1,14 +1,13 @@ -use crate::js::exports::Exportable; -use crate::js::externals::Extern; -use crate::js::imports::Imports; -use crate::js::store::Store; -use crate::js::types::{ExportType, ImportType}; -// use crate::js::InstantiationError; -use crate::js::error::CompileError; +use crate::js::context::{AsContextMut, ContextHandle}; #[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::Store; +use crate::js::types::{AsJs, ExportType, ImportType}; use crate::js::RuntimeError; use js_sys::{Reflect, Uint8Array, WebAssembly}; use std::fmt; @@ -220,8 +219,17 @@ impl Module { pub(crate) fn instantiate( &self, + ctx: &mut impl AsContextMut, imports: &Imports, - ) -> Result<(WebAssembly::Instance, Vec), RuntimeError> { + ) -> Result<(ContextHandle, Vec), RuntimeError> { + // Ensure all imports come from the same context. + if imports + .into_iter() + .any(|(_, import)| !import.is_from_context(ctx)) + { + // FIXME is RuntimeError::User appropriate? + return Err(RuntimeError::user(Box::new(InstantiationError::BadContext))); + } let imports_object = js_sys::Object::new(); let mut import_externs: Vec = vec![]; for import_type in self.imports() { @@ -233,7 +241,7 @@ impl Module { js_sys::Reflect::set( &val, &import_type.name().into(), - import.to_export().as_jsvalue(), + &import.as_jsvalue(&ctx.as_context_ref()), )?; } else { // If the namespace doesn't exist @@ -241,7 +249,7 @@ impl Module { js_sys::Reflect::set( &import_namespace, &import_type.name().into(), - import.to_export().as_jsvalue(), + &import.as_jsvalue(&ctx.as_context_ref()), )?; js_sys::Reflect::set( &imports_object, @@ -255,8 +263,11 @@ impl Module { // the error for us, so we don't need to handle it } Ok(( - WebAssembly::Instance::new(&self.module, &imports_object) - .map_err(|e: JsValue| -> RuntimeError { e.into() })?, + ContextHandle::new( + ctx.as_context_mut().objects_mut(), + WebAssembly::Instance::new(&self.module, &imports_object) + .map_err(|e: JsValue| -> RuntimeError { e.into() })?, + ), import_externs, )) } diff --git a/lib/api/src/js/native.rs b/lib/api/src/js/native.rs index c99037b9fd8..ca21f752301 100644 --- a/lib/api/src/js/native.rs +++ b/lib/api/src/js/native.rs @@ -9,7 +9,9 @@ //! ``` use std::marker::PhantomData; -use crate::js::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList}; +use crate::js::context::{AsContextMut, AsContextRef, ContextHandle}; +use crate::js::externals::Function; +use crate::js::{FromToNativeWasmType, RuntimeError, WasmTypeList}; // use std::panic::{catch_unwind, AssertUnwindSafe}; use crate::js::export::VMFunction; use crate::js::types::param_from_js; @@ -21,8 +23,7 @@ use wasm_bindgen::JsValue; /// (using the Native ABI). #[derive(Clone)] pub struct TypedFunction { - store: Store, - exported: VMFunction, + pub(crate) handle: ContextHandle, _phantom: PhantomData<(Args, Rets)>, } @@ -34,34 +35,18 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - pub(crate) fn new(store: Store, exported: VMFunction) -> Self { + #[allow(dead_code)] + pub(crate) fn new(ctx: &mut impl AsContextMut, vm_function: VMFunction) -> Self { Self { - store, - exported, + handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), vm_function), _phantom: PhantomData, } } -} -impl From<&TypedFunction> for VMFunction -where - Args: WasmTypeList, - Rets: WasmTypeList, -{ - fn from(other: &TypedFunction) -> Self { - other.exported.clone() - } -} - -impl From> for Function -where - Args: WasmTypeList, - Rets: WasmTypeList, -{ - fn from(other: TypedFunction) -> Self { + pub(crate) fn from_handle(f: Function) -> Self { Self { - store: other.store, - exported: other.exported, + handle: f.handle, + _phantom: PhantomData, } } } @@ -75,20 +60,23 @@ macro_rules! impl_native_traits { Rets: WasmTypeList, { /// Call the typed func and return results. - pub fn call(&self, $( $x: $x, )* ) -> Result { - let params_list: Vec = vec![ $( JsValue::from_f64($x.to_native().to_binary() as f64) ),* ]; - let results = self.exported.function.apply( + #[allow(clippy::too_many_arguments)] + pub fn call(&self, ctx: &mut impl AsContextMut, $( $x: $x, )* ) -> Result where + $( $x: FromToNativeWasmType + crate::js::NativeWasmTypeInto, )* + { + let params_list: Vec = vec![ $( JsValue::from_f64($x.into_raw(ctx))),* ]; + let results = self.handle.get(ctx.as_context_ref().objects()).function.apply( &JsValue::UNDEFINED, &Array::from_iter(params_list.iter()) )?; let mut rets_list_array = Rets::empty_array(); - let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128; + let mut_rets = rets_list_array.as_mut() as *mut [f64] as *mut f64; match Rets::size() { 0 => {}, 1 => unsafe { let ty = Rets::wasm_types()[0]; let val = param_from_js(&ty, &results); - val.write_value_to(mut_rets); + *mut_rets = val.as_raw(&mut ctx.as_context_mut()); } _n => { let results: Array = results.into(); @@ -96,12 +84,13 @@ macro_rules! impl_native_traits { let ret = results.get(i as u32); unsafe { let val = param_from_js(&ret_type, &ret); - val.write_value_to(mut_rets.add(i)); + let slot = mut_rets.add(i); + *slot = val.as_raw(&mut ctx.as_context_mut()); } } } } - Ok(Rets::from_array(rets_list_array)) + Ok(unsafe { Rets::from_array(ctx, rets_list_array) }) } } @@ -112,9 +101,9 @@ macro_rules! impl_native_traits { $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, { - fn get_self_from_extern_with_generics(_extern: &crate::js::externals::Extern) -> Result { + fn get_self_from_extern_with_generics(ctx: &impl AsContextRef, _extern: &crate::js::externals::Extern) -> Result { use crate::js::exports::Exportable; - crate::js::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::js::exports::ExportError::IncompatibleType) + crate::js::Function::get_self_from_extern(_extern)?.native(ctx).map_err(|_| crate::js::exports::ExportError::IncompatibleType) } } }; diff --git a/lib/api/src/js/native_type.rs b/lib/api/src/js/native_type.rs new file mode 100644 index 00000000000..e6dc2805462 --- /dev/null +++ b/lib/api/src/js/native_type.rs @@ -0,0 +1,160 @@ +//! This module permits to create native functions +//! easily in Rust, thanks to its advanced typing system. + +use wasmer_types::{NativeWasmType, Type}; + +use crate::Function; + +use super::context::AsContextMut; + +/// `NativeWasmTypeInto` performs conversions from and into `NativeWasmType` +/// types with a context. +pub trait NativeWasmTypeInto: NativeWasmType + Sized { + #[doc(hidden)] + fn into_abi(self, ctx: &mut impl AsContextMut) -> Self::Abi; + + #[doc(hidden)] + unsafe fn from_abi(ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self; + + /// Convert self to raw value representation. + fn into_raw(self, ctx: &mut impl AsContextMut) -> f64; + + /// Convert to self from raw value representation. + /// + /// # Safety + /// + unsafe fn from_raw(ctx: &mut impl AsContextMut, raw: f64) -> Self; +} + +impl NativeWasmTypeInto for i32 { + #[inline] + unsafe fn from_abi(_ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _ctx: &mut impl AsContextMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _ctx: &mut impl AsContextMut) -> f64 { + self.into() + } + + #[inline] + unsafe fn from_raw(_ctx: &mut impl AsContextMut, raw: f64) -> Self { + raw as _ + } +} + +impl NativeWasmTypeInto for i64 { + #[inline] + unsafe fn from_abi(_ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _ctx: &mut impl AsContextMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _ctx: &mut impl AsContextMut) -> f64 { + self as _ + } + + #[inline] + unsafe fn from_raw(_ctx: &mut impl AsContextMut, raw: f64) -> Self { + raw as _ + } +} + +impl NativeWasmTypeInto for f32 { + #[inline] + unsafe fn from_abi(_ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _ctx: &mut impl AsContextMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _ctx: &mut impl AsContextMut) -> f64 { + self as _ + } + + #[inline] + unsafe fn from_raw(_ctx: &mut impl AsContextMut, raw: f64) -> Self { + raw as _ + } +} + +impl NativeWasmTypeInto for f64 { + #[inline] + unsafe fn from_abi(_ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _ctx: &mut impl AsContextMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _ctx: &mut impl AsContextMut) -> f64 { + self + } + + #[inline] + unsafe fn from_raw(_ctx: &mut impl AsContextMut, raw: f64) -> Self { + raw + } +} + +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 index 36d76481bf2..95a1f8f1704 100644 --- a/lib/api/src/js/ptr.rs +++ b/lib/api/src/js/ptr.rs @@ -1,3 +1,5 @@ +use crate::js::context::AsContextRef; +use crate::js::NativeWasmTypeInto; use crate::js::{externals::Memory, FromToNativeWasmType}; use crate::{MemoryAccessError, WasmRef, WasmSlice}; use std::convert::TryFrom; @@ -136,20 +138,25 @@ 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, memory: &'a Memory) -> WasmRef<'a, T> { - WasmRef::new(memory, self.offset.into()) + pub fn deref<'a>(self, ctx: &'a impl AsContextRef, memory: &'a Memory) -> WasmRef<'a, T> { + WasmRef::new(ctx, memory, self.offset.into()) } /// Reads the address pointed to by this `WasmPtr` in a memory. #[inline] - pub fn read(self, memory: &Memory) -> Result { - self.deref(memory).read() + pub fn read(self, ctx: &impl AsContextRef, memory: &Memory) -> Result { + self.deref(ctx, memory).read() } /// Writes to the address pointed to by this `WasmPtr` in a memory. #[inline] - pub fn write(self, memory: &Memory, val: T) -> Result<(), MemoryAccessError> { - self.deref(memory).write(val) + pub fn write( + self, + ctx: &impl AsContextRef, + memory: &Memory, + val: T, + ) -> Result<(), MemoryAccessError> { + self.deref(ctx, memory).write(val) } /// Creates a `WasmSlice` starting at this `WasmPtr` which allows reading @@ -160,10 +167,11 @@ impl WasmPtr { #[inline] pub fn slice<'a>( self, + ctx: &'a impl AsContextRef, memory: &'a Memory, len: M::Offset, ) -> Result, MemoryAccessError> { - WasmSlice::new(memory, self.offset.into(), len.into()) + WasmSlice::new(ctx, memory, self.offset.into(), len.into()) } /// Reads a sequence of values from this `WasmPtr` until a value that @@ -173,13 +181,14 @@ impl WasmPtr { #[inline] pub fn read_until<'a>( self, + ctx: &'a impl AsContextRef, memory: &'a Memory, 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(memory).read()?; + let val = self.add_offset(i)?.deref(ctx, memory).read()?; if end(&val) { break; } @@ -197,10 +206,11 @@ impl WasmPtr { #[inline] pub fn read_utf8_string<'a>( self, + ctx: &'a impl AsContextRef, memory: &'a Memory, len: M::Offset, ) -> Result { - let vec = self.slice(memory, len)?.read_to_vec()?; + let vec = self.slice(ctx, memory, len)?.read_to_vec()?; Ok(String::from_utf8(vec)?) } @@ -211,14 +221,18 @@ impl WasmPtr { #[inline] pub fn read_utf8_string_with_nul<'a>( self, + ctx: &'a impl AsContextRef, memory: &'a Memory, ) -> Result { - let vec = self.read_until(memory, |&byte| byte == 0)?; + let vec = self.read_until(ctx, memory, |&byte| byte == 0)?; Ok(String::from_utf8(vec)?) } } -unsafe impl FromToNativeWasmType for WasmPtr { +unsafe impl FromToNativeWasmType for WasmPtr +where + ::Native: NativeWasmTypeInto, +{ type Native = M::Native; fn to_native(self) -> Self::Native { diff --git a/lib/api/src/js/types.rs b/lib/api/src/js/types.rs index 8b9723987f8..baf52038ddd 100644 --- a/lib/api/src/js/types.rs +++ b/lib/api/src/js/types.rs @@ -1,8 +1,9 @@ -use crate::js::externals::Function; +//use crate::js::externals::Function; // use crate::js::store::{Store, StoreObject}; // use crate::js::RuntimeError; +use crate::js::context::AsContextRef; +use crate::js::value::Value; use wasm_bindgen::JsValue; -use wasmer_types::Value; pub use wasmer_types::{ ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, TableType, Type as ValType, @@ -14,20 +15,20 @@ pub use wasmer_types::{ /// * Vectors (128 bits, with 32 or 64 bit lanes) /// /// Spec: -// pub type Val = (); -pub type Val = Value; +// pub type Value = (); +//pub type Value = Value; pub trait AsJs { - fn as_jsvalue(&self) -> JsValue; + fn as_jsvalue(&self, ctx: &impl AsContextRef) -> JsValue; } #[inline] -pub fn param_from_js(ty: &ValType, js_val: &JsValue) -> Val { +pub fn param_from_js(ty: &ValType, js_val: &JsValue) -> Value { match ty { - ValType::I32 => Val::I32(js_val.as_f64().unwrap() as _), - ValType::I64 => Val::I64(js_val.as_f64().unwrap() as _), - ValType::F32 => Val::F32(js_val.as_f64().unwrap() as _), - ValType::F64 => Val::F64(js_val.as_f64().unwrap()), + 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 @@ -35,18 +36,20 @@ pub fn param_from_js(ty: &ValType, js_val: &JsValue) -> Val { } } -impl AsJs for Val { - fn as_jsvalue(&self) -> JsValue { +impl AsJs for Value { + fn as_jsvalue(&self, ctx: &impl AsContextRef) -> 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::FuncRef(func) => func.as_ref().unwrap().exported.function.clone().into(), - v => unimplemented!( - "The value `{:?}` is not yet supported in the JS Function API", - v - ), + Self::FuncRef(Some(func)) => func + .handle + .get(ctx.as_context_ref().objects()) + .function + .clone() + .into(), + Self::FuncRef(None) => JsValue::null(), } } } diff --git a/lib/api/src/js/value.rs b/lib/api/src/js/value.rs new file mode 100644 index 00000000000..703b6df4502 --- /dev/null +++ b/lib/api/src/js/value.rs @@ -0,0 +1,423 @@ +use std::convert::TryFrom; +use std::fmt; +use std::string::{String, ToString}; + +use wasmer_types::Type; + +//use crate::ExternRef; +use crate::js::externals::function::Function; + +use super::context::AsContextRef; + +/// 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), +} + +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::ExternRef(_) => Type::ExternRef, + Self::FuncRef(_) => Type::FuncRef, + } + } + + /// Converts the `Value` into a `f64`. + pub fn as_raw(&self, ctx: &impl AsContextRef) -> 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::FuncRef(Some(ref f)) => f + .handle + .get(ctx.as_context_ref().objects()) + .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 a `f64` to a `Value`. + /// + /// # Safety + /// + pub unsafe fn from_raw(_ctx: &impl AsContextRef, ty: Type, raw: f64) -> Self { + match ty { + Type::I32 => Self::I32(raw as i32), + Type::I64 => Self::I64(raw as i64), + Type::F32 => Self::F32(raw as f32), + Type::F64 => Self::F64(raw), + Type::FuncRef => todo!(), + Type::V128 => todo!(), + Type::ExternRef => todo!(), + //Self::ExternRef( + //{ + //VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(ctx, 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_context(&self, ctx: &impl AsContextRef) -> bool { + match self { + Self::I32(_) + | Self::I64(_) + | Self::F32(_) + | Self::F64(_) + //| Self::ExternRef(None) + | Self::FuncRef(None) => true, + //Self::ExternRef(Some(e)) => e.is_from_context(ctx), + Self::FuncRef(Some(f)) => f.is_from_context(ctx), + } + } + + 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) + //(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::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::ExternRef(_) => "externref".to_string(), + Self::FuncRef(_) => "funcref".to_string(), + } + } +} + +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_FUNCREF: &str = "Value is not of Wasm type funcref"; +//const NOT_EXTERNREF: &str = "Value is not of Wasm type externref"; + +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/lib.rs b/lib/api/src/lib.rs index 6fdbb99f555..9bf0520290f 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -26,7 +26,6 @@ ) )] #![cfg_attr(feature = "js", crate_type = "cdylib")] -#![cfg_attr(feature = "js", crate_type = "rlib")] //! [`Wasmer`](https://wasmer.io/) is the most popular //! [WebAssembly](https://webassembly.org/) runtime for Rust. It supports diff --git a/lib/api/src/sys/context.rs b/lib/api/src/sys/context.rs index d72e68499db..4289b818647 100644 --- a/lib/api/src/sys/context.rs +++ b/lib/api/src/sys/context.rs @@ -27,7 +27,7 @@ pub(crate) struct ContextInner { /// The `T` generic parameter allows arbitrary data to be attached to a context. /// This data can be accessed using the [`Context::data`] and /// [`Context::data_mut`] methods. Host functions defined using -/// [`Function::new_with_env`] and [`Function::new_native_with_env`] receive +/// [`Function::new`] and [`Function::new_native`] receive /// a reference to the context when they are called. pub struct Context { pub(crate) inner: Box>, diff --git a/lib/api/src/sys/exports.rs b/lib/api/src/sys/exports.rs index 85381bc216d..dd595d540f6 100644 --- a/lib/api/src/sys/exports.rs +++ b/lib/api/src/sys/exports.rs @@ -180,7 +180,7 @@ impl Exports { } /// Like `get_with_generics` but with a WeakReference to the `InstanceRef` internally. - /// This is useful for passing data into `WasmerEnv`, for example. + /// This is useful for passing data into Context data, for example. pub fn get_with_generics_weak<'a, T, Args, Rets>(&'a self, name: &str) -> Result where Args: WasmTypeList, diff --git a/lib/api/tests/js_externals.rs b/lib/api/tests/js_externals.rs index d46104faede..187e6a6b1fe 100644 --- a/lib/api/tests/js_externals.rs +++ b/lib/api/tests/js_externals.rs @@ -6,18 +6,19 @@ mod js { #[wasm_bindgen_test] fn global_new() { let store = Store::default(); - let global = Global::new(&store, Value::I32(10)); + let mut ctx = Context::new(&store, ()); + let global = Global::new(&mut ctx, Value::I32(10)); assert_eq!( - *global.ty(), + global.ty(&ctx), GlobalType { ty: Type::I32, mutability: Mutability::Const } ); - let global_mut = Global::new_mut(&store, Value::I32(10)); + let global_mut = Global::new_mut(&mut ctx, Value::I32(10)); assert_eq!( - *global_mut.ty(), + global_mut.ty(&ctx), GlobalType { ty: Type::I32, mutability: Mutability::Var @@ -28,15 +29,16 @@ mod js { #[wasm_bindgen_test] fn global_get() { let store = Store::default(); - let global_i32 = Global::new(&store, Value::I32(10)); - assert_eq!(global_i32.get(), Value::I32(10)); + let mut ctx = Context::new(&store, ()); + let global_i32 = Global::new(&mut ctx, Value::I32(10)); + assert_eq!(global_i32.get(&ctx), Value::I32(10)); // 64-bit values are not yet fully supported in some versions of Node // Commenting this tests for now: // let global_i64 = Global::new(&store, Value::I64(20)); // assert_eq!(global_i64.get(), Value::I64(20)); - let global_f32 = Global::new(&store, Value::F32(10.0)); - assert_eq!(global_f32.get(), Value::F32(10.0)); + let global_f32 = Global::new(&mut ctx, Value::F32(10.0)); + assert_eq!(global_f32.get(&ctx), Value::F32(10.0)); // let global_f64 = Global::new(&store, Value::F64(20.0)); // assert_eq!(global_f64.get(), Value::F64(20.0)); } @@ -44,30 +46,32 @@ mod js { #[wasm_bindgen_test] fn global_set() { let store = Store::default(); - let global_i32 = Global::new(&store, Value::I32(10)); + let mut ctx = Context::new(&store, ()); + let global_i32 = Global::new(&mut ctx, Value::I32(10)); // Set on a constant should error - assert!(global_i32.set(Value::I32(20)).is_err()); + assert!(global_i32.set(&mut ctx, Value::I32(20)).is_err()); - let global_i32_mut = Global::new_mut(&store, Value::I32(10)); + let global_i32_mut = Global::new_mut(&mut ctx, Value::I32(10)); // Set on different type should error - assert!(global_i32_mut.set(Value::I64(20)).is_err()); + assert!(global_i32_mut.set(&mut ctx, Value::I64(20)).is_err()); // Set on same type should succeed - global_i32_mut.set(Value::I32(20)).unwrap(); - assert_eq!(global_i32_mut.get(), Value::I32(20)); + global_i32_mut.set(&mut ctx, Value::I32(20)).unwrap(); + assert_eq!(global_i32_mut.get(&ctx), Value::I32(20)); } #[wasm_bindgen_test] fn table_new() { let store = Store::default(); + let mut ctx = Context::new(&store, ()); let table_type = TableType { ty: Type::FuncRef, minimum: 0, maximum: None, }; - let f = Function::new_native(&store, || {}); - let table = Table::new(&store, table_type, Value::FuncRef(Some(f))).unwrap(); - assert_eq!(*table.ty(), table_type); + let f = Function::new_native(&mut ctx, |_: ContextMut<'_, ()>| {}); + let table = Table::new(&mut ctx, table_type, Value::FuncRef(Some(f))).unwrap(); + assert_eq!(table.ty(&ctx), table_type); // table.get() // Anyrefs not yet supported @@ -77,7 +81,7 @@ mod js { // maximum: None, // }; // let table = Table::new(&store, table_type, Value::ExternRef(ExternRef::Null))?; - // assert_eq!(*table.ty(), table_type); + // assert_eq!(*table.ty(&ctx), table_type); } // Tables are not yet fully supported in Wasm @@ -87,14 +91,15 @@ mod js { // #[ignore] // fn table_get() -> Result<()> { // let store = Store::default(); + // let mut ctx = Context::new(&store, ()); // let table_type = TableType { // ty: Type::FuncRef, // minimum: 0, // maximum: Some(1), // }; - // let f = Function::new_native(&store, |num: i32| num + 1); + // let f = Function::new(&mut ctx, |num: i32| num + 1); // let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?; - // assert_eq!(*table.ty(), table_type); + // assert_eq!(*table.ty(&ctx), table_type); // let _elem = table.get(0).unwrap(); // // assert_eq!(elem.funcref().unwrap(), f); // Ok(()) @@ -110,12 +115,13 @@ mod js { // #[test] // fn table_grow() -> Result<()> { // let store = Store::default(); + // let mut ctx = Context::new(&store, ()); // let table_type = TableType { // ty: Type::FuncRef, // minimum: 0, // maximum: Some(10), // }; - // let f = Function::new_native(&store, |num: i32| num + 1); + // let f = Function::new(&mut ctx, |num: i32| num + 1); // let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?; // // Growing to a bigger maximum should return None // let old_len = table.grow(12, Value::FuncRef(Some(f.clone()))); @@ -138,29 +144,31 @@ mod js { #[wasm_bindgen_test] fn memory_new() { let store = Store::default(); + let mut ctx = Context::new(&store, ()); let memory_type = MemoryType { shared: false, minimum: Pages(0), maximum: Some(Pages(10)), }; - let memory = Memory::new(&store, memory_type).unwrap(); - assert_eq!(memory.size(), Pages(0)); - assert_eq!(memory.ty(), memory_type); + let memory = Memory::new(&mut ctx, memory_type).unwrap(); + assert_eq!(memory.size(&ctx), Pages(0)); + assert_eq!(memory.ty(&ctx), memory_type); } #[wasm_bindgen_test] fn memory_grow() { let store = Store::default(); + let mut ctx = Context::new(&store, ()); let desc = MemoryType::new(Pages(10), Some(Pages(16)), false); - let memory = Memory::new(&store, desc).unwrap(); - assert_eq!(memory.size(), Pages(10)); + let memory = Memory::new(&mut ctx, desc).unwrap(); + assert_eq!(memory.size(&ctx), Pages(10)); - let result = memory.grow(Pages(2)).unwrap(); + let result = memory.grow(&mut ctx, Pages(2)).unwrap(); assert_eq!(result, Pages(10)); - assert_eq!(memory.size(), Pages(12)); + assert_eq!(memory.size(&ctx), Pages(12)); - let result = memory.grow(Pages(10)); + let result = memory.grow(&mut ctx, Pages(10)); assert!(result.is_err()); assert_eq!( result, @@ -174,27 +182,33 @@ mod js { #[wasm_bindgen_test] fn function_new() { let store = Store::default(); - let function = Function::new_native(&store, || {}); - assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![])); - let function = Function::new_native(&store, |_a: i32| {}); + let mut ctx = Context::new(&store, ()); + let function = Function::new_native(&mut ctx, |_ctx: ContextMut<'_, ()>| {}); + assert_eq!(function.ty(&ctx).clone(), FunctionType::new(vec![], vec![])); + let function = Function::new_native(&mut ctx, |_ctx: ContextMut<'_, ()>, _a: i32| {}); assert_eq!( - function.ty().clone(), + function.ty(&ctx).clone(), FunctionType::new(vec![Type::I32], vec![]) ); - let function = Function::new_native(&store, |_a: i32, _b: i64, _c: f32, _d: f64| {}); + let function = Function::new_native( + &mut ctx, + |_ctx: ContextMut<'_, ()>, _a: i32, _b: i64, _c: f32, _d: f64| {}, + ); assert_eq!( - function.ty().clone(), + function.ty(&ctx).clone(), FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) ); - let function = Function::new_native(&store, || -> i32 { 1 }); + let function = Function::new_native(&mut ctx, |_ctx: ContextMut<'_, ()>| -> i32 { 1 }); assert_eq!( - function.ty().clone(), + function.ty(&ctx).clone(), FunctionType::new(vec![], vec![Type::I32]) ); - let function = - Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }); + let function = Function::new_native( + &mut ctx, + |_ctx: ContextMut<'_, ()>| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }, + ); assert_eq!( - function.ty().clone(), + function.ty(&ctx).clone(), FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) ); } @@ -202,40 +216,38 @@ mod js { #[wasm_bindgen_test] fn function_new_env() { let store = Store::default(); - #[derive(Clone, WasmerEnv)] + #[derive(Clone)] struct MyEnv {} let my_env = MyEnv {}; - let function = Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| {}); - assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![])); - let function = - Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv, _a: i32| {}); + let mut ctx = Context::new(&store, my_env); + + let function = Function::new_native(&mut ctx, |_: ContextMut<'_, MyEnv>| {}); + assert_eq!(function.ty(&ctx).clone(), FunctionType::new(vec![], vec![])); + let function = Function::new_native(&mut ctx, |_: ContextMut<'_, MyEnv>, _a: i32| {}); assert_eq!( - function.ty().clone(), + function.ty(&ctx).clone(), FunctionType::new(vec![Type::I32], vec![]) ); - let function = Function::new_native_with_env( - &store, - my_env.clone(), - |_env: &MyEnv, _a: i32, _b: i64, _c: f32, _d: f64| {}, + let function = Function::new_native( + &mut ctx, + |_: ContextMut<'_, MyEnv>, _a: i32, _b: i64, _c: f32, _d: f64| {}, ); assert_eq!( - function.ty().clone(), + function.ty(&ctx).clone(), FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) ); - let function = - Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| -> i32 { 1 }); + let function = Function::new_native(&mut ctx, |_: ContextMut<'_, MyEnv>| -> i32 { 1 }); assert_eq!( - function.ty().clone(), + function.ty(&ctx).clone(), FunctionType::new(vec![], vec![Type::I32]) ); - let function = Function::new_native_with_env( - &store, - my_env.clone(), - |_env: &MyEnv| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }, + let function = Function::new_native( + &mut ctx, + |_: ContextMut<'_, MyEnv>| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }, ); assert_eq!( - function.ty().clone(), + function.ty(&ctx).clone(), FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) ); } @@ -243,183 +255,185 @@ mod js { #[wasm_bindgen_test] fn function_new_dynamic() { let store = Store::default(); + let mut ctx = Context::new(&store, ()); // Using &FunctionType signature let function_type = FunctionType::new(vec![], vec![]); - let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!()); - assert_eq!(function.ty().clone(), function_type); + let function = Function::new( + &mut ctx, + &function_type, + |_ctx: ContextMut<'_, ()>, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&ctx).clone(), function_type); let function_type = FunctionType::new(vec![Type::I32], vec![]); - let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!()); - assert_eq!(function.ty().clone(), function_type); + let function = Function::new( + &mut ctx, + &function_type, + |_ctx: ContextMut<'_, ()>, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&ctx).clone(), function_type); let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); - let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!()); - assert_eq!(function.ty().clone(), function_type); + let function = Function::new( + &mut ctx, + &function_type, + |_ctx: ContextMut<'_, ()>, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&ctx).clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32]); - let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!()); - assert_eq!(function.ty().clone(), function_type); + let function = Function::new( + &mut ctx, + &function_type, + |_ctx: ContextMut<'_, ()>, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&ctx).clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); - let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!()); - assert_eq!(function.ty().clone(), function_type); + let function = Function::new( + &mut ctx, + &function_type, + |_ctx: ContextMut<'_, ()>, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&ctx).clone(), function_type); // Using array signature let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); - let function = Function::new(&store, function_type, |_values: &[Value]| unimplemented!()); - assert_eq!(function.ty().params(), [Type::V128]); - assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]); + let function = Function::new( + &mut ctx, + function_type, + |_ctx: ContextMut<'_, ()>, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&ctx).params(), [Type::V128]); + assert_eq!( + function.ty(&ctx).results(), + [Type::I32, Type::F32, Type::F64] + ); } #[wasm_bindgen_test] fn function_new_dynamic_env() { let store = Store::default(); - #[derive(Clone, WasmerEnv)] + #[derive(Clone)] struct MyEnv {} + let my_env = MyEnv {}; + let mut ctx = Context::new(&store, my_env); // Using &FunctionType signature let function_type = FunctionType::new(vec![], vec![]); - let function = Function::new_with_env( - &store, + let function = Function::new( + &mut ctx, &function_type, - my_env.clone(), - |_env: &MyEnv, _values: &[Value]| unimplemented!(), + |_ctx: ContextMut<'_, MyEnv>, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty().clone(), function_type); + assert_eq!(function.ty(&ctx).clone(), function_type); let function_type = FunctionType::new(vec![Type::I32], vec![]); - let function = Function::new_with_env( - &store, + let function = Function::new( + &mut ctx, &function_type, - my_env.clone(), - |_env: &MyEnv, _values: &[Value]| unimplemented!(), + |_ctx: ContextMut<'_, MyEnv>, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty().clone(), function_type); + assert_eq!(function.ty(&ctx).clone(), function_type); let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); - let function = Function::new_with_env( - &store, + let function = Function::new( + &mut ctx, &function_type, - my_env.clone(), - |_env: &MyEnv, _values: &[Value]| unimplemented!(), + |_ctx: ContextMut<'_, MyEnv>, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty().clone(), function_type); + assert_eq!(function.ty(&ctx).clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32]); - let function = Function::new_with_env( - &store, + let function = Function::new( + &mut ctx, &function_type, - my_env.clone(), - |_env: &MyEnv, _values: &[Value]| unimplemented!(), + |_ctx: ContextMut<'_, MyEnv>, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty().clone(), function_type); + assert_eq!(function.ty(&ctx).clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); - let function = Function::new_with_env( - &store, + let function = Function::new( + &mut ctx, &function_type, - my_env.clone(), - |_env: &MyEnv, _values: &[Value]| unimplemented!(), + |_ctx: ContextMut<'_, MyEnv>, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty().clone(), function_type); + assert_eq!(function.ty(&ctx).clone(), function_type); // Using array signature let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); - let function = Function::new_with_env( - &store, + let function = Function::new( + &mut ctx, function_type, - my_env.clone(), - |_env: &MyEnv, _values: &[Value]| unimplemented!(), + |_ctx: ContextMut<'_, MyEnv>, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&ctx).params(), [Type::V128]); + assert_eq!( + function.ty(&ctx).results(), + [Type::I32, Type::F32, Type::F64] ); - assert_eq!(function.ty().params(), [Type::V128]); - assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]); } #[wasm_bindgen_test] fn native_function_works() { let store = Store::default(); - let function = Function::new_native(&store, || {}); - let typed_function: TypedFunction<(), ()> = function.native().unwrap(); - let result = typed_function.call(); + let mut ctx = Context::new(&store, ()); + let function = Function::new_native(&mut ctx, |_: ContextMut<'_, ()>| {}); + let typed_function: TypedFunction<(), ()> = function.native(&mut ctx).unwrap(); + let result = typed_function.call(&mut ctx); assert!(result.is_ok()); - let function = Function::new_native(&store, |a: i32| -> i32 { a + 1 }); - let typed_function: TypedFunction = function.native().unwrap(); - assert_eq!(typed_function.call(3).unwrap(), 4); + let function = + Function::new_native(&mut ctx, |_: ContextMut<'_, ()>, a: i32| -> i32 { a + 1 }); + let typed_function: TypedFunction = function.native(&mut ctx).unwrap(); + assert_eq!(typed_function.call(&mut ctx, 3).unwrap(), 4); // fn rust_abi(a: i32, b: i64, c: f32, d: f64) -> u64 { // (a as u64 * 1000) + (b as u64 * 100) + (c as u64 * 10) + (d as u64) // } - // let function = Function::new_native(&store, rust_abi); - // let typed_function: TypedFunction<(i32, i64, f32, f64), u64> = function.native().unwrap(); + // let function = Function::new(&mut ctx, rust_abi); + // let typed_function: TypedFunction<(i32, i64, f32, f64), u64> = function.native(&mut ctx).unwrap(); // assert_eq!(typed_function.call(8, 4, 1.5, 5.).unwrap(), 8415); - let function = Function::new_native(&store, || -> i32 { 1 }); - let typed_function: TypedFunction<(), i32> = function.native().unwrap(); - assert_eq!(typed_function.call().unwrap(), 1); + let function = Function::new_native(&mut ctx, |_: ContextMut<'_, ()>| -> i32 { 1 }); + let typed_function: TypedFunction<(), i32> = function.native(&mut ctx).unwrap(); + assert_eq!(typed_function.call(&mut ctx).unwrap(), 1); - let function = Function::new_native(&store, |_a: i32| {}); - let typed_function: TypedFunction = function.native().unwrap(); - assert!(typed_function.call(4).is_ok()); + let function = Function::new_native(&mut ctx, |_: ContextMut<'_, ()>, _a: i32| {}); + let typed_function: TypedFunction = function.native(&mut ctx).unwrap(); + assert!(typed_function.call(&mut ctx, 4).is_ok()); - // let function = Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }); - // let typed_function: TypedFunction<(), (i32, i64, f32, f64)> = function.native().unwrap(); + // let function = Function::new(&mut ctx, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }); + // let typed_function: TypedFunction<(), (i32, i64, f32, f64)> = function.native(&mut ctx).unwrap(); // assert_eq!(typed_function.call().unwrap(), (1, 2, 3.0, 4.0)); } #[wasm_bindgen_test] fn function_outlives_instance() { let store = Store::default(); + let mut ctx = Context::new(&store, ()); let wat = r#"(module - (type $sum_t (func (param i32 i32) (result i32))) - (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) - local.get $x - local.get $y - i32.add) - (export "sum" (func $sum_f))) -"#; + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) + "#; let f = { let module = Module::new(&store, wat).unwrap(); - let instance = Instance::new(&module, &imports! {}).unwrap(); + let instance = Instance::new(&mut ctx, &module, &imports! {}).unwrap(); let f = instance.exports.get_function("sum").unwrap(); assert_eq!( - f.call(&[Val::I32(4), Val::I32(5)]).unwrap(), + f.call(&mut ctx, &[Val::I32(4), Val::I32(5)]).unwrap(), vec![Val::I32(9)].into_boxed_slice() ); f.clone() }; assert_eq!( - f.call(&[Val::I32(4), Val::I32(5)]).unwrap(), + f.call(&mut ctx, &[Val::I32(4), Val::I32(5)]).unwrap(), vec![Val::I32(9)].into_boxed_slice() ); } - - #[wasm_bindgen_test] - fn manually_generate_wasmer_env() { - let store = Store::default(); - #[derive(WasmerEnv, Clone)] - struct MyEnv { - val: u32, - memory: LazyInit, - } - - fn host_function(env: &mut MyEnv, arg1: u32, arg2: u32) -> u32 { - env.val + arg1 + arg2 - } - - let mut env = MyEnv { - val: 5, - memory: LazyInit::new(), - }; - - let result = host_function(&mut env, 7, 9); - assert_eq!(result, 21); - - let memory = Memory::new(&store, MemoryType::new(0, None, false)).unwrap(); - env.memory.initialize(memory); - - let result = host_function(&mut env, 1, 2); - assert_eq!(result, 8); - } } diff --git a/lib/api/tests/js_instance.rs b/lib/api/tests/js_instance.rs index 00a4bc5666d..c64d2e45431 100644 --- a/lib/api/tests/js_instance.rs +++ b/lib/api/tests/js_instance.rs @@ -4,16 +4,33 @@ mod js { use wasm_bindgen_test::*; use wasmer::*; + /* + * For debugging, put web_sys in dev dependencies in Cargo.toml with the "console" feature + * on. + * + extern crate web_sys; + + // A macro to provide `println!(..)`-style syntax for `console.log` logging. + macro_rules! log { + ( $( $t:tt )* ) => { + web_sys::console::log_1(&format!( $( $t )* ).into()); + } + } + */ + macro_rules! log { + ( $( $t:tt )* ) => {}; + } + #[wasm_bindgen_test] fn test_exported_memory() { let store = Store::default(); let mut module = Module::new( &store, br#" - (module - (memory (export "mem") 1) - ) - "#, + (module + (memory (export "mem") 1) + ) + "#, ) .unwrap(); module @@ -24,17 +41,19 @@ mod js { .unwrap(); let import_object = imports! {}; - let instance = Instance::new(&module, &import_object).unwrap(); + let mut ctx = Context::new(&store, ()); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let memory = instance.exports.get_memory("mem").unwrap(); - assert_eq!(memory.ty(), MemoryType::new(Pages(1), None, false)); - assert_eq!(memory.size(), Pages(1)); - assert_eq!(memory.data_size(), 65536); - - memory.grow(Pages(1)).unwrap(); - assert_eq!(memory.ty(), MemoryType::new(Pages(2), None, false)); - assert_eq!(memory.size(), Pages(2)); - assert_eq!(memory.data_size(), 65536 * 2); + assert!(memory.is_from_context(&ctx)); + assert_eq!(memory.ty(&ctx), MemoryType::new(Pages(1), None, false)); + assert_eq!(memory.size(&ctx), Pages(1)); + assert_eq!(memory.data_size(&ctx), 65536); + + memory.grow(&mut ctx, Pages(1)).unwrap(); + assert_eq!(memory.ty(&ctx), MemoryType::new(Pages(1), None, false)); + assert_eq!(memory.size(&ctx), Pages(2)); + assert_eq!(memory.data_size(&ctx), 65536 * 2); } #[wasm_bindgen_test] @@ -43,12 +62,12 @@ mod js { let mut module = Module::new( &store, br#" - (module - (func (export "get_magic") (result i32) - (i32.const 42) + (module + (func (export "get_magic") (result i32) + (i32.const 42) + ) ) - ) - "#, + "#, ) .unwrap(); module @@ -62,16 +81,17 @@ mod js { .unwrap(); let import_object = imports! {}; - let instance = Instance::new(&module, &import_object).unwrap(); + let mut ctx = Context::new(&store, ()); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let get_magic = instance.exports.get_function("get_magic").unwrap(); assert_eq!( - get_magic.ty().clone(), + get_magic.ty(&ctx).clone(), FunctionType::new(vec![], vec![Type::I32]) ); let expected = vec![Val::I32(42)].into_boxed_slice(); - assert_eq!(get_magic.call(&[]), Ok(expected)); + assert_eq!(get_magic.call(&mut ctx, &[]).unwrap(), expected); } #[wasm_bindgen_test] @@ -80,13 +100,13 @@ mod js { let mut module = Module::new( &store, br#" - (module - (func $imported (import "env" "imported") (param i32) (result i32)) - (func (export "exported") (param i32) (result i32) - (call $imported (local.get 0)) + (module + (func $imported (import "env" "imported") (param i32) (result i32)) + (func (export "exported") (param i32) (result i32) + (call $imported (local.get 0)) + ) ) - ) - "#, + "#, ) .unwrap(); module @@ -101,12 +121,14 @@ mod js { ))], }) .unwrap(); + let mut ctx = Context::new(&store, ()); let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); - let imported = Function::new(&store, imported_signature, |args| { - println!("Calling `imported`..."); + + let imported = Function::new(&mut ctx, imported_signature, |_env, args| { + log!("Calling `imported`..."); let result = args[0].unwrap_i32() * 2; - println!("Result of `imported`: {:?}", result); + log!("Result of `imported`: {:?}", result); Ok(vec![Value::I32(result)]) }); @@ -115,12 +137,12 @@ mod js { "imported" => imported, } }; - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let exported = instance.exports.get_function("exported").unwrap(); let expected = vec![Val::I32(6)].into_boxed_slice(); - assert_eq!(exported.call(&[Val::I32(3)]), Ok(expected)); + assert_eq!(exported.call(&mut ctx, &[Val::I32(3)]).unwrap(), expected); } // We comment it for now because in old versions of Node, only single return values are supported @@ -158,9 +180,9 @@ mod js { // let multivalue_signature = // FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]); // let multivalue = Function::new(&store, &multivalue_signature, |args| { - // println!("Calling `imported`..."); + // log!("Calling `imported`..."); // // let result = args[0].unwrap_i32() * ; - // // println!("Result of `imported`: {:?}", result); + // // log!("Result of `imported`: {:?}", result); // Ok(vec![args[1].clone(), args[0].clone()]) // }); @@ -189,13 +211,13 @@ mod js { let mut module = Module::new( &store, br#" - (module - (func $imported (import "env" "imported") (param i32) (result i32)) - (func (export "exported") (param i32) (result i32) - (call $imported (local.get 0)) + (module + (func $imported (import "env" "imported") (param i32) (result i32)) + (func (export "exported") (param i32) (result i32) + (call $imported (local.get 0)) + ) ) - ) - "#, + "#, ) .unwrap(); module @@ -211,35 +233,32 @@ mod js { }) .unwrap(); - #[derive(WasmerEnv, Clone)] + #[derive(Clone)] struct Env { multiplier: i32, } + let mut ctx = Context::new(&store, Env { multiplier: 3 }); + let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); - let imported = Function::new_with_env( - &store, - &imported_signature, - Env { multiplier: 3 }, - |env, args| { - println!("Calling `imported`..."); - let result = args[0].unwrap_i32() * env.multiplier; - println!("Result of `imported`: {:?}", result); - Ok(vec![Value::I32(result)]) - }, - ); + let imported = Function::new(&mut ctx, &imported_signature, |ctx, args| { + log!("Calling `imported`..."); + let result = args[0].unwrap_i32() * ctx.data().multiplier; + log!("Result of `imported`: {:?}", result); + Ok(vec![Value::I32(result)]) + }); let import_object = imports! { "env" => { "imported" => imported, } }; - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let exported = instance.exports.get_function("exported").unwrap(); let expected = vec![Val::I32(9)].into_boxed_slice(); - assert_eq!(exported.call(&[Val::I32(3)]), Ok(expected)); + assert_eq!(exported.call(&mut ctx, &[Val::I32(3)]), Ok(expected)); } #[wasm_bindgen_test] @@ -248,13 +267,13 @@ mod js { let mut module = Module::new( &store, br#" - (module - (func $imported (import "env" "imported") (param i32) (result i32)) - (func (export "exported") (param i32) (result i32) - (call $imported (local.get 0)) + (module + (func $imported (import "env" "imported") (param i32) (result i32)) + (func (export "exported") (param i32) (result i32) + (call $imported (local.get 0)) + ) ) - ) - "#, + "#, ) .unwrap(); module @@ -270,23 +289,24 @@ mod js { }) .unwrap(); - fn imported_fn(arg: u32) -> u32 { + fn imported_fn(_: ContextMut<'_, ()>, arg: u32) -> u32 { return arg + 1; } - let imported = Function::new_native(&store, imported_fn); + let mut ctx = Context::new(&store, ()); + let imported = Function::new_native(&mut ctx, imported_fn); let import_object = imports! { "env" => { "imported" => imported, } }; - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let exported = instance.exports.get_function("exported").unwrap(); let expected = vec![Val::I32(5)].into_boxed_slice(); - assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected)); + assert_eq!(exported.call(&mut ctx, &[Val::I32(4)]), Ok(expected)); } #[wasm_bindgen_test] @@ -295,13 +315,13 @@ mod js { let mut module = Module::new( &store, br#" - (module - (func $imported (import "env" "imported") (param i32) (result i32)) - (func (export "exported") (param i32) (result i32) - (call $imported (local.get 0)) + (module + (func $imported (import "env" "imported") (param i32) (result i32)) + (func (export "exported") (param i32) (result i32) + (call $imported (local.get 0)) + ) ) - ) - "#, + "#, ) .unwrap(); module @@ -317,28 +337,33 @@ mod js { }) .unwrap(); - #[derive(WasmerEnv, Clone)] + #[derive(Clone, Debug)] struct Env { multiplier: u32, } - fn imported_fn(env: &Env, arg: u32) -> u32 { - return env.multiplier * arg; + fn imported_fn(ctx: ContextMut<'_, Env>, arg: u32) -> u32 { + log!("inside imported_fn: ctx.data is {:?}", ctx.data()); + // log!("inside call id is {:?}", ctx.as_context_ref().objects().id); + return ctx.data().multiplier * arg; } - let imported = Function::new_native_with_env(&store, Env { multiplier: 3 }, imported_fn); + let mut ctx = Context::new(&store, Env { multiplier: 3 }); + + let imported = Function::new_native(&mut ctx, imported_fn); let import_object = imports! { "env" => { "imported" => imported, } }; - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let exported = instance.exports.get_function("exported").unwrap(); let expected = vec![Val::I32(12)].into_boxed_slice(); - assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected)); + ctx.data_mut().multiplier = 3; + assert_eq!(exported.call(&mut ctx, &[Val::I32(4)]), Ok(expected)); } #[wasm_bindgen_test] @@ -347,14 +372,14 @@ mod js { let mut module = Module::new( &store, br#" - (module - (func $imported (import "env" "imported") (param i32) (result i32)) - (func (export "exported") (param i32) (result i32) - (call $imported (local.get 0)) + (module + (func $imported (import "env" "imported") (param i32) (result i32)) + (func (export "exported") (param i32) (result i32) + (call $imported (local.get 0)) + ) + (memory (export "memory") 1) ) - (memory (export "memory") 1) - ) - "#, + "#, ) .unwrap(); module @@ -370,79 +395,77 @@ mod js { }) .unwrap(); - #[derive(WasmerEnv, Clone)] + #[derive(Clone, Debug)] struct Env { multiplier: u32, - #[wasmer(export)] - memory: LazyInit, + memory: Option, } - fn imported_fn(env: &Env, arg: u32) -> u32 { - let memory = env.memory_ref().unwrap(); - let memory_val = memory.uint8view().get_index(0); - return (memory_val as u32) * env.multiplier * arg; + fn imported_fn(ctx: ContextMut<'_, Env>, arg: u32) -> u32 { + let memory: &Memory = ctx.data().memory.as_ref().unwrap(); + let memory_val = memory.uint8view(&ctx).get_index(0); + return (memory_val as u32) * ctx.data().multiplier * arg; } - let imported = Function::new_native_with_env( + let mut ctx = Context::new( &store, Env { multiplier: 3, - memory: LazyInit::new(), + memory: None, }, - imported_fn, ); + let imported = Function::new_native(&mut ctx, imported_fn); let import_object = imports! { "env" => { "imported" => imported, } }; - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let memory = instance.exports.get_memory("memory").unwrap(); - assert_eq!(memory.data_size(), 65536); - let memory_val = memory.uint8view().get_index(0); + assert_eq!(memory.data_size(&ctx), 65536); + let memory_val = memory.uint8view(&ctx).get_index(0); assert_eq!(memory_val, 0); - memory.uint8view().set_index(0, 2); - let memory_val = memory.uint8view().get_index(0); + memory.uint8view(&ctx).set_index(0, 2); + let memory_val = memory.uint8view(&ctx).get_index(0); assert_eq!(memory_val, 2); + ctx.data_mut().memory = Some(memory.clone()); + let exported = instance.exports.get_function("exported").unwrap(); // It works with the provided memory let expected = vec![Val::I32(24)].into_boxed_slice(); - assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected)); + assert_eq!(exported.call(&mut ctx, &[Val::I32(4)]), Ok(expected)); // It works if we update the memory - memory.uint8view().set_index(0, 3); + memory.uint8view(&ctx).set_index(0, 3); let expected = vec![Val::I32(36)].into_boxed_slice(); - assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected)); + assert_eq!(exported.call(&mut ctx, &[Val::I32(4)]), Ok(expected)); } #[wasm_bindgen_test] fn test_unit_native_function_env() { let store = Store::default(); - #[derive(WasmerEnv, Clone)] + #[derive(Clone)] struct Env { multiplier: u32, } - fn imported_fn(env: &Env, args: &[Val]) -> Result, RuntimeError> { - let value = env.multiplier * args[0].unwrap_i32() as u32; + let mut ctx = Context::new(&store, Env { multiplier: 3 }); + + fn imported_fn(ctx: ContextMut<'_, Env>, args: &[Val]) -> Result, RuntimeError> { + let value = ctx.data().multiplier * args[0].unwrap_i32() as u32; return Ok(vec![Val::I32(value as _)]); } let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); - let imported = Function::new_with_env( - &store, - imported_signature, - Env { multiplier: 3 }, - imported_fn, - ); + let imported = Function::new(&mut ctx, imported_signature, imported_fn); let expected = vec![Val::I32(12)].into_boxed_slice(); - assert_eq!(imported.call(&[Val::I32(4)]), Ok(expected)); + assert_eq!(imported.call(&mut ctx, &[Val::I32(4)]), Ok(expected)); } #[wasm_bindgen_test] @@ -451,14 +474,14 @@ mod js { let mut module = Module::new( &store, br#" - (module - (func $imported (import "env" "imported") (param i32) (result i32)) - (func (export "exported") (param i32) (result i32) - (call $imported (local.get 0)) + (module + (func $imported (import "env" "imported") (param i32) (result i32)) + (func (export "exported") (param i32) (result i32) + (call $imported (local.get 0)) + ) + (memory (export "memory") 1) ) - (memory (export "memory") 1) - ) - "#, + "#, ) .unwrap(); module @@ -474,57 +497,58 @@ mod js { }) .unwrap(); - #[derive(WasmerEnv, Clone)] + #[derive(Clone, Debug)] struct Env { multiplier: u32, - #[wasmer(export)] - memory: LazyInit, + memory: Option, } - fn imported_fn(env: &Env, args: &[Val]) -> Result, RuntimeError> { - let memory = env.memory_ref().unwrap(); - let memory_val = memory.uint8view().get_index(0); - let value = (memory_val as u32) * env.multiplier * args[0].unwrap_i32() as u32; + fn imported_fn(ctx: ContextMut<'_, Env>, args: &[Val]) -> Result, RuntimeError> { + let memory: &Memory = ctx.data().memory.as_ref().unwrap(); + let memory_val = memory.uint8view(&ctx).get_index(0); + let value = (memory_val as u32) * ctx.data().multiplier * args[0].unwrap_i32() as u32; return Ok(vec![Val::I32(value as _)]); } - let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); - let imported = Function::new_with_env( + let mut ctx = Context::new( &store, - imported_signature, Env { multiplier: 3, - memory: LazyInit::new(), + memory: None, }, - imported_fn, ); + let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); + let imported = Function::new(&mut ctx, imported_signature, imported_fn); + let import_object = imports! { "env" => { "imported" => imported, } }; - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let memory = instance.exports.get_memory("memory").unwrap(); - assert_eq!(memory.data_size(), 65536); - let memory_val = memory.uint8view().get_index(0); + assert_eq!(memory.data_size(&ctx), 65536); + let memory_val = memory.uint8view(&ctx).get_index(0); assert_eq!(memory_val, 0); - memory.uint8view().set_index(0, 2); - let memory_val = memory.uint8view().get_index(0); + memory.uint8view(&ctx).set_index(0, 2); + let memory_val = memory.uint8view(&ctx).get_index(0); assert_eq!(memory_val, 2); + ctx.data_mut().memory = Some(memory.clone()); + let exported = instance.exports.get_function("exported").unwrap(); // It works with the provided memory let expected = vec![Val::I32(24)].into_boxed_slice(); - assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected)); + assert_eq!(exported.call(&mut ctx, &[Val::I32(4)]), Ok(expected)); // It works if we update the memory - memory.uint8view().set_index(0, 3); + memory.uint8view(&ctx).set_index(0, 3); let expected = vec![Val::I32(36)].into_boxed_slice(); - assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected)); + assert_eq!(exported.call(&mut ctx, &[Val::I32(4)]), Ok(expected)); } #[wasm_bindgen_test] @@ -533,14 +557,14 @@ mod js { let mut module = Module::new( &store, br#" - (module - (global $mut_i32_import (import "" "global") (mut i32)) - (func (export "getGlobal") (result i32) (global.get $mut_i32_import)) - (func (export "incGlobal") (global.set $mut_i32_import ( - i32.add (i32.const 1) (global.get $mut_i32_import) - ))) - ) - "#, + (module + (global $mut_i32_import (import "" "global") (mut i32)) + (func (export "getGlobal") (result i32) (global.get $mut_i32_import)) + (func (export "incGlobal") (global.set $mut_i32_import ( + i32.add (i32.const 1) (global.get $mut_i32_import) + ))) + ) + "#, ) .unwrap(); module @@ -555,33 +579,34 @@ mod js { ], }) .unwrap(); - let global = Global::new_mut(&store, Value::I32(0)); + let mut ctx = Context::new(&store, ()); + let global = Global::new_mut(&mut ctx, Value::I32(0)); let import_object = imports! { "" => { "global" => global.clone() } }; - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let get_global = instance.exports.get_function("getGlobal").unwrap(); assert_eq!( - get_global.call(&[]), + get_global.call(&mut ctx, &[]), Ok(vec![Val::I32(0)].into_boxed_slice()) ); - global.set(Value::I32(42)).unwrap(); + global.set(&mut ctx, Value::I32(42)).unwrap(); assert_eq!( - get_global.call(&[]), + get_global.call(&mut ctx, &[]), Ok(vec![Val::I32(42)].into_boxed_slice()) ); let inc_global = instance.exports.get_function("incGlobal").unwrap(); - inc_global.call(&[]).unwrap(); + inc_global.call(&mut ctx, &[]).unwrap(); assert_eq!( - get_global.call(&[]), + get_global.call(&mut ctx, &[]), Ok(vec![Val::I32(43)].into_boxed_slice()) ); - assert_eq!(global.get(), Val::I32(43)); + assert_eq!(global.get(&ctx), Val::I32(43)); } #[wasm_bindgen_test] @@ -590,28 +615,33 @@ mod js { let module = Module::new( &store, br#"(module - (func $add (import "env" "sum") (param i32 i32) (result i32)) - (func (export "add_one") (param i32) (result i32) - (call $add (local.get 0) (i32.const 1)) - ) - )"#, + (func $add (import "env" "sum") (param i32 i32) (result i32)) + (func (export "add_one") (param i32) (result i32) + (call $add (local.get 0) (i32.const 1)) + ) + )"#, ) .unwrap(); - fn sum(a: i32, b: i32) -> i32 { + fn sum(_: ContextMut<'_, ()>, a: i32, b: i32) -> i32 { a + b } + let mut ctx = Context::new(&store, ()); + let import_object = imports! { "env" => { - "sum" => Function::new_native(&store, sum), + "sum" => Function::new_native(&mut ctx, sum), } }; - let instance = Instance::new(&module, &import_object).unwrap(); - let add_one: TypedFunction = - instance.exports.get_typed_function("add_one").unwrap(); - assert_eq!(add_one.call(1), Ok(2)); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); + + let add_one: TypedFunction = instance + .exports + .get_typed_function(&ctx, "add_one") + .unwrap(); + assert_eq!(add_one.call(&mut ctx, 1), Ok(2)); } #[wasm_bindgen_test] @@ -620,39 +650,45 @@ mod js { let module = Module::new( &store, br#" -(module - (type $run_t (func (param i32 i32) (result i32))) - (type $early_exit_t (func (param) (result))) - (import "env" "early_exit" (func $early_exit (type $early_exit_t))) - (func $run (type $run_t) (param $x i32) (param $y i32) (result i32) - (call $early_exit) - (i32.add - local.get $x - local.get $y)) - (export "run" (func $run))) -"#, + (module + (type $run_t (func (param i32 i32) (result i32))) + (type $early_exit_t (func (param) (result))) + (import "env" "early_exit" (func $early_exit (type $early_exit_t))) + (func $run (type $run_t) (param $x i32) (param $y i32) (result i32) + (call $early_exit) + (i32.add + local.get $x + local.get $y)) + (export "run" (func $run))) + "#, ) .unwrap(); - fn early_exit() { + fn early_exit(_: ContextMut<'_, ()>) { panic!("Do panic") } + let mut ctx = Context::new(&store, ()); let import_object = imports! { "env" => { - "early_exit" => Function::new_native(&store, early_exit), + "early_exit" => Function::new_native(&mut ctx, early_exit), } }; - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); let run_func: TypedFunction<(i32, i32), i32> = - instance.exports.get_typed_function("run").unwrap(); + instance.exports.get_typed_function(&ctx, "run").unwrap(); - assert!(run_func.call(1, 7).is_err(), "Expected early termination",); + assert!( + run_func.call(&mut ctx, 1, 7).is_err(), + "Expected early termination", + ); let run_func = instance.exports.get_function("run").unwrap(); assert!( - run_func.call(&[Val::I32(1), Val::I32(7)]).is_err(), + run_func + .call(&mut ctx, &[Val::I32(1), Val::I32(7)]) + .is_err(), "Expected early termination", ); } @@ -663,20 +699,22 @@ mod js { let module = Module::new( &store, br#" -(module - (type $run_t (func (param i32 i32) (result i32))) - (type $early_exit_t (func (param) (result))) - (import "env" "early_exit" (func $early_exit (type $early_exit_t))) - (func $run (type $run_t) (param $x i32) (param $y i32) (result i32) - (call $early_exit) - (i32.add - local.get $x - local.get $y)) - (export "run" (func $run))) -"#, + (module + (type $run_t (func (param i32 i32) (result i32))) + (type $early_exit_t (func (param) (result))) + (import "env" "early_exit" (func $early_exit (type $early_exit_t))) + (func $run (type $run_t) (param $x i32) (param $y i32) (result i32) + (call $early_exit) + (i32.add + local.get $x + local.get $y)) + (export "run" (func $run))) + "#, ) .unwrap(); + let mut ctx = Context::new(&store, ()); + use std::fmt; #[derive(Debug, Clone, Copy)] @@ -690,16 +728,17 @@ mod js { impl std::error::Error for ExitCode {} - fn early_exit() -> Result<(), ExitCode> { + fn early_exit(_: ContextMut<'_, ()>) -> Result<(), ExitCode> { Err(ExitCode(1)) } let import_object = imports! { "env" => { - "early_exit" => Function::new_native(&store, early_exit), + "early_exit" => Function::new_native(&mut ctx, early_exit), } }; - let instance = Instance::new(&module, &import_object).unwrap(); + + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); fn test_result(result: Result) { match result { @@ -725,11 +764,11 @@ mod js { } let run_func: TypedFunction<(i32, i32), i32> = - instance.exports.get_typed_function("run").unwrap(); - test_result(run_func.call(1, 7)); + instance.exports.get_typed_function(&ctx, "run").unwrap(); + test_result(run_func.call(&mut ctx, 1, 7)); let run_func = instance.exports.get_function("run").unwrap(); - test_result(run_func.call(&[Val::I32(1), Val::I32(7)])); + test_result(run_func.call(&mut ctx, &[Val::I32(1), Val::I32(7)])); } #[wasm_bindgen_test] @@ -753,7 +792,8 @@ mod js { .unwrap(); let import_object = imports! {}; - let result = Instance::new(&module, &import_object); + let mut ctx = Context::new(&store, ()); + let result = Instance::new(&mut ctx, &module, &import_object); let err = result.unwrap_err(); assert!(format!("{:?}", err).contains("zero")) } diff --git a/lib/api/tests/sys_export.rs b/lib/api/tests/sys_export.rs index ca9a13850a7..b9c20988922 100644 --- a/lib/api/tests/sys_export.rs +++ b/lib/api/tests/sys_export.rs @@ -106,10 +106,9 @@ mod sys { #[test] fn strong_weak_behavior_works_memory() -> Result<()> { - #[derive(Clone, Debug, WasmerEnv, Default)] + #[derive(Clone, Debug, Default)] struct MemEnv { - #[wasmer(export)] - memory: LazyInit, + memory: Option, } let host_fn = |env: &MemEnv| { @@ -129,7 +128,7 @@ mod sys { &module, &imports! { "env" => { - "host_fn" => Function::new_native_with_env(&store, env, host_fn) + "host_fn" => Function::new_native(&store, env, host_fn) } }, )?; @@ -150,10 +149,9 @@ mod sys { #[test] fn strong_weak_behavior_works_global() -> Result<()> { - #[derive(Clone, Debug, WasmerEnv, Default)] + #[derive(Clone, Debug, Default)] struct GlobalEnv { - #[wasmer(export)] - global: LazyInit, + global: Option, } let host_fn = |env: &GlobalEnv| { @@ -173,7 +171,7 @@ mod sys { &module, &imports! { "env" => { - "host_fn" => Function::new_native_with_env(&store, env, host_fn) + "host_fn" => Function::new_native(&store, env, host_fn) } }, )?; @@ -194,10 +192,9 @@ mod sys { #[test] fn strong_weak_behavior_works_table() -> Result<()> { - #[derive(Clone, WasmerEnv, Default)] + #[derive(Clone, Default)] struct TableEnv { - #[wasmer(export)] - table: LazyInit
, + table: Option
, } let host_fn = |env: &TableEnv| { @@ -217,7 +214,7 @@ mod sys { &module, &imports! { "env" => { - "host_fn" => Function::new_native_with_env(&store, env, host_fn) + "host_fn" => Function::new_native(&store, env, host_fn) } }, )?; @@ -238,10 +235,9 @@ mod sys { #[test] fn strong_weak_behavior_works_function() -> Result<()> { - #[derive(Clone, WasmerEnv, Default)] + #[derive(Clone, Default)] struct FunctionEnv { - #[wasmer(export)] - call_host_fn: LazyInit, + call_host_fn: Option, } let host_fn = |env: &FunctionEnv| { @@ -261,7 +257,7 @@ mod sys { &module, &imports! { "env" => { - "host_fn" => Function::new_native_with_env(&store, env, host_fn) + "host_fn" => Function::new_native(&store, env, host_fn) } }, )?; @@ -282,10 +278,9 @@ mod sys { #[test] fn strong_weak_behavior_works_native_function() -> Result<()> { - #[derive(Clone, WasmerEnv, Default)] + #[derive(Clone, Default)] struct FunctionEnv { - #[wasmer(export)] - call_host_fn: LazyInit>, + call_host_fn: Option>, } let host_fn = |env: &FunctionEnv| { @@ -314,7 +309,7 @@ mod sys { &module, &imports! { "env" => { - "host_fn" => Function::new_native_with_env(&store, env, host_fn) + "host_fn" => Function::new_native(&store, env, host_fn) } }, )?; diff --git a/lib/api/tests/sys_externals.rs b/lib/api/tests/sys_externals.rs index 45ad77da476..fffa9b62e59 100644 --- a/lib/api/tests/sys_externals.rs +++ b/lib/api/tests/sys_externals.rs @@ -211,19 +211,18 @@ mod sys { #[test] fn function_new_env() -> Result<()> { let store = Store::default(); - #[derive(Clone, WasmerEnv)] + #[derive(Clone)] struct MyEnv {} let my_env = MyEnv {}; - let function = Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| {}); + let function = Function::new_native(&store, my_env.clone(), |_env: &MyEnv| {}); assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![])); - let function = - Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv, _a: i32| {}); + let function = Function::new_native(&store, my_env.clone(), |_env: &MyEnv, _a: i32| {}); assert_eq!( function.ty().clone(), FunctionType::new(vec![Type::I32], vec![]) ); - let function = Function::new_native_with_env( + let function = Function::new_native( &store, my_env.clone(), |_env: &MyEnv, _a: i32, _b: i64, _c: f32, _d: f64| {}, @@ -232,14 +231,13 @@ mod sys { function.ty().clone(), FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) ); - let function = - Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| -> i32 { 1 }); + let function = Function::new_native(&store, my_env.clone(), |_env: &MyEnv| -> i32 { 1 }); assert_eq!( function.ty().clone(), FunctionType::new(vec![], vec![Type::I32]) ); let function = - Function::new_native_with_env(&store, my_env, |_env: &MyEnv| -> (i32, i64, f32, f64) { + Function::new_native(&store, my_env, |_env: &MyEnv| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }); assert_eq!( @@ -284,13 +282,13 @@ mod sys { #[test] fn function_new_dynamic_env() -> Result<()> { let store = Store::default(); - #[derive(Clone, WasmerEnv)] + #[derive(Clone)] struct MyEnv {} let my_env = MyEnv {}; // Using &FunctionType signature let function_type = FunctionType::new(vec![], vec![]); - let function = Function::new_with_env( + let function = Function::new( &store, &function_type, my_env.clone(), @@ -298,7 +296,7 @@ mod sys { ); assert_eq!(function.ty().clone(), function_type); let function_type = FunctionType::new(vec![Type::I32], vec![]); - let function = Function::new_with_env( + let function = Function::new( &store, &function_type, my_env.clone(), @@ -307,7 +305,7 @@ mod sys { assert_eq!(function.ty().clone(), function_type); let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); - let function = Function::new_with_env( + let function = Function::new( &store, &function_type, my_env.clone(), @@ -315,7 +313,7 @@ mod sys { ); assert_eq!(function.ty().clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32]); - let function = Function::new_with_env( + let function = Function::new( &store, &function_type, my_env.clone(), @@ -324,7 +322,7 @@ mod sys { assert_eq!(function.ty().clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); - let function = Function::new_with_env( + let function = Function::new( &store, &function_type, my_env.clone(), @@ -334,7 +332,7 @@ mod sys { // Using array signature let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); - let function = Function::new_with_env( + let function = Function::new( &store, function_type, my_env, @@ -438,10 +436,10 @@ mod sys { #[test] fn manually_generate_wasmer_env() -> Result<()> { let store = Store::default(); - #[derive(WasmerEnv, Clone)] + #[derive(Clone)] struct MyEnv { val: u32, - memory: LazyInit, + memory: Option, } fn host_function(env: &mut MyEnv, arg1: u32, arg2: u32) -> u32 { @@ -450,7 +448,7 @@ mod sys { let mut env = MyEnv { val: 5, - memory: LazyInit::new(), + memory: None, }; let result = host_function(&mut env, 7, 9); diff --git a/lib/api/tests/sys_instance.rs b/lib/api/tests/sys_instance.rs index a89c5e6ceaf..8fd30b66a7d 100644 --- a/lib/api/tests/sys_instance.rs +++ b/lib/api/tests/sys_instance.rs @@ -43,7 +43,7 @@ mod sys { #[test] fn unit_native_function_env() -> Result<()> { let store = Store::default(); - #[derive(WasmerEnv, Clone)] + #[derive(Clone)] struct Env { multiplier: u32, } @@ -54,7 +54,7 @@ mod sys { } let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); - let imported = Function::new_with_env( + let imported = Function::new( &store, imported_signature, Env { multiplier: 3 }, diff --git a/lib/api/tests/sys_reference_types.rs b/lib/api/tests/sys_reference_types.rs index 809103bfaca..0878fb2bd96 100644 --- a/lib/api/tests/sys_reference_types.rs +++ b/lib/api/tests/sys_reference_types.rs @@ -39,11 +39,11 @@ mod sys { panic!("funcref not found!"); } - #[derive(Clone, Debug, WasmerEnv)] + #[derive(Clone, Debug)] pub struct Env(Arc); let env = Env(Arc::new(AtomicBool::new(false))); - let func_to_call = Function::new_native_with_env(&store, env.clone(), |env: &Env| -> i32 { + let func_to_call = Function::new_native(&store, env.clone(), |env: &Env| -> i32 { env.0.store(true, Ordering::SeqCst); 343 }); diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index da185329db3..e04dadb6d73 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -132,6 +132,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); mod native { use super::Type; + use crate::memory::{Memory32, Memory64, MemorySize}; use std::fmt; /// `NativeWasmType` represents a Wasm type that has a direct @@ -183,6 +184,16 @@ mod native { type Abi = Self; } + impl NativeWasmType for Memory32 { + const WASM_TYPE: Type = <::Native as NativeWasmType>::WASM_TYPE; + type Abi = <::Native as NativeWasmType>::Abi; + } + + impl NativeWasmType for Memory64 { + const WASM_TYPE: Type = <::Native as NativeWasmType>::WASM_TYPE; + type Abi = <::Native as NativeWasmType>::Abi; + } + impl NativeWasmType for Option { const WASM_TYPE: Type = T::WASM_TYPE; type Abi = T::Abi; diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 2940817142c..69c0f6b1cac 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -50,7 +50,7 @@ pub use crate::syscalls::types; pub use crate::utils::{ get_wasi_version, get_wasi_versions, is_wasi_module, is_wasix_module, WasiVersion, }; -use wasmer::{Context, ContextMut}; +use wasmer::ContextMut; pub use wasmer_vbus::{UnsupportedVirtualBus, VirtualBus}; #[deprecated(since = "2.1.0", note = "Please use `wasmer_vfs::FsError`")] pub use wasmer_vfs::FsError as WasiFsError; @@ -230,29 +230,29 @@ impl WasiEnv { } /// Get an `Imports` for a specific version of WASI detected in the module. - pub fn import_object(&mut self, module: &Module) -> Result { + pub fn import_object( + &mut self, + ctx: &mut ContextMut<'_, WasiEnv>, + module: &Module, + ) -> Result { let wasi_version = get_wasi_version(module, false).ok_or(WasiError::UnknownWasiVersion)?; - let mut context = Context::new(module.store(), self.clone()); - Ok(generate_import_object_from_ctx( - &mut context.as_context_mut(), - wasi_version, - )) + Ok(generate_import_object_from_ctx(ctx, wasi_version)) } /// Like `import_object` but containing all the WASI versions detected in /// the module. pub fn import_object_for_all_wasi_versions( &mut self, + ctx: &mut ContextMut<'_, WasiEnv>, module: &Module, ) -> Result { let wasi_versions = get_wasi_versions(module, false).ok_or(WasiError::UnknownWasiVersion)?; let mut resolver = Imports::new(); - let mut context = Context::new(module.store(), self.clone()); for version in wasi_versions.iter() { let new_import_object = - generate_import_object_from_ctx(&mut context.as_context_mut(), *version); + generate_import_object_from_ctx(&mut ctx.as_context_mut(), *version); for ((n, m), e) in new_import_object.into_iter() { resolver.define(&n, &m, e); } diff --git a/lib/wasi/tests/stdio.rs b/lib/wasi/tests/stdio.rs index 45dcd57e4de..3af490b8d74 100644 --- a/lib/wasi/tests/stdio.rs +++ b/lib/wasi/tests/stdio.rs @@ -1,6 +1,6 @@ use std::io::{Read, Write}; -use wasmer::{Instance, Module, Store}; +use wasmer::{AsContextMut, Context, Instance, Module, Store}; use wasmer_wasi::{Pipe, WasiState}; mod sys { @@ -80,15 +80,22 @@ fn test_stdout() { .finalize() .unwrap(); + // Create a context state that will hold all objects created by this Instance + let mut ctx = Context::new(&store, wasi_env.clone()); + // Generate an `ImportObject`. - let import_object = wasi_env.import_object(&module).unwrap(); + let import_object = wasi_env + .import_object(&mut ctx.as_context_mut(), &module) + .unwrap(); // Let's instantiate the module with the imports. - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); + let memory = instance.exports.get_memory("memory").unwrap(); + ctx.data_mut().set_memory(memory.clone()); // Let's call the `_start` function, which is our `main` function in Rust. let start = instance.exports.get_function("_start").unwrap(); - start.call(&[]).unwrap(); + start.call(&mut ctx, &[]).unwrap(); let mut stdout_str = String::new(); stdout.read_to_string(&mut stdout_str).unwrap(); @@ -121,15 +128,22 @@ fn test_env() { .finalize() .unwrap(); + // Create a context state that will hold all objects created by this Instance + let mut ctx = Context::new(&store, wasi_env.clone()); + // Generate an `ImportObject`. - let import_object = wasi_env.import_object(&module).unwrap(); + let import_object = wasi_env + .import_object(&mut ctx.as_context_mut(), &module) + .unwrap(); // Let's instantiate the module with the imports. - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); + let memory = instance.exports.get_memory("memory").unwrap(); + ctx.data_mut().set_memory(memory.clone()); // Let's call the `_start` function, which is our `main` function in Rust. let start = instance.exports.get_function("_start").unwrap(); - start.call(&[]).unwrap(); + start.call(&mut ctx, &[]).unwrap(); let mut stdout_str = String::new(); stdout.read_to_string(&mut stdout_str).unwrap(); @@ -152,15 +166,22 @@ fn test_stdin() { let buf = "Hello, stdin!\n".as_bytes().to_owned(); stdin.write(&buf[..]).unwrap(); + // Create a context state that will hold all objects created by this Instance + let mut ctx = Context::new(&store, wasi_env.clone()); + // Generate an `ImportObject`. - let import_object = wasi_env.import_object(&module).unwrap(); + let import_object = wasi_env + .import_object(&mut ctx.as_context_mut(), &module) + .unwrap(); // Let's instantiate the module with the imports. - let instance = Instance::new(&module, &import_object).unwrap(); + let instance = Instance::new(&mut ctx, &module, &import_object).unwrap(); + let memory = instance.exports.get_memory("memory").unwrap(); + ctx.data_mut().set_memory(memory.clone()); // Let's call the `_start` function, which is our `main` function in Rust. let start = instance.exports.get_function("_start").unwrap(); - let result = start.call(&[]); + let result = start.call(&mut ctx, &[]); assert!(!result.is_err()); // We assure stdin is now empty