#wasm-component #component-model #wasm-interface #run-time

wasm_component_layer

WebAssembly component model implementation for any backend

19 releases

0.1.18 Aug 2, 2024
0.1.17 May 24, 2024
0.1.16 Feb 25, 2024
0.1.15 Jan 5, 2024
0.1.7 Aug 30, 2023

#920 in WebAssembly

33 downloads per month
Used in modularity

Apache-2.0

345KB
7K SLoC

wasm_component_layer

Crates.io Docs.rs Unsafe Forbidden

wasm_component_layer is a runtime agnostic implementation of the WebAssembly component model. It supports loading and linking WASM components, inspecting and generating component interface types at runtime, and more atop any WebAssembly backend. The implementation is based upon the wasmtime, js-component-bindgen, and wit-parser crates.

Usage

To use wasm_component_layer, a runtime is required. The wasm_runtime_layer crate provides the common interface used for WebAssembly runtimes, so when using this crate it must also be added to the Cargo.toml file with the appropriate runtime selected. For instance, the examples in this repository use the wasmi_runtime_layer runtime:

wasm_component_layer = "0.1.16"
wasmi_runtime_layer = "0.31.0"
# wasmtime_runtime_layer = "21.0.0"
# js_wasm_runtime_layer = "0.4.0"

The following is a small overview of wasm_component_layer's API. The complete example may be found in the examples folder. Consider a WASM component with the following WIT:

package test:guest

interface foo {
    // Selects the item in position n within list x
    select-nth: func(x: list<string>, n: u32) -> string
}

world guest {
    export foo
}

The component can be loaded into wasm_component_layer and invoked as follows:

use wasm_component_layer::*;

// The bytes of the component.
const WASM: &[u8] = include_bytes!("single_component/component.wasm");

pub fn main() {
    // Create a new engine for instantiating a component.
    let engine = Engine::new(wasmi_runtime_layer::Engine::default());

    // Create a store for managing WASM data and any custom user-defined state.
    let mut store = Store::new(&engine, ());

    // Parse the component bytes and load its imports and exports.
    let component = Component::new(&engine, WASM).unwrap();
    // Create a linker that will be used to resolve the component's imports, if any.
    let linker = Linker::default();
    // Create an instance of the component using the linker.
    let instance = linker.instantiate(&mut store, &component).unwrap();

    // Get the interface that the interface exports.
    let interface = instance.exports().instance(&"test:guest/foo".try_into().unwrap()).unwrap();
    // Get the function for selecting a list element.
    let select_nth = interface.func("select-nth").unwrap().typed::<(Vec<String>, u32), String>().unwrap();

    // Create an example list to test upon.
    let example = ["a", "b", "c"].iter().map(ToString::to_string).collect::<Vec<_>>();

    println!("Calling select-nth({example:?}, 1) == {}", select_nth.call(&mut store, (example.clone(), 1)).unwrap());
    // Prints 'Calling select-nth(["a", "b", "c"], 1) == b'
}

Supported capabilities

wasm_component_layer supports the following major capabilities:

  • Parsing and instantiating WASM component binaries
  • Runtime generation of component interface types
  • Specialized list types for faster lifting/lowering
  • Structural equality of component interface types, as mandated by the spec
  • Support for guest resources
  • Support for strongly-typed host resources with destructors

The following things have yet to be implemented:

  • String transcoders
  • A macro for generating host bindings
  • More comprehensive tests
  • Subtyping

Optional features

serde - Allows for the serialization of identifiers, types, and values. Note that serializing resources is not allowed, because resources may be tied to specific instances.

Examples

# EXAMPLE_NAME: [single_component|resource|multilevel_resource|...]

# build example wasm file
cd examples/EXAMPLE_NAME # cd examples/single_component
rustup toolchain install nightly
rustup override set nightly
cargo build
wasm-tools component new target/wasm32-unknown-unknown/debug/component_example.wasm -o component.wasm
wasm-tools print component.wasm -o component.wat

# run example in host implementation
cd ../../
cargo run --example EXAMPLE_NAME # cargo run --example single_component

Dependencies

~13MB
~243K SLoC