Skip to content

Commit

Permalink
Update docs with new component example
Browse files Browse the repository at this point in the history
  • Loading branch information
kajacx committed Jul 22, 2023
1 parent 2a1f91e commit df2d3f4
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 109 deletions.
62 changes: 10 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,69 +18,27 @@
</p>
</div>

## 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<i32> {
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::<i32, i32>(&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

Expand Down
42 changes: 27 additions & 15 deletions docs/CM/my_first_component.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,28 @@ 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

Be sure to install all the necessary tooling, list is in [Component model](../component_model.md).

## 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"
Expand All @@ -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!({
Expand All @@ -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
}
}
```
Expand All @@ -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:
Expand Down Expand Up @@ -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

Expand Down
62 changes: 21 additions & 41 deletions docs/CM/wit_imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<s32>
import add-one: func(num: s32) -> s32
export add: func(a: s32, b: s32) -> s32
export add-three: func(num: s32) -> s32
}
```

Expand All @@ -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
}
}
```
Expand All @@ -41,32 +43,28 @@ impl Calculator for MyCalculator {
```rust
// In host (runtime)

struct CalculatorData {
variables: HashMap<String, i32>,
}
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<Option<i32>> {
Ok(self.variables.get(&name).copied())
fn add_one(&mut self, num: i32) -> Result<i32> {
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,
Expand All @@ -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.
2 changes: 1 addition & 1 deletion docs/component_model.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
48 changes: 48 additions & 0 deletions docs/wasm_modules.md
Original file line number Diff line number Diff line change
@@ -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<i32> {
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::<i32, i32>(&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)

0 comments on commit df2d3f4

Please sign in to comment.