use crate::{InstanceState, SnapshotVal, Wizer};
use anyhow::Context;
use wasmtime::{Extern, Instance, Module, Result, Store, Val};
impl Wizer {
pub async fn run<T: Send>(
&self,
store: &mut Store<T>,
wasm: &[u8],
instantiate: impl AsyncFnOnce(&mut Store<T>, &Module) -> Result<wasmtime::Instance>,
) -> anyhow::Result<Vec<u8>> {
let (cx, instrumented_wasm) = self.instrument(wasm)?;
let engine = store.engine();
let module = wasmtime::Module::new(engine, &instrumented_wasm)
.context("failed to compile the Wasm module")?;
self.validate_init_func(&module)?;
let instance = instantiate(store, &module).await?;
self.initialize(store, &instance).await?;
self.snapshot(cx, &mut WasmtimeWizer { store, instance })
.await
}
fn validate_init_func(&self, module: &wasmtime::Module) -> anyhow::Result<()> {
log::debug!("Validating the exported initialization function");
match module.get_export(self.get_init_func()) {
Some(wasmtime::ExternType::Func(func_ty)) => {
if func_ty.params().len() != 0 || func_ty.results().len() != 0 {
anyhow::bail!(
"the Wasm module's `{}` function export does not have type `[] -> []`",
self.get_init_func()
);
}
}
Some(_) => anyhow::bail!(
"the Wasm module's `{}` export is not a function",
self.get_init_func()
),
None => anyhow::bail!(
"the Wasm module does not have a `{}` export",
self.get_init_func()
),
}
Ok(())
}
async fn initialize<T: Send>(
&self,
store: &mut Store<T>,
instance: &wasmtime::Instance,
) -> anyhow::Result<()> {
log::debug!("Calling the initialization function");
if let Some(export) = instance.get_export(&mut *store, "_initialize") {
if let Extern::Func(func) = export {
func.typed::<(), ()>(&store)?
.call_async(&mut *store, ())
.await
.context("calling the Reactor initialization function")?;
if self.get_init_func() == "_initialize" {
return Ok(());
}
}
}
let init_func = instance
.get_typed_func::<(), ()>(&mut *store, self.get_init_func())
.expect("checked by `validate_init_func`");
init_func
.call_async(&mut *store, ())
.await
.with_context(|| format!("the `{}` function trapped", self.get_init_func()))?;
Ok(())
}
}
pub struct WasmtimeWizer<'a, T: 'static> {
pub store: &'a mut Store<T>,
pub instance: Instance,
}
impl<T: Send> InstanceState for WasmtimeWizer<'_, T> {
async fn global_get(&mut self, name: &str) -> SnapshotVal {
let global = self.instance.get_global(&mut *self.store, name).unwrap();
match global.get(&mut *self.store) {
Val::I32(x) => SnapshotVal::I32(x),
Val::I64(x) => SnapshotVal::I64(x),
Val::F32(x) => SnapshotVal::F32(x),
Val::F64(x) => SnapshotVal::F64(x),
Val::V128(x) => SnapshotVal::V128(x.as_u128()),
_ => panic!("unsupported global value type"),
}
}
async fn memory_contents(&mut self, name: &str, contents: impl FnOnce(&[u8]) + Send) {
let memory = self.instance.get_memory(&mut *self.store, name).unwrap();
contents(memory.data(&self.store))
}
}