Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ca2cbe8
[wasmparser] Add `[implements=<I>]L` component name support
ricochet Feb 27, 2026
f4950af
[wit-parser] Add `implements` syntax for named interface imports/exports
ricochet Feb 28, 2026
a8ace98
[wit-component] Add implements encoding support to wit-component
ricochet Feb 28, 2026
84e6174
[wit-dylib] add implements to WorldItem::Interface
ricochet Feb 28, 2026
7c5a1ba
[wit-smith] Add implements interface generation
ricochet Feb 28, 2026
d9bc42f
Merge remote-tracking branch 'origin/main' into implements
alexcrichton May 11, 2026
dc55501
Generate nominal interface/type ids in `wit-component`
alexcrichton May 12, 2026
c334f81
Leverage nominal types/interfaces in encoding
alexcrichton May 12, 2026
2030d98
Merge branch 'main' into implements
alexcrichton May 12, 2026
077e790
Add `cm-implements` feature
alexcrichton May 11, 2026
70d4a42
Shift where `implements` is in the AST
alexcrichton May 11, 2026
05648dc
Remove hand-rolled parsing
alexcrichton May 11, 2026
5f7d62b
Only write blessed files if they change
alexcrichton May 11, 2026
d09c7e6
Reimplement how `implements` is represented
alexcrichton May 11, 2026
807cb8b
Handle `import a: b` in `generate_nominal_type_ids`
alexcrichton May 12, 2026
cec17d3
Fix recording stability of named interfaces
alexcrichton May 12, 2026
f582cbb
Fix the order nominalization happens in
alexcrichton May 12, 2026
e8397cd
Use the 2024 edition for rustfmt in this workspace
alexcrichton May 12, 2026
36958a6
Merge remote-tracking branch 'origin/main' into implements
alexcrichton May 12, 2026
c9e43a7
Fix configured build
alexcrichton May 12, 2026
77623c1
Merge remote-tracking branch 'origin/main' into implements
alexcrichton May 13, 2026
db4be4c
Reduce the diff with `main`
alexcrichton May 13, 2026
d976720
Improve ergonomics of wasm-encoder
alexcrichton May 13, 2026
6adbce9
Undo another interim change
alexcrichton May 13, 2026
a8a6165
More diff minimizing
alexcrichton May 13, 2026
6bf8663
Add failing test
alexcrichton May 13, 2026
ecd401b
Don't permute imports/exports
alexcrichton May 13, 2026
d860cd7
Update binary encoding
alexcrichton May 13, 2026
757849e
Review comments
alexcrichton May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions crates/wasm-compose/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ impl<'a> TypeEncoder<'a> {
id: ComponentInstanceTypeId,
) -> u32 {
let ty = &self.0.types[id];
let instance = self.instance(state, ty.exports.iter().map(|(n, t)| (n.as_str(), *t)));
let instance = self.instance(state, ty.exports.iter().map(|(n, t)| (n.as_str(), t.ty)));
let index = state.cur.encodable.type_count();
state.cur.encodable.ty().instance(&instance);
index
Expand All @@ -466,8 +466,8 @@ impl<'a> TypeEncoder<'a> {

let component = self.component(
state,
ty.imports.iter().map(|(n, t)| (n.as_str(), *t)),
ty.exports.iter().map(|(n, t)| (n.as_str(), *t)),
ty.imports.iter().map(|(n, t)| (n.as_str(), t.ty)),
ty.exports.iter().map(|(n, t)| (n.as_str(), t.ty)),
);

let index = state.cur.encodable.type_count();
Expand Down Expand Up @@ -884,7 +884,7 @@ impl ArgumentImport<'_> {

let mut map = IndexMap::with_capacity(exports.len());
for (name, ty) in exports {
map.insert(name.as_str(), vec![(*component, *ty)]);
map.insert(name.as_str(), vec![(*component, ty.ty)]);
}

self.kind = ArgumentImportKind::Instance(map);
Expand All @@ -907,7 +907,7 @@ impl ArgumentImport<'_> {
existing_component,
*existing_type,
new_component,
*new_type,
new_type.ty,
remapping,
) {
continue;
Expand All @@ -923,7 +923,7 @@ impl ArgumentImport<'_> {
ecname = existing_component.name,
)
}
dst.push((new_component, *new_type));
dst.push((new_component, new_type.ty));
}
}
// Otherwise, an attempt to merge an instance with a non-instance is an error
Expand Down Expand Up @@ -1244,14 +1244,14 @@ impl DependencyRegistrar<'_, '_> {

fn component(&mut self, ty: ComponentTypeId) {
let ty = &self.types[ty];
for (_, ty) in ty.imports.iter().chain(&ty.exports) {
self.entity(*ty);
for ty in ty.imports.values().chain(ty.exports.values()) {
self.entity(ty.ty);
}
}

fn instance(&mut self, ty: ComponentInstanceTypeId) {
for (_, ty) in self.types[ty].exports.iter() {
self.entity(*ty);
self.entity(ty.ty);
}
}

Expand Down
21 changes: 11 additions & 10 deletions crates/wasm-compose/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,14 @@ impl<'a> Component<'a> {
Payload::ComponentImportSection(s) => {
for import in s {
let import = import?;
let name = import.name.0.to_string();
let name = import.name.name.to_string();
imports.insert(name, import.ty);
}
}
Payload::ComponentExportSection(s) => {
for export in s {
let export = export?;
let name = export.name.0.to_string();
let name = export.name.name.to_string();
exports.insert(name, (export.kind, export.index));
}
}
Expand Down Expand Up @@ -277,7 +277,7 @@ impl<'a> Component<'a> {
let (name, _kind, _index) = self.export(index)?;
Some((
name,
self.types.as_ref().component_entity_type_of_export(name)?,
self.types.as_ref().component_item_for_export(name)?.ty,
))
}

Expand All @@ -288,7 +288,7 @@ impl<'a> Component<'a> {
let (name, _ty) = self.import(index)?;
Some((
name,
self.types.as_ref().component_entity_type_of_import(name)?,
self.types.as_ref().component_item_for_import(name)?.ty,
))
}

Expand Down Expand Up @@ -333,7 +333,7 @@ impl<'a> Component<'a> {
match self.exports.get_full(k.as_str()) {
Some((ai, _, _)) => {
let (_, a) = self.export_entity_type(ExportIndex(ai)).unwrap();
if !ComponentEntityType::is_subtype_of(&a, self.types(), b, types) {
if !ComponentEntityType::is_subtype_of(&a, self.types(), &b.ty, types) {
return false;
}
}
Expand Down Expand Up @@ -497,7 +497,7 @@ impl ResourceMapping {
if let ComponentEntityType::Type {
referenced: ComponentAnyTypeId::Resource(resource_id),
..
} = ty
} = ty.ty
{
exports.insert(export_name, (export_component, resource_id.resource()));
}
Expand All @@ -508,7 +508,7 @@ impl ResourceMapping {
if let ComponentEntityType::Type {
referenced: ComponentAnyTypeId::Resource(resource_id),
..
} = ty
} = ty.ty
{
let import_resource = resource_id.resource();
if let Some((export_component, export_resource)) =
Expand Down Expand Up @@ -591,16 +591,17 @@ impl<'a> CompositionGraph<'a> {
let ty = component
.types
.as_ref()
.component_entity_type_of_import(import_name)
.unwrap();
.component_item_for_import(import_name)
.unwrap()
.ty;

if let ComponentEntityType::Instance(instance_id) = ty {
for (export_name, ty) in &component.types[instance_id].exports {
// TODO: support nested instances
if let ComponentEntityType::Type {
referenced: ComponentAnyTypeId::Resource(resource_id),
..
} = ty
} = ty.ty
{
let set = resource_imports
.entry(vec![import_name.to_string(), export_name.to_string()])
Expand Down
28 changes: 17 additions & 11 deletions crates/wasm-encoder/src/component/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,19 @@ impl ComponentBuilder {
}

/// Imports a new item into this component with the `name` and `ty` specified.
pub fn import(&mut self, name: &str, ty: ComponentTypeRef) -> u32 {
pub fn import<'a>(
&mut self,
name: impl Into<ComponentExternName<'a>>,
ty: ComponentTypeRef,
) -> u32 {
let name = name.into();
let ret = match &ty {
ComponentTypeRef::Instance(_) => self.instances.add(Some(name)),
ComponentTypeRef::Func(_) => self.funcs.add(Some(name)),
ComponentTypeRef::Type(..) => self.types.add(Some(name)),
ComponentTypeRef::Component(_) => self.components.add(Some(name)),
ComponentTypeRef::Module(_) => self.core_modules.add(Some(name)),
ComponentTypeRef::Value(_) => self.values.add(Some(name)),
ComponentTypeRef::Instance(_) => self.instances.add(Some(&name.name)),
ComponentTypeRef::Func(_) => self.funcs.add(Some(&name.name)),
ComponentTypeRef::Type(..) => self.types.add(Some(&name.name)),
ComponentTypeRef::Component(_) => self.components.add(Some(&name.name)),
ComponentTypeRef::Module(_) => self.core_modules.add(Some(&name.name)),
ComponentTypeRef::Value(_) => self.values.add(Some(&name.name)),
};
self.imports().import(name, ty);
ret
Expand All @@ -345,15 +350,16 @@ impl ComponentBuilder {
///
/// The `idx` is the item to export and the `ty` is an optional type to
/// ascribe to the export.
pub fn export(
pub fn export<'a>(
&mut self,
name: &str,
name: impl Into<ComponentExternName<'a>>,
kind: ComponentExportKind,
idx: u32,
ty: Option<ComponentTypeRef>,
) -> u32 {
self.exports().export(name, kind, idx, ty);
self.inc_kind(Some(name), kind)
let name = name.into();
self.exports().export(name.clone(), kind, idx, ty);
self.inc_kind(Some(&name.name), kind)
}

/// Creates a new encoder for the next core type in this component.
Expand Down
17 changes: 7 additions & 10 deletions crates/wasm-encoder/src/component/exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use super::{
COMPONENT_SORT, CORE_MODULE_SORT, CORE_SORT, FUNCTION_SORT, INSTANCE_SORT, TYPE_SORT,
VALUE_SORT,
};
use crate::{ComponentSection, ComponentSectionId, ComponentTypeRef, Encode, encode_section};
use crate::{
ComponentExternName, ComponentSection, ComponentSectionId, ComponentTypeRef, Encode,
encode_section,
};
use alloc::vec::Vec;

/// Represents the kind of an export from a WebAssembly component.
Expand Down Expand Up @@ -87,14 +90,14 @@ impl ComponentExportSection {
}

/// Define an export in the export section.
pub fn export(
pub fn export<'a>(
&mut self,
name: &str,
name: impl Into<ComponentExternName<'a>>,
kind: ComponentExportKind,
index: u32,
ty: Option<ComponentTypeRef>,
) -> &mut Self {
crate::encode_component_export_name(&mut self.bytes, name);
name.into().encode(&mut self.bytes);
kind.encode(&mut self.bytes);
index.encode(&mut self.bytes);
match ty {
Expand Down Expand Up @@ -122,9 +125,3 @@ impl ComponentSection for ComponentExportSection {
ComponentSectionId::Export.into()
}
}

/// For more information on this see `encode_component_import_name`.
pub(crate) fn encode_component_export_name(bytes: &mut Vec<u8>, name: &str) {
bytes.push(0x00);
name.encode(bytes);
}
113 changes: 95 additions & 18 deletions crates/wasm-encoder/src/component/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::{
ComponentExportKind, ComponentSection, ComponentSectionId, ComponentValType, Encode,
encode_section,
};
use alloc::borrow::Cow;
use alloc::string::String;
use alloc::vec::Vec;

/// Represents the possible type bounds for type references.
Expand Down Expand Up @@ -131,8 +133,12 @@ impl ComponentImportSection {
}

/// Define an import in the component import section.
pub fn import(&mut self, name: &str, ty: ComponentTypeRef) -> &mut Self {
encode_component_import_name(&mut self.bytes, name);
pub fn import<'a>(
&mut self,
name: impl Into<ComponentExternName<'a>>,
ty: ComponentTypeRef,
) -> &mut Self {
name.into().encode(&mut self.bytes);
ty.encode(&mut self.bytes);
self.num_added += 1;
self
Expand All @@ -151,20 +157,91 @@ impl ComponentSection for ComponentImportSection {
}
}

/// Prior to WebAssembly/component-model#263 import and export names were
/// discriminated with a leading byte indicating what kind of import they are.
/// After that PR though names are always prefixed with a 0x00 byte.
///
/// On 2023-10-28 in bytecodealliance/wasm-tools#1262 was landed to start
/// transitioning to "always lead with 0x00". That updated the validator/parser
/// to accept either 0x00 or 0x01 but the encoder wasn't updated at the time.
///
/// On 2024-09-03 in bytecodealliance/wasm-tools#TODO this encoder was updated
/// to always emit 0x00 as a leading byte.
///
/// This function corresponds with the `importname'` production in the
/// specification.
pub(crate) fn encode_component_import_name(bytes: &mut Vec<u8>, name: &str) {
bytes.push(0x00);
name.encode(bytes);
/// Full options for encoding a component name.
#[derive(Debug, Clone)]
pub struct ComponentExternName<'a> {
/// The name to encode.
pub name: Cow<'a, str>,
/// An optional `(implements ...)` directive (See 🏷️ in the component model
/// explainer).
pub implements: Option<Cow<'a, str>>,
}

impl Encode for ComponentExternName<'_> {
fn encode(&self, bytes: &mut Vec<u8>) {
let mut options = Vec::new();

if let Some(s) = &self.implements {
options.push((0x00, s.as_bytes()));
}

if options.is_empty() {
// Prior to WebAssembly/component-model#263 import and export names
// were discriminated with a leading byte indicating what kind of
// import they are. After that PR though names are always prefixed
// with a 0x00 byte.
//
// On 2023-10-28 in bytecodealliance/wasm-tools#1262 was landed to
// start transitioning to "always lead with 0x00". That updated the
// validator/parser to accept either 0x00 or 0x01 but the encoder
// wasn't updated at the time.
//
// On 2024-09-03 in bytecodealliance/wasm-tools#TODO this encoder
// was updated to always emit 0x00 as a leading byte.
//
// This corresponds with the `importname'` production in the
// specification.
bytes.push(0x00);
} else {
bytes.push(0x02);
}

self.name.encode(bytes);

if !options.is_empty() {
options.len().encode(bytes);
for (kind, val) in options {
bytes.push(kind);
val.encode(bytes);
}
}
}
}

impl<'a> From<&'a str> for ComponentExternName<'a> {
fn from(name: &'a str) -> Self {
ComponentExternName {
name: Cow::Borrowed(name),
implements: None,
}
}
}

impl<'a> From<&'a String> for ComponentExternName<'a> {
fn from(name: &'a String) -> Self {
ComponentExternName {
name: Cow::Borrowed(name),
implements: None,
}
}
}

impl<'a> From<String> for ComponentExternName<'a> {
fn from(name: String) -> Self {
ComponentExternName {
name: Cow::Owned(name),
implements: None,
}
}
}

#[cfg(feature = "wasmparser")]
impl<'a> From<wasmparser::ComponentExternName<'a>> for ComponentExternName<'a> {
fn from(name: wasmparser::ComponentExternName<'a>) -> Self {
let wasmparser::ComponentExternName { name, implements } = name;
ComponentExternName {
name: name.into(),
implements: implements.map(|s| s.into()),
}
}
}
10 changes: 6 additions & 4 deletions crates/wasm-encoder/src/component/instances.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::CORE_INSTANCE_SORT;
use crate::{
ComponentExportKind, ComponentSection, ComponentSectionId, Encode, ExportKind, encode_section,
ComponentExportKind, ComponentExternName, ComponentSection, ComponentSectionId, Encode,
ExportKind, encode_section,
};
use alloc::vec::Vec;

Expand Down Expand Up @@ -169,16 +170,17 @@ impl ComponentInstanceSection {
}

/// Define an instance by exporting items.
pub fn export_items<'a, E>(&mut self, exports: E) -> &mut Self
pub fn export_items<'a, N, E>(&mut self, exports: E) -> &mut Self
where
E: IntoIterator<Item = (&'a str, ComponentExportKind, u32)>,
E: IntoIterator<Item = (N, ComponentExportKind, u32)>,
E::IntoIter: ExactSizeIterator,
N: Into<ComponentExternName<'a>>,
{
let exports = exports.into_iter();
self.bytes.push(0x01);
exports.len().encode(&mut self.bytes);
for (name, kind, index) in exports {
crate::encode_component_export_name(&mut self.bytes, name);
name.into().encode(&mut self.bytes);
kind.encode(&mut self.bytes);
index.encode(&mut self.bytes);
}
Expand Down
Loading
Loading