use std::env::home_dir;
use snafu::{OptionExt as _, ResultExt};
use wasmtime::{
Cache, CacheConfig, Config, Engine, Store,
component::{Component, HasSelf, Linker, Val},
};
use wasmtime_wasi::{DirPerms, FilePerms, ResourceTable, WasiCtx, WasiCtxView, WasiView};
pub async fn run_wasm(wasm_bytes: &[u8]) -> Result<Val, snafu::Whatever> {
let cache = home_dir().map(|home| {
let mut cache_config = CacheConfig::new();
cache_config.with_directory(home.join(".blr").join("wasmtime_cache"));
Cache::new(cache_config).unwrap()
});
let mut config = Config::new();
config
.wasm_component_model(true)
.wasm_component_model_async(true)
.wasm_function_references(true)
.cache(cache)
.async_support(true);
let engine = Engine::new(&config)
.with_whatever_context(|err| format!("failed to initialize wasm engine: {err}"))?;
let component = Component::from_binary(&engine, wasm_bytes)
.with_whatever_context(|err| format!("failed to parse component: {err}"))?;
let mut builder = WasiCtx::builder();
builder
.preopened_dir(".", "/", DirPerms::all(), FilePerms::all())
.unwrap();
builder.inherit_stdio();
let mut store = Store::new(
&engine,
State {
ctx: builder.build(),
table: ResourceTable::new(),
df: Default::default(),
math: Default::default(),
fmt: Default::default(),
},
);
let mut linker = Linker::<State>::new(&engine);
add_blr_stdlib_to_linker(&mut linker)
.with_whatever_context(|err| format!("failed to link blr std lib: {err}"))?;
wasmtime_wasi::p2::add_to_linker_async(&mut linker).unwrap();
let instance = linker
.instantiate_async(&mut store, &component)
.await
.with_whatever_context(|err| format!("failed to instantiate component: {err}"))?;
let main = instance
.get_func(&mut store, "main")
.whatever_context("no main function")?;
let mut results = [Val::Float64(0.0)];
main.call_async(
&mut store,
&[Val::S64(-1)],
&mut results,
)
.await
.with_whatever_context(|err| format!("wasm execution failed: {err}"))?;
Ok(results[0].clone())
}
struct State {
ctx: WasiCtx,
table: ResourceTable,
df: blr_stdlib::df::Host,
math: blr_stdlib::math::Host,
fmt: blr_stdlib::fmt::Host,
}
impl WasiView for State {
fn ctx(&mut self) -> WasiCtxView<'_> {
WasiCtxView {
ctx: &mut self.ctx,
table: &mut self.table,
}
}
}
fn add_blr_stdlib_to_linker(linker: &mut Linker<State>) -> Result<(), snafu::Whatever> {
blr_stdlib::df::Df::add_to_linker::<_, HasSelf<_>>(linker, |state| &mut state.df)
.with_whatever_context(|err| format!("failed to link std::df: {err}"))?;
blr_stdlib::math::Math::add_to_linker::<_, HasSelf<_>>(linker, |state| &mut state.math)
.with_whatever_context(|err| format!("failed to link std::math: {err}"))?;
blr_stdlib::fmt::Fmt::add_to_linker::<_, HasSelf<_>>(linker, |state| &mut state.fmt)
.with_whatever_context(|err| format!("failed to link std::fmt: {err}"))?;
Ok(())
}