Skip to content

Commit c816535

Browse files
committed
Add implements clause support to wac
Support the component model's [implements=<I>]L name form, which allows importing/exporting the same interface multiple times under different plain-name labels (e.g., primary/backup stores). - Extract the interface name from implements names in Package::from_bytes so Interface.id is set correctly (e.g., "wasi:keyvalue/store" instead of the raw "[implements=<wasi:keyvalue/store>]primary") - Skip instance deduplication for implements imports during graph encoding so both imports are preserved - Use entry().or_insert() for the instances map in both TypeEncoder and CompositionGraphEncoder to prevent a second implements import from overwriting the first's alias index
1 parent 7ed5fa0 commit c816535

File tree

10 files changed

+244
-43
lines changed

10 files changed

+244
-43
lines changed

Cargo.lock

Lines changed: 64 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ wac-parser = { path = "crates/wac-parser", version = "0.10.0-dev", default-featu
6363
wac-resolver = { path = "crates/wac-resolver", version = "0.10.0-dev", default-features = false }
6464
wac-graph = { path = "crates/wac-graph", version = "0.10.0-dev" }
6565
wac-types = { path = "crates/wac-types", version = "0.10.0-dev" }
66-
wit-parser = "0.245.1"
67-
wasmparser = "0.245.1"
68-
wit-component = "0.245.1"
69-
wasm-encoder = "0.245.1"
70-
wasmprinter = "0.245.1"
71-
wasm-metadata = "0.245.1"
72-
wat = "1.245.1"
66+
wit-parser = { git = "https://github.com/ricochet/wasm-tools", branch = "wasmparser-implements" }
67+
wasmparser = { git = "https://github.com/ricochet/wasm-tools", branch = "wasmparser-implements" }
68+
wit-component = { git = "https://github.com/ricochet/wasm-tools", branch = "wasmparser-implements" }
69+
wasm-encoder = { git = "https://github.com/ricochet/wasm-tools", branch = "wasmparser-implements" }
70+
wasmprinter = { git = "https://github.com/ricochet/wasm-tools", branch = "wasmparser-implements" }
71+
wasm-metadata = { git = "https://github.com/ricochet/wasm-tools", branch = "wasmparser-implements" }
72+
wat = { git = "https://github.com/ricochet/wasm-tools", branch = "wasmparser-implements" }
7373
anyhow = "1.0.81"
7474
clap = { version = "4.5.4", features = ["derive"] }
7575
semver = { version = "1.0.22", features = ["serde"] }

crates/wac-graph/src/encoding.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,11 @@ impl<'a> TypeEncoder<'a> {
690690
.import_type(name, ComponentTypeRef::Instance(index));
691691
if let Some(iid) = &self.0[id].id {
692692
log::debug!("instance index {import_index} ({iid}) is available for aliasing");
693-
state.current.instances.insert(iid.clone(), import_index);
693+
state
694+
.current
695+
.instances
696+
.entry(iid.clone())
697+
.or_insert(import_index);
694698
}
695699
}
696700
_ => panic!("expected only types, functions, and instance types"),

crates/wac-graph/src/graph.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,11 +1690,15 @@ impl<'a> CompositionGraphEncoder<'a> {
16901690

16911691
fn import(&self, state: &mut State, name: &str, types: &Types, kind: ItemKind) -> u32 {
16921692
// Check to see if this is an import of an interface that's already been
1693-
// imported; this can happen based on importing of shared dependencies
1693+
// imported; this can happen based on importing of shared dependencies.
1694+
// Skip deduplication for implements names, as they can import the same
1695+
// interface multiple times under different labels.
16941696
if let ItemKind::Instance(id) = kind {
16951697
if let Some(id) = &types[id].id {
1696-
if let Some(index) = state.current.instances.get(id) {
1697-
return *index;
1698+
if !name.starts_with("[implements=<") {
1699+
if let Some(index) = state.current.instances.get(id) {
1700+
return *index;
1701+
}
16981702
}
16991703
}
17001704
}
@@ -1739,7 +1743,7 @@ impl<'a> CompositionGraphEncoder<'a> {
17391743
log::debug!(
17401744
"interface `{id}` is available for aliasing as instance index {index}"
17411745
);
1742-
state.current.instances.insert(id.clone(), index);
1746+
state.current.instances.entry(id.clone()).or_insert(index);
17431747
}
17441748
}
17451749
_ => {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(component
2+
(type (;0;)
3+
(instance
4+
(type (;0;) (func))
5+
(export (;0;) "do-something" (func (type 0)))
6+
)
7+
)
8+
(import "[implements=<test:test/iface>]primary" (instance (;0;) (type 0)))
9+
(import "[implements=<test:test/iface>]backup" (instance (;1;) (type 0)))
10+
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
(component
2+
(type (;0;)
3+
(instance
4+
(type (;0;) (func))
5+
(export (;0;) "do-something" (func (type 0)))
6+
)
7+
)
8+
(import "[implements=<test:test/iface>]primary" (instance (;0;) (type 0)))
9+
(import "[implements=<test:test/iface>]backup" (instance (;1;) (type 0)))
10+
(type (;1;)
11+
(component
12+
(type (;0;)
13+
(instance
14+
(type (;0;) (func))
15+
(export (;0;) "do-something" (func (type 0)))
16+
)
17+
)
18+
(import "[implements=<test:test/iface>]primary" (instance (;0;) (type 0)))
19+
(import "[implements=<test:test/iface>]backup" (instance (;1;) (type 0)))
20+
)
21+
)
22+
(import "unlocked-dep=<test:consumer>" (component (;0;) (type 1)))
23+
(instance (;2;) (instantiate 0
24+
(with "[implements=<test:test/iface>]primary" (instance 0))
25+
(with "[implements=<test:test/iface>]backup" (instance 1))
26+
)
27+
)
28+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"packages": [
3+
{
4+
"name": "test:consumer",
5+
"path": "consumer.wat"
6+
}
7+
],
8+
"nodes": [
9+
{
10+
"type": "instantiation",
11+
"package": 0
12+
}
13+
]
14+
}

crates/wac-types/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,8 @@ serde = { workspace = true, optional = true }
1818
wasmparser = { workspace = true }
1919
wasm-encoder = { workspace = true }
2020

21+
[dev-dependencies]
22+
wat = { workspace = true }
23+
2124
[features]
2225
serde = ["dep:serde"]

crates/wac-types/src/package.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,19 @@ impl<'a> TypeConverter<'a> {
567567
wasm::ComponentEntityType::Func(id) => {
568568
Ok(ItemKind::Func(self.component_func_type(id)?))
569569
}
570-
wasm::ComponentEntityType::Instance(id) => Ok(ItemKind::Instance(
571-
self.component_instance_type(Some(name), id)?,
572-
)),
570+
wasm::ComponentEntityType::Instance(id) => {
571+
let implements_iface =
572+
ComponentName::new(name, 0)
573+
.ok()
574+
.and_then(|cn| match cn.kind() {
575+
ComponentNameKind::Implements(i) => Some(i.interface().to_owned()),
576+
_ => None,
577+
});
578+
let effective_name = implements_iface.as_deref().unwrap_or(name);
579+
Ok(ItemKind::Instance(
580+
self.component_instance_type(Some(effective_name), id)?,
581+
))
582+
}
573583
wasm::ComponentEntityType::Component(id) => {
574584
Ok(ItemKind::Component(self.component_type(Some(name), id)?))
575585
}

0 commit comments

Comments
 (0)