From e1becf1a21c60cc99a9a06e338688af236192850 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Thu, 12 Dec 2024 01:04:38 +0800 Subject: [PATCH 01/17] update examples/wasi/main.rs and related doc with a runtime issue --- docs/examples-rust-wasi.md | 20 ++++++++++++----- examples/wasi/main.rs | 45 ++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index ee5dfa43abaf..82144d0688ca 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -5,33 +5,41 @@ repository to run the example locally. [code]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/wasi/main.rs -This example shows how to use the [`wasi-common`] crate to define WASI +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 module. -[`wasi-common`]: https://crates.io/crates/wasi-common +[`wasmtime-wasi`]: https://crates.io/crates/wasmtime-wasi [`Linker`]: https://docs.rs/wasmtime/*/wasmtime/struct.Linker.html ### WebAssembly module source code -For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASI Preview 1 API. +For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASI Preview 2 API. `wasi.rs` ```rust {{#include ../examples/wasi/wasm/wasi.rs}} ``` -Building this program generates `target/wasm32-wasip1/debug/wasi.wasm`, used below. +> Building instructions: +> 1. Update your Rust toolchain if it is not >= 1.83.0, as Rust 1.83.0 brings Tier 2 support for WASI Preview 2. +> `rustup update stable` +> 2. Add WASIp2 target if you haven't already: `rustup target add wasm32-wasip2` +> 3. `cargo build --target wasm32-wasip2` -### Invoke the WASM module +Building this program generates `target/wasm32-wasip2/debug/wasi.wasm`, used below. -This example shows adding and configuring the WASI imports to invoke the above WASM module. +### Invoke the WASM component + +This example shows adding and configuring the WASI imports to invoke the above WASM component. `main.rs` ```rust,ignore {{#include ../examples/wasi/main.rs}} ``` +TODO: Update the following section after `../examples/wasi/main.rs` is fixed + ## WASI state with other custom host state The [`add_to_linker`] takes a second argument which is a closure to access `&mut diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index c6a3592436fa..b4c4dc1c8a32 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -6,31 +6,52 @@ You can execute this example with: cargo run --example wasi */ -use wasi_common::sync::WasiCtxBuilder; use wasmtime::*; +use wasmtime_wasi::{WasiCtx, WasiView, WasiCtxBuilder}; +use wasmtime::component::{Component, Linker, ResourceTable}; + +pub struct ComponentRunStates { + // These two are required basically as a standard way to enable the impl of WasiView + // impl of WasiView is required by [`wasmtime_wasi::add_to_linker_sync`] + pub wasi_ctx: WasiCtx, + pub resource_table: ResourceTable, +} + +impl WasiView for ComponentRunStates { + fn table(&mut self) -> &mut ResourceTable { + &mut self.resource_table + } + 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); - wasi_common::sync::add_to_linker(&mut linker, |s| s)?; + 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()? + .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-wasip1/debug/wasi.wasm")?; - linker.module(&mut store, "", &module)?; - linker - .get_default(&mut store, "")? - .typed::<(), ()>(&store)? - .call(&mut store, ())?; + 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 instance = linker.instantiate(&mut store, &component)?; + let exp_idx= instance.get_export(&mut store, None, "wasi:cli/run@0.2.0").unwrap(); + // FIXME: Why can't I get an exported function here? The exp_idx is valid + let func = instance.get_func(&mut store, exp_idx).unwrap(); + let typed = func.typed::<(), ()>(&store)?; + typed.call(&mut store, ())?; Ok(()) } From c040b6ccc6daf391827235a21b7265ba72718bf2 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Fri, 13 Dec 2024 10:47:13 +0800 Subject: [PATCH 02/17] simplify building instruction --- docs/examples-rust-wasi.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index 82144d0688ca..da68bc929590 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -22,8 +22,7 @@ For this WASI example, this Hello World program is compiled to a WebAssembly mod ``` > Building instructions: -> 1. Update your Rust toolchain if it is not >= 1.83.0, as Rust 1.83.0 brings Tier 2 support for WASI Preview 2. -> `rustup update stable` +> 1. Have Rust installed > 2. Add WASIp2 target if you haven't already: `rustup target add wasm32-wasip2` > 3. `cargo build --target wasm32-wasip2` From 709836027ae84f026513b87558ffeb09a2d20c15 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Fri, 13 Dec 2024 10:52:50 +0800 Subject: [PATCH 03/17] fix runtime issue --- docs/examples-rust-wasi.md | 2 +- examples/wasi/main.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index da68bc929590..c0eda451126a 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -37,7 +37,7 @@ This example shows adding and configuring the WASI imports to invoke the above W {{#include ../examples/wasi/main.rs}} ``` -TODO: Update the following section after `../examples/wasi/main.rs` is fixed +TODO: Make the example a bit more generalizable, not only wasi:cli components ## WASI state with other custom host state diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index b4c4dc1c8a32..2e4aee2a8803 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -9,6 +9,7 @@ You can execute this example with: use wasmtime::*; use wasmtime_wasi::{WasiCtx, WasiView, WasiCtxBuilder}; use wasmtime::component::{Component, Linker, ResourceTable}; +use wasmtime_wasi::bindings::sync::Command; pub struct ComponentRunStates { // These two are required basically as a standard way to enable the impl of WasiView @@ -47,11 +48,10 @@ fn main() -> Result<()> { // 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 instance = linker.instantiate(&mut store, &component)?; - let exp_idx= instance.get_export(&mut store, None, "wasi:cli/run@0.2.0").unwrap(); - // FIXME: Why can't I get an exported function here? The exp_idx is valid - let func = instance.get_func(&mut store, exp_idx).unwrap(); - let typed = func.typed::<(), ()>(&store)?; - typed.call(&mut store, ())?; - Ok(()) + let command = Command::instantiate(&mut store, &component, &linker)?; + let program_result = command.wasi_cli_run().call_run(&mut store)?; + match program_result { + Ok(()) => Ok(()), + Err(()) => std::process::exit(1), + } } From 88481426691ef00cce31a0b77c9c1527fd30246a Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Sat, 14 Dec 2024 20:37:31 +0800 Subject: [PATCH 04/17] add a comment and cargo fmt --- examples/wasi/main.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index 2e4aee2a8803..1bcce80f0ade 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -6,16 +6,17 @@ You can execute this example with: cargo run --example wasi */ -use wasmtime::*; -use wasmtime_wasi::{WasiCtx, WasiView, WasiCtxBuilder}; use wasmtime::component::{Component, Linker, ResourceTable}; +use wasmtime::*; use wasmtime_wasi::bindings::sync::Command; +use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView}; pub struct ComponentRunStates { // These two are required basically as a standard way to enable the impl of 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 WasiView for ComponentRunStates { @@ -36,10 +37,7 @@ fn main() -> Result<()> { // 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 wasi = WasiCtxBuilder::new().inherit_stdio().inherit_args().build(); let state = ComponentRunStates { wasi_ctx: wasi, resource_table: ResourceTable::new(), From 5ab2c86c6aad4638116421a287b3281d4ac35aa5 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Sat, 14 Dec 2024 20:37:14 +0800 Subject: [PATCH 05/17] revamped wasi-async --- examples/wasi-async/main.rs | 72 +++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/examples/wasi-async/main.rs b/examples/wasi-async/main.rs index b1c3a4d19b34..03072c76d924 100644 --- a/examples/wasi-async/main.rs +++ b/examples/wasi-async/main.rs @@ -7,10 +7,27 @@ You can execute this example with: cargo run --example wasi-async */ -use anyhow::Result; -use wasmtime::{Config, Engine, Linker, Module, Store}; -use wasmtime_wasi::preview1::{self, WasiP1Ctx}; -use wasmtime_wasi::WasiCtxBuilder; +use wasmtime::component::{Component, Linker, ResourceTable}; +use wasmtime::*; +use wasmtime_wasi::bindings::Command; +use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView}; + +pub struct ComponentRunStates { + // These two are required basically as a standard way to enable the impl of 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 WasiView for ComponentRunStates { + fn table(&mut self) -> &mut ResourceTable { + &mut self.resource_table + } + fn ctx(&mut self) -> &mut WasiCtx { + &mut self.wasi_ctx + } +} #[tokio::main] async fn main() -> Result<()> { @@ -18,30 +35,25 @@ async fn main() -> Result<()> { 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 = Linker::new(&engine); - preview1::add_to_linker_async(&mut linker, |t| t)?; - - // Add capabilities (e.g. filesystem access) to the WASI preview2 context - // here. Here only stdio is inherited, but see docs of `WasiCtxBuilder` for - // more. - let wasi_ctx = WasiCtxBuilder::new().inherit_stdio().build_p1(); - - let mut store = Store::new(&engine, wasi_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-wasip1/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(()) + 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), + } } From 0df67563a9a220884455f0dbe04f94b97b6e437c Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Sat, 14 Dec 2024 20:51:37 +0800 Subject: [PATCH 06/17] remove the example of custom host states and add references --- docs/examples-rust-wasi.md | 71 +++++++------------------------------- 1 file changed, 12 insertions(+), 59 deletions(-) diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index c0eda451126a..e571cc8aa31f 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -1,4 +1,4 @@ -# WASI +# WASI (Preview 2) You can also [browse this source code online][code] and clone the wasmtime repository to run the example locally. @@ -12,9 +12,9 @@ WebAssembly module. [`wasmtime-wasi`]: https://crates.io/crates/wasmtime-wasi [`Linker`]: https://docs.rs/wasmtime/*/wasmtime/struct.Linker.html -### WebAssembly module source code +## WebAssembly Component Source Code -For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASI Preview 2 API. +For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASI Preview 2 (WASIp2) API. `wasi.rs` ```rust @@ -37,68 +37,15 @@ This example shows adding and configuring the WASI imports to invoke the above W {{#include ../examples/wasi/main.rs}} ``` -TODO: Make the example a bit more generalizable, not only wasi:cli components - -## 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` itself. In the above -example this is trivial because the `T` in `Store` is `WasiCtx` itself, but -you can also store other state in `Store` like so: - -[`add_to_linker`]: https://docs.rs/wasi-common/*/wasi_common/sync/fn.add_to_linker.html -[`Store`]: https://docs.rs/wasmtime/*/wasmtime/struct.Store.html -[`BorrowMut`]: https://doc.rust-lang.org/stable/std/borrow/trait.BorrowMut.html -[`WasiCtx`]: https://docs.rs/wasi-common/*/wasi_common/struct.WasiCtx.html - -```rust -# 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 = 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][code2] shows how to use the [wasmtime-wasi::preview2][`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 [async example code][code2] shows how to use the [wasmtime-wasi][`wasmtime-wasi`] module to +execute the same WASI Preview 2 WebAssembly component 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](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support). [code2]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/wasi-async/main.rs -[`preview2`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/preview2/index.html +[`wasmtime-wasi`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/preview2/index.html ```rust,ignore {{#include ../examples/wasi-async/main.rs}} @@ -106,3 +53,9 @@ This does not require any change to the WebAssembly module, it's just the WASI A You can also [browse this source code online][code2] and clone the wasmtime repository to run the example locally. + +## Beyond Basics + +Please see these references: +* The [book](https://component-model.bytecodealliance.org) for understanding the component model of WASIp2. +* [Bindgen Examples](https://docs.rs/wasmtime/latest/wasmtime/component/bindgen_examples/index.html) for implementing WASIp2 hosts and guests. \ No newline at end of file From 62703f186cc684adb77c497146fdd03d22e6e318 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Sat, 14 Dec 2024 21:36:03 +0800 Subject: [PATCH 07/17] add calling function dynamically --- examples/wasi/main.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index 1bcce80f0ade..49e7c878be7e 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -48,8 +48,31 @@ fn main() -> Result<()> { 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)?; - match program_result { - Ok(()) => Ok(()), - Err(()) => std::process::exit(1), + 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").unwrap(); + // 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").unwrap(); + let func = instance.get_func(&mut store, func_idx).unwrap(); + // 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, ())?; + result.map_err(|_| anyhow::anyhow!("error")) } From 4c6cce2b88cee8e47cb3ab6b6418160846abc36f Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Sat, 14 Dec 2024 22:51:54 +0800 Subject: [PATCH 08/17] cargo fmt --- examples/wasi/main.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index 49e7c878be7e..d6a2ec715409 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -63,10 +63,14 @@ fn main() -> Result<()> { // 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").unwrap(); + let interface_idx = instance + .get_export(&mut store, None, "wasi:cli/run@0.2.0") + .unwrap(); // 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").unwrap(); + let func_idx = instance + .get_export(&mut store, parent_export_idx, "run") + .unwrap(); let func = instance.get_func(&mut store, func_idx).unwrap(); // 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: From ceead4a1ac7f41b096d784b24c5a60f4185d5774 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Sun, 15 Dec 2024 20:38:08 +0800 Subject: [PATCH 09/17] add post_return --- examples/wasi/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index d6a2ec715409..05fa5a91cb62 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -78,5 +78,7 @@ fn main() -> Result<()> { // * 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")) } From 953740acbee61c5f1079197690d4c21c69d922be Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Tue, 17 Dec 2024 01:25:47 +0800 Subject: [PATCH 10/17] update wording --- docs/examples-rust-wasi.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index e571cc8aa31f..b10a876e9e3a 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -1,4 +1,4 @@ -# WASI (Preview 2) +# WASIp2 You can also [browse this source code online][code] and clone the wasmtime repository to run the example locally. @@ -14,7 +14,7 @@ WebAssembly module. ## WebAssembly Component Source Code -For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASI Preview 2 (WASIp2) API. +For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASIp2 API. `wasi.rs` ```rust @@ -40,9 +40,9 @@ This example shows adding and configuring the WASI imports to invoke the above W ### Async example This [async example code][code2] shows how to use the [wasmtime-wasi][`wasmtime-wasi`] module to -execute the same WASI Preview 2 WebAssembly component from the example above. This example requires the `wasmtime` crate `async` feature to be enabled. +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 WebAssembly module, it's just the WASI API host functions which are implemented to be async. See [wasmtime async support](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support). +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](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support). [code2]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/wasi-async/main.rs [`wasmtime-wasi`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/preview2/index.html From a936a8a923aa73d88183a12617474748d0332985 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Tue, 17 Dec 2024 01:29:14 +0800 Subject: [PATCH 11/17] add expects --- examples/wasi/main.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index 05fa5a91cb62..8777b88369b2 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -65,13 +65,15 @@ fn main() -> Result<()> { // Get the index for the exported interface let interface_idx = instance .get_export(&mut store, None, "wasi:cli/run@0.2.0") - .unwrap(); + .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") - .unwrap(); - let func = instance.get_func(&mut store, func_idx).unwrap(); + .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 From 4e4304ae62ad6c567d64860976325024c2ad4d77 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Tue, 17 Dec 2024 01:32:34 +0800 Subject: [PATCH 12/17] change wording from "module" to "component" --- docs/examples-rust-wasi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index b10a876e9e3a..692e45f54ceb 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -7,14 +7,14 @@ 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 module. +WebAssembly component. [`wasmtime-wasi`]: https://crates.io/crates/wasmtime-wasi [`Linker`]: https://docs.rs/wasmtime/*/wasmtime/struct.Linker.html ## WebAssembly Component Source Code -For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASIp2 API. +For this WASI example, this Hello World program is compiled to a WebAssembly component using the WASIp2 API. `wasi.rs` ```rust @@ -39,7 +39,7 @@ This example shows adding and configuring the WASI imports to invoke the above W ### Async example -This [async example code][code2] shows how to use the [wasmtime-wasi][`wasmtime-wasi`] module to +This [async example code][code2] shows how to use the [wasmtime-wasi][`wasmtime-wasi`] component 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](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support). From c8e67404c9449404d473711de119cc340a275bc5 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Tue, 17 Dec 2024 11:14:37 +0800 Subject: [PATCH 13/17] fix CMakeLists.txt --- examples/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 84a02e46fb56..c5285e51e9b7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -63,7 +63,7 @@ create_target(wasi wasi/main.c) create_rust_test(anyref) create_rust_wasm(fib-debug wasm32-unknown-unknown) create_rust_wasm(tokio wasm32-wasip1) -create_rust_wasm(wasi wasm32-wasip1) +create_rust_wasm(wasi wasm32-wasip2) create_rust_wasm(component wasm32-unknown-unknown) create_rust_test(epochs) create_rust_test(externref) From 7816c09b4b3d4dac6feca9a1aaf770163c2d14f5 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Thu, 19 Dec 2024 23:06:25 +0800 Subject: [PATCH 14/17] compile wasi example with wasip1 to make the outdated WASIp1 C example happy --- examples/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c5285e51e9b7..2eb3b9b69c0b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -63,6 +63,7 @@ create_target(wasi wasi/main.c) create_rust_test(anyref) create_rust_wasm(fib-debug wasm32-unknown-unknown) create_rust_wasm(tokio wasm32-wasip1) +create_rust_wasm(wasi wasm32-wasip1) # Make the outdated WASIp1 C example happy create_rust_wasm(wasi wasm32-wasip2) create_rust_wasm(component wasm32-unknown-unknown) create_rust_test(epochs) From 469548c796a12f864458d4a977726943f319d503 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Fri, 20 Dec 2024 21:16:46 +0800 Subject: [PATCH 15/17] rename wasi examples --- Cargo.toml | 2 +- docs/examples-c-wasi.md | 2 +- docs/examples-rust-wasi.md | 2 +- examples/CMakeLists.txt | 7 +-- examples/wasip1-async/main.rs | 47 +++++++++++++++++++ examples/{wasi => wasip1}/main.c | 0 examples/wasip1/main.rs | 36 ++++++++++++++ examples/{wasi-async => wasip2-async}/main.rs | 2 +- examples/{wasi => wasip2}/main.rs | 2 +- examples/{wasi => }/wasm/Cargo.toml | 0 examples/{wasi => }/wasm/wasi.rs | 0 11 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 examples/wasip1-async/main.rs rename examples/{wasi => wasip1}/main.c (100%) create mode 100644 examples/wasip1/main.rs rename examples/{wasi-async => wasip2-async}/main.rs (98%) rename examples/{wasi => wasip2}/main.rs (99%) rename examples/{wasi => }/wasm/Cargo.toml (100%) rename examples/{wasi => }/wasm/wasi.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index e5d524207ad4..12b0c2723d39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,7 +148,7 @@ members = [ "crates/wasi-preview1-component-adapter", "crates/wasi-preview1-component-adapter/verify", "examples/fib-debug/wasm", - "examples/wasi/wasm", + "examples/wasm", "examples/tokio/wasm", "examples/component/wasm", "examples/min-platform", diff --git a/docs/examples-c-wasi.md b/docs/examples-c-wasi.md index 70dda92c5e2e..7367fb538bca 100644 --- a/docs/examples-c-wasi.md +++ b/docs/examples-c-wasi.md @@ -10,7 +10,7 @@ This example shows off how to instantiate a wasm module using WASI imports. ## Wasm Source code ```rust,ignore -{{#include ../examples/wasi/wasm/wasi.rs}} +{{#include ../examples/wasm/wasi.rs}} ``` diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index 692e45f54ceb..ca5805520ab0 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -18,7 +18,7 @@ For this WASI example, this Hello World program is compiled to a WebAssembly com `wasi.rs` ```rust -{{#include ../examples/wasi/wasm/wasi.rs}} +{{#include ../examples/wasm/wasi.rs}} ``` > Building instructions: diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2eb3b9b69c0b..000bbdf661f5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -57,13 +57,13 @@ create_target(multi multi.c) create_target(multimemory multimemory.c) create_target(serialize serialize.c) create_target(threads threads.c) -create_target(wasi wasi/main.c) +create_target(wasip1 wasip1/main.c) # Add rust tests create_rust_test(anyref) create_rust_wasm(fib-debug wasm32-unknown-unknown) create_rust_wasm(tokio wasm32-wasip1) -create_rust_wasm(wasi wasm32-wasip1) # Make the outdated WASIp1 C example happy +create_rust_wasm(wasi wasm32-wasip1) create_rust_wasm(wasi wasm32-wasip2) create_rust_wasm(component wasm32-unknown-unknown) create_rust_test(epochs) @@ -79,6 +79,7 @@ create_rust_test(multi) create_rust_test(multimemory) create_rust_test(serialize) create_rust_test(threads) -create_rust_test(wasi) +create_rust_test(wasip1) +create_rust_test(wasip2) create_rust_test(tokio wasi-common/tokio) create_rust_test(component) diff --git a/examples/wasip1-async/main.rs b/examples/wasip1-async/main.rs new file mode 100644 index 000000000000..66d11fb7bf75 --- /dev/null +++ b/examples/wasip1-async/main.rs @@ -0,0 +1,47 @@ +//! 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 wasip1-async +*/ + +use anyhow::Result; +use wasmtime::{Config, Engine, Linker, Module, Store}; +use wasmtime_wasi::preview1::{self, WasiP1Ctx}; +use wasmtime_wasi::WasiCtxBuilder; + +#[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 = Linker::new(&engine); + preview1::add_to_linker_async(&mut linker, |t| t)?; + + // Add capabilities (e.g. filesystem access) to the WASI preview2 context + // here. Here only stdio is inherited, but see docs of `WasiCtxBuilder` for + // more. + let wasi_ctx = WasiCtxBuilder::new().inherit_stdio().build_p1(); + + let mut store = Store::new(&engine, wasi_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-wasip1/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(()) +} \ No newline at end of file diff --git a/examples/wasi/main.c b/examples/wasip1/main.c similarity index 100% rename from examples/wasi/main.c rename to examples/wasip1/main.c diff --git a/examples/wasip1/main.rs b/examples/wasip1/main.rs new file mode 100644 index 000000000000..d81b4c864752 --- /dev/null +++ b/examples/wasip1/main.rs @@ -0,0 +1,36 @@ +//! Example of instantiating a wasm module which uses WASI imports. + +/* +You can execute this example with: + cmake examples/ + cargo run --example wasip1 +*/ + +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-wasip1/debug/wasi.wasm")?; + linker.module(&mut store, "", &module)?; + linker + .get_default(&mut store, "")? + .typed::<(), ()>(&store)? + .call(&mut store, ())?; + + Ok(()) +} \ No newline at end of file diff --git a/examples/wasi-async/main.rs b/examples/wasip2-async/main.rs similarity index 98% rename from examples/wasi-async/main.rs rename to examples/wasip2-async/main.rs index 03072c76d924..29877ac3c693 100644 --- a/examples/wasi-async/main.rs +++ b/examples/wasip2-async/main.rs @@ -4,7 +4,7 @@ /* You can execute this example with: cmake examples/ - cargo run --example wasi-async + cargo run --example wasip2-async */ use wasmtime::component::{Component, Linker, ResourceTable}; diff --git a/examples/wasi/main.rs b/examples/wasip2/main.rs similarity index 99% rename from examples/wasi/main.rs rename to examples/wasip2/main.rs index 8777b88369b2..cfcfcb543123 100644 --- a/examples/wasi/main.rs +++ b/examples/wasip2/main.rs @@ -3,7 +3,7 @@ /* You can execute this example with: cmake examples/ - cargo run --example wasi + cargo run --example wasip2 */ use wasmtime::component::{Component, Linker, ResourceTable}; diff --git a/examples/wasi/wasm/Cargo.toml b/examples/wasm/Cargo.toml similarity index 100% rename from examples/wasi/wasm/Cargo.toml rename to examples/wasm/Cargo.toml diff --git a/examples/wasi/wasm/wasi.rs b/examples/wasm/wasi.rs similarity index 100% rename from examples/wasi/wasm/wasi.rs rename to examples/wasm/wasi.rs From 27043b48184e3c9a7caa18337c292d0c1ddf41db Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Fri, 20 Dec 2024 21:19:34 +0800 Subject: [PATCH 16/17] fix wording --- docs/examples-rust-wasi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index ca5805520ab0..7dc223fc08e3 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -39,7 +39,7 @@ This example shows adding and configuring the WASI imports to invoke the above W ### Async example -This [async example code][code2] shows how to use the [wasmtime-wasi][`wasmtime-wasi`] component to +This [async example code][code2] shows how to use the [wasmtime-wasi][`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](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support). From 6e86762205ac86c0b01f49ffed6f7d72a9adaf65 Mon Sep 17 00:00:00 2001 From: "maple@max" Date: Fri, 20 Dec 2024 21:27:25 +0800 Subject: [PATCH 17/17] fix format --- examples/wasip1-async/main.rs | 2 +- examples/wasip1/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/wasip1-async/main.rs b/examples/wasip1-async/main.rs index 66d11fb7bf75..1c266752af88 100644 --- a/examples/wasip1-async/main.rs +++ b/examples/wasip1-async/main.rs @@ -44,4 +44,4 @@ async fn main() -> Result<()> { func.call_async(&mut store, ()).await?; Ok(()) -} \ No newline at end of file +} diff --git a/examples/wasip1/main.rs b/examples/wasip1/main.rs index d81b4c864752..6bc01bca52c4 100644 --- a/examples/wasip1/main.rs +++ b/examples/wasip1/main.rs @@ -33,4 +33,4 @@ fn main() -> Result<()> { .call(&mut store, ())?; Ok(()) -} \ No newline at end of file +}