use crate::js::env::HostEnvInitError;
use crate::js::export::Export;
use crate::js::exports::Exports;
use crate::js::externals::Extern;
use crate::js::module::Module;
use crate::js::resolver::Resolver;
use crate::js::store::Store;
use crate::js::trap::RuntimeError;
use js_sys::WebAssembly;
use std::fmt;
#[cfg(feature = "std")]
use thiserror::Error;
#[derive(Clone)]
pub struct Instance {
instance: WebAssembly::Instance,
module: Module,
pub exports: Exports,
}
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
pub enum InstantiationError {
#[cfg_attr(feature = "std", error("Link error: {0}"))]
Link(String),
#[cfg_attr(feature = "std", error(transparent))]
Start(RuntimeError),
#[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 {
pub fn new(
module: &Module,
resolver: &(dyn Resolver + Send + Sync),
) -> Result<Self, InstantiationError> {
let (instance, imports) = module
.instantiate(resolver)
.map_err(|e| InstantiationError::Start(e))?;
let self_instance = Self::from_module_and_instance(module, instance)?;
self_instance.init_envs(&imports)?;
Ok(self_instance)
}
pub fn from_module_and_instance(
module: &Module,
instance: WebAssembly::Instance,
) -> Result<Self, InstantiationError> {
let store = module.store();
let instance_exports = instance.exports();
let exports = module
.exports()
.map(|export_type| {
let name = export_type.name();
let extern_type = export_type.ty().clone();
let js_export =
js_sys::Reflect::get(&instance_exports, &name.into()).map_err(|_e| {
InstantiationError::Link(format!(
"Can't get {} from the instance exports",
&name
))
})?;
let export: Export = (js_export, extern_type).into();
let extern_ = Extern::from_vm_export(store, export);
Ok((name.to_string(), extern_))
})
.collect::<Result<Exports, InstantiationError>>()?;
Ok(Self {
instance,
module: module.clone(),
exports,
})
}
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(())
}
pub fn module(&self) -> &Module {
&self.module
}
pub fn store(&self) -> &Store {
self.module.store()
}
#[doc(hidden)]
pub fn raw(&self) -> &WebAssembly::Instance {
&self.instance
}
}
impl fmt::Debug for Instance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Instance")
.field("exports", &self.exports)
.finish()
}
}