use crate::js::error::InstantiationError; use crate::js::exports::Exports; use crate::js::externals::Extern; use crate::js::imports::Imports; use crate::js::module::Module; use crate::js::store::{AsStoreMut, AsStoreRef}; use js_sys::WebAssembly; use std::fmt; /// A WebAssembly Instance is a stateful, executable /// instance of a WebAssembly [`Module`]. /// /// Instance objects contain all the exported WebAssembly /// functions, memories, tables and globals that allow /// interacting with WebAssembly. /// /// Spec: #[derive(Clone)] pub struct Instance { handle: WebAssembly::Instance, module: Module, /// The exports for an instance. pub exports: Exports, } impl Instance { /// Creates a new `Instance` from a WebAssembly [`Module`] and a /// set of imports resolved by the [`Resolver`]. /// /// The resolver can be anything that implements the [`Resolver`] trait, /// so you can plug custom resolution for the imports, if you wish not /// to use [`ImportObject`]. /// /// The [`ImportObject`] is the easiest way to provide imports to the instance. /// /// [`ImportObject`]: crate::js::ImportObject /// /// ``` /// # use wasmer::{imports, Store, Module, Global, Value, Instance}; /// # fn main() -> anyhow::Result<()> { /// let mut store = Store::default(); /// let module = Module::new(&store, "(module)")?; /// let imports = imports!{ /// "host" => { /// "var" => Global::new(&store, Value::I32(2)) /// } /// }; /// let instance = Instance::new(&module, &imports)?; /// # Ok(()) /// # } /// ``` /// /// ## Errors /// /// The function can return [`InstantiationError`]s. /// /// Those are, as defined by the spec: /// * Link errors that happen when plugging the imports into the instance /// * Runtime errors that happen when running the module `start` function. pub fn new( mut store: &mut impl AsStoreMut, module: &Module, imports: &Imports, ) -> Result { let (instance, externs) = module .instantiate(&mut store, imports) .map_err(|e| InstantiationError::Start(e))?; let mut self_instance = Self::from_module_and_instance(store, module, instance)?; self_instance.ensure_memory_export(store, externs); //self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::>())?; Ok(self_instance) } /// Creates a new `Instance` from a WebAssembly [`Module`] and a /// vector of imports. /// /// ## Errors /// /// The function can return [`InstantiationError`]s. /// /// Those are, as defined by the spec: /// * Link errors that happen when plugging the imports into the instance /// * Runtime errors that happen when running the module `start` function. pub fn new_by_index( store: &mut impl AsStoreMut, module: &Module, externs: &[Extern], ) -> Result { let mut imports = Imports::new(); for (import_ty, extern_ty) in module.imports().zip(externs.iter()) { imports.define(import_ty.module(), import_ty.name(), extern_ty.clone()); } Self::new(store, module, &imports) } /// Creates a Wasmer `Instance` from a Wasmer `Module` and a WebAssembly Instance /// /// # Important /// /// Is expected that the function [`Instance::init_envs`] is run manually /// by the user in case the instance has any Wasmer imports, so the function /// environments are properly initiated. /// /// *This method is only available when targeting JS environments* pub fn from_module_and_instance( mut store: &mut impl AsStoreMut, module: &Module, instance: WebAssembly::Instance, ) -> Result { use crate::js::externals::VMExtern; let instance_exports = instance.exports(); let exports = module .exports() .map(|export_type| { let name = export_type.name(); let extern_type = export_type.ty().clone(); // Annotation is here to prevent spurious IDE warnings. #[allow(unused_unsafe)] let js_export = unsafe { js_sys::Reflect::get(&instance_exports, &name.into()) .map_err(|_e| InstantiationError::NotInExports(name.to_string()))? }; let export: VMExtern = VMExtern::from_js_value(js_export, &mut store, extern_type)?.into(); let extern_ = Extern::from_vm_extern(&mut store, export); Ok((name.to_string(), extern_)) }) .collect::>()?; Ok(Self { handle: instance, module: module.clone(), exports, }) } /// This will check the memory is correctly setup /// If the memory is imported then also export it for backwards compatibility reasons /// (many will assume the memory is always exported) - later we can remove this pub fn ensure_memory_export(&mut self, store: &mut impl AsStoreMut, externs: Vec) { if self.exports.get_memory("memory").is_err() { if let Some(memory) = externs .iter() .filter(|a| a.ty(store).memory().is_some()) .next() { self.exports.insert("memory", memory.clone()); } } } /// Gets the [`Module`] associated with this instance. pub fn module(&self) -> &Module { &self.module } /// Returns the inner WebAssembly Instance #[doc(hidden)] pub fn raw<'context>( &'context self, store: &'context impl AsStoreRef, ) -> &'context WebAssembly::Instance { &self.handle } } impl fmt::Debug for Instance { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Instance") .field("exports", &self.exports) .finish() } }