WASI

You can also browse this source code online and clone the wasmtime repository to run the example locally.

This example shows how to use the wasi-common crate to define WASI functions within a Linker which can then be used to instantiate a WebAssembly module.

WebAssembly module source code

For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASI Preview 1 API.

wasi.rs

fn main() {
    println!("Hello, world!");
}

Building this program generates target/wasm32-wasi/debug/wasi.wasm, used below.

Invoke the WASM module

This example shows adding and configuring the WASI imports to invoke the above WASM module.

main.rs

//! Example of instantiating a wasm module which uses WASI imports.

/*
You can execute this example with:
    cmake examples/
    cargo run --example wasi
*/

use wasi_common::sync::WasiCtxBuilder;
use wasmtime::*;

fn main() -> Result<()> {
    // Define the WASI functions globally on the `Config`.
    let engine = Engine::default();
    let mut linker = Linker::new(&engine);
    wasi_common::sync::add_to_linker(&mut linker, |s| s)?;

    // Create a WASI context and put it in a Store; all instances in the store
    // share this context. `WasiCtxBuilder` provides a number of ways to
    // configure what the target program will have access to.
    let wasi = WasiCtxBuilder::new()
        .inherit_stdio()
        .inherit_args()?
        .build();
    let mut store = Store::new(&engine, wasi);

    // Instantiate our module with the imports we've created, and run it.
    let module = Module::from_file(&engine, "target/wasm32-wasi/debug/wasi.wasm")?;
    linker.module(&mut store, "", &module)?;
    linker
        .get_default(&mut store, "")?
        .typed::<(), ()>(&store)?
        .call(&mut store, ())?;

    Ok(())
}

WASI state with other custom host state

The add_to_linker takes a second argument which is a closure to access &mut WasiCtx from within the T stored in the Store<T> itself. In the above example this is trivial because the T in Store<T> is WasiCtx itself, but you can also store other state in Store like so:

extern crate wasmtime;
extern crate wasi_common;
extern crate anyhow;
use anyhow::Result;
use std::borrow::{Borrow, BorrowMut};
use wasmtime::*;
use wasi_common::{WasiCtx, sync::WasiCtxBuilder};

struct MyState {
    message: String,
    wasi: WasiCtx,
}

fn main() -> Result<()> {
    let engine = Engine::default();
    let mut linker = Linker::new(&engine);
    wasi_common::sync::add_to_linker(&mut linker, |state: &mut MyState| &mut state.wasi)?;

    let wasi = WasiCtxBuilder::new()
        .inherit_stdio()
        .inherit_args()?
        .build();
    let mut store = Store::new(&engine, MyState {
        message: format!("hello!"),
        wasi,
    });

    // ...

let _linker: Linker<MyState> = linker;
    Ok(())
}

WASI Preview 2

An experimental implementation of the WASI Preview 2 API is also available, along with an adapter layer for WASI Preview 1 WebAssembly modules. In future this preview2 API will become the default. There are some features which are currently only accessible through the preview2 API such as async support and overriding the clock and random implementations.

Async example

This async example code shows how to use the wasmtime-wasi::preview2 module to execute the same WASI Preview 1 WebAssembly module from the example above. This example requires the wasmtime crate async feature to be enabled.

This does not require any change to the WebAssembly module, it's just the WASI API host functions which are implemented to be async. See wasmtime async support.

//! Example of instantiating a wasm module which uses WASI preview1 imports
//! implemented through the async preview2 WASI implementation.

/*
You can execute this example with:
    cmake examples/
    cargo run --example wasi-async
*/

use anyhow::Result;
use wasmtime::{Config, Engine, Linker, Module, Store};

struct WasiHostCtx {
    preview2_ctx: wasmtime_wasi::WasiCtx,
    preview2_table: wasmtime::component::ResourceTable,
    preview1_adapter: wasmtime_wasi::preview1::WasiPreview1Adapter,
}

impl wasmtime_wasi::WasiView for WasiHostCtx {
    fn table(&mut self) -> &mut wasmtime::component::ResourceTable {
        &mut self.preview2_table
    }

    fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx {
        &mut self.preview2_ctx
    }
}

impl wasmtime_wasi::preview1::WasiPreview1View for WasiHostCtx {
    fn adapter(&self) -> &wasmtime_wasi::preview1::WasiPreview1Adapter {
        &self.preview1_adapter
    }

    fn adapter_mut(&mut self) -> &mut wasmtime_wasi::preview1::WasiPreview1Adapter {
        &mut self.preview1_adapter
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    // Construct the wasm engine with async support enabled.
    let mut config = Config::new();
    config.async_support(true);
    let engine = Engine::new(&config)?;

    // Add the WASI preview1 API to the linker (will be implemented in terms of
    // the preview2 API)
    let mut linker: Linker<WasiHostCtx> = Linker::new(&engine);
    wasmtime_wasi::preview1::add_to_linker_async(&mut linker)?;

    // Add capabilities (e.g. filesystem access) to the WASI preview2 context here.
    let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new().inherit_stdio().build();

    let host_ctx = WasiHostCtx {
        preview2_ctx: wasi_ctx,
        preview2_table: wasmtime::component::ResourceTable::new(),
        preview1_adapter: wasmtime_wasi::preview1::WasiPreview1Adapter::new(),
    };
    let mut store: Store<WasiHostCtx> = Store::new(&engine, host_ctx);

    // Instantiate our 'Hello World' wasm module.
    // Note: This is a module built against the preview1 WASI API.
    let module = Module::from_file(&engine, "target/wasm32-wasi/debug/wasi.wasm")?;
    let func = linker
        .module_async(&mut store, "", &module)
        .await?
        .get_default(&mut store, "")?
        .typed::<(), ()>(&store)?;

    // Invoke the WASI program default function.
    func.call_async(&mut store, ()).await?;

    Ok(())
}

You can also browse this source code online and clone the wasmtime repository to run the example locally.