Skip to content

Commit a8ace98

Browse files
committed
[wit-component] Add implements encoding support to wit-component
Update component encoding to use `name_world_key_with_item` at sites that produce component-level extern names, so that `implements` imports and exports are encoded as `[implements=<I>]L` in the binary format. Five call sites are changed from `name_world_key` to the implements-aware variant: import_map key construction, component export names, ImportedResourceDrop lookups, and both direct and indirect InterfaceFunc lookups.
1 parent f4950af commit a8ace98

File tree

7 files changed

+259
-26
lines changed

7 files changed

+259
-26
lines changed

crates/wit-component/src/encoding.rs

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ pub struct EncodingState<'a> {
401401

402402
/// Imported instances and what index they were imported as.
403403
imported_instances: IndexMap<InterfaceId, u32>,
404+
imported_instances_by_name: HashMap<String, u32>,
404405
imported_funcs: IndexMap<String, u32>,
405406
exported_instances: IndexMap<InterfaceId, u32>,
406407

@@ -456,6 +457,18 @@ impl<'a> EncodingState<'a> {
456457
}
457458
}
458459

460+
fn world_import_name(&self, key: &WorldKey) -> String {
461+
let resolve = &self.info.encoder.metadata.resolve;
462+
let world = &resolve.worlds[self.info.encoder.metadata.world];
463+
resolve.name_world_key_with_item(key, &world.imports[key])
464+
}
465+
466+
fn world_export_name(&self, key: &WorldKey) -> String {
467+
let resolve = &self.info.encoder.metadata.resolve;
468+
let world = &resolve.worlds[self.info.encoder.metadata.world];
469+
resolve.name_world_key_with_item(key, &world.exports[key])
470+
}
471+
459472
fn root_import_type_encoder(
460473
&mut self,
461474
interface: Option<InterfaceId>,
@@ -492,7 +505,8 @@ impl<'a> EncodingState<'a> {
492505
for (name, info) in self.info.import_map.iter() {
493506
match name {
494507
Some(name) => {
495-
self.encode_interface_import(name_map.get(name).unwrap_or(name), info)?
508+
let import_name = name_map.get(name).unwrap_or(name);
509+
self.encode_interface_import(import_name, name, info)?
496510
}
497511
None => has_funcs = true,
498512
}
@@ -517,7 +531,12 @@ impl<'a> EncodingState<'a> {
517531
Ok(())
518532
}
519533

520-
fn encode_interface_import(&mut self, name: &str, info: &ImportedInterface) -> Result<()> {
534+
fn encode_interface_import(
535+
&mut self,
536+
name: &str,
537+
import_key: &str,
538+
info: &ImportedInterface,
539+
) -> Result<()> {
521540
let resolve = &self.info.encoder.metadata.resolve;
522541
let interface_id = info.interface.as_ref().unwrap();
523542
let interface_id = *interface_id;
@@ -566,8 +585,11 @@ impl<'a> EncodingState<'a> {
566585
let instance_idx = self
567586
.component
568587
.import(name, ComponentTypeRef::Instance(instance_type_idx));
569-
let prev = self.imported_instances.insert(interface_id, instance_idx);
570-
assert!(prev.is_none());
588+
self.imported_instances
589+
.entry(interface_id)
590+
.or_insert(instance_idx);
591+
self.imported_instances_by_name
592+
.insert(import_key.to_string(), instance_idx);
571593
Ok(())
572594
}
573595

@@ -704,9 +726,9 @@ impl<'a> EncodingState<'a> {
704726
let prev = world_func_core_names.insert(name, core_name);
705727
assert!(prev.is_none());
706728
}
707-
Export::InterfaceFunc(_, id, name, _) => {
729+
Export::InterfaceFunc(key, _id, name, _) => {
708730
let prev = interface_func_core_names
709-
.entry(id)
731+
.entry(key)
710732
.or_insert(IndexMap::new())
711733
.insert(name.as_str(), core_name);
712734
assert!(prev.is_none());
@@ -731,7 +753,7 @@ impl<'a> EncodingState<'a> {
731753
let world = &resolve.worlds[self.info.encoder.metadata.world];
732754

733755
for export_name in exports {
734-
let export_string = resolve.name_world_key(export_name);
756+
let export_string = self.world_export_name(export_name);
735757
match &world.exports[export_name] {
736758
WorldItem::Function(func) => {
737759
let ty = self
@@ -743,7 +765,7 @@ impl<'a> EncodingState<'a> {
743765
.export(&export_string, ComponentExportKind::Func, idx, None);
744766
}
745767
WorldItem::Interface { id, .. } => {
746-
let core_names = interface_func_core_names.get(id);
768+
let core_names = interface_func_core_names.get(export_name);
747769
self.encode_interface_export(
748770
&export_string,
749771
module,
@@ -966,8 +988,7 @@ impl<'a> EncodingState<'a> {
966988
instance_index,
967989
None,
968990
);
969-
let prev = self.exported_instances.insert(export, idx);
970-
assert!(prev.is_none());
991+
self.exported_instances.entry(export).or_insert(idx);
971992

972993
// After everything is all said and done remove all the type information
973994
// about type exports of this interface. Any entries in the map
@@ -1292,11 +1313,12 @@ impl<'a> EncodingState<'a> {
12921313
realloc,
12931314
encoding,
12941315
} => {
1295-
let interface = &self.info.import_map[interface];
1296-
let ((name, _), _) = interface.lowerings.get_index(*index).unwrap();
1297-
let func_index = match &interface.interface {
1298-
Some(interface_id) => {
1299-
let instance_index = self.imported_instances[interface_id];
1316+
let import = &self.info.import_map[interface];
1317+
let ((name, _), _) = import.lowerings.get_index(*index).unwrap();
1318+
let func_index = match interface {
1319+
Some(iface_key) => {
1320+
let instance_index =
1321+
self.imported_instances_by_name[iface_key.as_str()];
13001322
self.component.alias_export(
13011323
instance_index,
13021324
name,
@@ -1309,7 +1331,7 @@ impl<'a> EncodingState<'a> {
13091331
let realloc = self
13101332
.info
13111333
.exports_for(*realloc)
1312-
.import_realloc_for(interface.interface, name)
1334+
.import_realloc_for(import.interface, name)
13131335
.map(|name| {
13141336
let instance = self.instance_for(*realloc);
13151337
self.core_alias_export(
@@ -1785,7 +1807,7 @@ impl<'a> EncodingState<'a> {
17851807
self.materialize_wit_import(
17861808
shims,
17871809
for_module,
1788-
iface.map(|_| resolve.name_world_key(key)),
1810+
iface.map(|_| self.world_import_name(key)),
17891811
&format!("{name}_drop"),
17901812
key,
17911813
AbiVariant::GuestImport,
@@ -1955,7 +1977,7 @@ impl<'a> EncodingState<'a> {
19551977
Import::InterfaceFunc(key, _, name, abi) => self.materialize_wit_import(
19561978
shims,
19571979
for_module,
1958-
Some(resolve.name_world_key(key)),
1980+
Some(self.world_import_name(key)),
19591981
name,
19601982
key,
19611983
*abi,
@@ -2072,9 +2094,9 @@ impl<'a> EncodingState<'a> {
20722094
// All direct lowerings can be `canon lower`'d here immediately
20732095
// and passed as arguments.
20742096
Lowering::Direct => {
2075-
let func_index = match &import.interface {
2076-
Some(interface) => {
2077-
let instance_index = self.imported_instances[interface];
2097+
let func_index = match &interface_key {
2098+
Some(iface_key) => {
2099+
let instance_index = self.imported_instances_by_name[iface_key.as_str()];
20782100
self.component
20792101
.alias_export(instance_index, name, ComponentExportKind::Func)
20802102
}
@@ -2849,14 +2871,15 @@ impl<'a> Shims<'a> {
28492871
// metadata out of this `match` to the loop below to figure that
28502872
// out.
28512873
Import::InterfaceFunc(key, _, name, abi) => {
2874+
let wit_world = &resolve.worlds[world.encoder.metadata.world];
28522875
self.append_indirect_wit_func(
28532876
world,
28542877
for_module,
28552878
module,
28562879
field,
28572880
key,
28582881
name,
2859-
Some(resolve.name_world_key(key)),
2882+
Some(resolve.name_world_key_with_item(key, &wit_world.imports[key])),
28602883
*abi,
28612884
)?;
28622885
}
@@ -3343,6 +3366,7 @@ impl ComponentEncoder {
33433366
import_type_encoding_maps: Default::default(),
33443367
export_type_encoding_maps: Default::default(),
33453368
imported_instances: Default::default(),
3369+
imported_instances_by_name: Default::default(),
33463370
imported_funcs: Default::default(),
33473371
exported_instances: Default::default(),
33483372
aliased_core_items: Default::default(),

crates/wit-component/src/encoding/wit.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentTyp
7373

7474
// Encode the imports
7575
for (name, import) in world.imports.iter() {
76-
let name = resolve.name_world_key(name);
76+
let name = resolve.name_world_key_with_item(name, import);
7777
log::trace!("encoding import {name}");
7878
let ty = match import {
7979
WorldItem::Interface { id, .. } => {
@@ -98,7 +98,7 @@ pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentTyp
9898
}
9999
// Encode the exports
100100
for (name, export) in world.exports.iter() {
101-
let name = resolve.name_world_key(name);
101+
let name = resolve.name_world_key_with_item(name, export);
102102
log::trace!("encoding export {name}");
103103
let ty = match export {
104104
WorldItem::Interface { id, .. } => {

crates/wit-component/src/encoding/world.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ impl<'a> ComponentWorld<'a> {
248248
item: &WorldItem,
249249
required: &Required<'_>,
250250
) -> Result<()> {
251-
let name = resolve.name_world_key(name);
251+
let name = resolve.name_world_key_with_item(name, item);
252252
log::trace!("register import `{name}`");
253253
let import_map_key = match item {
254254
WorldItem::Function(_) | WorldItem::Type { .. } => None,
@@ -314,7 +314,10 @@ impl<'a> ComponentWorld<'a> {
314314
// set. This means that only types not defined by referenced exports
315315
// will get added here.
316316
for (name, item) in resolve.worlds[world].exports.iter() {
317-
log::trace!("add live world export `{}`", resolve.name_world_key(name));
317+
log::trace!(
318+
"add live world export `{}`",
319+
resolve.name_world_key_with_item(name, item)
320+
);
318321
let id = match item {
319322
WorldItem::Interface { id, .. } => id,
320323
WorldItem::Function(_) | WorldItem::Type { .. } => {
@@ -453,6 +456,10 @@ impl<'a> ComponentWorld<'a> {
453456
WorldItem::Interface { id, .. } => *id,
454457
WorldItem::Type { .. } => unreachable!(),
455458
};
459+
// Skip already-processed interfaces (implements duplicates).
460+
if self.exports_used.contains_key(&id) {
461+
continue;
462+
}
456463
let mut set = HashSet::new();
457464

458465
for other in resolve.interface_direct_deps(id) {

crates/wit-component/src/printing.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,17 @@ impl<O: Output> WitPrinter<O> {
468468
match name {
469469
WorldKey::Name(name) => {
470470
match item {
471+
WorldItem::Interface {
472+
id,
473+
implements: Some(_),
474+
..
475+
} => {
476+
// `import label: use-path;` syntax
477+
self.print_name_type(name, TypeKind::Other);
478+
self.output.str(": ");
479+
self.print_path_to_interface(resolve, *id, cur_pkg)?;
480+
self.output.semicolon();
481+
}
471482
WorldItem::Interface { id, .. } => {
472483
self.print_name_type(name, TypeKind::Other);
473484
self.output.str(": ");
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
(component
2+
(type (;0;)
3+
(component
4+
(type (;0;)
5+
(instance
6+
(type (;0;) (option string))
7+
(type (;1;) (func (param "key" string) (result 0)))
8+
(export (;0;) "get" (func (type 1)))
9+
(type (;2;) (func (param "key" string) (param "value" string)))
10+
(export (;1;) "set" (func (type 2)))
11+
)
12+
)
13+
(export (;0;) "foo:foo/store" (instance (type 0)))
14+
)
15+
)
16+
(export (;1;) "store" (type 0))
17+
(type (;2;)
18+
(component
19+
(type (;0;)
20+
(instance
21+
(type (;0;) (func (param "msg" string)))
22+
(export (;0;) "log" (func (type 0)))
23+
)
24+
)
25+
(export (;0;) "foo:foo/logger" (instance (type 0)))
26+
)
27+
)
28+
(export (;3;) "logger" (type 2))
29+
(type (;4;)
30+
(component
31+
(type (;0;)
32+
(component
33+
(type (;0;)
34+
(instance
35+
(type (;0;) (option string))
36+
(type (;1;) (func (param "key" string) (result 0)))
37+
(export (;0;) "get" (func (type 1)))
38+
(type (;2;) (func (param "key" string) (param "value" string)))
39+
(export (;1;) "set" (func (type 2)))
40+
)
41+
)
42+
(import "[implements=<foo:foo/store>]primary" (instance (;0;) (type 0)))
43+
(type (;1;)
44+
(instance
45+
(type (;0;) (option string))
46+
(type (;1;) (func (param "key" string) (result 0)))
47+
(export (;0;) "get" (func (type 1)))
48+
(type (;2;) (func (param "key" string) (param "value" string)))
49+
(export (;1;) "set" (func (type 2)))
50+
)
51+
)
52+
(import "[implements=<foo:foo/store>]backup" (instance (;1;) (type 1)))
53+
(type (;2;)
54+
(instance
55+
(type (;0;) (func (param "msg" string)))
56+
(export (;0;) "log" (func (type 0)))
57+
)
58+
)
59+
(import "foo:foo/logger" (instance (;2;) (type 2)))
60+
)
61+
)
62+
(export (;0;) "foo:foo/multi-import" (component (type 0)))
63+
)
64+
)
65+
(export (;5;) "multi-import" (type 4))
66+
(type (;6;)
67+
(component
68+
(type (;0;)
69+
(component
70+
(type (;0;)
71+
(instance
72+
(type (;0;) (option string))
73+
(type (;1;) (func (param "key" string) (result 0)))
74+
(export (;0;) "get" (func (type 1)))
75+
(type (;2;) (func (param "key" string) (param "value" string)))
76+
(export (;1;) "set" (func (type 2)))
77+
)
78+
)
79+
(export (;0;) "[implements=<foo:foo/store>]primary" (instance (type 0)))
80+
(type (;1;)
81+
(instance
82+
(type (;0;) (option string))
83+
(type (;1;) (func (param "key" string) (result 0)))
84+
(export (;0;) "get" (func (type 1)))
85+
(type (;2;) (func (param "key" string) (param "value" string)))
86+
(export (;1;) "set" (func (type 2)))
87+
)
88+
)
89+
(export (;1;) "[implements=<foo:foo/store>]backup" (instance (type 1)))
90+
)
91+
)
92+
(export (;0;) "foo:foo/multi-export" (component (type 0)))
93+
)
94+
)
95+
(export (;7;) "multi-export" (type 6))
96+
(type (;8;)
97+
(component
98+
(type (;0;)
99+
(component
100+
(type (;0;)
101+
(instance
102+
(type (;0;) (option string))
103+
(type (;1;) (func (param "key" string) (result 0)))
104+
(export (;0;) "get" (func (type 1)))
105+
(type (;2;) (func (param "key" string) (param "value" string)))
106+
(export (;1;) "set" (func (type 2)))
107+
)
108+
)
109+
(import "foo:foo/store" (instance (;0;) (type 0)))
110+
(type (;1;)
111+
(instance
112+
(type (;0;) (option string))
113+
(type (;1;) (func (param "key" string) (result 0)))
114+
(export (;0;) "get" (func (type 1)))
115+
(type (;2;) (func (param "key" string) (param "value" string)))
116+
(export (;1;) "set" (func (type 2)))
117+
)
118+
)
119+
(import "[implements=<foo:foo/store>]cache" (instance (;1;) (type 1)))
120+
)
121+
)
122+
(export (;0;) "foo:foo/mixed" (component (type 0)))
123+
)
124+
)
125+
(export (;9;) "mixed" (type 8))
126+
(@custom "package-docs" "\01{\22worlds\22:{\22multi-import\22:{\22docs\22:\22A world that imports the same interface multiple times under different\5cnplain names using the `implements` syntax.\22},\22multi-export\22:{\22docs\22:\22A world that exports the same interface multiple times.\22},\22mixed\22:{\22docs\22:\22A world with both implements imports and a normal interface import,\5cntesting that elaboration adds the dependency correctly.\22}}}")
127+
(@producers
128+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
129+
)
130+
)

0 commit comments

Comments
 (0)