Skip to content

Commit e0a96b3

Browse files
committed
Add implements clause support to wac
Support the component model's implements name form, which allows importing/exporting the same interface multiple times under different plain-name labels (e.g., primary/backup stores).
1 parent 30c8a73 commit e0a96b3

17 files changed

Lines changed: 429 additions & 93 deletions

File tree

Cargo.lock

Lines changed: 40 additions & 45 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.1", default-features
6363
wac-resolver = { path = "crates/wac-resolver", version = "0.10.1", default-features = false }
6464
wac-graph = { path = "crates/wac-graph", version = "0.10.1" }
6565
wac-types = { path = "crates/wac-types", version = "0.10.1" }
66-
wit-parser = "0.247.0"
67-
wasmparser = "0.247.0"
68-
wit-component = "0.247.0"
69-
wasm-encoder = "0.247.0"
70-
wasmprinter = "0.247.0"
71-
wasm-metadata = "0.247.0"
72-
wat = "1.245.1"
66+
wit-parser = "0.252.0"
67+
wasmparser = "0.252.0"
68+
wit-component = "0.252.0"
69+
wasm-encoder = "0.252.0"
70+
wasmprinter = "0.252.0"
71+
wasm-metadata = "0.252.0"
72+
wat = "1.252.0"
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: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,37 @@
11
use crate::PackageId;
22
use indexmap::IndexMap;
33
use petgraph::graph::NodeIndex;
4+
use std::borrow::Cow;
45
use std::collections::HashMap;
56
use wac_types::{
67
CoreExtern, DefinedType, DefinedTypeId, Enum, Flags, FuncTypeId, InterfaceId, ItemKind,
78
ModuleTypeId, PrimitiveType, Record, ResourceId, Type, Types, UsedType, ValueType, Variant,
89
WorldId,
910
};
1011
use wasm_encoder::{
11-
Alias, ComponentBuilder, ComponentCoreTypeEncoder, ComponentExportKind,
12+
Alias, ComponentBuilder, ComponentCoreTypeEncoder, ComponentExportKind, ComponentExternName,
1213
ComponentOuterAliasKind, ComponentType, ComponentTypeEncoder, ComponentTypeRef,
1314
ComponentValType, EntityType, GlobalType, InstanceType, MemoryType, ModuleType, TableType,
1415
TagKind, TagType, TypeBounds,
1516
};
17+
use wasmparser::names::{ComponentName, ComponentNameKind};
18+
19+
/// Returns the `(implements "I")` interface id for an instance import or export,
20+
/// if its extern name is a plain-name label rather than an interface name.
21+
///
22+
/// A component model `(implements "I")` import/export is an instance whose
23+
/// extern name is a plain-name label (e.g. `primary`) rather than an interface
24+
/// name, while the interface it implements has its own id (e.g. `foo:bar/iface`).
25+
/// A regular interface import/export instead uses the interface name itself as
26+
/// its extern name.
27+
pub(crate) fn implements_of<'a>(types: &'a Types, name: &str, id: InterfaceId) -> Option<&'a str> {
28+
types[id].id.as_deref().filter(|_| {
29+
matches!(
30+
ComponentName::new(name, 0).as_ref().map(|n| n.kind()),
31+
Ok(ComponentNameKind::Label(_))
32+
)
33+
})
34+
}
1635

1736
/// A type used to abstract the API differences between a component builder,
1837
/// component type, and instance type from `wasm-encoder`.
@@ -65,7 +84,8 @@ impl Encodable {
6584
}
6685
}
6786

68-
fn import_type(&mut self, name: &str, ty: ComponentTypeRef) {
87+
fn import_type<'a>(&mut self, name: impl Into<ComponentExternName<'a>>, ty: ComponentTypeRef) {
88+
let name = name.into();
6989
match self {
7090
Encodable::Component(t) => {
7191
t.import(name, ty);
@@ -730,13 +750,22 @@ impl<'a> TypeEncoder<'a> {
730750
}
731751
ItemKind::Instance(id) => {
732752
let import_index = state.current.encodable.instance_count();
753+
let implements = implements_of(self.0, name, id);
754+
let extern_name = ComponentExternName {
755+
name: Cow::Borrowed(name),
756+
implements: implements.map(Cow::Borrowed),
757+
};
733758
state
734759
.current
735760
.encodable
736-
.import_type(name, ComponentTypeRef::Instance(index));
737-
if let Some(iid) = &self.0[id].id {
738-
log::debug!("instance index {import_index} ({iid}) is available for aliasing");
739-
state.current.instances.insert(iid.clone(), import_index);
761+
.import_type(extern_name, ComponentTypeRef::Instance(index));
762+
if implements.is_none() {
763+
if let Some(iid) = &self.0[id].id {
764+
log::debug!(
765+
"instance index {import_index} ({iid}) is available for aliasing"
766+
);
767+
state.current.instances.insert(iid.clone(), import_index);
768+
}
740769
}
741770
}
742771
_ => panic!("expected only types, functions, and instance types"),
@@ -827,9 +856,19 @@ impl<'a> TypeEncoder<'a> {
827856

828857
let ty = kind.ty();
829858
let index = self.ty(state, ty, Some(name));
859+
// Preserve the `(implements "I")` directive when re-encoding an
860+
// instance export that uses a plain-name label.
861+
let implements = match kind {
862+
ItemKind::Instance(id) => implements_of(self.0, name, id),
863+
_ => None,
864+
};
865+
let extern_name = ComponentExternName {
866+
name: Cow::Borrowed(name),
867+
implements: implements.map(Cow::Borrowed),
868+
};
830869
let index = Self::export_type(
831870
state,
832-
name,
871+
extern_name,
833872
match kind {
834873
ItemKind::Type(_) => ComponentTypeRef::Type(TypeBounds::Eq(index)),
835874
ItemKind::Func(_) => ComponentTypeRef::Func(index),
@@ -880,7 +919,12 @@ impl<'a> TypeEncoder<'a> {
880919
index
881920
}
882921

883-
fn export_type(state: &mut State, name: &str, ty: ComponentTypeRef) -> u32 {
922+
fn export_type<'b>(
923+
state: &mut State,
924+
name: impl Into<ComponentExternName<'b>>,
925+
ty: ComponentTypeRef,
926+
) -> u32 {
927+
let name = name.into();
884928
match &mut state.current.encodable {
885929
Encodable::Component(t) => {
886930
let index = t.type_count();

0 commit comments

Comments
 (0)