diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs index 0980d88ba7e..f8a8d1cb35d 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/externals/function.rs @@ -18,34 +18,10 @@ use std::sync::Arc; use wasmer_engine::{Export, ExportFunction, ExportFunctionMetadata}; use wasmer_vm::{ raise_user_trap, resume_panic, wasmer_call_trampoline, ImportInitializerFuncPtr, - VMCallerCheckedAnyfunc, VMDynamicFunctionContext, VMExportFunction, VMFuncRef, VMFunctionBody, + VMCallerCheckedAnyfunc, VMDynamicFunctionContext, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline, }; -/// A function defined in the Wasm module -#[derive(Clone, PartialEq, MemoryUsage)] -pub struct WasmFunctionDefinition { - // Address of the trampoline to do the call. - #[loupe(skip)] - pub(crate) trampoline: VMTrampoline, -} - -/// A function defined in the Host -#[derive(Clone, PartialEq, MemoryUsage)] -pub struct HostFunctionDefinition { - /// If the host function has a custom environment attached - pub(crate) has_env: bool, -} - -/// The inner helper -#[derive(Clone, PartialEq, MemoryUsage)] -pub enum FunctionDefinition { - /// A function defined in the Wasm side - Wasm(WasmFunctionDefinition), - /// A function defined in the Host side - Host(HostFunctionDefinition), -} - /// A WebAssembly `function` instance. /// /// A function instance is the runtime representation of a function. @@ -66,7 +42,6 @@ pub enum FunctionDefinition { #[derive(Clone, PartialEq, MemoryUsage)] pub struct Function { pub(crate) store: Store, - pub(crate) definition: FunctionDefinition, pub(crate) exported: ExportFunction, } @@ -135,6 +110,8 @@ where (env, metadata) } +impl WasmerEnv for WithoutEnv {} + impl Function { /// Creates a new host `Function` (dynamic) with the provided signature. /// @@ -174,60 +151,9 @@ impl Function { FT: Into, F: Fn(&[Val]) -> Result, RuntimeError> + 'static + Send + Sync, { - let ty: FunctionType = ty.into(); - let dynamic_ctx: VMDynamicFunctionContext = - VMDynamicFunctionContext::from_context(DynamicFunctionWithoutEnv { - func: Arc::new(func), - store: store.clone(), - function_type: ty.clone(), - }); - // We don't yet have the address with the Wasm ABI signature. - // The engine linker will replace the address with one pointing to a - // generated dynamic trampoline. - let address = std::ptr::null() as *const VMFunctionBody; - let host_env = Box::into_raw(Box::new(dynamic_ctx)) as *mut _; - let vmctx = VMFunctionEnvironment { host_env }; - let host_env_clone_fn: fn(*mut c_void) -> *mut c_void = |ptr| { - let duped_env: VMDynamicFunctionContext = unsafe { - let ptr: *mut VMDynamicFunctionContext = ptr as _; - let item: &VMDynamicFunctionContext = &*ptr; - item.clone() - }; - Box::into_raw(Box::new(duped_env)) as _ - }; - let host_env_drop_fn: fn(*mut c_void) = |ptr: *mut c_void| { - unsafe { - Box::from_raw(ptr as *mut VMDynamicFunctionContext) - }; - }; - - Self { - store: store.clone(), - definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: false }), - exported: ExportFunction { - metadata: Some(Arc::new( - // # Safety - // - All these functions work on all threads - // - The host env is `Send`. - unsafe { - ExportFunctionMetadata::new( - host_env, - None, - host_env_clone_fn, - host_env_drop_fn, - ) - }, - )), - vm_function: VMExportFunction { - address, - kind: VMFunctionKind::Dynamic, - vmctx, - signature: ty, - call_trampoline: None, - instance_ref: None, - }, - }, - } + let wrapped_func = + move |_env: &WithoutEnv, args: &[Val]| -> Result, RuntimeError> { func(args) }; + Self::new_with_env(store, ty, WithoutEnv, wrapped_func) } /// Creates a new host `Function` (dynamic) with the provided signature and environment. @@ -282,8 +208,8 @@ impl Function { Env: Sized + WasmerEnv + 'static, { let ty: FunctionType = ty.into(); - let dynamic_ctx: VMDynamicFunctionContext> = - VMDynamicFunctionContext::from_context(DynamicFunctionWithEnv { + let dynamic_ctx: VMDynamicFunctionContext> = + VMDynamicFunctionContext::from_context(DynamicFunction { env: Box::new(env), func: Arc::new(func), store: store.clone(), @@ -291,13 +217,13 @@ impl Function { }); let import_init_function_ptr: for<'a> fn(&'a mut _, &'a _) -> Result<(), _> = - |env: &mut VMDynamicFunctionContext>, + |env: &mut VMDynamicFunctionContext>, instance: &crate::Instance| { Env::init_with_instance(&mut *env.ctx.env, instance) }; let (host_env, metadata) = build_export_function_metadata::< - VMDynamicFunctionContext>, + VMDynamicFunctionContext>, >(dynamic_ctx, import_init_function_ptr); // We don't yet have the address with the Wasm ABI signature. @@ -308,10 +234,9 @@ impl Function { Self { store: store.clone(), - definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }), exported: ExportFunction { metadata: Some(Arc::new(metadata)), - vm_function: VMExportFunction { + vm_function: VMFunction { address, kind: VMFunctionKind::Dynamic, vmctx, @@ -359,13 +284,11 @@ impl Function { Self { store: store.clone(), - definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: false }), - exported: ExportFunction { // TODO: figure out what's going on in this function: it takes an `Env` // param but also marks itself as not having an env metadata: None, - vm_function: VMExportFunction { + vm_function: VMFunction { address, vmctx, signature, @@ -421,10 +344,9 @@ impl Function { Self { store: store.clone(), - definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }), exported: ExportFunction { metadata: Some(Arc::new(metadata)), - vm_function: VMExportFunction { + vm_function: VMFunction { address, kind: VMFunctionKind::Static, vmctx, @@ -469,10 +391,9 @@ impl Function { Self { store: store.clone(), - definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }), exported: ExportFunction { metadata: Some(Arc::new(metadata)), - vm_function: VMExportFunction { + vm_function: VMFunction { address, kind: VMFunctionKind::Static, vmctx, @@ -512,7 +433,7 @@ impl Function { fn call_wasm( &self, - func: &WasmFunctionDefinition, + trampoline: VMTrampoline, params: &[Val], results: &mut [Val], ) -> Result<(), RuntimeError> { @@ -560,7 +481,7 @@ impl Function { if let Err(error) = unsafe { wasmer_call_trampoline( self.exported.vm_function.vmctx, - func.trampoline, + trampoline, self.exported.vm_function.address, values_vec.as_mut_ptr() as *mut u8, ) @@ -649,34 +570,19 @@ 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> { - let mut results = vec![Val::null(); self.result_arity()]; - - match &self.definition { - FunctionDefinition::Wasm(wasm) => { - self.call_wasm(&wasm, params, &mut results)?; - } - // TODO: we can trivially hit this, look into it - _ => unimplemented!("The function definition isn't supported for the moment"), + if let Some(trampoline) = self.exported.vm_function.call_trampoline { + let mut results = vec![Val::null(); self.result_arity()]; + self.call_wasm(trampoline, params, &mut results)?; + return Ok(results.into_boxed_slice()); } - Ok(results.into_boxed_slice()) + unimplemented!("The function definition isn't supported for the moment"); } pub(crate) fn from_vm_export(store: &Store, wasmer_export: ExportFunction) -> Self { - if let Some(trampoline) = wasmer_export.vm_function.call_trampoline { - Self { - store: store.clone(), - definition: FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }), - exported: wasmer_export, - } - } else { - Self { - store: store.clone(), - definition: FunctionDefinition::Host(HostFunctionDefinition { - has_env: !wasmer_export.vm_function.vmctx.is_null(), - }), - exported: wasmer_export, - } + Self { + store: store.clone(), + exported: wasmer_export, } } @@ -798,11 +704,7 @@ impl Function { } } - Ok(NativeFunc::new( - self.store.clone(), - self.exported.clone(), - self.definition.clone(), - )) + Ok(NativeFunc::new(self.store.clone(), self.exported.clone())) } #[track_caller] @@ -840,27 +742,7 @@ pub(crate) trait VMDynamicFunction: Send + Sync { fn store(&self) -> &Store; } -#[derive(Clone)] -pub(crate) struct DynamicFunctionWithoutEnv { - #[allow(clippy::type_complexity)] - func: Arc Result, RuntimeError> + 'static + Send + Sync>, - function_type: FunctionType, - store: Store, -} - -impl VMDynamicFunction for DynamicFunctionWithoutEnv { - fn call(&self, args: &[Val]) -> Result, RuntimeError> { - (*self.func)(&args) - } - fn function_type(&self) -> &FunctionType { - &self.function_type - } - fn store(&self) -> &Store { - &self.store - } -} - -pub(crate) struct DynamicFunctionWithEnv +pub(crate) struct DynamicFunction where Env: Sized + 'static + Send + Sync, { @@ -871,7 +753,7 @@ where env: Box, } -impl Clone for DynamicFunctionWithEnv { +impl Clone for DynamicFunction { fn clone(&self) -> Self { Self { env: self.env.clone(), @@ -882,7 +764,7 @@ impl Clone for DynamicFunctionWithEn } } -impl VMDynamicFunction for DynamicFunctionWithEnv +impl VMDynamicFunction for DynamicFunction where Env: Sized + 'static + Send + Sync, { @@ -1285,6 +1167,7 @@ mod inner { /// 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 {} diff --git a/lib/api/src/externals/global.rs b/lib/api/src/externals/global.rs index 0d111e0eeea..1d03cc10a24 100644 --- a/lib/api/src/externals/global.rs +++ b/lib/api/src/externals/global.rs @@ -8,8 +8,8 @@ use crate::RuntimeError; use loupe::MemoryUsage; use std::fmt; use std::sync::Arc; -use wasmer_engine::{Export, ExportGlobal}; -use wasmer_vm::{Global as RuntimeGlobal, VMExportGlobal}; +use wasmer_engine::Export; +use wasmer_vm::{Global as RuntimeGlobal, VMGlobal}; /// A WebAssembly `global` instance. /// @@ -20,7 +20,7 @@ use wasmer_vm::{Global as RuntimeGlobal, VMExportGlobal}; #[derive(Clone, MemoryUsage)] pub struct Global { store: Store, - global: Arc, + vm_global: VMGlobal, } impl Global { @@ -75,7 +75,10 @@ impl Global { Ok(Self { store: store.clone(), - global: Arc::new(global), + vm_global: VMGlobal { + from: Arc::new(global), + instance_ref: None, + }, }) } @@ -94,7 +97,7 @@ impl Global { /// assert_eq!(v.ty(), &GlobalType::new(Type::I64, Mutability::Var)); /// ``` pub fn ty(&self) -> &GlobalType { - self.global.ty() + self.vm_global.from.ty() } /// Returns the [`Store`] where the `Global` belongs. @@ -126,7 +129,7 @@ impl Global { /// assert_eq!(g.get(), Value::I32(1)); /// ``` pub fn get(&self) -> Val { - self.global.get(&self.store) + self.vm_global.from.get(&self.store) } /// Sets a custom value [`Val`] to the runtime Global. @@ -175,17 +178,18 @@ impl Global { return Err(RuntimeError::new("cross-`Store` values are not supported")); } unsafe { - self.global + self.vm_global + .from .set(val) .map_err(|e| RuntimeError::new(format!("{}", e)))?; } Ok(()) } - pub(crate) fn from_vm_export(store: &Store, wasmer_export: ExportGlobal) -> Self { + pub(crate) fn from_vm_export(store: &Store, vm_global: VMGlobal) -> Self { Self { store: store.clone(), - global: wasmer_export.vm_global.from, + vm_global, } } @@ -202,7 +206,7 @@ impl Global { /// assert!(g.same(&g)); /// ``` pub fn same(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.global, &other.global) + Arc::ptr_eq(&self.vm_global.from, &other.vm_global.from) } } @@ -218,13 +222,7 @@ impl fmt::Debug for Global { impl<'a> Exportable<'a> for Global { fn to_export(&self) -> Export { - ExportGlobal { - vm_global: VMExportGlobal { - from: self.global.clone(), - instance_ref: None, - }, - } - .into() + self.vm_global.clone().into() } fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { diff --git a/lib/api/src/externals/memory.rs b/lib/api/src/externals/memory.rs index 8cfe9f876b3..a71653c6db7 100644 --- a/lib/api/src/externals/memory.rs +++ b/lib/api/src/externals/memory.rs @@ -6,9 +6,9 @@ use loupe::MemoryUsage; use std::convert::TryInto; use std::slice; use std::sync::Arc; -use wasmer_engine::{Export, ExportMemory}; +use wasmer_engine::Export; use wasmer_types::{Pages, ValueType}; -use wasmer_vm::{Memory as RuntimeMemory, MemoryError, VMExportMemory}; +use wasmer_vm::{MemoryError, VMMemory}; /// A WebAssembly `memory` instance. /// @@ -27,7 +27,7 @@ use wasmer_vm::{Memory as RuntimeMemory, MemoryError, VMExportMemory}; #[derive(Debug, Clone, MemoryUsage)] pub struct Memory { store: Store, - memory: Arc, + vm_memory: VMMemory, } impl Memory { @@ -51,7 +51,12 @@ impl Memory { Ok(Self { store: store.clone(), - memory, + vm_memory: VMMemory { + from: memory, + // We are creating it from the host, and therefore there is no + // associated instance with this memory + instance_ref: None, + }, }) } @@ -69,7 +74,7 @@ impl Memory { /// assert_eq!(m.ty(), &mt); /// ``` pub fn ty(&self) -> &MemoryType { - self.memory.ty() + self.vm_memory.from.ty() } /// Returns the [`Store`] where the `Memory` belongs. @@ -110,21 +115,21 @@ impl Memory { /// by resizing this Memory. #[allow(clippy::mut_from_ref)] pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { - let definition = self.memory.vmmemory(); + let definition = self.vm_memory.from.vmmemory(); let def = definition.as_ref(); slice::from_raw_parts_mut(def.base, def.current_length.try_into().unwrap()) } /// Returns the pointer to the raw bytes of the `Memory`. pub fn data_ptr(&self) -> *mut u8 { - let definition = self.memory.vmmemory(); + let definition = self.vm_memory.from.vmmemory(); let def = unsafe { definition.as_ref() }; def.base } /// Returns the size (in bytes) of the `Memory`. pub fn data_size(&self) -> u64 { - let definition = self.memory.vmmemory(); + let definition = self.vm_memory.from.vmmemory(); let def = unsafe { definition.as_ref() }; def.current_length.into() } @@ -142,7 +147,7 @@ impl Memory { /// assert_eq!(m.size(), Pages(1)); /// ``` pub fn size(&self) -> Pages { - self.memory.size() + self.vm_memory.from.size() } /// Grow memory by the specified amount of WebAssembly [`Pages`] and return @@ -179,7 +184,7 @@ impl Memory { where IntoPages: Into, { - self.memory.grow(delta.into()) + self.vm_memory.from.grow(delta.into()) } /// Return a "view" of the currently accessible memory. By @@ -221,10 +226,10 @@ impl Memory { unsafe { MemoryView::new(base as _, length as u32) } } - pub(crate) fn from_vm_export(store: &Store, wasmer_export: ExportMemory) -> Self { + pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self { Self { store: store.clone(), - memory: wasmer_export.vm_memory.from, + vm_memory, } } @@ -241,19 +246,13 @@ impl Memory { /// assert!(m.same(&m)); /// ``` pub fn same(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.memory, &other.memory) + Arc::ptr_eq(&self.vm_memory.from, &other.vm_memory.from) } } impl<'a> Exportable<'a> for Memory { fn to_export(&self) -> Export { - ExportMemory { - vm_memory: VMExportMemory { - from: self.memory.clone(), - instance_ref: None, - }, - } - .into() + self.vm_memory.clone().into() } fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { diff --git a/lib/api/src/externals/mod.rs b/lib/api/src/externals/mod.rs index ead70f2ec15..2c7e942fa46 100644 --- a/lib/api/src/externals/mod.rs +++ b/lib/api/src/externals/mod.rs @@ -47,7 +47,7 @@ impl Extern { } } - /// Create an `Extern` from an `wasmer_vm::Export`. + /// Create an `Extern` from an `wasmer_engine::Export`. pub fn from_vm_export(store: &Store, export: Export) -> Self { match export { Export::Function(f) => Self::Function(Function::from_vm_export(store, f)), diff --git a/lib/api/src/externals/table.rs b/lib/api/src/externals/table.rs index 6806067d2bc..d959212ba31 100644 --- a/lib/api/src/externals/table.rs +++ b/lib/api/src/externals/table.rs @@ -6,8 +6,8 @@ use crate::RuntimeError; use crate::TableType; use loupe::MemoryUsage; use std::sync::Arc; -use wasmer_engine::{Export, ExportTable}; -use wasmer_vm::{Table as RuntimeTable, TableElement, VMExportTable}; +use wasmer_engine::Export; +use wasmer_vm::{Table as RuntimeTable, TableElement, VMTable}; /// A WebAssembly `table` instance. /// @@ -21,7 +21,7 @@ use wasmer_vm::{Table as RuntimeTable, TableElement, VMExportTable}; #[derive(Clone, MemoryUsage)] pub struct Table { store: Store, - table: Arc, + vm_table: VMTable, } fn set_table_item( @@ -54,13 +54,16 @@ impl Table { Ok(Self { store: store.clone(), - table, + vm_table: VMTable { + from: table, + instance_ref: None, + }, }) } /// Returns the [`TableType`] of the `Table`. pub fn ty(&self) -> &TableType { - self.table.ty() + self.vm_table.from.ty() } /// Returns the [`Store`] where the `Table` belongs. @@ -70,19 +73,19 @@ impl Table { /// Retrieves an element of the table at the provided `index`. pub fn get(&self, index: u32) -> Option { - let item = self.table.get(index)?; + let item = self.vm_table.from.get(index)?; Some(ValFuncRef::from_table_reference(item, &self.store)) } /// Sets an element `val` in the Table at the provided `index`. pub fn set(&self, index: u32, val: Val) -> Result<(), RuntimeError> { let item = val.into_table_reference(&self.store)?; - set_table_item(self.table.as_ref(), index, item) + set_table_item(self.vm_table.from.as_ref(), index, item) } /// Retrieves the size of the `Table` (in elements) pub fn size(&self) -> u32 { - self.table.size() + self.vm_table.from.size() } /// Grows the size of the `Table` by `delta`, initializating @@ -96,7 +99,8 @@ impl Table { /// Returns an error if the `delta` is out of bounds for the table. pub fn grow(&self, delta: u32, init: Val) -> Result { let item = init.into_table_reference(&self.store)?; - self.table + self.vm_table + .from .grow(delta, item) .ok_or_else(|| RuntimeError::new(format!("failed to grow table by `{}`", delta))) } @@ -121,8 +125,8 @@ impl Table { )); } RuntimeTable::copy( - dst_table.table.as_ref(), - src_table.table.as_ref(), + dst_table.vm_table.from.as_ref(), + src_table.vm_table.from.as_ref(), dst_index, src_index, len, @@ -131,28 +135,22 @@ impl Table { Ok(()) } - pub(crate) fn from_vm_export(store: &Store, wasmer_export: ExportTable) -> Self { + pub(crate) fn from_vm_export(store: &Store, vm_table: VMTable) -> Self { Self { store: store.clone(), - table: wasmer_export.vm_table.from, + vm_table, } } /// Returns whether or not these two tables refer to the same data. pub fn same(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.table, &other.table) + Arc::ptr_eq(&self.vm_table.from, &other.vm_table.from) } } impl<'a> Exportable<'a> for Table { fn to_export(&self) -> Export { - ExportTable { - vm_table: VMExportTable { - from: self.table.clone(), - instance_ref: None, - }, - } - .into() + self.vm_table.clone().into() } fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { diff --git a/lib/api/src/import_object.rs b/lib/api/src/import_object.rs index 1d507baa7c9..ab4b61d36fd 100644 --- a/lib/api/src/import_object.rs +++ b/lib/api/src/import_object.rs @@ -304,7 +304,7 @@ mod test { let happy_dog_entry = resolver.resolve_by_name("dog", "happy").unwrap(); assert!(if let Export::Global(happy_dog_global) = happy_dog_entry { - happy_dog_global.vm_global.from.ty().ty == Type::I64 + happy_dog_global.from.ty().ty == Type::I64 } else { false }); @@ -330,7 +330,7 @@ mod test { let happy_dog_entry = resolver.resolve_by_name("dog", "happy").unwrap(); assert!(if let Export::Global(happy_dog_global) = happy_dog_entry { - happy_dog_global.vm_global.from.ty().ty == Type::I32 + happy_dog_global.from.ty().ty == Type::I32 } else { false }); @@ -350,7 +350,7 @@ mod test { let happy_dog_entry = imports1.resolve_by_name("dog", "happy").unwrap(); assert!(if let Export::Global(happy_dog_global) = happy_dog_entry { - happy_dog_global.vm_global.from.ty().ty == Type::I32 + happy_dog_global.from.ty().ty == Type::I32 } else { false }); diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index cc151315bc7..cb178282f81 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -322,12 +322,13 @@ pub use wasmer_types::{ }; // TODO: should those be moved into wasmer::vm as well? -pub use wasmer_vm::{raise_user_trap, MemoryError, VMExport}; +pub use wasmer_vm::{raise_user_trap, MemoryError}; pub mod vm { //! The vm module re-exports wasmer-vm types. pub use wasmer_vm::{ - Memory, MemoryError, MemoryStyle, Table, TableStyle, VMMemoryDefinition, VMTableDefinition, + Memory, MemoryError, MemoryStyle, Table, TableStyle, VMExtern, VMMemoryDefinition, + VMTableDefinition, }; } diff --git a/lib/api/src/module.rs b/lib/api/src/module.rs index ea2b8b8d8a8..8cb8fdd7939 100644 --- a/lib/api/src/module.rs +++ b/lib/api/src/module.rs @@ -264,9 +264,11 @@ impl Module { resolver: &dyn Resolver, ) -> Result { unsafe { - let instance_handle = - self.artifact - .instantiate(self.store.tunables(), resolver, Box::new(()))?; + let instance_handle = self.artifact.instantiate( + self.store.tunables(), + resolver, + Box::new((self.store.clone(), self.artifact.clone())), + )?; // After the instance handle is created, we need to initialize // the data, call the start function and so. However, if any diff --git a/lib/api/src/native.rs b/lib/api/src/native.rs index 2229dc01f64..5f46254534c 100644 --- a/lib/api/src/native.rs +++ b/lib/api/src/native.rs @@ -9,10 +9,7 @@ //! ``` use std::marker::PhantomData; -use crate::externals::function::{ - DynamicFunctionWithEnv, DynamicFunctionWithoutEnv, FunctionDefinition, HostFunctionDefinition, - VMDynamicFunction, WasmFunctionDefinition, -}; +use crate::externals::function::{DynamicFunction, VMDynamicFunction}; use crate::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList}; use std::panic::{catch_unwind, AssertUnwindSafe}; use wasmer_engine::ExportFunction; @@ -23,7 +20,6 @@ use wasmer_vm::{VMDynamicFunctionContext, VMFunctionBody, VMFunctionEnvironment, /// (using the Native ABI). #[derive(Clone)] pub struct NativeFunc { - definition: FunctionDefinition, store: Store, exported: ExportFunction, _phantom: PhantomData<(Args, Rets)>, @@ -36,19 +32,18 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - pub(crate) fn new( - store: Store, - exported: ExportFunction, - definition: FunctionDefinition, - ) -> Self { + pub(crate) fn new(store: Store, exported: ExportFunction) -> Self { Self { - definition, store, exported, _phantom: PhantomData, } } + pub(crate) fn is_host(&self) -> bool { + self.exported.vm_function.instance_ref.is_none() + } + pub(crate) fn vmctx(&self) -> VMFunctionEnvironment { self.exported.vm_function.vmctx } @@ -63,7 +58,7 @@ where } /* -impl From<&NativeFunc> for VMExportFunction +impl From<&NativeFunc> for VMFunction where Args: WasmTypeList, Rets: WasmTypeList, @@ -99,7 +94,6 @@ where fn from(other: NativeFunc) -> Self { Self { store: other.store, - definition: other.definition, exported: other.exported, } } @@ -115,98 +109,89 @@ macro_rules! impl_native_traits { { /// Call the typed func and return results. pub fn call(&self, $( $x: $x, )* ) -> Result { - match self.definition { - FunctionDefinition::Wasm(WasmFunctionDefinition { - trampoline - }) => { - // TODO: when `const fn` related features mature more, we can declare a single array - // of the correct size here. - let mut params_list = [ $( $x.to_native().to_binary() ),* ]; - let mut rets_list_array = Rets::empty_array(); - let rets_list = rets_list_array.as_mut(); - let using_rets_array; - let args_rets: &mut [i128] = if params_list.len() > rets_list.len() { - using_rets_array = false; - params_list.as_mut() - } else { - using_rets_array = true; - for (i, &arg) in params_list.iter().enumerate() { - rets_list[i] = arg; - } - rets_list.as_mut() - }; + if !self.is_host() { + // We assume the trampoline is always going to be present for + // Wasm functions + let trampoline = self.exported.vm_function.call_trampoline.expect("Call trampoline not found in wasm function"); + // TODO: when `const fn` related features mature more, we can declare a single array + // of the correct size here. + let mut params_list = [ $( $x.to_native().to_binary() ),* ]; + let mut rets_list_array = Rets::empty_array(); + let rets_list = rets_list_array.as_mut(); + let using_rets_array; + let args_rets: &mut [i128] = if params_list.len() > rets_list.len() { + using_rets_array = false; + params_list.as_mut() + } else { + using_rets_array = true; + for (i, &arg) in params_list.iter().enumerate() { + rets_list[i] = arg; + } + rets_list.as_mut() + }; + unsafe { + wasmer_vm::wasmer_call_trampoline( + self.vmctx(), + trampoline, + self.address(), + args_rets.as_mut_ptr() as *mut u8, + ) + }?; + let num_rets = rets_list.len(); + if !using_rets_array && num_rets > 0 { + let src_pointer = params_list.as_ptr(); + let rets_list = &mut rets_list_array.as_mut()[0] as *mut i128; unsafe { - wasmer_vm::wasmer_call_trampoline( - self.vmctx(), - trampoline, - self.address(), - args_rets.as_mut_ptr() as *mut u8, - ) - }?; - let num_rets = rets_list.len(); - if !using_rets_array && num_rets > 0 { - let src_pointer = params_list.as_ptr(); - let rets_list = &mut rets_list_array.as_mut()[0] as *mut i128; - unsafe { - // TODO: we can probably remove this copy by doing some clever `transmute`s. - // we know it's not overlapping because `using_rets_array` is false - std::ptr::copy_nonoverlapping(src_pointer, - rets_list, - num_rets); - } + // TODO: we can probably remove this copy by doing some clever `transmute`s. + // we know it's not overlapping because `using_rets_array` is false + std::ptr::copy_nonoverlapping(src_pointer, + rets_list, + num_rets); } - Ok(Rets::from_array(rets_list_array)) - // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead: - // but we can't currently detect whether that's safe. - // - // let results = unsafe { - // wasmer_vm::catch_traps_with_result(self.vmctx, || { - // let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address()); - // // We always pass the vmctx - // f( self.vmctx, $( $x, )* ) - // }).map_err(RuntimeError::from_trap)? - // }; - // Ok(Rets::from_c_struct(results)) - } - FunctionDefinition::Host(HostFunctionDefinition { - has_env - }) => { - match self.arg_kind() { - VMFunctionKind::Static => { - let results = catch_unwind(AssertUnwindSafe(|| unsafe { - let f = std::mem::transmute::<_, unsafe extern "C" fn( VMFunctionEnvironment, $( $x, )*) -> Rets::CStruct>(self.address()); - // We always pass the vmctx - f( self.vmctx(), $( $x, )* ) - })).map_err(|e| RuntimeError::new(format!("{:?}", e)))?; - Ok(Rets::from_c_struct(results)) - }, - VMFunctionKind::Dynamic => { - let params_list = [ $( $x.to_native().to_value() ),* ]; - let results = if !has_env { - type VMContextWithoutEnv = VMDynamicFunctionContext; - unsafe { - let ctx = self.vmctx().host_env as *mut VMContextWithoutEnv; - (*ctx).ctx.call(¶ms_list)? - } - } else { - type VMContextWithEnv = VMDynamicFunctionContext>; - unsafe { - let ctx = self.vmctx().host_env as *mut VMContextWithEnv; - (*ctx).ctx.call(¶ms_list)? - } - }; - let mut rets_list_array = Rets::empty_array(); - let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128; - for (i, ret) in results.iter().enumerate() { - unsafe { - ret.write_value_to(mut_rets.add(i)); - } + Ok(Rets::from_array(rets_list_array)) + // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead: + // but we can't currently detect whether that's safe. + // + // let results = unsafe { + // wasmer_vm::catch_traps_with_result(self.vmctx, || { + // let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address()); + // // We always pass the vmctx + // f( self.vmctx, $( $x, )* ) + // }).map_err(RuntimeError::from_trap)? + // }; + // Ok(Rets::from_c_struct(results)) + + } + else { + match self.arg_kind() { + VMFunctionKind::Static => { + let results = catch_unwind(AssertUnwindSafe(|| unsafe { + let f = std::mem::transmute::<_, unsafe extern "C" fn( VMFunctionEnvironment, $( $x, )*) -> Rets::CStruct>(self.address()); + // We always pass the vmctx + f( self.vmctx(), $( $x, )* ) + })).map_err(|e| RuntimeError::new(format!("{:?}", e)))?; + Ok(Rets::from_c_struct(results)) + }, + VMFunctionKind::Dynamic => { + let params_list = [ $( $x.to_native().to_value() ),* ]; + let results = { + type VMContextWithEnv = VMDynamicFunctionContext>; + unsafe { + let ctx = self.vmctx().host_env as *mut VMContextWithEnv; + (*ctx).ctx.call(¶ms_list)? + } + }; + let mut rets_list_array = Rets::empty_array(); + let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128; + for (i, ret) in results.iter().enumerate() { + unsafe { + ret.write_value_to(mut_rets.add(i)); } - Ok(Rets::from_array(rets_list_array)) } + Ok(Rets::from_array(rets_list_array)) } - }, + } } } diff --git a/lib/api/src/types.rs b/lib/api/src/types.rs index 5a0ab3db634..70c7a196173 100644 --- a/lib/api/src/types.rs +++ b/lib/api/src/types.rs @@ -74,7 +74,7 @@ impl ValFuncRef for Val { // TODO: // figure out if we ever need a value here: need testing with complicated import patterns metadata: None, - vm_function: wasmer_vm::VMExportFunction { + vm_function: wasmer_vm::VMFunction { address: item.func_ptr, signature, // TODO: review this comment (unclear if it's still correct): diff --git a/lib/engine/src/export.rs b/lib/engine/src/export.rs index d0014eb8c2c..62f3bde2cc3 100644 --- a/lib/engine/src/export.rs +++ b/lib/engine/src/export.rs @@ -1,9 +1,6 @@ use loupe::MemoryUsage; use std::sync::Arc; -use wasmer_vm::{ - ImportInitializerFuncPtr, VMExport, VMExportFunction, VMExportGlobal, VMExportMemory, - VMExportTable, -}; +use wasmer_vm::{ImportInitializerFuncPtr, VMExtern, VMFunction, VMGlobal, VMMemory, VMTable}; /// The value of an export passed from one instance to another. #[derive(Debug, Clone)] @@ -12,36 +9,36 @@ pub enum Export { Function(ExportFunction), /// A table export value. - Table(ExportTable), + Table(VMTable), /// A memory export value. - Memory(ExportMemory), + Memory(VMMemory), /// A global export value. - Global(ExportGlobal), + Global(VMGlobal), } -impl From for VMExport { +impl From for VMExtern { fn from(other: Export) -> Self { match other { Export::Function(ExportFunction { vm_function, .. }) => Self::Function(vm_function), - Export::Memory(ExportMemory { vm_memory }) => Self::Memory(vm_memory), - Export::Table(ExportTable { vm_table }) => Self::Table(vm_table), - Export::Global(ExportGlobal { vm_global }) => Self::Global(vm_global), + Export::Memory(vm_memory) => Self::Memory(vm_memory), + Export::Table(vm_table) => Self::Table(vm_table), + Export::Global(vm_global) => Self::Global(vm_global), } } } -impl From for Export { - fn from(other: VMExport) -> Self { +impl From for Export { + fn from(other: VMExtern) -> Self { match other { - VMExport::Function(vm_function) => Self::Function(ExportFunction { + VMExtern::Function(vm_function) => Self::Function(ExportFunction { vm_function, metadata: None, }), - VMExport::Memory(vm_memory) => Self::Memory(ExportMemory { vm_memory }), - VMExport::Table(vm_table) => Self::Table(ExportTable { vm_table }), - VMExport::Global(vm_global) => Self::Global(ExportGlobal { vm_global }), + VMExtern::Memory(vm_memory) => Self::Memory(vm_memory), + VMExtern::Table(vm_table) => Self::Table(vm_table), + VMExtern::Global(vm_global) => Self::Global(vm_global), } } } @@ -66,7 +63,7 @@ pub struct ExportFunctionMetadata { /// Thus, we only bother to store the master copy at all here so that /// we can free it. /// - /// See `wasmer_vm::export::VMExportFunction::vmctx` for the version of + /// See `wasmer_vm::export::VMFunction::vmctx` for the version of /// this pointer that is used by the VM when creating an `Instance`. pub(crate) host_env: *mut std::ffi::c_void, @@ -141,7 +138,7 @@ impl Drop for ExportFunctionMetadata { #[derive(Debug, Clone, PartialEq, MemoryUsage)] pub struct ExportFunction { /// The VM function, containing most of the data. - pub vm_function: VMExportFunction, + pub vm_function: VMFunction, /// Contains functions necessary to create and initialize host envs /// with each `Instance` as well as being responsible for the /// underlying memory of the host env. @@ -154,41 +151,20 @@ impl From for Export { } } -/// A table export value. -#[derive(Debug, Clone)] -pub struct ExportTable { - /// The VM table, containing info about the table. - pub vm_table: VMExportTable, -} - -impl From for Export { - fn from(table: ExportTable) -> Self { +impl From for Export { + fn from(table: VMTable) -> Self { Self::Table(table) } } -/// A memory export value. -#[derive(Debug, Clone)] -pub struct ExportMemory { - /// The VM memory, containing info about the table. - pub vm_memory: VMExportMemory, -} - -impl From for Export { - fn from(memory: ExportMemory) -> Self { +impl From for Export { + fn from(memory: VMMemory) -> Self { Self::Memory(memory) } } -/// A global export value. -#[derive(Debug, Clone)] -pub struct ExportGlobal { - /// The VM global, containing info about the global. - pub vm_global: VMExportGlobal, -} - -impl From for Export { - fn from(global: ExportGlobal) -> Self { +impl From for Export { + fn from(global: VMGlobal) -> Self { Self::Global(global) } } diff --git a/lib/engine/src/lib.rs b/lib/engine/src/lib.rs index 203d84e868b..9cc5f1a68b9 100644 --- a/lib/engine/src/lib.rs +++ b/lib/engine/src/lib.rs @@ -33,9 +33,7 @@ pub use crate::engine::{Engine, EngineId}; pub use crate::error::{ DeserializeError, ImportError, InstantiationError, LinkError, SerializeError, }; -pub use crate::export::{ - Export, ExportFunction, ExportFunctionMetadata, ExportGlobal, ExportMemory, ExportTable, -}; +pub use crate::export::{Export, ExportFunction, ExportFunctionMetadata}; pub use crate::resolver::{ resolve_imports, ChainableNamedResolver, NamedResolver, NamedResolverChain, NullResolver, Resolver, diff --git a/lib/engine/src/resolver.rs b/lib/engine/src/resolver.rs index cc0123015f4..a447899c3ea 100644 --- a/lib/engine/src/resolver.rs +++ b/lib/engine/src/resolver.rs @@ -105,10 +105,10 @@ fn get_extern_from_import(module: &ModuleInfo, import_index: &ImportIndex) -> Ex fn get_extern_from_export(_module: &ModuleInfo, export: &Export) -> ExternType { match export { Export::Function(ref f) => ExternType::Function(f.vm_function.signature.clone()), - Export::Table(ref t) => ExternType::Table(*t.vm_table.ty()), - Export::Memory(ref m) => ExternType::Memory(*m.vm_memory.ty()), + Export::Table(ref t) => ExternType::Table(*t.ty()), + Export::Memory(ref m) => ExternType::Memory(*m.ty()), Export::Global(ref g) => { - let global = g.vm_global.from.ty(); + let global = g.from.ty(); ExternType::Global(*global) } } @@ -223,8 +223,8 @@ pub fn resolve_imports( } Export::Table(ref t) => { table_imports.push(VMTableImport { - definition: t.vm_table.from.vmtable(), - from: t.vm_table.from.clone(), + definition: t.from.vmtable(), + from: t.from.clone(), }); } Export::Memory(ref m) => { @@ -232,7 +232,7 @@ pub fn resolve_imports( ImportIndex::Memory(index) => { // Sanity-check: Ensure that the imported memory has at least // guard-page protections the importing module expects it to have. - let export_memory_style = m.vm_memory.style(); + let export_memory_style = m.style(); let import_memory_style = &memory_styles[*index]; if let ( MemoryStyle::Static { bound, .. }, @@ -257,15 +257,15 @@ pub fn resolve_imports( } memory_imports.push(VMMemoryImport { - definition: m.vm_memory.from.vmmemory(), - from: m.vm_memory.from.clone(), + definition: m.from.vmmemory(), + from: m.from.clone(), }); } Export::Global(ref g) => { global_imports.push(VMGlobalImport { - definition: g.vm_global.from.vmglobal(), - from: g.vm_global.from.clone(), + definition: g.from.vmglobal(), + from: g.from.clone(), }); } } diff --git a/lib/vm/src/export.rs b/lib/vm/src/export.rs index 806178ab47f..b316515e816 100644 --- a/lib/vm/src/export.rs +++ b/lib/vm/src/export.rs @@ -12,23 +12,23 @@ use wasmer_types::{FunctionType, MemoryType, TableType}; /// The value of an export passed from one instance to another. #[derive(Debug)] -pub enum VMExport { +pub enum VMExtern { /// A function export value. - Function(VMExportFunction), + Function(VMFunction), /// A table export value. - Table(VMExportTable), + Table(VMTable), /// A memory export value. - Memory(VMExportMemory), + Memory(VMMemory), /// A global export value. - Global(VMExportGlobal), + Global(VMGlobal), } /// A function export value. #[derive(Debug, Clone, PartialEq, MemoryUsage)] -pub struct VMExportFunction { +pub struct VMFunction { /// The address of the native-code function. pub address: *const VMFunctionBody, @@ -58,20 +58,20 @@ pub struct VMExportFunction { /// # Safety /// There is no non-threadsafe logic directly in this type. Calling the function /// may not be threadsafe. -unsafe impl Send for VMExportFunction {} +unsafe impl Send for VMFunction {} /// # Safety -/// The members of an VMExportFunction are immutable after construction. -unsafe impl Sync for VMExportFunction {} +/// The members of an VMFunction are immutable after construction. +unsafe impl Sync for VMFunction {} -impl From for VMExport { - fn from(func: VMExportFunction) -> Self { +impl From for VMExtern { + fn from(func: VMFunction) -> Self { Self::Function(func) } } /// A table export value. -#[derive(Debug, Clone)] -pub struct VMExportTable { +#[derive(Debug, Clone, MemoryUsage)] +pub struct VMTable { /// Pointer to the containing `Table`. pub from: Arc, @@ -84,15 +84,15 @@ pub struct VMExportTable { /// This is correct because there is no non-threadsafe logic directly in this type; /// correct use of the raw table from multiple threads via `definition` requires `unsafe` /// and is the responsibilty of the user of this type. -unsafe impl Send for VMExportTable {} +unsafe impl Send for VMTable {} /// # Safety /// This is correct because the values directly in `definition` should be considered immutable /// and the type is both `Send` and `Clone` (thus marking it `Sync` adds no new behavior, it /// only makes this type easier to use) -unsafe impl Sync for VMExportTable {} +unsafe impl Sync for VMTable {} -impl VMExportTable { +impl VMTable { /// Get the table type for this exported table pub fn ty(&self) -> &TableType { self.from.ty() @@ -103,21 +103,21 @@ impl VMExportTable { self.from.style() } - /// Returns whether or not the two `VMExportTable`s refer to the same Memory. + /// Returns whether or not the two `VMTable`s refer to the same Memory. pub fn same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.from, &other.from) } } -impl From for VMExport { - fn from(table: VMExportTable) -> Self { +impl From for VMExtern { + fn from(table: VMTable) -> Self { Self::Table(table) } } /// A memory export value. -#[derive(Debug, Clone)] -pub struct VMExportMemory { +#[derive(Debug, Clone, MemoryUsage)] +pub struct VMMemory { /// Pointer to the containing `Memory`. pub from: Arc, @@ -130,15 +130,15 @@ pub struct VMExportMemory { /// This is correct because there is no non-threadsafe logic directly in this type; /// correct use of the raw memory from multiple threads via `definition` requires `unsafe` /// and is the responsibilty of the user of this type. -unsafe impl Send for VMExportMemory {} +unsafe impl Send for VMMemory {} /// # Safety /// This is correct because the values directly in `definition` should be considered immutable /// and the type is both `Send` and `Clone` (thus marking it `Sync` adds no new behavior, it /// only makes this type easier to use) -unsafe impl Sync for VMExportMemory {} +unsafe impl Sync for VMMemory {} -impl VMExportMemory { +impl VMMemory { /// Get the type for this exported memory pub fn ty(&self) -> &MemoryType { self.from.ty() @@ -149,21 +149,21 @@ impl VMExportMemory { self.from.style() } - /// Returns whether or not the two `VMExportMemory`s refer to the same Memory. + /// Returns whether or not the two `VMMemory`s refer to the same Memory. pub fn same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.from, &other.from) } } -impl From for VMExport { - fn from(memory: VMExportMemory) -> Self { +impl From for VMExtern { + fn from(memory: VMMemory) -> Self { Self::Memory(memory) } } /// A global export value. -#[derive(Debug, Clone)] -pub struct VMExportGlobal { +#[derive(Debug, Clone, MemoryUsage)] +pub struct VMGlobal { /// The global declaration, used for compatibility checking. pub from: Arc, @@ -176,23 +176,23 @@ pub struct VMExportGlobal { /// This is correct because there is no non-threadsafe logic directly in this type; /// correct use of the raw global from multiple threads via `definition` requires `unsafe` /// and is the responsibilty of the user of this type. -unsafe impl Send for VMExportGlobal {} +unsafe impl Send for VMGlobal {} /// # Safety /// This is correct because the values directly in `definition` should be considered immutable /// from the perspective of users of this type and the type is both `Send` and `Clone` (thus /// marking it `Sync` adds no new behavior, it only makes this type easier to use) -unsafe impl Sync for VMExportGlobal {} +unsafe impl Sync for VMGlobal {} -impl VMExportGlobal { - /// Returns whether or not the two `VMExportGlobal`s refer to the same Global. +impl VMGlobal { + /// Returns whether or not the two `VMGlobal`s refer to the same Global. pub fn same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.from, &other.from) } } -impl From for VMExport { - fn from(global: VMExportGlobal) -> Self { +impl From for VMExtern { + fn from(global: VMGlobal) -> Self { Self::Global(global) } } diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index 4108e63d320..2b31c72af36 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -13,7 +13,7 @@ mod r#ref; pub use allocator::InstanceAllocator; pub use r#ref::InstanceRef; -use crate::export::VMExport; +use crate::export::VMExtern; use crate::func_data_registry::{FuncDataRegistry, VMFuncRef}; use crate::global::Global; use crate::imports::Imports; @@ -27,7 +27,7 @@ use crate::vmcontext::{ VMTrampoline, }; use crate::{FunctionBodyPtr, ModuleInfo, VMOffsets}; -use crate::{VMExportFunction, VMExportGlobal, VMExportMemory, VMExportTable}; +use crate::{VMFunction, VMGlobal, VMMemory, VMTable}; use loupe::{MemoryUsage, MemoryUsageTracker}; use memoffset::offset_of; use more_asserts::assert_lt; @@ -1067,7 +1067,7 @@ impl InstanceHandle { } /// Lookup an export with the given name. - pub fn lookup(&self, field: &str) -> Option { + pub fn lookup(&self, field: &str) -> Option { let export = self.module_ref().exports.get(field)?; Some(self.lookup_by_declaration(&export)) @@ -1075,7 +1075,7 @@ impl InstanceHandle { /// Lookup an export with the given export declaration. // TODO: maybe EngineExport - pub fn lookup_by_declaration(&self, export: &ExportIndex) -> VMExport { + pub fn lookup_by_declaration(&self, export: &ExportIndex) -> VMExtern { let instance = self.instance().clone(); let instance_ref = instance.as_ref(); @@ -1099,7 +1099,7 @@ impl InstanceHandle { let call_trampoline = Some(instance_ref.function_call_trampolines[*sig_index]); let signature = instance_ref.module.signatures[*sig_index].clone(); - VMExportFunction { + VMFunction { address, // Any function received is already static at this point as: // 1. All locally defined functions in the Wasm have a static signature. @@ -1120,7 +1120,7 @@ impl InstanceHandle { let import = instance_ref.imported_table(*index); import.from.clone() }; - VMExportTable { + VMTable { from, instance_ref: Some(instance), } @@ -1133,7 +1133,7 @@ impl InstanceHandle { let import = instance_ref.imported_memory(*index); import.from.clone() }; - VMExportMemory { + VMMemory { from, instance_ref: Some(instance), } @@ -1148,7 +1148,7 @@ impl InstanceHandle { import.from.clone() } }; - VMExportGlobal { + VMGlobal { from, instance_ref: Some(instance), } diff --git a/tests/compilers/imports.rs b/tests/compilers/imports.rs index 4614eb93e1d..2b2c6dfac16 100644 --- a/tests/compilers/imports.rs +++ b/tests/compilers/imports.rs @@ -390,3 +390,43 @@ fn multi_use_host_fn_manages_memory_correctly() -> Result<()> { drop(instance2); Ok(()) } + +#[test] +fn instance_local_memory_lifetime() -> Result<()> { + let store = get_store(false); + + let memory: Memory = { + let wat = r#"(module + (memory $mem 1) + (export "memory" (memory $mem)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&module, &imports! {})?; + instance.exports.get_memory("memory")?.clone() + }; + + let wat = r#"(module + (import "env" "memory" (memory $mem 1) ) + (func $get_at (type $get_at_t) (param $idx i32) (result i32) + (i32.load (local.get $idx))) + (type $get_at_t (func (param i32) (result i32))) + (type $set_at_t (func (param i32) (param i32))) + (func $set_at (type $set_at_t) (param $idx i32) (param $val i32) + (i32.store (local.get $idx) (local.get $val))) + (export "get_at" (func $get_at)) + (export "set_at" (func $set_at)) +)"#; + let module = Module::new(&store, wat)?; + let imports = imports! { + "env" => { + "memory" => memory, + }, + }; + let instance = Instance::new(&module, &imports)?; + let set_at: NativeFunc<(i32, i32), ()> = instance.exports.get_native_function("set_at")?; + let get_at: NativeFunc = instance.exports.get_native_function("get_at")?; + set_at.call(200, 123)?; + assert_eq!(get_at.call(200)?, 123); + + Ok(()) +}