use crate::{Store, abi::Regs, store};
use anyhow::{Result, anyhow, bail};
use std::{marker::PhantomData, sync::Arc};
#[derive(Clone)]
pub struct Instance {
module: Arc<compiler::Module>,
}
impl std::fmt::Debug for Instance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Instance")
.field("functions", &self.module.program().functions.len())
.finish()
}
}
impl Instance {
pub(crate) fn new(module: Arc<compiler::Module>) -> Self {
Instance { module }
}
pub fn get_typed_func<P: Regs, R: Regs>(&self, name: &str) -> Result<TypedFunc<P, R>> {
let entry = self
.module
.entry(name)
.ok_or_else(|| anyhow!("no exported function named {name:?}"))?;
if P::COUNT > rv::REGISTER_ARGS {
bail!(
"a guest call takes at most {} arguments, {} given",
rv::REGISTER_ARGS,
P::COUNT
);
}
if R::COUNT > translator::RESULT_REGS {
bail!(
"a guest call returns at most {} values, {} requested",
translator::RESULT_REGS,
R::COUNT
);
}
Ok(TypedFunc {
module: self.module.clone(),
entry,
name: name.to_string(),
marker: PhantomData,
})
}
pub fn run<T>(&self, store: &mut Store<T>) -> Result<()> {
self.check(store)?;
let entry = self
.module
.entry_at(self.module.program().entry)
.ok_or_else(|| anyhow!("the entry point is not a compiled function"))?;
store::enter::<T, (), ()>(store, entry, ())
}
pub fn exports(&self) -> impl Iterator<Item = &str> {
self.module
.program()
.functions
.values()
.map(|f| f.name.as_str())
}
fn check<T>(&self, store: &Store<T>) -> Result<()> {
let state = store
.state
.as_ref()
.ok_or_else(|| anyhow!("store has no instance; call Linker::instantiate first"))?;
if !Arc::ptr_eq(&state.module, &self.module) {
bail!("this instance belongs to a different store");
}
Ok(())
}
}
pub struct TypedFunc<P, R> {
module: Arc<compiler::Module>,
entry: *const u8,
name: String,
marker: PhantomData<fn(P) -> R>,
}
unsafe impl<P, R> Send for TypedFunc<P, R> {}
unsafe impl<P, R> Sync for TypedFunc<P, R> {}
impl<P, R> std::fmt::Debug for TypedFunc<P, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TypedFunc")
.field("name", &self.name)
.finish_non_exhaustive()
}
}
impl<P: Regs, R: Regs> TypedFunc<P, R> {
pub fn call<T>(&self, store: &mut Store<T>, params: P) -> Result<R> {
let state = store
.state
.as_ref()
.ok_or_else(|| anyhow!("store has no instance; call Linker::instantiate first"))?;
if !Arc::ptr_eq(&state.module, &self.module) {
bail!(
"{} belongs to a different instance than this store",
self.name
);
}
store::enter::<T, P, R>(store, self.entry, params)
}
pub fn name(&self) -> &str {
&self.name
}
}