diff --git a/README.md b/README.md index 1e3592b6..b0886d8d 100644 --- a/README.md +++ b/README.md @@ -18,69 +18,27 @@

-## Goals - -The goal of this crate is to "run wasmtime on the web". - -Since wasmtime cannot actually _run_ on the web, the goal is to **provide a unified API** for both sys (desktop) and js (web) runtimes. - -## How to use - -The provided API is identical to wasmtime's API, so read [wasmtime's documentation](https://docs.wasmtime.dev/) on how to use this crate. - -Here is an example of adding three using a WASM module compiled from WAT, using `wasm-bridge` version `0.1.1`: -```rust -use wasm_bride::*; - -fn add_three(number: i32) -> Result { - let mut store = Store::<()>::default(); - - let wat = r#" - (module - (func $add_three (export "add_three") - (param $p0 i32) (result i32) - (i32.add (local.get $p0) (i32.const 3)) - ) - ) - "#; - let module = Module::new(store.engine(), wat.as_bytes())?; - - let instance = Instance::new(&mut store, &module, &[])?; - let add_three_wasm = instance.get_typed_func::(&mut store, "add_three")?; - - let result = add_three_wasm.call(&mut store, number)?; - Ok(result) -} -``` - -Alternatively, watch the [video tutorial](https://youtu.be/CqpZjouAOvg): +## Goals -[![Youtube video](https://img.youtube.com/vi/CqpZjouAOvg/0.jpg)](https://youtu.be/CqpZjouAOvg) +The goal of this crate is to "run wasmtime on the web", that means **providing a unified API** for desktop and web runtimes. -## Using `component-model` +With `wasm-bridge`, you can write a *single source code in Rust* that you would normally write when using wasmtime, +but it works on desktop as well as on the web. -Component model is available since version `0.1.2`, but only some types are supported. -See [Component model](/docs/component_model.md). -## Switching from `wasmtime` +## Component model -Simply replace the `wasmtime` dependency and imports with `wasm-bridge`, and you _should_ be good to go. +The main focus is to support [wasmtime's component model](https://github.com/WebAssembly/component-model). +See the [Component model](/docs/component_model.md) page on how to get started. -Most of wasmtime's API is still not implemented, so you will likely run into compile errors when compiling to wasm. -Create an issue with a code snippet describing your use case. +## Non-component model use -## Implemented features +The provided API is identical to wasmtime's API, so read [wasmtime's documentation](https://docs.wasmtime.dev/) on how to use this crate. -- Load a module from bytes or from WAT text -- Instantiate a module with or without imports -- Get (typed) exported function and call it -- Multivalue returns from exported and imported functions -- Supported value types: `i32`, `i64`, `u32`, `u64`, `f32`, `f64` -- Access store's data from Caller (imported fn) +See [this page](/docs/wasm_modules.md) for an example usage. -See the [`no_bindgen`](/tests/no_bindgen) test folder for supported example usages. ## License diff --git a/docs/CM/my_first_component.md b/docs/CM/my_first_component.md index 71469aa0..5e37e852 100644 --- a/docs/CM/my_first_component.md +++ b/docs/CM/my_first_component.md @@ -5,7 +5,7 @@ and call it from Rust runtime on desktop and on the web using the same source co ## Full minimal example -Full minimal example can be found [here](https://github.com/kajacx/wasm-playground/tree/wasm-bridge-03-universal-component). +Full minimal example can be found [here](https://github.com/kajacx/wasm-playground/tree/wasm-bridge-05-cargo-component). ## Prerequisites @@ -13,18 +13,20 @@ Be sure to install all the necessary tooling, list is in [Component model](../co ## Create the WIT protocol -1. Create a simple file describing the interface using the [wit format](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md), for example: +1. Create a simple file describing the interface using +the [wit format](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md), for example: + ```wit package usage:example world calculator { - export add: func(a: s32, b: s32) -> s32 + export add_three: func(a: s32) -> s32 } ``` ## Create the Rust guest -1. Create a new Rust crate for the guest and add `wit-bindgen` crate as a dependency. Example `Cargo.toml`: +1. Create a new Rust library crate for the guest and add `wit-bindgen` crate as a dependency. Example `Cargo.toml`: ```toml [package] name = "guest" @@ -34,10 +36,16 @@ edition = "2021" [lib] crate-type = ["cdylib"] +[package.metadata.component] +package = "component:guest" + [dependencies] wit-bindgen = "0.8.0" ``` +Be sure to specify `cdylib` as the library type and include the `package.metadata.component` info +so that `cargo component` can recognize the crate as a proper wasm component. + 2. Import the WIT world definition in the guest like this: ```rust wit_bindgen::generate!({ @@ -51,8 +59,8 @@ wit_bindgen::generate!({ struct MyCalculator; impl Calculator for MyCalculator { - fn add(a: i32, b: i32) -> i32 { - a + b + fn add_three(num: i32) -> i32 { + num + 3 } } ``` @@ -64,20 +72,19 @@ export_calculator!(MyCalculator); ## Build the guest -1. Build the guest with `cargo build --target=wasm32-unknown-unknown` +1. Build the guest with `cargo component --target=wasm32-unknown-unknown` -2. `cd` into the build folder `target/wasm32-unknown-unknown/debug` (use `/release` in release mode) +2. This should generate a `guest.wasm` file in the `target/wasm32-unknown-unknown/debug` folder -3. Convert the WASM module into a WASM component with `wasm-tools component new guest.wasm -o component.wasm` +3. Your wasm component will be in `target/wasm32-unknown-unknown/debug` if you use `--release` -This is the `component.wasm` file you would use with wasmtime normally. We will need it later. ## Create the runtime -1. Add the `wasm-bridge` crate as a dependency with the `component-model` feature. Use version at least `0.2.0`. Example: +1. Create a new Rust crate with `wasm-bridge` as a dependency with the `component-model` feature. Use version at least `0.2.0`. Example: ```toml [dependencies] -wasm-bridge = { version = "0.1.4", features = ["component-model"] } +wasm-bridge = { version = "0.2.0", features = ["component-model"] } ``` 2. Generate the host bindings with the `bindgen` macro, like this: @@ -108,20 +115,25 @@ let linker = Linker::new(store.engine()); let (calculator, _) = Calculator::instantiate(&mut store, &component, &linker)?; ``` -5. Call the exported function on the component: +5. Call the exported function on the component instance: ```rust -let result = calculator.call_add(&mut store, 5, 3)?; +let result = calculator.call_add_three(&mut store, 5)?; assert_eq!(result, 8); ``` + ## Summary The steps are identical to using wasmtime with component model "normally", except we used `wasm-bridge` instead of `wasmtime` as our dependency. + ## Next steps -If your wit world defines imports, you can read the [Wit imports](./wit_imports.md) guide. The code is identical to wasmtime, though. +If your wit world defines imports, you can read the [Wit imports](./wit_imports.md) guide. + +The code is again the same as when using wasmtime. + ## Universal mode / zipped components discontinuation diff --git a/docs/CM/wit_imports.md b/docs/CM/wit_imports.md index d7f6f4d5..b2ce76d9 100644 --- a/docs/CM/wit_imports.md +++ b/docs/CM/wit_imports.md @@ -4,21 +4,22 @@ First, read [My first component](./my_first_component.md) to understand how to g ## Full minimal example -A full example of using wit imports can be found [here](https://github.com/kajacx/wasm-playground/tree/wasm-bridge-04-wit-imports). +A full example of using wit imports can be found [here](https://github.com/kajacx/wasm-playground/tree/wasm-bridge-06-readd-imports). ## Steps to add imports -Wit imports work the same way as they do in wasmtime. Here is a quick refresher: +Wit imports work the same way as they do in wasmtime. -1. Define a wit file with imports +This is how to add imports to the "My first component" created in the previous tutorial: + +1. Add an "import" function to the wit file ```wit package usage:example world calculator { - import store-variable(name: string, value: s32) - import get-variable(name: string) -> option + import add-one: func(num: s32) -> s32 - export add: func(a: s32, b: s32) -> s32 + export add-three: func(num: s32) -> s32 } ``` @@ -29,10 +30,11 @@ world calculator { struct MyCalculator; impl Calculator for MyCalculator { - fn add(a: i32, b: i32) -> i32 { - let times_called = get_variable("times_called").unwrap_or(0); - store_variable("times_called", times_called + 1); - a + b + times_called + fn add(num: i32) -> i32 { + let num = add_one(num); + let num = add_one(num); + let num = add_one(num); + num } } ``` @@ -41,32 +43,28 @@ impl Calculator for MyCalculator { ```rust // In host (runtime) -struct CalculatorData { - variables: HashMap, -} +struct CalculatorData {} ``` 4. Implement the world imports for your struct ```rust // Name based on world name impl CalculatorImports for CalculatorData { - fn store_variable(&mut self, name: String, value: i32) -> Result<()> { - self.variables.insert(name, value); - Ok(()) - } - - fn get_variable(&mut self, name: String) -> Result> { - Ok(self.variables.get(&name).copied()) + fn add_one(&mut self, num: i32) -> Result { + Ok(num + 1) } } ``` -The functions must return a `wasm_bride::Result`, but handling the `Err` case is not yet implemented and will result in a panic on the web. +The functions must return a `wasm_bride::Result`, which is re-exported `anyhow::Result`. + +Returning an `Err` variant will result in an error return on the call site, +but the instance will not be re-entriable. 5. Add your struct to the store's data. ```rust -let mut store = Store::new(&engine, CalculatorData::new()); +let mut store = Store::new(&engine, CalculatorData {}); ``` Note: the store's data can be a different type than your struct that implements the imports, @@ -84,29 +82,11 @@ If store's data *is* CalculatorData, it can just return the input. 7. And that's it -Now you can instantiate you guest with this linker: +Now you can instantiate your guest with this linker and call exported functions on the instance as before: ```rust let (calculator, _) = Calculator::instantiate(&mut store, &component, &linker)?; let result = calculator.call_add(5, 3)?; assert_eq!(result, 8); - -// Calling again, should add an extra 1 -let result = calculator.call_add(5, 3)?; -assert_eq!(result, 9); ``` - -8. Access store's data - -You can also read and manipulate the store's data between calls: - -```rust -// Get a variable -let var_x = store.data().variables.get("x").copied(); - -// Clear all variables -store.data_mut().variables.clear(); -``` - -This is not unique to the component model, but it might come in handy. diff --git a/docs/component_model.md b/docs/component_model.md index b51ee768..f2d0063e 100644 --- a/docs/component_model.md +++ b/docs/component_model.md @@ -1,6 +1,6 @@ # Component model -Since version `0.1.2`, it is now possible to use the [wit component model](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md) with the `component-model` feature. +It is possible to use the [wit component model](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md) with the `component-model` feature. ## Pre-requirements diff --git a/docs/wasm_modules.md b/docs/wasm_modules.md new file mode 100644 index 00000000..86ce7149 --- /dev/null +++ b/docs/wasm_modules.md @@ -0,0 +1,48 @@ +# Non-component model use + +You can use wasm-bridge without the component model, but not many features are implemented. + + +## Implemented features + +- Load a module from bytes or from WAT text +- Instantiate a module with or without imports +- Get (typed) exported function and call it +- Multivalue returns from exported and imported functions +- Supported value types: `i32`, `i64`, `u32`, `u64`, `f32`, `f64` +- Access store's data from Caller (imported fn) + +See the [`no_bindgen`](/tests/no_bindgen) test folder for supported example usages. + + +## Example usage + +Here is an example of adding three using a WASM module compiled from WAT, using `wasm-bridge` version `0.1.1`: + +```rust +use wasm_bride::*; + +fn add_three(number: i32) -> Result { + let mut store = Store::<()>::default(); + + let wat = r#" + (module + (func $add_three (export "add_three") + (param $p0 i32) (result i32) + (i32.add (local.get $p0) (i32.const 3)) + ) + ) + "#; + let module = Module::new(store.engine(), wat.as_bytes())?; + + let instance = Instance::new(&mut store, &module, &[])?; + let add_three_wasm = instance.get_typed_func::(&mut store, "add_three")?; + + let result = add_three_wasm.call(&mut store, number)?; + Ok(result) +} +``` + +Alternatively, watch the [video tutorial](https://youtu.be/CqpZjouAOvg): + +[![Youtube video](https://img.youtube.com/vi/CqpZjouAOvg/0.jpg)](https://youtu.be/CqpZjouAOvg)