Skip to content

Commit cffa858

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 cffa858

17 files changed

Lines changed: 449 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: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,57 @@
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+
/// Determines whether an instance import/export needs a component model
20+
/// `(implements "…")` directive, and if so returns the interface name to put
21+
/// inside it.
22+
///
23+
/// An instance extern is encoded one of two ways depending on its name:
24+
///
25+
/// * Regular interface extern: the extern name *is* the interface name, e.g.
26+
/// `(import "foo:bar/iface" (instance …))`. Nothing to disambiguate, so this
27+
/// returns `None`.
28+
///
29+
/// * `implements` extern: the extern name is a plain-name label and the
30+
/// interface it implements is carried separately, e.g.
31+
/// `(import "primary" (implements "foo:bar/iface") (instance …))`. This is
32+
/// how a component imports/exports the same interface more than once under
33+
/// different labels. Here this returns `Some("foo:bar/iface")`, i.e. the
34+
/// string the caller should encode as the `implements` directive.
35+
///
36+
/// `name` is the extern name (`primary`) and `interface` identifies the
37+
/// instance's interface within `types`, whose own name (`foo:bar/iface`) is the
38+
/// returned value.
39+
pub(crate) fn implements_directive<'a>(
40+
types: &'a Types,
41+
name: &str,
42+
interface: InterfaceId,
43+
) -> Option<&'a str> {
44+
// Only a plain-name label is encoded with an `implements` directive; a
45+
// regular interface extern already names its interface directly.
46+
let is_label = matches!(
47+
ComponentName::new(name, 0).as_ref().map(|n| n.kind()),
48+
Ok(ComponentNameKind::Label(_))
49+
);
50+
if !is_label {
51+
return None;
52+
}
53+
types[interface].id.as_deref()
54+
}
1655

1756
/// A type used to abstract the API differences between a component builder,
1857
/// component type, and instance type from `wasm-encoder`.
@@ -65,7 +104,8 @@ impl Encodable {
65104
}
66105
}
67106

68-
fn import_type(&mut self, name: &str, ty: ComponentTypeRef) {
107+
fn import_type<'a>(&mut self, name: impl Into<ComponentExternName<'a>>, ty: ComponentTypeRef) {
108+
let name = name.into();
69109
match self {
70110
Encodable::Component(t) => {
71111
t.import(name, ty);
@@ -730,13 +770,22 @@ impl<'a> TypeEncoder<'a> {
730770
}
731771
ItemKind::Instance(id) => {
732772
let import_index = state.current.encodable.instance_count();
773+
let implements = implements_directive(self.0, name, id);
774+
let extern_name = ComponentExternName {
775+
name: Cow::Borrowed(name),
776+
implements: implements.map(Cow::Borrowed),
777+
};
733778
state
734779
.current
735780
.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);
781+
.import_type(extern_name, ComponentTypeRef::Instance(index));
782+
if implements.is_none() {
783+
if let Some(iid) = &self.0[id].id {
784+
log::debug!(
785+
"instance index {import_index} ({iid}) is available for aliasing"
786+
);
787+
state.current.instances.insert(iid.clone(), import_index);
788+
}
740789
}
741790
}
742791
_ => panic!("expected only types, functions, and instance types"),
@@ -827,9 +876,19 @@ impl<'a> TypeEncoder<'a> {
827876

828877
let ty = kind.ty();
829878
let index = self.ty(state, ty, Some(name));
879+
// Preserve the `(implements "I")` directive when re-encoding an
880+
// instance export that uses a plain-name label.
881+
let implements = match kind {
882+
ItemKind::Instance(id) => implements_directive(self.0, name, id),
883+
_ => None,
884+
};
885+
let extern_name = ComponentExternName {
886+
name: Cow::Borrowed(name),
887+
implements: implements.map(Cow::Borrowed),
888+
};
830889
let index = Self::export_type(
831890
state,
832-
name,
891+
extern_name,
833892
match kind {
834893
ItemKind::Type(_) => ComponentTypeRef::Type(TypeBounds::Eq(index)),
835894
ItemKind::Func(_) => ComponentTypeRef::Func(index),
@@ -880,7 +939,12 @@ impl<'a> TypeEncoder<'a> {
880939
index
881940
}
882941

883-
fn export_type(state: &mut State, name: &str, ty: ComponentTypeRef) -> u32 {
942+
fn export_type<'b>(
943+
state: &mut State,
944+
name: impl Into<ComponentExternName<'b>>,
945+
ty: ComponentTypeRef,
946+
) -> u32 {
947+
let name = name.into();
884948
match &mut state.current.encodable {
885949
Encodable::Component(t) => {
886950
let index = t.type_count();

0 commit comments

Comments
 (0)