Commit 4dfdf6b
committed
feat: Add support for implements syntax
DO NOT MERGE until wasm-tools release with
bytecodealliance/wasm-tools#2453
Points wasm-tools to PR branch `wasmparser-implements`
Add support for the component model `[implements=<I>]L`
(spec PR [bytecodealliance#613](WebAssembly/component-model#613)),
which allows components to import/export the same
interface multiple times under different plain names.
A component can import the same interface twice under different labels,
each bound to a distinct host implementation:
```wit
import primary: wasi:keyvalue/store;
import secondary: wasi:keyvalue/store;
```
Guest code sees two separate namespaces with identical shapes:
```rust
let val = primary::get("my-key"); // calls the primary store
let val = secondary::get("my-key"); // calls the secondary store
```
Host Import-side codegen: shared trait + label-parameterized add_to_linker
For imports, wit-bindgen generates one Host trait per interface (not per
label). The add_to_linker function takes a name: &str parameter so the
same trait implementation can be registered under different instance labels.
Duplicate implements imports don't generate separate modules — only the
first import produces bindings.
```rust
struct PrimaryBackend;
impl primary::Host for PrimaryBackend {
fn get(&mut self, key: String) -> String {
self.primary_db.get(&key).cloned().unwrap_or_default()
}
}
struct SecondaryBackend;
impl primary::Host for SecondaryBackend {
fn get(&mut self, key: String) -> String {
self.secondary_db.get(&key).cloned().unwrap_or_default()
}
}
// Same add_to_linker, different labels and host_getter closures
primary::add_to_linker(&mut linker, "primary", |s| &mut s.primary)?;
primary::add_to_linker(&mut linker, "secondary", |s| &mut s.secondary)?;
```
Export-side codegen: per-label modules with shared types
For exports, each label gets its own module with fresh Guest/GuestIndices
types but re-exports shared interface types from the first module via
`pub use super::{first}::*`.
Runtime name resolution
The linker supports registering by plain label without knowing the annotation:
```rust
// Component imports [implements=<wasi:keyvalue/store>]primary
// but the host just registers "primary" — label fallback handles it
linker.root().instance("primary")?.func_wrap("get", /* ... */)?;
Users can also register to the linker with the full encoded implements name:
linker
.root()
.instance("[implements=<wasi:keyvalue/store>]primary")?
.func_wrap("get", |_, (key,): (String,)| Ok((String::new(),)))?;
```
Semver matching works inside the implements annotation, just like
regular interface imports:
```rust
// Host provides v1.0.1
linker
.root()
.instance("[implements=<wasi:keyvalue/store@1.0.1>]primary")?
.func_wrap("get", |_, (key,): (String,)| Ok((String::new(),)))?;
// Component requests v1.0.0, matches via semver
let component = Component::new(&engine, r#"(component
(type $store (instance
(export "get" (func (param "key" string) (result string)))
))
(import "[implements=<wasi:keyvalue/store@1.0.0>]primary" (instance (type $store)))
)"#)?;
linker.instantiate(&mut store, &component)?; // works, 1.0.1 is semver-compatible with 1.0.0
```
- Add three-tier lookup in NameMap::get: exact → semver → label fallback
- Add implements_label_key() helper for extracting plain labels from
`[implements=<I>]L`
- Add unit tests for all lookup tiers
- Track first-seen implements imports per `InterfaceId`
- One `Host` trait per interface; `generate_add_to_linker` takes
`named: bool` — when true, emits `name: &str` parameter instead of
hardcoding the instance name
- Duplicate `implements` imports: just record the label in
`implements_labels`, no module generation
- `world_add_to_linker`: iterate over `implements_labels` to emit one
`add_to_linker` call per label, passing label as name argument
- Guard `populate_world_and_interface_options` with `entry()` to avoid
overwriting link options for duplicate interfaces
- Duplicate exports: re-export types via `pub use super::{first}::*`,
generate fresh `Guest`/`GuestIndices`, plus regenerate resource wrapper
structs to reference the local `Guest` type
- Use `name_world_key_with_item` for export instance name lookups1 parent 4fd25c0 commit 4dfdf6b
File tree
6 files changed
+1933
-1057
lines changed- crates
- environ/src/component
- wit-bindgen/src
- tests/all/component_model
6 files changed
+1933
-1057
lines changed
0 commit comments