use crate::utils;
use anyhow::{anyhow, Error};
use std::path::Path;
use wasmi::{CompilationMode, Config, ExternType, Func, FuncType, Instance, Module, Store};
use wasmi_wasi::WasiCtx;
pub struct Context {
module: Module,
store: Store<WasiCtx>,
instance: Instance,
}
impl Context {
pub fn new(
wasm_file: &Path,
wasi_ctx: WasiCtx,
fuel: Option<u64>,
compilation_mode: CompilationMode,
) -> Result<Self, Error> {
let mut config = Config::default();
if fuel.is_some() {
config.consume_fuel(true);
}
config.compilation_mode(compilation_mode);
let engine = wasmi::Engine::new(&config);
let wasm_bytes = utils::read_wasm_or_wat(wasm_file)?;
let module = wasmi::Module::new(&engine, &wasm_bytes[..]).map_err(|error| {
anyhow!("failed to parse and validate Wasm module {wasm_file:?}: {error}")
})?;
let mut store = wasmi::Store::new(&engine, wasi_ctx);
if let Some(fuel) = fuel {
store.set_fuel(fuel).unwrap_or_else(|error| {
panic!("error: fuel metering is enabled but encountered: {error}")
});
}
let mut linker = <wasmi::Linker<WasiCtx>>::new(&engine);
wasmi_wasi::add_to_linker(&mut linker, |ctx| ctx)
.map_err(|error| anyhow!("failed to add WASI definitions to the linker: {error}"))?;
let instance = linker
.instantiate(&mut store, &module)
.and_then(|pre| pre.start(&mut store))
.map_err(|error| anyhow!("failed to instantiate and start the Wasm module: {error}"))?;
Ok(Self {
module,
store,
instance,
})
}
pub fn exported_funcs(&self) -> impl Iterator<Item = (&str, FuncType)> {
self.module.exports().filter_map(|export| {
let name = export.name();
match export.ty() {
ExternType::Func(func_type) => Some((name, func_type.clone())),
_ => None,
}
})
}
pub fn store(&self) -> &Store<WasiCtx> {
&self.store
}
pub fn store_mut(&mut self) -> &mut Store<WasiCtx> {
&mut self.store
}
pub fn get_func(&self, name: &str) -> Result<Func, Error> {
self.instance
.get_func(&self.store, name)
.ok_or_else(|| anyhow!("failed to find function named {name:?} in the Wasm module"))
}
}