Skip to content

Commit

Permalink
Update Component and WIT import/export syntax (#1027)
Browse files Browse the repository at this point in the history
* Implement basic support for new import/export names

* Remove the old URL fields.
* Change the binary format, updating the major version, to have a
  discriminator byte.
* Implement validation of ID names including parsing and uniqueness.
* Update existing tests for new changes.
* Add text-parsing support
* Add text-printing support
* Add encoding support

* Support new WIT syntax in `wit-parser`

This commit implements the changes outlined in
WebAssembly/component-model#193 for the `wit-parser` crate. Namely this
updates all parsing, lexing, and resolution of a WIT package. The
largest change is that the concept of a "document" has been removed.
Additionally most tests needed an update to have a `package foo` header.

Intra-package resolution is also a bit trickier now and required a
restructuring of the AST resolution pass, but nothing too too radical
for what it's doing.

* Update the wit-component crate

This is a very large commit which gets the wit-component crate's tests
working again. The main changes this accounts for are:

* URLs are removed.
* Documents are removed and instead interfaces/worlds are directly in a
  package.
* The encoding structure of a package was slightly updated to reflect this.
* A few minor bugs were fixed now that the ID format is more expressive
  than the prior format.

* Update the wasm-compose crate

This is mostly dealing with the fallout of removing URLs.

* Update the wasm-tools CLI

This removed a few now-obsolete options to `wasm-tools component wit`
(yay!) as they're contextually no longer needed given the new structure
of WIT.

* Update the wit-smith crates

* Require namespaces in WIT IDs

* Add Ord/PartialOrd for PackageName

* Review comments

* Merge import/export names

These may get split again in the future, but for now they're the same.

* rustfmt

* Improve world selection

Add support for syntax to select any world within the `Resolve`,
avoiding the need for the `WorldId` to be defined within the package
specified.

* Support full semver

Add support for full semver versions on interface strings
  • Loading branch information
alexcrichton committed May 25, 2023
1 parent e7e7976 commit a636906
Show file tree
Hide file tree
Showing 731 changed files with 5,589 additions and 5,311 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ serde = { version = "1.0.137", features = ["derive"] }
wasmtime = { version = "3.0.0", default-features = false, features = ['cranelift'] }
url = "2.0.0"
pretty_assertions = "1.3.0"
semver = "1.0.0"

wasm-encoder = { version = "0.28.0", path = "crates/wasm-encoder"}
wasm-compose = { version = "0.2.16", path = "crates/wasm-compose"}
Expand Down
6 changes: 3 additions & 3 deletions crates/wasm-compose/src/composer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl<'a> CompositionGraphBuilder<'a> {
let (_, instance_id) = self.instances.get_index(instance).unwrap();
let (_, component) = self.graph.get_component_of_instance(*instance_id).unwrap();
match component.export_by_name(export) {
Some((export_index, _, kind, index)) if kind == ComponentExternalKind::Instance => {
Some((export_index, kind, index)) if kind == ComponentExternalKind::Instance => {
let export_ty = component.types.component_instance_at(index).unwrap();
if !ComponentEntityType::is_subtype_of(
&ComponentEntityType::Instance(export_ty),
Expand All @@ -236,7 +236,7 @@ impl<'a> CompositionGraphBuilder<'a> {
/// Resolves an import instance reference.
fn resolve_import_ref(&self, r: InstanceImportRef) -> (&Component, &str, TypeId) {
let component = self.graph.get_component(r.component).unwrap();
let (name, _, ty) = component.import(r.import).unwrap();
let (name, ty) = component.import(r.import).unwrap();
match ty {
ComponentTypeRef::Instance(index) => (
component,
Expand Down Expand Up @@ -307,7 +307,7 @@ impl<'a> CompositionGraphBuilder<'a> {
let count = queue.len();

// Push a dependency for every instance import
for (import, name, _, ty) in component.imports() {
for (import, name, ty) in component.imports() {
match ty {
ComponentTypeRef::Instance(_) => {}
_ => bail!(
Expand Down
129 changes: 35 additions & 94 deletions crates/wasm-compose/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,35 +132,35 @@ impl<'a> TypeEncoder<'a> {

pub fn component<I, E>(&self, imports: I, exports: E) -> ComponentType
where
I: IntoIterator<Item = (&'a str, &'a str, wasmparser::types::ComponentEntityType)>,
E: IntoIterator<Item = (&'a str, &'a str, wasmparser::types::ComponentEntityType)>,
I: IntoIterator<Item = (&'a str, wasmparser::types::ComponentEntityType)>,
E: IntoIterator<Item = (&'a str, wasmparser::types::ComponentEntityType)>,
{
let mut encoded = ComponentType::new();
let mut types = TypeMap::new();

for (name, url, ty) in imports {
for (name, ty) in imports {
let ty = self.component_entity_type(&mut encoded, &mut types, ty);
encoded.import(name, url, ty);
encoded.import(name, ty);
}

for (name, url, ty) in exports {
for (name, ty) in exports {
let export = self.export(ty, &mut encoded, &mut types);
encoded.export(name, url, export);
encoded.export(name, export);
}

encoded
}

pub fn instance<E>(&self, exports: E) -> InstanceType
where
E: IntoIterator<Item = (&'a str, &'a str, wasmparser::types::ComponentEntityType)>,
E: IntoIterator<Item = (&'a str, wasmparser::types::ComponentEntityType)>,
{
let mut encoded = InstanceType::new();
let mut types = TypeMap::new();

for (name, url, ty) in exports {
for (name, ty) in exports {
let export = self.export(ty, &mut encoded, &mut types);
encoded.export(name, url, export);
encoded.export(name, export);
}

encoded
Expand Down Expand Up @@ -372,9 +372,7 @@ impl<'a> TypeEncoder<'a> {
Entry::Occupied(e) => *e.get(),
Entry::Vacant(e) => {
let ty = ty.as_component_instance_type().unwrap();
let instance = self.instance(ty.exports.iter().map(|(n, (u, t))| {
(n.as_str(), u.as_ref().map(|u| u.as_str()).unwrap_or(""), *t)
}));
let instance = self.instance(ty.exports.iter().map(|(n, t)| (n.as_str(), *t)));
let index = encodable.type_count();
encodable.ty().instance(&instance);
*e.insert(index)
Expand All @@ -395,12 +393,8 @@ impl<'a> TypeEncoder<'a> {
let ty = ty.as_component_type().unwrap();

let component = self.component(
ty.imports.iter().map(|(n, (u, t))| {
(n.as_str(), u.as_ref().map(|u| u.as_str()).unwrap_or(""), *t)
}),
ty.exports.iter().map(|(n, (u, t))| {
(n.as_str(), u.as_ref().map(|u| u.as_str()).unwrap_or(""), *t)
}),
ty.imports.iter().map(|(n, t)| (n.as_str(), *t)),
ty.exports.iter().map(|(n, t)| (n.as_str(), *t)),
);

let index = encodable.type_count();
Expand Down Expand Up @@ -721,24 +715,13 @@ enum ArgumentImportKind<'a> {
/// Instances are unioned together to form a single instance to
/// import that will satisfy all instantiation arguments that
/// reference the import.
Instance(
IndexMap<
&'a str,
(
&'a str,
&'a crate::graph::Component<'a>,
ComponentEntityType,
),
>,
),
Instance(IndexMap<&'a str, (&'a crate::graph::Component<'a>, ComponentEntityType)>),
}

/// Represents an import for an instantiation argument.
struct ArgumentImport<'a> {
// The name of the import.
name: &'a str,
/// The URL of the import.
url: &'a str,
/// The kind of import.
kind: ArgumentImportKind<'a>,
/// The instances that will use the import for an argument.
Expand All @@ -763,15 +746,8 @@ impl ArgumentImport<'_> {
.iter();

let mut map = IndexMap::with_capacity(exports.len());
for (name, (url, ty)) in exports {
map.insert(
name.as_str(),
(
url.as_ref().map(|u| u.as_str()).unwrap_or(""),
*component,
*ty,
),
);
for (name, ty) in exports {
map.insert(name.as_str(), (*component, *ty));
}

self.kind = ArgumentImportKind::Instance(map);
Expand All @@ -787,16 +763,7 @@ impl ArgumentImport<'_> {
ArgumentImportKind::Instance(exports),
ArgumentImportKind::Item(new_component, ComponentEntityType::Instance(id)),
) => {
if arg.url != self.url {
bail!(
"cannot import instance with name `{name}` because import URL `{url}` conflicts with `{other}`",
name = self.name,
url = self.url,
other = arg.url,
);
}

for (name, (url, new_type)) in new_component
for (name, new_type) in new_component
.types
.type_from_id(id)
.unwrap()
Expand All @@ -807,7 +774,7 @@ impl ArgumentImport<'_> {
{
match exports.entry(name.as_str()) {
indexmap::map::Entry::Occupied(mut e) => {
let (_, existing_component, existing_type) = e.get_mut();
let (existing_component, existing_type) = e.get_mut();
match Self::compatible_type(
existing_component,
*existing_type,
Expand All @@ -827,11 +794,7 @@ impl ArgumentImport<'_> {
}
}
indexmap::map::Entry::Vacant(e) => {
e.insert((
url.as_ref().map(|u| u.as_str()).unwrap_or(""),
new_component,
*new_type,
));
e.insert((new_component, *new_type));
}
}
}
Expand All @@ -857,16 +820,6 @@ impl ArgumentImport<'_> {
new_type,
) {
Some((c, ty)) => {
if arg.url != self.url {
bail!(
"cannot import {ty} with name `{name}` because import URL `{url}` conflicts with `{other}`",
ty = type_desc(new_type),
name = self.name,
url = self.url,
other = arg.url
);
}

*existing_component = c;
*existing_type = ty;
}
Expand Down Expand Up @@ -968,16 +921,15 @@ impl<'a> ImportMap<'a> {
let instance_index = InstanceIndex(instance_index);

// Import any unconnected instantiation arguments for the instance
for (import_index, name, _, _) in entry.component.imports() {
for (import_index, name, _) in entry.component.imports() {
if instance.connected.contains(&import_index) {
continue;
}

let (_, url, ty) = entry.component.import_entity_type(import_index).unwrap();
let (_, ty) = entry.component.import_entity_type(import_index).unwrap();

let arg = ArgumentImport {
name,
url,
kind: ArgumentImportKind::Item(&entry.component, ty),
instances: smallvec::smallvec![(instance_index, import_index)],
};
Expand Down Expand Up @@ -1095,20 +1047,19 @@ impl<'a> CompositionGraphEncoder<'a> {
for (name, entry) in imports.0 {
match entry {
ImportMapEntry::Component(component) => {
self.encode_component_import(encoded, name.as_ref(), "", component);
self.encode_component_import(encoded, name.as_ref(), component);
}
ImportMapEntry::Argument(arg) => {
let index = match arg.kind {
ArgumentImportKind::Item(component, ty) => self.encode_item_import(
encoded,
&mut type_map,
name.as_ref(),
arg.url,
component,
ty,
),
ArgumentImportKind::Instance(exports) => {
self.encode_instance_import(encoded, name.as_ref(), arg.url, exports)
self.encode_instance_import(encoded, name.as_ref(), exports)
}
};

Expand All @@ -1125,11 +1076,10 @@ impl<'a> CompositionGraphEncoder<'a> {
&mut self,
encoded: &mut Component,
name: &str,
url: &str,
component: &'a crate::graph::Component,
) -> u32 {
let type_index = self.define_component_type(encoded, component);
let index = self.import(encoded, name, url, ComponentTypeRef::Component(type_index));
let index = self.import(encoded, name, ComponentTypeRef::Component(type_index));

assert!(self
.encoded_components
Expand All @@ -1144,7 +1094,6 @@ impl<'a> CompositionGraphEncoder<'a> {
encoded: &mut Component,
type_map: &mut TypeMap<'a>,
name: &str,
url: &str,
component: &'a crate::graph::Component,
ty: ComponentEntityType,
) -> u32 {
Expand All @@ -1169,22 +1118,21 @@ impl<'a> CompositionGraphEncoder<'a> {
self.types += type_section.len();
}

self.import(encoded, name, url, ty)
self.import(encoded, name, ty)
}

fn encode_instance_import(
&mut self,
encoded: &mut Component,
name: &str,
url: &str,
exports: IndexMap<&'a str, (&'a str, &'a crate::graph::Component, ComponentEntityType)>,
exports: IndexMap<&'a str, (&'a crate::graph::Component, ComponentEntityType)>,
) -> u32 {
let mut instance_type = InstanceType::new();
let mut types = TypeMap::new();
for (name, (url, component, ty)) in exports {
for (name, (component, ty)) in exports {
let encoder = TypeEncoder::new(&component.types);
let export = encoder.export(ty, &mut instance_type, &mut types);
instance_type.export(name, url, export);
instance_type.export(name, export);
}

let index = self.types;
Expand All @@ -1193,7 +1141,7 @@ impl<'a> CompositionGraphEncoder<'a> {
encoded.section(&type_section);
self.types += 1;

self.import(encoded, name, url, ComponentTypeRef::Instance(index))
self.import(encoded, name, ComponentTypeRef::Instance(index))
}

fn encode_instantiations(&mut self, encoded: &mut Component) -> Result<()> {
Expand Down Expand Up @@ -1227,7 +1175,7 @@ impl<'a> CompositionGraphEncoder<'a> {

let mut alias_section = ComponentAliasSection::new();
let mut export_section = ComponentExportSection::new();
for (export_index, export_name, export_url, kind, _) in entry.component.exports() {
for (export_index, export_name, kind, _) in entry.component.exports() {
let kind = match kind {
ComponentExternalKind::Module => ComponentExportKind::Module,
ComponentExternalKind::Func => ComponentExportKind::Func,
Expand All @@ -1251,7 +1199,7 @@ impl<'a> CompositionGraphEncoder<'a> {
}
};

export_section.export(export_name, export_url, kind, index, None);
export_section.export(export_name, kind, index, None);
}

if !alias_section.is_empty() {
Expand Down Expand Up @@ -1376,11 +1324,10 @@ impl<'a> CompositionGraphEncoder<'a> {

for (import_index, export_index) in map {
// Check to see if we need to alias the item from the source instance
let (name, _, ty) = component.import(*import_index).unwrap();
let (name, ty) = component.import(*import_index).unwrap();
let index = match export_index {
Some(export_index) => {
let (export_name, _, _, _) =
source_component.export(*export_index).unwrap();
let (export_name, _, _) = source_component.export(*export_index).unwrap();
match self.aliases.get(&(source_id, *export_index)) {
Some(index) => *index,
None => {
Expand All @@ -1402,7 +1349,7 @@ impl<'a> CompositionGraphEncoder<'a> {
}

// Finally, add any instantiation arguments that are being imported
for (i, (name, (_, ty))) in component.imports.iter().enumerate() {
for (i, (name, ty)) in component.imports.iter().enumerate() {
let import_index = ImportIndex(i);
if instance.connected.contains(&import_index) {
continue;
Expand All @@ -1415,13 +1362,7 @@ impl<'a> CompositionGraphEncoder<'a> {
args
}

fn import(
&mut self,
encoded: &mut Component,
name: &str,
url: &str,
ty: ComponentTypeRef,
) -> u32 {
fn import(&mut self, encoded: &mut Component, name: &str, ty: ComponentTypeRef) -> u32 {
let (desc, count) = match ty {
ComponentTypeRef::Module(_) => ("module", &mut self.modules),
ComponentTypeRef::Func(_) => ("function", &mut self.funcs),
Expand All @@ -1434,7 +1375,7 @@ impl<'a> CompositionGraphEncoder<'a> {
log::debug!("importing {desc} with `{name}` (encoded index {count}) in composed component");

let mut import_section = ComponentImportSection::new();
import_section.import(name, url, ty);
import_section.import(name, ty);
encoded.section(&import_section);

let index = *count;
Expand Down
Loading

0 comments on commit a636906

Please sign in to comment.