WASIp2
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 wasmtime-wasi
crate to define WASI
functions within a Linker
which can then be used to instantiate a
WebAssembly component.
WebAssembly Component Source Code
For this WASI example, this Hello World program is compiled to a WebAssembly component using the WASIp2 API.
wasi.rs
fn main() { println!("Hello, world!"); }
Building instructions:
- Have Rust installed
- Add WASIp2 target if you haven't already:
rustup target add wasm32-wasip2
cargo build --target wasm32-wasip2
Building this program generates target/wasm32-wasip2/debug/wasi.wasm
, used below.
Invoke the WASM component
This example shows adding and configuring the WASI imports to invoke the above WASM component.
main.rs
//! Example of instantiating a wasm module which uses WASI imports.
/*
You can execute this example with:
cmake examples/
cargo run --example wasip2
*/
use wasmtime::component::{Component, Linker, ResourceTable};
use wasmtime::*;
use wasmtime_wasi::bindings::sync::Command;
use wasmtime_wasi::{IoView, WasiCtx, WasiCtxBuilder, WasiView};
pub struct ComponentRunStates {
// These two are required basically as a standard way to enable the impl of IoView and
// WasiView.
// impl of WasiView is required by [`wasmtime_wasi::add_to_linker_sync`]
pub wasi_ctx: WasiCtx,
pub resource_table: ResourceTable,
// You can add other custom host states if needed
}
impl IoView for ComponentRunStates {
fn table(&mut self) -> &mut ResourceTable {
&mut self.resource_table
}
}
impl WasiView for ComponentRunStates {
fn ctx(&mut self) -> &mut WasiCtx {
&mut self.wasi_ctx
}
}
fn main() -> Result<()> {
// Define the WASI functions globally on the `Config`.
let engine = Engine::default();
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker_sync(&mut linker)?;
// 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 state = ComponentRunStates {
wasi_ctx: wasi,
resource_table: ResourceTable::new(),
};
let mut store = Store::new(&engine, state);
// Instantiate our component with the imports we've created, and run it.
let component = Component::from_file(&engine, "target/wasm32-wasip2/debug/wasi.wasm")?;
let command = Command::instantiate(&mut store, &component, &linker)?;
let program_result = command.wasi_cli_run().call_run(&mut store)?;
if program_result.is_err() {
std::process::exit(1)
}
// Alternatively, instead of using `Command`, just instantiate it as a normal component
// New states
let wasi = WasiCtxBuilder::new().inherit_stdio().inherit_args().build();
let state = ComponentRunStates {
wasi_ctx: wasi,
resource_table: ResourceTable::new(),
};
let mut store = Store::new(&engine, state);
// Instantiate it as a normal component
let instance = linker.instantiate(&mut store, &component)?;
// Get the index for the exported interface
let interface_idx = instance
.get_export(&mut store, None, "wasi:cli/run@0.2.0")
.expect("Cannot get `wasi:cli/run@0.2.0` interface");
// Get the index for the exported function in the exported interface
let parent_export_idx = Some(&interface_idx);
let func_idx = instance
.get_export(&mut store, parent_export_idx, "run")
.expect("Cannot get `run` function in `wasi:cli/run@0.2.0` interface");
let func = instance
.get_func(&mut store, func_idx)
.expect("Unreachable since we've got func_idx");
// As the `run` function in `wasi:cli/run@0.2.0` takes no argument and return a WASI result that correspond to a `Result<(), ()>`
// Reference:
// * https://github.com/WebAssembly/wasi-cli/blob/main/wit/run.wit
// * Documentation for [Func::typed](https://docs.rs/wasmtime/latest/wasmtime/component/struct.Func.html#method.typed) and [ComponentNamedList](https://docs.rs/wasmtime/latest/wasmtime/component/trait.ComponentNamedList.html)
let typed = func.typed::<(), (Result<(), ()>,)>(&store)?;
let (result,) = typed.call(&mut store, ())?;
// Required, see documentation of TypedFunc::call
typed.post_return(&mut store)?;
result.map_err(|_| anyhow::anyhow!("error"))
}
Async example
This async example code shows how to use the wasmtime-wasi crate to
execute the same WASIp2 component from the example above. This example requires the wasmtime
crate async
feature to be enabled.
This does not require any change to the WASIp2 component, it's just the WASIp2 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 wasip2-async
*/
use wasmtime::component::{Component, Linker, ResourceTable};
use wasmtime::*;
use wasmtime_wasi::bindings::Command;
use wasmtime_wasi::{IoView, WasiCtx, WasiCtxBuilder, WasiView};
pub struct ComponentRunStates {
// These two are required basically as a standard way to enable the impl of IoView and
// WasiView.
// impl of WasiView is required by [`wasmtime_wasi::add_to_linker_sync`]
pub wasi_ctx: WasiCtx,
pub resource_table: ResourceTable,
// You can add other custom host states if needed
}
impl IoView for ComponentRunStates {
fn table(&mut self) -> &mut ResourceTable {
&mut self.resource_table
}
}
impl WasiView for ComponentRunStates {
fn ctx(&mut self) -> &mut WasiCtx {
&mut self.wasi_ctx
}
}
#[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)?;
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker_async(&mut linker)?;
// 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 state = ComponentRunStates {
wasi_ctx: wasi,
resource_table: ResourceTable::new(),
};
let mut store = Store::new(&engine, state);
// Instantiate our component with the imports we've created, and run it.
let component = Component::from_file(&engine, "target/wasm32-wasip2/debug/wasi.wasm")?;
let command = Command::instantiate_async(&mut store, &component, &linker).await?;
let program_result = command.wasi_cli_run().call_run(&mut store).await?;
match program_result {
Ok(()) => Ok(()),
Err(()) => std::process::exit(1),
}
}
You can also browse this source code online and clone the wasmtime repository to run the example locally.
Beyond Basics
Please see these references:
- The book for understanding the component model of WASIp2.
- Bindgen Examples for implementing WASIp2 hosts and guests.