deterministic-wasi-ctx 1.2.0

A wasmtime-wasi WasiCtx implementation that is fully deterministic
Documentation
use anyhow::Result;
use std::{fs, path::Path};
use wasmtime::*;
use wasmtime_wasi::{preview1::WasiP1Ctx, WasiCtxBuilder};

pub fn test_instance<Params, Results, F>(module_name: &str, testcase: F) -> Result<()>
where
    Params: WasmParams,
    Results: WasmResults,
    F: Fn(Box<dyn FnOnce(&str, Params) -> Result<Results>>) -> Result<()>,
{
    let module_path = Path::new("../../target/wasm32-wasip1/debug").join(module_name);
    let bytes = fs::read(module_path)?;
    test_instance_with_bytes(&bytes, testcase)
}

pub fn test_instance_with_bytes<Params, Results, F>(bytes: &[u8], testcase: F) -> Result<()>
where
    Params: WasmParams,
    Results: WasmResults,
    F: Fn(Box<dyn FnOnce(&str, Params) -> Result<Results>>) -> Result<()>,
{
    let engine = Engine::default();
    let module = Module::new(&engine, bytes)?;

    #[cfg(feature = "wasi-common")]
    {
        let (store, instance) = create_instance_with_legacy_ctx(&engine, &module)?;
        testcase(Box::new(move |func_name, params| {
            invoke_func(store, instance, func_name, params)
        }))?;
    }

    let (store, instance) = create_instance(&engine, &module)?;
    testcase(Box::new(move |func_name, params| {
        invoke_func(store, instance, func_name, params)
    }))?;

    Ok(())
}

#[cfg(feature = "wasi-common")]
fn create_instance_with_legacy_ctx(
    engine: &Engine,
    module: &Module,
) -> Result<(Store<wasi_common::WasiCtx>, Instance)> {
    let wasi = deterministic_wasi_ctx::build_wasi_ctx();

    let mut linker = Linker::new(&engine);
    wasi_common::sync::add_to_linker(&mut linker, |s| s)?;

    setup_store_and_instance(engine, module, wasi, &mut linker)
}

fn create_instance(engine: &Engine, module: &Module) -> Result<(Store<WasiP1Ctx>, Instance)> {
    let mut wasi_builder = WasiCtxBuilder::new();
    deterministic_wasi_ctx::add_determinism_to_wasi_ctx_builder(&mut wasi_builder);
    let wasi = wasi_builder.build_p1();

    let mut linker = Linker::new(&engine);
    wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |s| s)?;
    deterministic_wasi_ctx::replace_scheduling_functions(&mut linker)?;

    setup_store_and_instance(engine, module, wasi, &mut linker)
}

fn setup_store_and_instance<T>(
    engine: &Engine,
    module: &Module,
    wasi: T,
    linker: &mut Linker<T>,
) -> Result<(Store<T>, Instance)> {
    let mut store = Store::new(&engine, wasi);
    let instance = linker.instantiate(&mut store, &module)?;
    Ok((store, instance))
}

fn invoke_func<Params, Results>(
    mut store: impl AsContextMut,
    instance: Instance,
    func_name: &str,
    params: Params,
) -> Result<Results>
where
    Params: WasmParams,
    Results: WasmResults,
{
    let answer = instance
        .get_func(&mut store, func_name)
        .unwrap_or_else(|| panic!("`{}` was not an exported function", func_name));
    let answer = answer.typed::<Params, Results>(&store)?;
    answer.call(&mut store, params)
}