From 3277e9c9e36d68b6bf8b4b20fd42013ad18e95f5 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 10 Jun 2024 23:17:39 -0500 Subject: [PATCH 01/16] enable binary encoding and printing of multi package wit files --- crates/wit-component/src/encoding/wit/mod.rs | 10 +- crates/wit-component/src/encoding/wit/v1.rs | 124 ++++++++++--------- crates/wit-component/src/encoding/wit/v2.rs | 60 ++++----- crates/wit-component/src/printing.rs | 2 +- crates/wit-component/tests/interfaces.rs | 9 +- crates/wit-parser/src/decoding.rs | 90 +++++++------- crates/wit-smith/src/lib.rs | 2 +- src/bin/wasm-tools/component.rs | 7 +- 8 files changed, 152 insertions(+), 152 deletions(-) diff --git a/crates/wit-component/src/encoding/wit/mod.rs b/crates/wit-component/src/encoding/wit/mod.rs index a5b61a5370..d78706e64d 100644 --- a/crates/wit-component/src/encoding/wit/mod.rs +++ b/crates/wit-component/src/encoding/wit/mod.rs @@ -32,8 +32,8 @@ fn use_v2_encoding() -> bool { /// /// The binary returned can be [`decode`d](crate::decode) to recover the WIT /// package provided. -pub fn encode(use_v2: Option, resolve: &Resolve, package: PackageId) -> Result> { - let mut component = encode_component(use_v2, resolve, package)?; +pub fn encode(use_v2: Option, resolve: &Resolve, packages: &[PackageId]) -> Result> { + let mut component = encode_component(use_v2, resolve, packages)?; component.raw_custom_section(&crate::base_producers().raw_custom_section()); Ok(component.finish()) } @@ -43,12 +43,12 @@ pub fn encode(use_v2: Option, resolve: &Resolve, package: PackageId) -> Re pub fn encode_component( use_v2: Option, resolve: &Resolve, - package: PackageId, + packages: &[PackageId], ) -> Result { if use_v2.unwrap_or_else(use_v2_encoding) { - v2::encode_component(resolve, package) + v2::encode_component(resolve, packages) } else { - v1::encode_component(resolve, package) + v1::encode_component(resolve, packages) } } diff --git a/crates/wit-component/src/encoding/wit/v1.rs b/crates/wit-component/src/encoding/wit/v1.rs index ed6c39a35f..b2dfb009fe 100644 --- a/crates/wit-component/src/encoding/wit/v1.rs +++ b/crates/wit-component/src/encoding/wit/v1.rs @@ -24,19 +24,21 @@ use wit_parser::*; /// /// The binary returned can be [`decode`d](crate::decode) to recover the WIT /// package provided. -pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result { +pub fn encode_component(resolve: &Resolve, packages: &[PackageId]) -> Result { let mut encoder = Encoder { component: ComponentBuilder::default(), resolve, - package, + packages, }; encoder.run()?; - let package_metadata = PackageMetadata::extract(resolve, package); - encoder.component.custom_section(&CustomSection { - name: PackageMetadata::SECTION_NAME.into(), - data: package_metadata.encode()?.into(), - }); + for package in packages { + let package_metadata = PackageMetadata::extract(resolve, *package); + encoder.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); + } Ok(encoder.component) } @@ -44,7 +46,7 @@ pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result { component: ComponentBuilder, resolve: &'a Resolve, - package: PackageId, + packages: &'a [PackageId], } impl Encoder<'_> { @@ -57,67 +59,69 @@ impl Encoder<'_> { // decoding process where everyone's view of a foreign document agrees // notably on the order that types are defined in to assist with // roundtripping. - let mut interfaces = IndexSet::new(); - for (_, id) in self.resolve.packages[self.package].interfaces.iter() { - self.add_live_interfaces(&mut interfaces, *id); - } + for pkg in self.packages { + let mut interfaces = IndexSet::new(); + for (_, id) in self.resolve.packages[*pkg].interfaces.iter() { + self.add_live_interfaces(&mut interfaces, *id); + } - // Seed the set of used names with all exported interfaces to ensure - // that imported interfaces choose different names as the import names - // aren't used during decoding. - let mut used_names = IndexSet::new(); - for id in interfaces.iter() { - let iface = &self.resolve.interfaces[*id]; - if iface.package == Some(self.package) { - let first = used_names.insert(iface.name.as_ref().unwrap().clone()); + // Seed the set of used names with all exported interfaces to ensure + // that imported interfaces choose different names as the import names + // aren't used during decoding. + let mut used_names = IndexSet::new(); + for id in interfaces.iter() { + let iface = &self.resolve.interfaces[*id]; + if iface.package == Some(*pkg) { + let first = used_names.insert(iface.name.as_ref().unwrap().clone()); + assert!(first); + } + } + for (name, _world) in self.resolve.packages[*pkg].worlds.iter() { + let first = used_names.insert(name.clone()); assert!(first); } - } - for (name, _world) in self.resolve.packages[self.package].worlds.iter() { - let first = used_names.insert(name.clone()); - assert!(first); - } - // Encode all interfaces, foreign and local, into this component type. - // Local interfaces get their functions defined as well and are - // exported. Foreign interfaces are imported and only have their types - // encoded. - let mut encoder = InterfaceEncoder::new(self.resolve); - for interface in interfaces { - encoder.interface = Some(interface); - let iface = &self.resolve.interfaces[interface]; - let name = self.resolve.id_of(interface).unwrap(); - log::trace!("encoding interface {name}"); - if iface.package == Some(self.package) { - let idx = encoder.encode_instance(interface)?; - encoder.outer.export(&name, ComponentTypeRef::Instance(idx)); - } else { - encoder.push_instance(); - for (_, id) in iface.types.iter() { - encoder.encode_valtype(self.resolve, &Type::Id(*id))?; + // Encode all interfaces, foreign and local, into this component type. + // Local interfaces get their functions defined as well and are + // exported. Foreign interfaces are imported and only have their types + // encoded. + let mut encoder = InterfaceEncoder::new(self.resolve); + for interface in interfaces { + encoder.interface = Some(interface); + let iface = &self.resolve.interfaces[interface]; + let name = self.resolve.id_of(interface).unwrap(); + log::trace!("encoding interface {name}"); + if iface.package == Some(*pkg) { + let idx = encoder.encode_instance(interface)?; + encoder.outer.export(&name, ComponentTypeRef::Instance(idx)); + } else { + encoder.push_instance(); + for (_, id) in iface.types.iter() { + encoder.encode_valtype(self.resolve, &Type::Id(*id))?; + } + let instance = encoder.pop_instance(); + let idx = encoder.outer.type_count(); + encoder.outer.ty().instance(&instance); + encoder.import_map.insert(interface, encoder.instances); + encoder.instances += 1; + encoder.outer.import(&name, ComponentTypeRef::Instance(idx)); } - let instance = encoder.pop_instance(); + } + encoder.interface = None; + + for (name, world) in self.resolve.packages[*pkg].worlds.iter() { + let component_ty = encode_world(self.resolve, *world)?; let idx = encoder.outer.type_count(); - encoder.outer.ty().instance(&instance); - encoder.import_map.insert(interface, encoder.instances); - encoder.instances += 1; - encoder.outer.import(&name, ComponentTypeRef::Instance(idx)); + encoder.outer.ty().component(&component_ty); + let id = self.resolve.packages[*pkg].name.interface_id(name); + encoder.outer.export(&id, ComponentTypeRef::Component(idx)); } - } - encoder.interface = None; - for (name, world) in self.resolve.packages[self.package].worlds.iter() { - let component_ty = encode_world(self.resolve, *world)?; - let idx = encoder.outer.type_count(); - encoder.outer.ty().component(&component_ty); - let id = self.resolve.packages[self.package].name.interface_id(name); - encoder.outer.export(&id, ComponentTypeRef::Component(idx)); + let ty = self.component.type_component(&encoder.outer); + let id = self.resolve.packages[*pkg].name.interface_id("wit"); + self.component + .export(&id, ComponentExportKind::Type, ty, None); } - - let ty = self.component.type_component(&encoder.outer); - let id = self.resolve.packages[self.package].name.interface_id("wit"); - self.component - .export(&id, ComponentExportKind::Type, ty, None); Ok(()) } diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index 7024c6ccf6..a4de81880b 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -24,19 +24,21 @@ use wit_parser::*; /// /// The binary returned can be [`decode`d](crate::decode) to recover the WIT /// package provided. -pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result { +pub fn encode_component(resolve: &Resolve, packages: &[PackageId]) -> Result { let mut encoder = Encoder { component: ComponentBuilder::default(), resolve, - package, + packages, }; encoder.run()?; - let package_metadata = PackageMetadata::extract(resolve, package); - encoder.component.custom_section(&CustomSection { - name: PackageMetadata::SECTION_NAME.into(), - data: package_metadata.encode()?.into(), - }); + for package in packages { + let package_metadata = PackageMetadata::extract(resolve, *package); + encoder.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); + } Ok(encoder.component) } @@ -44,7 +46,7 @@ pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result { component: ComponentBuilder, resolve: &'a Resolve, - package: PackageId, + packages: &'a [PackageId], } impl Encoder<'_> { @@ -57,33 +59,35 @@ impl Encoder<'_> { // decoding process where everyone's view of a foreign document agrees // notably on the order that types are defined in to assist with // roundtripping. - for (name, &id) in self.resolve.packages[self.package].interfaces.iter() { - let component_ty = self.encode_interface(id)?; - let ty = self.component.type_component(&component_ty); - self.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); - } + for pkg in self.packages { + for (name, &id) in self.resolve.packages[*pkg].interfaces.iter() { + let component_ty = self.encode_interface(id, pkg)?; + let ty = self.component.type_component(&component_ty); + self.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); + } - for (name, &world) in self.resolve.packages[self.package].worlds.iter() { - // Encode the `world` directly as a component, then create a wrapper - // component that exports that component. - let component_ty = super::encode_world(self.resolve, world)?; + for (name, &world) in self.resolve.packages[*pkg].worlds.iter() { + // Encode the `world` directly as a component, then create a wrapper + // component that exports that component. + let component_ty = super::encode_world(self.resolve, world)?; - let world = &self.resolve.worlds[world]; - let mut wrapper = ComponentType::new(); - wrapper.ty().component(&component_ty); - let pkg = &self.resolve.packages[world.package.unwrap()]; - wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); + let world = &self.resolve.worlds[world]; + let mut wrapper = ComponentType::new(); + wrapper.ty().component(&component_ty); + let pkg = &self.resolve.packages[world.package.unwrap()]; + wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); - let ty = self.component.type_component(&wrapper); - self.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); + let ty = self.component.type_component(&wrapper); + self.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); + } } Ok(()) } - fn encode_interface(&mut self, id: InterfaceId) -> Result { + fn encode_interface(&mut self, id: InterfaceId, pkg: &PackageId) -> Result { // Build a set of interfaces reachable from this document, including the // interfaces in the document itself. This is used to import instances // into the component type we're encoding. Note that entire interfaces @@ -101,7 +105,7 @@ impl Encoder<'_> { let mut used_names = IndexSet::new(); for id in interfaces.iter() { let iface = &self.resolve.interfaces[*id]; - if iface.package == Some(self.package) { + if iface.package == Some(*pkg) { let first = used_names.insert(iface.name.as_ref().unwrap().clone()); assert!(first); } diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index 28406a5023..7b863419df 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -69,7 +69,7 @@ impl WitPrinter { } if has_multiple_packages { - self.output.push_str("{"); + self.output.push_str(" {\n"); self.output.indent += 1 } else { self.print_semicolon(); diff --git a/crates/wit-component/tests/interfaces.rs b/crates/wit-component/tests/interfaces.rs index 926499db9c..63dbbc1c8a 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -62,7 +62,7 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { // First convert the WIT package to a binary WebAssembly output, then // convert that binary wasm to textual wasm, then assert it matches the // expectation. - let wasm = wit_component::encode(Some(true), &resolve, package)?; + let wasm = wit_component::encode(Some(true), &resolve, &[package])?; let wat = wasmprinter::print_bytes(&wasm)?; assert_output(&path.with_extension("wat"), &wat)?; wasmparser::Validator::new_with_features(features) @@ -72,11 +72,6 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { // Next decode a fresh WIT package from the WebAssembly generated. Print // this package's documents and assert they all match the expectations. let decoded = wit_component::decode(&wasm)?; - assert_eq!( - 1, - decoded.packages().len(), - "Each input WIT package should produce WASM that contains only one package" - ); let decoded_package = decoded.packages()[0]; let resolve = decoded.resolve(); @@ -85,7 +80,7 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { // Finally convert the decoded package to wasm again and make sure it // matches the prior wasm. - let wasm2 = wit_component::encode(Some(true), resolve, decoded_package)?; + let wasm2 = wit_component::encode(Some(true), resolve, &[decoded_package])?; if wasm != wasm2 { let wat2 = wasmprinter::print_bytes(&wasm)?; assert_eq!(wat, wat2, "document did not roundtrip correctly"); diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 1c61ecfd48..dfd169fcde 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -1,6 +1,7 @@ use crate::*; use anyhow::{anyhow, bail}; use indexmap::IndexSet; +use std::collections::HashSet; use std::mem; use std::slice; use std::{collections::HashMap, io::Read}; @@ -181,7 +182,7 @@ impl ComponentInfo { } } - fn decode_wit_v1_package(&self) -> Result<(Resolve, PackageId)> { + fn decode_wit_v1_package(&self) -> Result<(Resolve, Vec)> { let mut decoder = WitPackageDecoder::new(&self.types); let mut pkg = None; @@ -204,17 +205,19 @@ impl ComponentInfo { } let pkg = pkg.ok_or_else(|| anyhow!("no exported component type found"))?; - let (mut resolve, package) = decoder.finish(pkg); - if let Some(package_metadata) = &self.package_metadata { - package_metadata.inject(&mut resolve, package)?; + let (mut resolve, packages) = decoder.finish(&[pkg]); + for package in &packages { + if let Some(package_metadata) = &self.package_metadata { + package_metadata.inject(&mut resolve, *package)?; + } } - Ok((resolve, package)) + Ok((resolve, packages)) } - fn decode_wit_v2_package(&self) -> Result<(Resolve, PackageId)> { + fn decode_wit_v2_packages(&self) -> Result<(Resolve, Vec)> { let mut decoder = WitPackageDecoder::new(&self.types); - let mut pkg_name = None; + let mut pkg_names = HashSet::new(); let mut interfaces = IndexMap::new(); let mut worlds = IndexMap::new(); @@ -262,33 +265,26 @@ impl ComponentInfo { _ => unreachable!(), }; - if let Some(pkg_name) = pkg_name.as_ref() { - // TODO: when we have fully switched to the v2 format, we should switch to parsing - // multiple wit documents instead of bailing. - if pkg_name != &name { - bail!("item defined with mismatched package name") - } - } else { - pkg_name.replace(name); - } + pkg_names.insert(name); } - let pkg = if let Some(name) = pkg_name { - Package { - name, + let mut pkg_ids = Vec::new(); + for pkg_name in pkg_names { + let pkg = Package { + name: pkg_name, docs: Docs::default(), - interfaces, - worlds, + interfaces: interfaces.clone(), + worlds: worlds.clone(), + }; + pkg_ids.push(pkg); + } + let (mut resolve, packages) = decoder.finish(&pkg_ids); + for package in &packages { + if let Some(package_metadata) = &self.package_metadata { + package_metadata.inject(&mut resolve, package.clone())?; } - } else { - bail!("no exported component type found"); - }; - - let (mut resolve, package) = decoder.finish(pkg); - if let Some(package_metadata) = &self.package_metadata { - package_metadata.inject(&mut resolve, package)?; } - Ok((resolve, package)) + Ok((resolve, packages)) } fn decode_component(&self) -> Result<(Resolve, WorldId)> { @@ -338,7 +334,7 @@ impl ComponentInfo { } } - let (resolve, _) = decoder.finish(package); + let (resolve, _) = decoder.finish(&[package]); Ok((resolve, world)) } } @@ -386,12 +382,12 @@ pub fn decode_reader(reader: impl Read) -> Result { WitEncodingVersion::V1 => { log::debug!("decoding a v1 WIT package encoded as wasm"); let (resolve, pkg) = info.decode_wit_v1_package()?; - Ok(DecodedWasm::WitPackages(resolve, vec![pkg])) + Ok(DecodedWasm::WitPackages(resolve, pkg.to_vec())) } WitEncodingVersion::V2 => { log::debug!("decoding a v2 WIT package encoded as wasm"); - let (resolve, pkg) = info.decode_wit_v2_package()?; - Ok(DecodedWasm::WitPackages(resolve, vec![pkg])) + let (resolve, pkgs) = info.decode_wit_v2_packages()?; + Ok(DecodedWasm::WitPackages(resolve, pkgs)) } } } else { @@ -482,15 +478,15 @@ pub fn decode_world(wasm: &[u8]) -> Result<(Resolve, WorldId)> { worlds: &mut worlds, }, )?; - let (resolve, pkg) = decoder.finish(Package { + let (resolve, pkgs) = decoder.finish(&[Package { name, interfaces, worlds, docs: Default::default(), - }); + }]); // The package decoded here should only have a single world so extract that // here to return. - let world = *resolve.packages[pkg].worlds.iter().next().unwrap().1; + let world = *resolve.packages[pkgs[0]].worlds.iter().next().unwrap().1; Ok((resolve, world)) } @@ -1433,7 +1429,7 @@ impl WitPackageDecoder<'_> { /// their topological ordering within the returned `Resolve`. /// /// Takes the root package as an argument to insert. - fn finish(mut self, package: Package) -> (Resolve, PackageId) { + fn finish(mut self, packages: &[Package]) -> (Resolve, Vec) { // Build a topological ordering is then calculated by visiting all the // transitive dependencies of packages. let mut order = IndexSet::new(); @@ -1461,14 +1457,18 @@ impl WitPackageDecoder<'_> { self.insert_package(pkg); } - let id = self.insert_package(package); - assert!(self.resolve.worlds.iter().all(|(_, w)| w.package.is_some())); - assert!(self - .resolve - .interfaces - .iter() - .all(|(_, i)| i.package.is_some())); - (self.resolve, id) + let mut resolved = Vec::new(); + for package in packages { + let id = self.insert_package(package.clone()); + assert!(self.resolve.worlds.iter().all(|(_, w)| w.package.is_some())); + assert!(self + .resolve + .interfaces + .iter() + .all(|(_, i)| i.package.is_some())); + resolved.push(id); + } + (self.resolve, resolved) } fn insert_package(&mut self, package: Package) -> PackageId { diff --git a/crates/wit-smith/src/lib.rs b/crates/wit-smith/src/lib.rs index 3a70a00f83..caaa54dc61 100644 --- a/crates/wit-smith/src/lib.rs +++ b/crates/wit-smith/src/lib.rs @@ -41,5 +41,5 @@ pub fn smith(config: &Config, u: &mut Unstructured<'_>) -> Result> { } let pkg = last.unwrap(); - Ok(wit_component::encode(Some(true), &resolve, pkg).expect("failed to encode WIT document")) + Ok(wit_component::encode(Some(true), &resolve, &[pkg]).expect("failed to encode WIT document")) } diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs index b238adf4e4..a63d3a9aa1 100644 --- a/src/bin/wasm-tools/component.rs +++ b/src/bin/wasm-tools/component.rs @@ -583,12 +583,9 @@ impl WitOpts { fn emit_wasm(&self, decoded: &DecodedWasm) -> Result<()> { assert!(self.wasm || self.wat); assert!(self.out_dir.is_none()); - if decoded.packages().len() != 1 { - bail!("emitting WASM for multi-package WIT files is not yet supported") - } - let decoded_package = decoded.packages()[0]; - let bytes = wit_component::encode(None, decoded.resolve(), decoded_package)?; + let decoded_packages = decoded.packages(); + let bytes = wit_component::encode(None, decoded.resolve(), decoded_packages)?; if !self.skip_validation { wasmparser::Validator::new_with_features( WasmFeatures::default() | WasmFeatures::COMPONENT_MODEL, From 29da2071385b0769bc1252d9bfe56b16f1d6ce65 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 10 Jun 2024 23:23:37 -0500 Subject: [PATCH 02/16] fix fuzz --- fuzz/src/roundtrip_wit.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzz/src/roundtrip_wit.rs b/fuzz/src/roundtrip_wit.rs index 19967992e1..ae240e4308 100644 --- a/fuzz/src/roundtrip_wit.rs +++ b/fuzz/src/roundtrip_wit.rs @@ -28,8 +28,8 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> { } let pkg2 = pkgs2[0]; - let wasm2 = - wit_component::encode(Some(true), &resolve2, pkg2).expect("failed to encode WIT document"); + let wasm2 = wit_component::encode(Some(true), &resolve2, &[pkg2]) + .expect("failed to encode WIT document"); write_file("doc2.wasm", &wasm2); roundtrip_through_printing("doc2", &resolve2, &wasm2); @@ -96,7 +96,7 @@ fn roundtrip_through_printing(file: &str, resolve: &Resolve, wasm: &[u8]) { // Finally encode the `new_resolve` which should be the exact same as // before. - let wasm2 = wit_component::encode(Some(true), &new_resolve, last.unwrap()).unwrap(); + let wasm2 = wit_component::encode(Some(true), &new_resolve, &[last.unwrap()]).unwrap(); write_file(&format!("{file}-reencoded.wasm"), &wasm2); if wasm != wasm2 { panic!("failed to roundtrip through text printing"); From 3e8b0f8e983271e76f7c3b584fc42eef53b1ce49 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Wed, 26 Jun 2024 14:27:55 -0500 Subject: [PATCH 03/16] explicit packages encoded as nested components --- crates/wasm-encoder/src/component/builder.rs | 4 + crates/wasm-encoder/src/component/names.rs | 3 + crates/wit-component/src/encoding/wit/mod.rs | 2 +- crates/wit-component/src/encoding/wit/v1.rs | 124 +++--- crates/wit-component/src/encoding/wit/v2.rs | 98 +++-- crates/wit-component/src/metadata.rs | 3 +- crates/wit-component/src/printing.rs | 2 - crates/wit-component/tests/interfaces.rs | 49 ++- .../tests/interfaces/multiple-packages.wat | 62 +++ .../tests/interfaces/multiple-packages.wit | 26 ++ .../interfaces/multiple-packages.wit.print | 28 ++ crates/wit-parser/src/ast.rs | 7 +- crates/wit-parser/src/ast/resolve.rs | 39 +- crates/wit-parser/src/decoding.rs | 398 +++++++++++++----- crates/wit-parser/src/lib.rs | 5 +- crates/wit-parser/src/metadata.rs | 5 + crates/wit-parser/src/resolve.rs | 13 +- crates/wit-parser/tests/ui/comments.wit.json | 1 + .../tests/ui/complex-include.wit.json | 3 + .../tests/ui/cross-package-resource.wit.json | 2 + crates/wit-parser/tests/ui/diamond1.wit.json | 3 + .../tests/ui/disambiguate-diamond.wit.json | 1 + crates/wit-parser/tests/ui/empty.wit.json | 1 + .../tests/ui/feature-gates.wit.json | 1 + .../tests/ui/foreign-deps-union.wit.json | 7 + .../wit-parser/tests/ui/foreign-deps.wit.json | 7 + crates/wit-parser/tests/ui/functions.wit.json | 1 + .../tests/ui/ignore-files-deps.wit.json | 2 + .../tests/ui/import-export-overlap1.wit.json | 1 + .../tests/ui/import-export-overlap2.wit.json | 1 + .../wit-parser/tests/ui/include-reps.wit.json | 1 + .../tests/ui/kebab-name-include-with.wit.json | 1 + .../tests/ui/kinds-of-deps.wit.json | 5 + .../wit-parser/tests/ui/many-names.wit.json | 1 + .../ui/multi-file-multi-package.wit.json | 4 + .../wit-parser/tests/ui/multi-file.wit.json | 1 + .../ui/multi-package-shared-deps.wit.json | 4 + .../ui/multi-package-transitive-deps.wit.json | 4 + .../ui/name-both-resource-and-type.wit.json | 2 + .../tests/ui/package-syntax1.wit.json | 1 + .../tests/ui/package-syntax3.wit.json | 1 + .../tests/ui/package-syntax4.wit.json | 1 + ...ges-explicit-colliding-decl-names.wit.json | 2 + ...ages-explicit-internal-references.wit.json | 2 + .../ui/packages-explicit-with-semver.wit.json | 2 + .../ui/packages-multiple-explicit.wit.json | 2 + .../ui/packages-single-explicit.wit.json | 1 + crates/wit-parser/tests/ui/random.wit.json | 1 + .../tests/ui/resources-empty.wit.json | 1 + .../resources-multiple-returns-own.wit.json | 1 + .../tests/ui/resources-multiple.wit.json | 1 + .../tests/ui/resources-return-own.wit.json | 1 + crates/wit-parser/tests/ui/resources.wit.json | 1 + .../wit-parser/tests/ui/resources1.wit.json | 1 + .../tests/ui/same-name-import-export.wit.json | 1 + .../wit-parser/tests/ui/shared-types.wit.json | 1 + .../tests/ui/simple-wasm-text.wit.json | 1 + .../tests/ui/since-and-unstable.wit.json | 1 + .../tests/ui/stress-export-elaborate.wit.json | 1 + .../tests/ui/type-then-eof.wit.json | 1 + crates/wit-parser/tests/ui/types.wit.json | 1 + .../wit-parser/tests/ui/union-fuzz-1.wit.json | 1 + .../wit-parser/tests/ui/union-fuzz-2.wit.json | 1 + crates/wit-parser/tests/ui/use-chain.wit.json | 1 + crates/wit-parser/tests/ui/use.wit.json | 1 + crates/wit-parser/tests/ui/versions.wit.json | 3 + crates/wit-parser/tests/ui/wasi.wit.json | 1 + .../tests/ui/world-diamond.wit.json | 1 + .../tests/ui/world-iface-no-collide.wit.json | 1 + .../tests/ui/world-implicit-import1.wit.json | 1 + .../tests/ui/world-implicit-import2.wit.json | 1 + .../tests/ui/world-implicit-import3.wit.json | 1 + .../tests/ui/world-same-fields4.wit.json | 1 + .../tests/ui/world-top-level-funcs.wit.json | 1 + .../ui/world-top-level-resources.wit.json | 1 + .../tests/ui/worlds-union-dedup.wit.json | 1 + .../tests/ui/worlds-with-types.wit.json | 1 + 77 files changed, 723 insertions(+), 243 deletions(-) create mode 100644 crates/wit-component/tests/interfaces/multiple-packages.wat create mode 100644 crates/wit-component/tests/interfaces/multiple-packages.wit create mode 100644 crates/wit-component/tests/interfaces/multiple-packages.wit.print diff --git a/crates/wasm-encoder/src/component/builder.rs b/crates/wasm-encoder/src/component/builder.rs index 27c39ac8a0..58cfa28c6a 100644 --- a/crates/wasm-encoder/src/component/builder.rs +++ b/crates/wasm-encoder/src/component/builder.rs @@ -41,6 +41,10 @@ pub struct ComponentBuilder { } impl ComponentBuilder { + /// Adds sub component names + pub fn names(&mut self, names: &ComponentNameSection) { + self.component.section(names); + } /// Returns the current number of core modules. pub fn core_module_count(&self) -> u32 { self.core_modules diff --git a/crates/wasm-encoder/src/component/names.rs b/crates/wasm-encoder/src/component/names.rs index 1cbb1062c6..0939984440 100644 --- a/crates/wasm-encoder/src/component/names.rs +++ b/crates/wasm-encoder/src/component/names.rs @@ -16,6 +16,9 @@ enum Subsection { } impl ComponentNameSection { + /// Human readable component names + pub const SECTION_NAME: &'static str = "component-name"; + /// Creates a new blank `name` custom section. pub fn new() -> Self { Self::default() diff --git a/crates/wit-component/src/encoding/wit/mod.rs b/crates/wit-component/src/encoding/wit/mod.rs index d78706e64d..71c2b4f54c 100644 --- a/crates/wit-component/src/encoding/wit/mod.rs +++ b/crates/wit-component/src/encoding/wit/mod.rs @@ -48,7 +48,7 @@ pub fn encode_component( if use_v2.unwrap_or_else(use_v2_encoding) { v2::encode_component(resolve, packages) } else { - v1::encode_component(resolve, packages) + v1::encode_component(resolve, packages[0]) } } diff --git a/crates/wit-component/src/encoding/wit/v1.rs b/crates/wit-component/src/encoding/wit/v1.rs index b2dfb009fe..ed6c39a35f 100644 --- a/crates/wit-component/src/encoding/wit/v1.rs +++ b/crates/wit-component/src/encoding/wit/v1.rs @@ -24,21 +24,19 @@ use wit_parser::*; /// /// The binary returned can be [`decode`d](crate::decode) to recover the WIT /// package provided. -pub fn encode_component(resolve: &Resolve, packages: &[PackageId]) -> Result { +pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result { let mut encoder = Encoder { component: ComponentBuilder::default(), resolve, - packages, + package, }; encoder.run()?; - for package in packages { - let package_metadata = PackageMetadata::extract(resolve, *package); - encoder.component.custom_section(&CustomSection { - name: PackageMetadata::SECTION_NAME.into(), - data: package_metadata.encode()?.into(), - }); - } + let package_metadata = PackageMetadata::extract(resolve, package); + encoder.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); Ok(encoder.component) } @@ -46,7 +44,7 @@ pub fn encode_component(resolve: &Resolve, packages: &[PackageId]) -> Result { component: ComponentBuilder, resolve: &'a Resolve, - packages: &'a [PackageId], + package: PackageId, } impl Encoder<'_> { @@ -59,69 +57,67 @@ impl Encoder<'_> { // decoding process where everyone's view of a foreign document agrees // notably on the order that types are defined in to assist with // roundtripping. - for pkg in self.packages { - let mut interfaces = IndexSet::new(); - for (_, id) in self.resolve.packages[*pkg].interfaces.iter() { - self.add_live_interfaces(&mut interfaces, *id); - } + let mut interfaces = IndexSet::new(); + for (_, id) in self.resolve.packages[self.package].interfaces.iter() { + self.add_live_interfaces(&mut interfaces, *id); + } - // Seed the set of used names with all exported interfaces to ensure - // that imported interfaces choose different names as the import names - // aren't used during decoding. - let mut used_names = IndexSet::new(); - for id in interfaces.iter() { - let iface = &self.resolve.interfaces[*id]; - if iface.package == Some(*pkg) { - let first = used_names.insert(iface.name.as_ref().unwrap().clone()); - assert!(first); - } - } - for (name, _world) in self.resolve.packages[*pkg].worlds.iter() { - let first = used_names.insert(name.clone()); + // Seed the set of used names with all exported interfaces to ensure + // that imported interfaces choose different names as the import names + // aren't used during decoding. + let mut used_names = IndexSet::new(); + for id in interfaces.iter() { + let iface = &self.resolve.interfaces[*id]; + if iface.package == Some(self.package) { + let first = used_names.insert(iface.name.as_ref().unwrap().clone()); assert!(first); } + } + for (name, _world) in self.resolve.packages[self.package].worlds.iter() { + let first = used_names.insert(name.clone()); + assert!(first); + } - // Encode all interfaces, foreign and local, into this component type. - // Local interfaces get their functions defined as well and are - // exported. Foreign interfaces are imported and only have their types - // encoded. - let mut encoder = InterfaceEncoder::new(self.resolve); - for interface in interfaces { - encoder.interface = Some(interface); - let iface = &self.resolve.interfaces[interface]; - let name = self.resolve.id_of(interface).unwrap(); - log::trace!("encoding interface {name}"); - if iface.package == Some(*pkg) { - let idx = encoder.encode_instance(interface)?; - encoder.outer.export(&name, ComponentTypeRef::Instance(idx)); - } else { - encoder.push_instance(); - for (_, id) in iface.types.iter() { - encoder.encode_valtype(self.resolve, &Type::Id(*id))?; - } - let instance = encoder.pop_instance(); - let idx = encoder.outer.type_count(); - encoder.outer.ty().instance(&instance); - encoder.import_map.insert(interface, encoder.instances); - encoder.instances += 1; - encoder.outer.import(&name, ComponentTypeRef::Instance(idx)); + // Encode all interfaces, foreign and local, into this component type. + // Local interfaces get their functions defined as well and are + // exported. Foreign interfaces are imported and only have their types + // encoded. + let mut encoder = InterfaceEncoder::new(self.resolve); + for interface in interfaces { + encoder.interface = Some(interface); + let iface = &self.resolve.interfaces[interface]; + let name = self.resolve.id_of(interface).unwrap(); + log::trace!("encoding interface {name}"); + if iface.package == Some(self.package) { + let idx = encoder.encode_instance(interface)?; + encoder.outer.export(&name, ComponentTypeRef::Instance(idx)); + } else { + encoder.push_instance(); + for (_, id) in iface.types.iter() { + encoder.encode_valtype(self.resolve, &Type::Id(*id))?; } - } - encoder.interface = None; - - for (name, world) in self.resolve.packages[*pkg].worlds.iter() { - let component_ty = encode_world(self.resolve, *world)?; + let instance = encoder.pop_instance(); let idx = encoder.outer.type_count(); - encoder.outer.ty().component(&component_ty); - let id = self.resolve.packages[*pkg].name.interface_id(name); - encoder.outer.export(&id, ComponentTypeRef::Component(idx)); + encoder.outer.ty().instance(&instance); + encoder.import_map.insert(interface, encoder.instances); + encoder.instances += 1; + encoder.outer.import(&name, ComponentTypeRef::Instance(idx)); } + } + encoder.interface = None; - let ty = self.component.type_component(&encoder.outer); - let id = self.resolve.packages[*pkg].name.interface_id("wit"); - self.component - .export(&id, ComponentExportKind::Type, ty, None); + for (name, world) in self.resolve.packages[self.package].worlds.iter() { + let component_ty = encode_world(self.resolve, *world)?; + let idx = encoder.outer.type_count(); + encoder.outer.ty().component(&component_ty); + let id = self.resolve.packages[self.package].name.interface_id(name); + encoder.outer.export(&id, ComponentTypeRef::Component(idx)); } + + let ty = self.component.type_component(&encoder.outer); + let id = self.resolve.packages[self.package].name.interface_id("wit"); + self.component + .export(&id, ComponentExportKind::Type, ty, None); Ok(()) } diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index a4de81880b..a9fb022dce 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -32,14 +32,6 @@ pub fn encode_component(resolve: &Resolve, packages: &[PackageId]) -> Result { // decoding process where everyone's view of a foreign document agrees // notably on the order that types are defined in to assist with // roundtripping. + let mut names = NameMap::new(); for pkg in self.packages { - for (name, &id) in self.resolve.packages[*pkg].interfaces.iter() { - let component_ty = self.encode_interface(id, pkg)?; - let ty = self.component.type_component(&component_ty); - self.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); - } - - for (name, &world) in self.resolve.packages[*pkg].worlds.iter() { - // Encode the `world` directly as a component, then create a wrapper - // component that exports that component. - let component_ty = super::encode_world(self.resolve, world)?; - - let world = &self.resolve.worlds[world]; - let mut wrapper = ComponentType::new(); - wrapper.ty().component(&component_ty); - let pkg = &self.resolve.packages[world.package.unwrap()]; - wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); + let package = &self.resolve.packages[*pkg]; + if let PackageKind::Explicit = package.kind { + let mut sub_encoder = Encoder { + component: ComponentBuilder::default(), + resolve: self.resolve, + packages: self.packages, + }; + let package_metadata = PackageMetadata::extract(self.resolve, *pkg); + sub_encoder.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); + for (name, &id) in self.resolve.packages[*pkg].interfaces.iter() { + let component_ty = sub_encoder.encode_interface(id, pkg)?; + let ty = sub_encoder.component.type_component(&component_ty); + sub_encoder.component.export( + name.as_ref(), + ComponentExportKind::Type, + ty, + None, + ); + } + for (name, &world) in self.resolve.packages[*pkg].worlds.iter() { + // Encode the `world` directly as a component, then create a wrapper + // component that exports that component. + let component_ty = super::encode_world(self.resolve, world)?; - let ty = self.component.type_component(&wrapper); - self.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); + let world = &sub_encoder.resolve.worlds[world]; + let mut wrapper = ComponentType::new(); + wrapper.ty().component(&component_ty); + let pkg = &sub_encoder.resolve.packages[world.package.unwrap()]; + wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); + let ty = sub_encoder.component.type_component(&wrapper); + sub_encoder.component.export( + name.as_ref(), + ComponentExportKind::Type, + ty, + None, + ); + } + let sub = self.component.component(sub_encoder.component); + names.append(sub, &package.name.to_string()); + } else { + for (name, &id) in self.resolve.packages[*pkg].interfaces.iter() { + let component_ty = self.encode_interface(id, pkg)?; + let ty = self.component.type_component(&component_ty); + self.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); + } + for (name, &world) in self.resolve.packages[*pkg].worlds.iter() { + // Encode the `world` directly as a component, then create a wrapper + // component that exports that component. + let component_ty = super::encode_world(self.resolve, world)?; + let world = &self.resolve.worlds[world]; + let mut wrapper = ComponentType::new(); + wrapper.ty().component(&component_ty); + let pkg = &self.resolve.packages[world.package.unwrap()]; + wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); + let ty = self.component.type_component(&wrapper); + self.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); + } + let package_metadata = PackageMetadata::extract(self.resolve, *pkg); + self.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); } } + let mut final_names = ComponentNameSection::new(); + final_names.components(&names); + self.component.names(&final_names); Ok(()) } diff --git a/crates/wit-component/src/metadata.rs b/crates/wit-component/src/metadata.rs index 59cb7353ee..1128216dcb 100644 --- a/crates/wit-component/src/metadata.rs +++ b/crates/wit-component/src/metadata.rs @@ -51,7 +51,7 @@ use wasm_encoder::{ }; use wasm_metadata::Producers; use wasmparser::{BinaryReader, Encoding, Parser, Payload, WasmFeatures}; -use wit_parser::{Package, PackageName, Resolve, World, WorldId, WorldItem}; +use wit_parser::{Package, PackageKind, PackageName, Resolve, World, WorldId, WorldItem}; const CURRENT_VERSION: u8 = 0x04; const CUSTOM_SECTION_NAME: &str = "wit-component-encoding"; @@ -80,6 +80,7 @@ impl Default for Bindgen { name: "root".to_string(), version: None, }, + kind: PackageKind::Implicit, docs: Default::default(), interfaces: Default::default(), worlds: Default::default(), diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index 7b863419df..daad8ef67e 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -70,7 +70,6 @@ impl WitPrinter { if has_multiple_packages { self.output.push_str(" {\n"); - self.output.indent += 1 } else { self.print_semicolon(); self.output.push_str("\n\n"); @@ -98,7 +97,6 @@ impl WitPrinter { if has_multiple_packages { self.output.push_str("}"); - self.output.indent -= 1 } } diff --git a/crates/wit-component/tests/interfaces.rs b/crates/wit-component/tests/interfaces.rs index 63dbbc1c8a..c4ca27468c 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -54,38 +54,37 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { resolve.append(UnresolvedPackageGroup::parse_file(path)?)? }; - for package in packages { - assert_print(&resolve, &[package], path, is_dir)?; + assert_print(&resolve, &packages, path, is_dir)?; - let features = WasmFeatures::default() | WasmFeatures::COMPONENT_MODEL; + let features = WasmFeatures::default() | WasmFeatures::COMPONENT_MODEL; - // First convert the WIT package to a binary WebAssembly output, then - // convert that binary wasm to textual wasm, then assert it matches the - // expectation. - let wasm = wit_component::encode(Some(true), &resolve, &[package])?; - let wat = wasmprinter::print_bytes(&wasm)?; - assert_output(&path.with_extension("wat"), &wat)?; - wasmparser::Validator::new_with_features(features) - .validate_all(&wasm) - .context("failed to validate wasm output")?; + // First convert the WIT package to a binary WebAssembly output, then + // convert that binary wasm to textual wasm, then assert it matches the + // expectation. + let wasm = wit_component::encode(Some(true), &resolve, &packages)?; + let wat = wasmprinter::print_bytes(&wasm)?; + assert_output(&path.with_extension("wat"), &wat)?; + wasmparser::Validator::new_with_features(features) + .validate_all(&wasm) + .context("failed to validate wasm output")?; - // Next decode a fresh WIT package from the WebAssembly generated. Print - // this package's documents and assert they all match the expectations. - let decoded = wit_component::decode(&wasm)?; + // Next decode a fresh WIT package from the WebAssembly generated. Print + // this package's documents and assert they all match the expectations. + let decoded = wit_component::decode(&wasm)?; - let decoded_package = decoded.packages()[0]; - let resolve = decoded.resolve(); + let decoded_package = decoded.packages()[0]; + let resolve = decoded.resolve(); - assert_print(resolve, decoded.packages(), path, is_dir)?; + assert_print(resolve, decoded.packages(), path, is_dir)?; - // Finally convert the decoded package to wasm again and make sure it - // matches the prior wasm. - let wasm2 = wit_component::encode(Some(true), resolve, &[decoded_package])?; - if wasm != wasm2 { - let wat2 = wasmprinter::print_bytes(&wasm)?; - assert_eq!(wat, wat2, "document did not roundtrip correctly"); - } + // Finally convert the decoded package to wasm again and make sure it + // matches the prior wasm. + let wasm2 = wit_component::encode(Some(true), resolve, &[decoded_package])?; + if wasm != wasm2 { + let wat2 = wasmprinter::print_bytes(&wasm)?; + assert_eq!(wat, wat2, "document did not roundtrip correctly"); } + // } Ok(()) } diff --git a/crates/wit-component/tests/interfaces/multiple-packages.wat b/crates/wit-component/tests/interfaces/multiple-packages.wat new file mode 100644 index 0000000000..66cbafbff7 --- /dev/null +++ b/crates/wit-component/tests/interfaces/multiple-packages.wat @@ -0,0 +1,62 @@ +(component + (component $another:thing (;0;) + (@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22docs\22:\22documenting an interface\22,\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.2.3\22}}}}}}}") + (type (;0;) + (component + (type (;0;) + (instance + (type (;0;) (record (field "foo" string))) + (export (;1;) "my-record" (type (eq 0))) + ) + ) + (export (;0;) "another:thing/something" (instance (type 0))) + ) + ) + (export (;1;) "something" (type 0)) + ) + (component $third:pkg (;1;) + (@custom "package-docs" "\01{\22interfaces\22:{\22things\22:{\22stability\22:{\22stable\22:{\22since\22:\221.2.3\22}},\22types\22:{\22other-record\22:{\22docs\22:\22documenting an type\22}}}}}") + (type (;0;) + (component + (type (;0;) + (instance + (type (;0;) (record (field "foo" string))) + (export (;1;) "other-record" (type (eq 0))) + ) + ) + (export (;0;) "third:pkg/things" (instance (type 0))) + ) + ) + (export (;1;) "things" (type 0)) + ) + (component $foo:bar (;2;) + (@custom "package-docs" "\00{}") + (type (;0;) + (component + (type (;0;) + (component + (type (;0;) + (instance + (type (;0;) (record (field "foo" string))) + (export (;1;) "my-record" (type (eq 0))) + ) + ) + (import "another:thing/something" (instance (;0;) (type 0))) + (type (;1;) + (instance + (type (;0;) (record (field "foo" string))) + (export (;1;) "other-record" (type (eq 0))) + ) + ) + (import "third:pkg/things" (instance (;1;) (type 1))) + ) + ) + (export (;0;) "foo:bar/this-world" (component (type 0))) + ) + ) + (export (;1;) "this-world" (type 0)) + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/interfaces/multiple-packages.wit b/crates/wit-component/tests/interfaces/multiple-packages.wit new file mode 100644 index 0000000000..fb9b0e933a --- /dev/null +++ b/crates/wit-component/tests/interfaces/multiple-packages.wit @@ -0,0 +1,26 @@ +package foo:bar { + world this-world { + import another:thing/something; + import third:pkg/things; + } +} + +package another:thing { + // documenting an interface + interface something { + @since(version = 1.2.3) + record my-record { + foo: string + } + } +} + +package third:pkg { + @since(version = 1.2.3) + interface things { + // documenting an type + record other-record { + foo: string + } + } +} \ No newline at end of file diff --git a/crates/wit-component/tests/interfaces/multiple-packages.wit.print b/crates/wit-component/tests/interfaces/multiple-packages.wit.print new file mode 100644 index 0000000000..50aa8e9356 --- /dev/null +++ b/crates/wit-component/tests/interfaces/multiple-packages.wit.print @@ -0,0 +1,28 @@ +package another:thing { + /// documenting an interface + interface something { + @since(version = 1.2.3) + record my-record { + foo: string, + } + } + +} + +package third:pkg { + @since(version = 1.2.3) + interface things { + /// documenting an type + record other-record { + foo: string, + } + } + +} + +package foo:bar { + world this-world { + import another:thing/something; + import third:pkg/things; + } +} \ No newline at end of file diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index d65d5fed4f..d9dfbdbb18 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -1592,6 +1592,11 @@ enum ResolverKind<'a> { PartialImplicit(Resolver<'a>), } +pub(crate) enum ResolverKindTag { + Explicit, + Implicit, +} + fn parse_package( unparsed_pkgs: Vec, src: &Source, @@ -1724,7 +1729,7 @@ impl SourceMap { match resolver_kind { ResolverKind::Unknown => bail!("No WIT packages found in the supplied source"), ResolverKind::Explicit(pkgs) => Ok(pkgs), - ResolverKind::PartialImplicit(mut resolver) => match resolver.resolve()? { + ResolverKind::PartialImplicit(mut resolver) => match resolver.resolve(ResolverKindTag::Implicit)? { Some(pkg) => Ok(vec![pkg]), None => bail!("No WIT packages found in the supplied source"), }, diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index 0d3465a602..6fc6dc9e63 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -1,4 +1,4 @@ -use super::{ParamList, ResultList, WorldOrInterface}; +use super::{ParamList, ResolverKindTag, ResultList, WorldOrInterface}; use crate::ast::toposort::toposort; use crate::*; use anyhow::bail; @@ -14,7 +14,7 @@ pub struct Resolver<'a> { package_docs: Docs, /// All non-`package` WIT decls are going to be resolved together. - decl_lists: Vec>, + decl_lists: Vec<(PackageKind, ast::DeclList<'a>)>, // Arenas that get plumbed to the final `UnresolvedPackage` types: Arena, @@ -140,11 +140,12 @@ impl<'a> Resolver<'a> { self.package_docs = docs; } } - self.decl_lists.push(partial.decl_list); + self.decl_lists + .push((PackageKind::Implicit, partial.decl_list)); Ok(()) } - pub(crate) fn resolve(&mut self) -> Result> { + pub(crate) fn resolve(&mut self, kind: ResolverKindTag) -> Result> { // At least one of the WIT files must have a `package` annotation. let name = match &self.package_name { Some(name) => name.clone(), @@ -171,7 +172,7 @@ impl<'a> Resolver<'a> { let mut iface_id_to_ast = IndexMap::new(); let mut world_id_to_ast = IndexMap::new(); for (i, decl_list) in decl_lists.iter().enumerate() { - for item in decl_list.items.iter() { + for item in decl_list.1.items.iter() { match item { ast::AstItem::Interface(iface) => { let id = match self.ast_items[i][iface.name.name] { @@ -206,6 +207,10 @@ impl<'a> Resolver<'a> { Ok(Some(UnresolvedPackage { name, + kind: match kind { + ResolverKindTag::Explicit => PackageKind::Explicit, + ResolverKindTag::Implicit => PackageKind::Implicit, + }, docs: mem::take(&mut self.package_docs), worlds: mem::take(&mut self.worlds), types: mem::take(&mut self.types), @@ -237,20 +242,21 @@ impl<'a> Resolver<'a> { ) -> Result> { self.package_name = Some(package.package_id.package_name()); self.docs(&package.package_id.docs); - self.decl_lists = vec![package.decl_list]; - self.resolve() + self.decl_lists = vec![(PackageKind::Explicit, package.decl_list)]; + self.resolve(ResolverKindTag::Explicit) } /// Registers all foreign dependencies made within the ASTs provided. /// /// This will populate the `self.foreign_{deps,interfaces,worlds}` maps with all /// `UsePath::Package` entries. - fn populate_foreign_deps(&mut self, decl_lists: &[ast::DeclList<'a>]) { + fn populate_foreign_deps(&mut self, decl_lists: &[(PackageKind, ast::DeclList<'a>)]) { let mut foreign_deps = mem::take(&mut self.foreign_deps); let mut foreign_interfaces = mem::take(&mut self.foreign_interfaces); let mut foreign_worlds = mem::take(&mut self.foreign_worlds); for decl_list in decl_lists { decl_list + .1 .for_each_path(|_, path, _names, world_or_iface| { let (id, name) = match path { ast::UsePath::Package { id, name } => (id, name), @@ -338,7 +344,7 @@ impl<'a> Resolver<'a> { /// generated for resolving use-paths later on. fn populate_ast_items( &mut self, - decl_lists: &[ast::DeclList<'a>], + decl_lists: &[(PackageKind, ast::DeclList<'a>)], ) -> Result<(Vec, Vec)> { let mut package_items = IndexMap::new(); @@ -349,7 +355,7 @@ impl<'a> Resolver<'a> { let mut order = IndexMap::new(); for decl_list in decl_lists { let mut decl_list_ns = IndexMap::new(); - for item in decl_list.items.iter() { + for item in decl_list.1.items.iter() { match item { ast::AstItem::Interface(i) => { if package_items.insert(i.name.name, i.name.span).is_some() { @@ -401,7 +407,7 @@ impl<'a> Resolver<'a> { // package or foreign items. Foreign deps are ignored for // topological ordering. let mut decl_list_ns = IndexMap::new(); - for item in decl_list.items.iter() { + for item in decl_list.1.items.iter() { let (name, src) = match item { ast::AstItem::Use(u) => { let name = u.as_.as_ref().unwrap_or(u.item.name()); @@ -424,7 +430,7 @@ impl<'a> Resolver<'a> { // With this file's namespace information look at all `use` paths // and record dependencies between interfaces. - decl_list.for_each_path(|iface, path, _names, _| { + decl_list.1.for_each_path(|iface, path, _names, _| { // If this import isn't contained within an interface then it's // in a world and it doesn't need to participate in our // topo-sort. @@ -490,7 +496,7 @@ impl<'a> Resolver<'a> { } for decl_list in decl_lists { let mut items = IndexMap::new(); - for item in decl_list.items.iter() { + for item in decl_list.1.items.iter() { let (name, ast_item) = match item { ast::AstItem::Use(u) => { if !u.attributes.is_empty() { @@ -548,10 +554,13 @@ impl<'a> Resolver<'a> { /// This is done after all interfaces are generated so `self.resolve_path` /// can be used to determine if what's being imported from is a foreign /// interface or not. - fn populate_foreign_types(&mut self, decl_lists: &[ast::DeclList<'a>]) -> Result<()> { + fn populate_foreign_types( + &mut self, + decl_lists: &[(PackageKind, ast::DeclList<'a>)], + ) -> Result<()> { for (i, decl_list) in decl_lists.iter().enumerate() { self.cur_ast_index = i; - decl_list.for_each_path(|_, path, names, _| { + decl_list.1.for_each_path(|_, path, names, _| { let names = match names { Some(names) => names, None => return Ok(()), diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index dfd169fcde..69038da014 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -1,11 +1,14 @@ +use crate::resolve::PackageKind; use crate::*; use anyhow::{anyhow, bail}; use indexmap::IndexSet; -use std::collections::HashSet; use std::mem; use std::slice; use std::{collections::HashMap, io::Read}; use wasmparser::Chunk; +use wasmparser::ComponentNameSectionReader; +use wasmparser::KnownCustom; +use wasmparser::NameMap; use wasmparser::{ names::{ComponentName, ComponentNameKind}, types, @@ -21,10 +24,28 @@ struct ComponentInfo { types: types::Types, /// List of all imports and exports from this component. externs: Vec<(String, Extern)>, + /// Packages + explicit: Vec, + /// Packages + packages: Vec, /// Decoded package metadata package_metadata: Option, } +/// Represents information about a decoded WebAssembly component. +#[derive(Default)] +struct ExplicitPackageInfo { + /// Package name + name: Option, + /// Wasmparser-defined type information learned after a component is fully + /// validated. + types: Option, + /// List of all imports and exports from this component. + externs: Vec<(String, Extern)>, + // / Decoded package metadata + package_metadata: Option, +} + struct DecodingExport { name: String, kind: ComponentExternalKind, @@ -42,15 +63,50 @@ enum WitEncodingVersion { V2, } +fn register_names( + names: ComponentNameSectionReader, + explicit: &mut ExplicitPackageInfo, +) -> Result> { + let mut packages = Vec::new(); + for section in names { + match section? { + wasmparser::ComponentName::Components(s) => { + let map: NameMap = s.into(); + for item in map { + let name = item?; + let mut parts = name.name.split(":"); + let namespace = parts.next().expect("expected identifier"); + let id = parts.next(); + if let Some(id) = id { + let pkg_name = PackageName { + namespace: namespace.to_string(), + name: id.to_string(), + version: None, + }; + explicit.name = Some(pkg_name.clone()); + packages.push(pkg_name); + } + } + } + _ => {} + } + } + Ok(packages) +} + impl ComponentInfo { /// Creates a new component info by parsing the given WebAssembly component bytes. fn from_reader(mut reader: impl Read) -> Result { + let mut packages = Vec::new(); + let mut explicit = Vec::new(); + let mut cur_package = ExplicitPackageInfo::default(); + let mut is_implicit = true; let mut validator = Validator::new_with_features(WasmFeatures::all()); let mut externs = Vec::new(); let mut depth = 1; let mut types = None; - let mut _package_metadata = None; + let mut package_metadata = None; let mut cur = Parser::new(0); let mut eof = false; let mut stack = Vec::new(); @@ -82,45 +138,83 @@ impl ComponentInfo { ValidPayload::Parser(_) => depth += 1, ValidPayload::End(t) => { depth -= 1; - if depth == 0 { + if depth == 1 { + is_implicit = true; + cur_package.types = Some(t); + explicit.push(mem::take(&mut cur_package)); + } else if depth == 0 { types = Some(t); + } else { + cur_package.types = Some(t); + explicit.push(mem::take(&mut cur_package)); } } ValidPayload::Func(..) => {} } match payload { - Payload::ComponentImportSection(s) if depth == 1 => { + Payload::ComponentImportSection(s) => { for import in s { let import = import?; - externs.push(( - import.name.0.to_string(), - Extern::Import(import.name.0.to_string()), - )); + if is_implicit { + externs.push(( + import.name.0.to_string(), + Extern::Import(import.name.0.to_string()), + )); + } else { + cur_package.externs.push(( + import.name.0.to_string(), + Extern::Import(import.name.0.to_string()), + )); + } } } - Payload::ComponentExportSection(s) if depth == 1 => { + Payload::ComponentExportSection(s) => { for export in s { let export = export?; - externs.push(( - export.name.0.to_string(), - Extern::Export(DecodingExport { - name: export.name.0.to_string(), - kind: export.kind, - index: export.index, - }), - )); + if is_implicit { + externs.push(( + export.name.0.to_string(), + Extern::Export(DecodingExport { + name: export.name.0.to_string(), + kind: export.kind, + index: export.index, + }), + )); + } else { + cur_package.externs.push(( + export.name.0.to_string(), + Extern::Export(DecodingExport { + name: export.name.0.to_string(), + kind: export.kind, + index: export.index, + }), + )); + } } } #[cfg(feature = "serde")] - Payload::CustomSection(s) if s.name() == PackageMetadata::SECTION_NAME => { - if _package_metadata.is_some() { - bail!("multiple {:?} sections", PackageMetadata::SECTION_NAME); + Payload::CustomSection(s) => { + if s.name() == PackageMetadata::SECTION_NAME { + package_metadata = Some(PackageMetadata::decode(s.data())?); + cur_package.package_metadata = package_metadata.clone(); + } else if s.name() == "component-name" { + if let KnownCustom::ComponentName(reader) = s.as_known() { + packages = register_names(reader, &mut cur_package)?; + if packages.len() == explicit.len() { + for (i, exp) in explicit.iter_mut().enumerate() { + exp.name = Some(packages[i].clone()); + } + } + } else { + bail!("Expected component name section") + } } - _package_metadata = Some(PackageMetadata::decode(s.data())?); } Payload::ModuleSection { parser, .. } | Payload::ComponentSection { parser, .. } => { + is_implicit = false; + cur_package = ExplicitPackageInfo::default(); stack.push(cur.clone()); cur = parser.clone(); } @@ -141,14 +235,19 @@ impl ComponentInfo { Ok(Self { types: types.unwrap(), + explicit, + packages, externs, - package_metadata: _package_metadata, + package_metadata, }) } fn is_wit_package(&self) -> Option { // all wit package exports must be component types, and there must be at // least one + if !self.packages.is_empty() { + return Some(WitEncodingVersion::V2); + } if self.externs.is_empty() { return None; } @@ -177,12 +276,11 @@ impl ComponentInfo { ComponentNameKind::Interface(name) if name.interface().as_str() == "wit" => { Some(WitEncodingVersion::V1) } - ComponentNameKind::Label(_) => Some(WitEncodingVersion::V2), - _ => None, + _ => Some(WitEncodingVersion::V2), } } - fn decode_wit_v1_package(&self) -> Result<(Resolve, Vec)> { + fn decode_wit_v1_package(&self) -> Result<(Resolve, PackageId)> { let mut decoder = WitPackageDecoder::new(&self.types); let mut pkg = None; @@ -205,27 +303,23 @@ impl ComponentInfo { } let pkg = pkg.ok_or_else(|| anyhow!("no exported component type found"))?; - let (mut resolve, packages) = decoder.finish(&[pkg]); - for package in &packages { - if let Some(package_metadata) = &self.package_metadata { - package_metadata.inject(&mut resolve, *package)?; - } + let (mut resolve, packages) = decoder.finish(&mut [pkg]); + if let Some(package_metadata) = &self.package_metadata { + package_metadata.inject(&mut resolve, packages[0])?; } - Ok((resolve, packages)) + Ok((resolve, packages[0])) } fn decode_wit_v2_packages(&self) -> Result<(Resolve, Vec)> { let mut decoder = WitPackageDecoder::new(&self.types); - let mut pkg_names = HashSet::new(); + let mut pkg_names = Vec::new(); - let mut interfaces = IndexMap::new(); - let mut worlds = IndexMap::new(); let mut fields = PackageFields { - interfaces: &mut interfaces, - worlds: &mut worlds, + interfaces: &mut IndexMap::new(), + worlds: &mut IndexMap::new(), }; - + let mut pkg_ids: Vec = Vec::new(); for (_, item) in self.externs.iter() { let export = match item { Extern::Export(e) => e, @@ -264,24 +358,119 @@ impl ComponentInfo { } _ => unreachable!(), }; + if !pkg_names.contains(&name) { + pkg_names.push(name.clone()); + let pkg = Package { + name: name.clone(), + kind: PackageKind::Implicit, + docs: Docs::default(), + interfaces: fields.interfaces.clone(), + worlds: fields.worlds.clone(), + }; + if !pkg_ids.contains(&pkg) { + pkg_ids.push(pkg); + } + } else { + let pkg = Package { + name: name.clone(), + kind: PackageKind::Implicit, + docs: Docs::default(), + interfaces: fields.interfaces.clone(), + worlds: fields.worlds.clone(), + }; + let pkg_id = pkg_ids.iter_mut().find(|p| p.name == pkg.name).unwrap(); + pkg_id.interfaces = pkg.interfaces; + pkg_id.worlds = pkg.worlds; + } + } - pkg_names.insert(name); + for explicit in &self.explicit { + fields.interfaces.clear(); + fields.worlds.clear(); + for (_, item) in explicit.externs.iter() { + let export = match item { + Extern::Export(e) => e, + _ => unreachable!(), + }; + + let index = export.index; + let id = explicit.types.as_ref().unwrap().component_type_at(index); + let component = &explicit.types.as_ref().unwrap()[id]; + + // The single export of this component will determine if it's a world or an interface: + // worlds export a component, while interfaces export an instance. + if component.exports.len() != 1 { + bail!( + "Expected a single export, but found {} instead", + component.exports.len() + ); + } + + let name = component.exports.keys().nth(0).unwrap(); + + let name = match component.exports[name] { + types::ComponentEntityType::Component(ty) => { + let package_name = + decoder.decode_world(name.as_str(), &self.types[ty], &mut fields)?; + package_name + } + types::ComponentEntityType::Instance(ty) => { + let package_name = decoder.decode_interface( + name.as_str(), + &component.imports, + &self.types[ty], + &mut fields, + )?; + package_name + } + _ => unreachable!(), + }; + + if !pkg_names.contains(&name) { + pkg_names.push(name.clone()); + let pkg = Package { + name: name.clone(), + kind: PackageKind::Implicit, + docs: Docs::default(), + interfaces: fields.interfaces.clone(), + worlds: fields.worlds.clone(), + }; + if !pkg_ids.contains(&pkg) { + pkg_ids.push(pkg); + } + } else { + let pkg = Package { + name: name.clone(), + kind: PackageKind::Implicit, + docs: Docs::default(), + interfaces: fields.interfaces.clone(), + worlds: fields.worlds.clone(), + }; + let pkg_id = pkg_ids.iter_mut().find(|p| p.name == pkg.name).unwrap(); + pkg_id.interfaces = pkg.interfaces; + pkg_id.worlds = pkg.worlds; + } + } } - let mut pkg_ids = Vec::new(); - for pkg_name in pkg_names { - let pkg = Package { - name: pkg_name, - docs: Docs::default(), - interfaces: interfaces.clone(), - worlds: worlds.clone(), - }; - pkg_ids.push(pkg); + let (mut resolve, packages) = decoder.finish(&mut pkg_ids); + let copy = resolve.clone(); + for package in &self.explicit { + if let Some(package_metadata) = &package.package_metadata { + let name = package.name.as_ref().unwrap(); + let pkg = copy.package_names.get(&name.clone()); + if let Some(pkg) = pkg { + package_metadata.inject(&mut resolve, *pkg)?; + } + } } - let (mut resolve, packages) = decoder.finish(&pkg_ids); - for package in &packages { + // For now this is a sufficient condition to know that we're working with + // an implicit package declaration. This will need to be reworked when + // mixed package declarations are supported + if self.explicit.len() == 0 { + let package = packages[0]; if let Some(package_metadata) = &self.package_metadata { - package_metadata.inject(&mut resolve, package.clone())?; + package_metadata.inject(&mut resolve, package)?; } } Ok((resolve, packages)) @@ -313,6 +502,7 @@ impl ComponentInfo { version: None, name: "component".to_string(), }, + kind: PackageKind::Implicit, docs: Default::default(), worlds: [(world_name.to_string(), world)].into_iter().collect(), interfaces: Default::default(), @@ -334,7 +524,7 @@ impl ComponentInfo { } } - let (resolve, _) = decoder.finish(&[package]); + let (resolve, _) = decoder.finish(&mut [package]); Ok((resolve, world)) } } @@ -382,7 +572,7 @@ pub fn decode_reader(reader: impl Read) -> Result { WitEncodingVersion::V1 => { log::debug!("decoding a v1 WIT package encoded as wasm"); let (resolve, pkg) = info.decode_wit_v1_package()?; - Ok(DecodedWasm::WitPackages(resolve, pkg.to_vec())) + Ok(DecodedWasm::WitPackages(resolve, vec![pkg])) } WitEncodingVersion::V2 => { log::debug!("decoding a v2 WIT package encoded as wasm"); @@ -470,18 +660,16 @@ pub fn decode_world(wasm: &[u8]) -> Result<(Resolve, WorldId)> { types::ComponentEntityType::Component(ty) => ty, _ => unreachable!(), }; - let name = decoder.decode_world( - name, - &types[ty], - &mut PackageFields { - interfaces: &mut interfaces, - worlds: &mut worlds, - }, - )?; - let (resolve, pkgs) = decoder.finish(&[Package { + let mut fields = PackageFields { + interfaces: &mut interfaces, + worlds: &mut worlds, + }; + let name = decoder.decode_world(name, &types[ty], &mut fields)?; + let (resolve, pkgs) = decoder.finish(&mut [Package { name, - interfaces, - worlds, + kind: PackageKind::Implicit, + interfaces: fields.interfaces.clone(), + worlds: fields.worlds.clone(), docs: Default::default(), }]); // The package decoded here should only have a single world so extract that @@ -554,6 +742,7 @@ impl WitPackageDecoder<'_> { } _ => bail!("package name is not a valid id: {name}"), }, + kind: PackageKind::Implicit, docs: Default::default(), interfaces: Default::default(), worlds: Default::default(), @@ -579,7 +768,7 @@ impl WitPackageDecoder<'_> { _ => bail!("component export `{name}` is not an instance or component"), } } - Ok(package) + Ok(package.clone()) } fn decode_interface<'a>( @@ -587,7 +776,7 @@ impl WitPackageDecoder<'_> { name: &str, imports: &wasmparser::collections::IndexMap, ty: &types::ComponentInstanceType, - fields: &mut PackageFields<'a>, + fields: &mut PackageFields, ) -> Result { let component_name = self .parse_component_name(name) @@ -616,7 +805,7 @@ impl WitPackageDecoder<'_> { &mut self, name: &str, ty: &types::ComponentType, - fields: &mut PackageFields<'a>, + fields: &mut PackageFields, ) -> Result { let kebab_name = self .parse_component_name(name) @@ -636,7 +825,7 @@ impl WitPackageDecoder<'_> { &mut self, name: &str, world: WorldId, - package: &mut PackageFields<'a>, + package: &mut PackageFields, ) -> Result<()> { log::debug!("decoding component import `{name}`"); let ty = self.types.component_entity_type_of_import(name).unwrap(); @@ -686,7 +875,7 @@ impl WitPackageDecoder<'_> { &mut self, export: &DecodingExport, world: WorldId, - package: &mut PackageFields<'a>, + package: &mut PackageFields, ) -> Result<()> { let name = &export.name; log::debug!("decoding component export `{name}`"); @@ -882,6 +1071,7 @@ impl WitPackageDecoder<'_> { .entry(package_name.to_string()) .or_insert_with(|| Package { name: package_name.clone(), + kind: PackageKind::Implicit, docs: Default::default(), interfaces: Default::default(), worlds: Default::default(), @@ -930,7 +1120,7 @@ impl WitPackageDecoder<'_> { &mut self, name: &str, ty: &types::ComponentInstanceType, - package: &mut PackageFields<'a>, + package: &mut PackageFields, ) -> Result<(WorldKey, InterfaceId)> { // If this interface's name is already known then that means this is an // interface that's both imported and exported. Use `register_import` @@ -1071,7 +1261,7 @@ impl WitPackageDecoder<'_> { &mut self, name: &str, ty: &types::ComponentType, - package: &mut PackageFields<'a>, + package: &mut PackageFields, ) -> Result { let name = self .extract_interface_name_from_component_name(name)? @@ -1429,51 +1619,50 @@ impl WitPackageDecoder<'_> { /// their topological ordering within the returned `Resolve`. /// /// Takes the root package as an argument to insert. - fn finish(mut self, packages: &[Package]) -> (Resolve, Vec) { - // Build a topological ordering is then calculated by visiting all the - // transitive dependencies of packages. - let mut order = IndexSet::new(); - for i in 0..self.foreign_packages.len() { - self.visit_package(i, &mut order); - } - - // Using the topological ordering create a temporary map from - // index-in-`foreign_packages` to index-in-`order` - let mut idx_to_pos = vec![0; self.foreign_packages.len()]; - for (pos, idx) in order.iter().enumerate() { - idx_to_pos[*idx] = pos; - } - // .. and then using `idx_to_pos` sort the `foreign_packages` array based - // on the position it's at in the topological ordering - let mut deps = mem::take(&mut self.foreign_packages) - .into_iter() - .enumerate() - .collect::>(); - deps.sort_by_key(|(idx, _)| idx_to_pos[*idx]); - - // .. and finally insert the packages, in their final topological - // ordering, into the returned array. - for (_idx, (_url, pkg)) in deps { - self.insert_package(pkg); - } - + fn finish(mut self, packages: &mut [Package]) -> (Resolve, Vec) { let mut resolved = Vec::new(); for package in packages { + // Build a topological ordering is then calculated by visiting all the + // transitive dependencies of packages. + let mut order = IndexSet::new(); + for i in 0..self.foreign_packages.len() { + self.visit_package(i, &mut order); + } + // Using the topological ordering create a temporary map from + // index-in-`foreign_packages` to index-in-`order` + let mut idx_to_pos = vec![0; self.foreign_packages.len()]; + for (pos, idx) in order.iter().enumerate() { + idx_to_pos[*idx] = pos; + } + // .. and then using `idx_to_pos` sort the `foreign_packages` array based + // on the position it's at in the topological ordering + let mut deps = mem::take(&mut self.foreign_packages) + .into_iter() + .enumerate() + .collect::>(); + deps.sort_by_key(|(idx, _)| idx_to_pos[*idx]); + + // .. and finally insert the packages, in their final topological + // ordering, into the returned array. + for (_idx, (_url, pkg)) in deps { + self.insert_package(pkg); + } let id = self.insert_package(package.clone()); - assert!(self.resolve.worlds.iter().all(|(_, w)| w.package.is_some())); - assert!(self - .resolve - .interfaces - .iter() - .all(|(_, i)| i.package.is_some())); resolved.push(id); } + assert!(self.resolve.worlds.iter().all(|(_, w)| w.package.is_some())); + assert!(self + .resolve + .interfaces + .iter() + .all(|(_, i)| i.package.is_some())); (self.resolve, resolved) } fn insert_package(&mut self, package: Package) -> PackageId { let Package { name, + kind, interfaces, worlds, docs, @@ -1492,6 +1681,7 @@ impl WitPackageDecoder<'_> { .unwrap_or_else(|| { let id = self.resolve.packages.alloc(Package { name: name.clone(), + kind: kind.clone(), interfaces: Default::default(), worlds: Default::default(), docs, diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 72e0857bce..96230f6030 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -21,7 +21,7 @@ pub use ast::{parse_use_path, ParsedUsePath}; mod sizealign; pub use sizealign::*; mod resolve; -pub use resolve::{Package, PackageId, Remap, Resolve}; +pub use resolve::{Package, PackageId, PackageKind, Remap, Resolve}; mod live; pub use live::LiveTypes; @@ -72,6 +72,9 @@ pub struct UnresolvedPackage { /// The namespace, name, and version information for this package. pub name: PackageName, + /// Kind + pub kind: PackageKind, + /// All worlds from all documents within this package. /// /// Each world lists the document that it is from. diff --git a/crates/wit-parser/src/metadata.rs b/crates/wit-parser/src/metadata.rs index 103e3d03af..78554a3ad0 100644 --- a/crates/wit-parser/src/metadata.rs +++ b/crates/wit-parser/src/metadata.rs @@ -48,6 +48,7 @@ const TRY_TO_EMIT_V0_BY_DEFAULT: bool = true; /// Represents serializable doc comments parsed from a WIT package. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] +#[derive(Clone)] pub struct PackageMetadata { #[cfg_attr( feature = "serde", @@ -163,6 +164,7 @@ impl PackageMetadata { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] +#[derive(Clone)] struct WorldMetadata { #[cfg_attr( feature = "serde", @@ -500,6 +502,7 @@ impl WorldMetadata { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] +#[derive(Clone)] struct InterfaceMetadata { #[cfg_attr( feature = "serde", @@ -586,6 +589,7 @@ impl InterfaceMetadata { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(untagged, deny_unknown_fields))] +#[derive(Clone)] enum FunctionMetadata { /// In the v0 format function metadata was only a string so this variant /// is preserved for the v0 format. In the future this can be removed @@ -656,6 +660,7 @@ impl FunctionMetadata { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] +#[derive(Clone)] struct TypeMetadata { #[cfg_attr( feature = "serde", diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index 8a34a8e667..92efa4a990 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -76,17 +76,27 @@ pub struct Resolve { pub features: IndexSet, } +#[derive(PartialEq, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub enum PackageKind { + Explicit, + Implicit, +} + /// A WIT package within a `Resolve`. /// /// A package is a collection of interfaces and worlds. Packages additionally /// have a unique identifier that affects generated components and uniquely /// identifiers this particular package. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Package { /// A unique name corresponding to this package. pub name: PackageName, + /// Kind + pub kind: PackageKind, + /// Documentation associated with this package. #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, @@ -1213,6 +1223,7 @@ impl Remap { let pkgid = resolve.packages.alloc(Package { name: unresolved.name.clone(), + kind: unresolved.kind.clone(), docs: unresolved.docs.clone(), interfaces: Default::default(), worlds: Default::default(), diff --git a/crates/wit-parser/tests/ui/comments.wit.json b/crates/wit-parser/tests/ui/comments.wit.json index f2a04f9d39..2f18314f36 100644 --- a/crates/wit-parser/tests/ui/comments.wit.json +++ b/crates/wit-parser/tests/ui/comments.wit.json @@ -40,6 +40,7 @@ "packages": [ { "name": "foo:comments", + "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/complex-include.wit.json b/crates/wit-parser/tests/ui/complex-include.wit.json index f9b17f2792..38b0df2172 100644 --- a/crates/wit-parser/tests/ui/complex-include.wit.json +++ b/crates/wit-parser/tests/ui/complex-include.wit.json @@ -165,6 +165,7 @@ "packages": [ { "name": "foo:bar", + "kind": "Implicit", "interfaces": { "a": 0, "b": 1 @@ -175,6 +176,7 @@ }, { "name": "foo:baz", + "kind": "Implicit", "interfaces": { "a": 2, "b": 3 @@ -185,6 +187,7 @@ }, { "name": "foo:root", + "kind": "Implicit", "interfaces": { "ai": 4, "bi": 5 diff --git a/crates/wit-parser/tests/ui/cross-package-resource.wit.json b/crates/wit-parser/tests/ui/cross-package-resource.wit.json index 51ab92795d..f49163415e 100644 --- a/crates/wit-parser/tests/ui/cross-package-resource.wit.json +++ b/crates/wit-parser/tests/ui/cross-package-resource.wit.json @@ -51,6 +51,7 @@ "packages": [ { "name": "some:dep", + "kind": "Implicit", "interfaces": { "foo": 0 }, @@ -58,6 +59,7 @@ }, { "name": "foo:bar", + "kind": "Implicit", "interfaces": { "foo": 1 }, diff --git a/crates/wit-parser/tests/ui/diamond1.wit.json b/crates/wit-parser/tests/ui/diamond1.wit.json index 54e87e7b04..c524224815 100644 --- a/crates/wit-parser/tests/ui/diamond1.wit.json +++ b/crates/wit-parser/tests/ui/diamond1.wit.json @@ -36,6 +36,7 @@ "packages": [ { "name": "foo:dep1", + "kind": "Implicit", "interfaces": { "types": 0 }, @@ -43,6 +44,7 @@ }, { "name": "foo:dep2", + "kind": "Implicit", "interfaces": { "types": 1 }, @@ -50,6 +52,7 @@ }, { "name": "foo:foo", + "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json b/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json index 34f675da51..47bfdbe8a2 100644 --- a/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json +++ b/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json @@ -103,6 +103,7 @@ "packages": [ { "name": "foo:diamond", + "kind": "Implicit", "interfaces": { "shared1": 0, "shared2": 1 diff --git a/crates/wit-parser/tests/ui/empty.wit.json b/crates/wit-parser/tests/ui/empty.wit.json index 092abe5e1d..00ba7a6102 100644 --- a/crates/wit-parser/tests/ui/empty.wit.json +++ b/crates/wit-parser/tests/ui/empty.wit.json @@ -5,6 +5,7 @@ "packages": [ { "name": "foo:empty", + "kind": "Implicit", "interfaces": {}, "worlds": {} } diff --git a/crates/wit-parser/tests/ui/feature-gates.wit.json b/crates/wit-parser/tests/ui/feature-gates.wit.json index 9cdb44c582..45490954b2 100644 --- a/crates/wit-parser/tests/ui/feature-gates.wit.json +++ b/crates/wit-parser/tests/ui/feature-gates.wit.json @@ -291,6 +291,7 @@ "packages": [ { "name": "a:b", + "kind": "Implicit", "interfaces": { "ungated": 0, "ungated2": 1, diff --git a/crates/wit-parser/tests/ui/foreign-deps-union.wit.json b/crates/wit-parser/tests/ui/foreign-deps-union.wit.json index 419d62e7ce..32cbd8e6fa 100644 --- a/crates/wit-parser/tests/ui/foreign-deps-union.wit.json +++ b/crates/wit-parser/tests/ui/foreign-deps-union.wit.json @@ -346,6 +346,7 @@ "packages": [ { "name": "foo:another-pkg", + "kind": "Implicit", "interfaces": { "other-interface": 0 }, @@ -353,6 +354,7 @@ }, { "name": "foo:corp", + "kind": "Implicit", "interfaces": { "saas": 1 }, @@ -360,6 +362,7 @@ }, { "name": "foo:different-pkg", + "kind": "Implicit", "interfaces": { "i": 2 }, @@ -367,6 +370,7 @@ }, { "name": "foo:foreign-pkg", + "kind": "Implicit", "interfaces": { "the-default": 3 }, @@ -374,6 +378,7 @@ }, { "name": "foo:some-pkg", + "kind": "Implicit", "interfaces": { "the-default": 4, "some-interface": 5, @@ -383,6 +388,7 @@ }, { "name": "foo:wasi", + "kind": "Implicit", "interfaces": { "clocks": 7, "filesystem": 8 @@ -393,6 +399,7 @@ }, { "name": "foo:root", + "kind": "Implicit", "interfaces": { "foo": 9, "bar": 10, diff --git a/crates/wit-parser/tests/ui/foreign-deps.wit.json b/crates/wit-parser/tests/ui/foreign-deps.wit.json index 503c1b32c9..132fcdd301 100644 --- a/crates/wit-parser/tests/ui/foreign-deps.wit.json +++ b/crates/wit-parser/tests/ui/foreign-deps.wit.json @@ -301,6 +301,7 @@ "packages": [ { "name": "foo:another-pkg", + "kind": "Implicit", "interfaces": { "other-interface": 0 }, @@ -308,6 +309,7 @@ }, { "name": "foo:corp", + "kind": "Implicit", "interfaces": { "saas": 1 }, @@ -315,6 +317,7 @@ }, { "name": "foo:different-pkg", + "kind": "Implicit", "interfaces": { "i": 2 }, @@ -322,6 +325,7 @@ }, { "name": "foo:foreign-pkg", + "kind": "Implicit", "interfaces": { "the-default": 3 }, @@ -329,6 +333,7 @@ }, { "name": "foo:some-pkg", + "kind": "Implicit", "interfaces": { "the-default": 4, "some-interface": 5, @@ -338,6 +343,7 @@ }, { "name": "foo:wasi", + "kind": "Implicit", "interfaces": { "clocks": 7, "filesystem": 8 @@ -346,6 +352,7 @@ }, { "name": "foo:root", + "kind": "Implicit", "interfaces": { "foo": 9, "bar": 10, diff --git a/crates/wit-parser/tests/ui/functions.wit.json b/crates/wit-parser/tests/ui/functions.wit.json index 902fe35709..a5afc8eb43 100644 --- a/crates/wit-parser/tests/ui/functions.wit.json +++ b/crates/wit-parser/tests/ui/functions.wit.json @@ -157,6 +157,7 @@ "packages": [ { "name": "foo:functions", + "kind": "Implicit", "interfaces": { "functions": 0 }, diff --git a/crates/wit-parser/tests/ui/ignore-files-deps.wit.json b/crates/wit-parser/tests/ui/ignore-files-deps.wit.json index 7a812dbecf..27805e9468 100644 --- a/crates/wit-parser/tests/ui/ignore-files-deps.wit.json +++ b/crates/wit-parser/tests/ui/ignore-files-deps.wit.json @@ -25,6 +25,7 @@ "packages": [ { "name": "foo:bar", + "kind": "Implicit", "interfaces": { "types": 0 }, @@ -32,6 +33,7 @@ }, { "name": "foo:foo", + "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/import-export-overlap1.wit.json b/crates/wit-parser/tests/ui/import-export-overlap1.wit.json index 0c88a177b2..77d0c44838 100644 --- a/crates/wit-parser/tests/ui/import-export-overlap1.wit.json +++ b/crates/wit-parser/tests/ui/import-export-overlap1.wit.json @@ -30,6 +30,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/import-export-overlap2.wit.json b/crates/wit-parser/tests/ui/import-export-overlap2.wit.json index 0c991269c5..046c5af444 100644 --- a/crates/wit-parser/tests/ui/import-export-overlap2.wit.json +++ b/crates/wit-parser/tests/ui/import-export-overlap2.wit.json @@ -34,6 +34,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/include-reps.wit.json b/crates/wit-parser/tests/ui/include-reps.wit.json index 21debef2fe..b77c9a640e 100644 --- a/crates/wit-parser/tests/ui/include-reps.wit.json +++ b/crates/wit-parser/tests/ui/include-reps.wit.json @@ -55,6 +55,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "a": 0, "b": 1 diff --git a/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json b/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json index 14c6d0d048..64c15833dd 100644 --- a/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json +++ b/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json @@ -59,6 +59,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0, diff --git a/crates/wit-parser/tests/ui/kinds-of-deps.wit.json b/crates/wit-parser/tests/ui/kinds-of-deps.wit.json index f13afe6301..d044bdd3ab 100644 --- a/crates/wit-parser/tests/ui/kinds-of-deps.wit.json +++ b/crates/wit-parser/tests/ui/kinds-of-deps.wit.json @@ -58,6 +58,7 @@ "packages": [ { "name": "d:d", + "kind": "Implicit", "interfaces": { "d": 0 }, @@ -65,6 +66,7 @@ }, { "name": "e:e", + "kind": "Implicit", "interfaces": { "e": 1 }, @@ -72,6 +74,7 @@ }, { "name": "b:b", + "kind": "Implicit", "interfaces": { "b": 2 }, @@ -79,6 +82,7 @@ }, { "name": "c:c", + "kind": "Implicit", "interfaces": { "c": 3 }, @@ -86,6 +90,7 @@ }, { "name": "a:a", + "kind": "Implicit", "interfaces": {}, "worlds": { "a": 0 diff --git a/crates/wit-parser/tests/ui/many-names.wit.json b/crates/wit-parser/tests/ui/many-names.wit.json index 590c68471a..1c47f69f77 100644 --- a/crates/wit-parser/tests/ui/many-names.wit.json +++ b/crates/wit-parser/tests/ui/many-names.wit.json @@ -31,6 +31,7 @@ "packages": [ { "name": "foo:name", + "kind": "Implicit", "interfaces": { "x": 0 }, diff --git a/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json b/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json index a2dea31719..a20a0c78ec 100644 --- a/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json +++ b/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json @@ -212,6 +212,7 @@ "packages": [ { "name": "bar:name", + "kind": "Explicit", "interfaces": { "i2": 0 }, @@ -221,6 +222,7 @@ }, { "name": "baz:name", + "kind": "Explicit", "interfaces": { "i3": 2 }, @@ -230,6 +232,7 @@ }, { "name": "foo:name", + "kind": "Explicit", "interfaces": { "i1": 4 }, @@ -239,6 +242,7 @@ }, { "name": "qux:name", + "kind": "Explicit", "interfaces": { "i4": 6 }, diff --git a/crates/wit-parser/tests/ui/multi-file.wit.json b/crates/wit-parser/tests/ui/multi-file.wit.json index 45af0f5d32..6da127c468 100644 --- a/crates/wit-parser/tests/ui/multi-file.wit.json +++ b/crates/wit-parser/tests/ui/multi-file.wit.json @@ -283,6 +283,7 @@ "packages": [ { "name": "foo:multi-file", + "kind": "Implicit", "interfaces": { "irrelevant-name": 0, "depend-on-me": 1, diff --git a/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json b/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json index d3434da0c5..43cd97987a 100644 --- a/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json +++ b/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json @@ -53,6 +53,7 @@ "packages": [ { "name": "foo:dep1", + "kind": "Implicit", "interfaces": { "types": 0 }, @@ -60,6 +61,7 @@ }, { "name": "foo:dep2", + "kind": "Implicit", "interfaces": { "types": 1 }, @@ -67,6 +69,7 @@ }, { "name": "foo:bar", + "kind": "Explicit", "interfaces": {}, "worlds": { "w-bar": 0 @@ -74,6 +77,7 @@ }, { "name": "foo:qux", + "kind": "Explicit", "interfaces": {}, "worlds": { "w-qux": 1 diff --git a/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json b/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json index ece4cdade9..0fdb4dd5a9 100644 --- a/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json +++ b/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json @@ -86,6 +86,7 @@ "packages": [ { "name": "foo:dep2", + "kind": "Implicit", "interfaces": { "types": 0 }, @@ -93,6 +94,7 @@ }, { "name": "foo:dep1", + "kind": "Implicit", "interfaces": { "types": 1 }, @@ -100,6 +102,7 @@ }, { "name": "foo:bar", + "kind": "Explicit", "interfaces": {}, "worlds": { "w-bar": 0 @@ -107,6 +110,7 @@ }, { "name": "foo:qux", + "kind": "Explicit", "interfaces": {}, "worlds": { "w-qux": 1 diff --git a/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json b/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json index 1dad5120b5..ac93129918 100644 --- a/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json +++ b/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json @@ -73,6 +73,7 @@ "packages": [ { "name": "some:dep", + "kind": "Implicit", "interfaces": { "foo": 0 }, @@ -80,6 +81,7 @@ }, { "name": "foo:bar", + "kind": "Implicit", "interfaces": { "foo": 1 }, diff --git a/crates/wit-parser/tests/ui/package-syntax1.wit.json b/crates/wit-parser/tests/ui/package-syntax1.wit.json index 80af0cbb3b..5bf7712909 100644 --- a/crates/wit-parser/tests/ui/package-syntax1.wit.json +++ b/crates/wit-parser/tests/ui/package-syntax1.wit.json @@ -5,6 +5,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": {}, "worlds": {} } diff --git a/crates/wit-parser/tests/ui/package-syntax3.wit.json b/crates/wit-parser/tests/ui/package-syntax3.wit.json index e0323c511b..8ff0a1ea2a 100644 --- a/crates/wit-parser/tests/ui/package-syntax3.wit.json +++ b/crates/wit-parser/tests/ui/package-syntax3.wit.json @@ -5,6 +5,7 @@ "packages": [ { "name": "foo:bar", + "kind": "Implicit", "interfaces": {}, "worlds": {} } diff --git a/crates/wit-parser/tests/ui/package-syntax4.wit.json b/crates/wit-parser/tests/ui/package-syntax4.wit.json index 7bacc86f03..280678ddd5 100644 --- a/crates/wit-parser/tests/ui/package-syntax4.wit.json +++ b/crates/wit-parser/tests/ui/package-syntax4.wit.json @@ -5,6 +5,7 @@ "packages": [ { "name": "foo:bar@2.0.0", + "kind": "Implicit", "interfaces": {}, "worlds": {} } diff --git a/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json b/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json index f4c9e7055c..3b925bee34 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json @@ -110,6 +110,7 @@ "packages": [ { "name": "bar:name", + "kind": "Explicit", "interfaces": { "i": 0 }, @@ -119,6 +120,7 @@ }, { "name": "foo:name", + "kind": "Explicit", "interfaces": { "i": 2 }, diff --git a/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json b/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json index 1d59a07e3a..8edc232bd2 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json @@ -71,6 +71,7 @@ "packages": [ { "name": "foo:name", + "kind": "Explicit", "interfaces": { "i1": 0 }, @@ -78,6 +79,7 @@ }, { "name": "bar:name", + "kind": "Explicit", "interfaces": {}, "worlds": { "w1": 0 diff --git a/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json b/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json index 23a10462aa..fdbd064be1 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json @@ -110,6 +110,7 @@ "packages": [ { "name": "foo:name@1.0.0", + "kind": "Explicit", "interfaces": { "i1": 0 }, @@ -119,6 +120,7 @@ }, { "name": "foo:name@1.0.1", + "kind": "Explicit", "interfaces": { "i1": 2 }, diff --git a/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json b/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json index de73c51de3..c770931d30 100644 --- a/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json +++ b/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json @@ -110,6 +110,7 @@ "packages": [ { "name": "bar:name", + "kind": "Explicit", "interfaces": { "i2": 0 }, @@ -119,6 +120,7 @@ }, { "name": "foo:name", + "kind": "Explicit", "interfaces": { "i1": 2 }, diff --git a/crates/wit-parser/tests/ui/packages-single-explicit.wit.json b/crates/wit-parser/tests/ui/packages-single-explicit.wit.json index 3eefde2810..667eadfdaa 100644 --- a/crates/wit-parser/tests/ui/packages-single-explicit.wit.json +++ b/crates/wit-parser/tests/ui/packages-single-explicit.wit.json @@ -59,6 +59,7 @@ "packages": [ { "name": "foo:name", + "kind": "Explicit", "interfaces": { "i1": 0 }, diff --git a/crates/wit-parser/tests/ui/random.wit.json b/crates/wit-parser/tests/ui/random.wit.json index 5d46664671..977e824bb1 100644 --- a/crates/wit-parser/tests/ui/random.wit.json +++ b/crates/wit-parser/tests/ui/random.wit.json @@ -55,6 +55,7 @@ "packages": [ { "name": "wasi:random", + "kind": "Implicit", "interfaces": { "random": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-empty.wit.json b/crates/wit-parser/tests/ui/resources-empty.wit.json index 6c40c61dfc..2adbff8138 100644 --- a/crates/wit-parser/tests/ui/resources-empty.wit.json +++ b/crates/wit-parser/tests/ui/resources-empty.wit.json @@ -63,6 +63,7 @@ "packages": [ { "name": "foo:resources-empty", + "kind": "Implicit", "interfaces": { "resources-empty": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json b/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json index e7234f7ed8..f0bd001ecc 100644 --- a/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json +++ b/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json @@ -74,6 +74,7 @@ "packages": [ { "name": "foo:resources1", + "kind": "Implicit", "interfaces": { "resources1": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-multiple.wit.json b/crates/wit-parser/tests/ui/resources-multiple.wit.json index 2914ab99d9..9a54b96240 100644 --- a/crates/wit-parser/tests/ui/resources-multiple.wit.json +++ b/crates/wit-parser/tests/ui/resources-multiple.wit.json @@ -272,6 +272,7 @@ "packages": [ { "name": "foo:resources-multiple", + "kind": "Implicit", "interfaces": { "resources-multiple": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-return-own.wit.json b/crates/wit-parser/tests/ui/resources-return-own.wit.json index 3e44fc106c..0b2f0103d8 100644 --- a/crates/wit-parser/tests/ui/resources-return-own.wit.json +++ b/crates/wit-parser/tests/ui/resources-return-own.wit.json @@ -69,6 +69,7 @@ "packages": [ { "name": "foo:resources1", + "kind": "Implicit", "interfaces": { "resources1": 0 }, diff --git a/crates/wit-parser/tests/ui/resources.wit.json b/crates/wit-parser/tests/ui/resources.wit.json index 169fb02110..28b845fd30 100644 --- a/crates/wit-parser/tests/ui/resources.wit.json +++ b/crates/wit-parser/tests/ui/resources.wit.json @@ -328,6 +328,7 @@ "packages": [ { "name": "foo:bar", + "kind": "Implicit", "interfaces": { "foo": 0, "i": 1 diff --git a/crates/wit-parser/tests/ui/resources1.wit.json b/crates/wit-parser/tests/ui/resources1.wit.json index 447e5ec2b9..1883d6f6ef 100644 --- a/crates/wit-parser/tests/ui/resources1.wit.json +++ b/crates/wit-parser/tests/ui/resources1.wit.json @@ -87,6 +87,7 @@ "packages": [ { "name": "foo:resources1", + "kind": "Implicit", "interfaces": { "resources1": 0 }, diff --git a/crates/wit-parser/tests/ui/same-name-import-export.wit.json b/crates/wit-parser/tests/ui/same-name-import-export.wit.json index 3cf1798d93..a440e633d3 100644 --- a/crates/wit-parser/tests/ui/same-name-import-export.wit.json +++ b/crates/wit-parser/tests/ui/same-name-import-export.wit.json @@ -38,6 +38,7 @@ "packages": [ { "name": "demo:greeter", + "kind": "Implicit", "interfaces": {}, "worlds": { "greeter": 0 diff --git a/crates/wit-parser/tests/ui/shared-types.wit.json b/crates/wit-parser/tests/ui/shared-types.wit.json index 569f6d3861..7a0875d84e 100644 --- a/crates/wit-parser/tests/ui/shared-types.wit.json +++ b/crates/wit-parser/tests/ui/shared-types.wit.json @@ -78,6 +78,7 @@ "packages": [ { "name": "foo:shared-items", + "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/simple-wasm-text.wit.json b/crates/wit-parser/tests/ui/simple-wasm-text.wit.json index 969c13a506..5e575b16c1 100644 --- a/crates/wit-parser/tests/ui/simple-wasm-text.wit.json +++ b/crates/wit-parser/tests/ui/simple-wasm-text.wit.json @@ -12,6 +12,7 @@ "packages": [ { "name": "a:b", + "kind": "Implicit", "interfaces": { "x": 0 }, diff --git a/crates/wit-parser/tests/ui/since-and-unstable.wit.json b/crates/wit-parser/tests/ui/since-and-unstable.wit.json index 31c4fa0c4b..c27dad3e43 100644 --- a/crates/wit-parser/tests/ui/since-and-unstable.wit.json +++ b/crates/wit-parser/tests/ui/since-and-unstable.wit.json @@ -566,6 +566,7 @@ "packages": [ { "name": "a:b@1.0.1", + "kind": "Implicit", "interfaces": { "foo1": 0, "foo2": 1, diff --git a/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json b/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json index d77812776b..55d6f68e0b 100644 --- a/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json +++ b/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json @@ -1136,6 +1136,7 @@ "packages": [ { "name": "foo:bar", + "kind": "Implicit", "interfaces": { "i1": 0, "i2": 1, diff --git a/crates/wit-parser/tests/ui/type-then-eof.wit.json b/crates/wit-parser/tests/ui/type-then-eof.wit.json index beb0a7adca..85a5688d5e 100644 --- a/crates/wit-parser/tests/ui/type-then-eof.wit.json +++ b/crates/wit-parser/tests/ui/type-then-eof.wit.json @@ -23,6 +23,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/types.wit.json b/crates/wit-parser/tests/ui/types.wit.json index 8874e3541d..d428f7f671 100644 --- a/crates/wit-parser/tests/ui/types.wit.json +++ b/crates/wit-parser/tests/ui/types.wit.json @@ -765,6 +765,7 @@ "packages": [ { "name": "foo:types", + "kind": "Implicit", "interfaces": { "types": 0 }, diff --git a/crates/wit-parser/tests/ui/union-fuzz-1.wit.json b/crates/wit-parser/tests/ui/union-fuzz-1.wit.json index 31c7c82687..705cd46134 100644 --- a/crates/wit-parser/tests/ui/union-fuzz-1.wit.json +++ b/crates/wit-parser/tests/ui/union-fuzz-1.wit.json @@ -24,6 +24,7 @@ "packages": [ { "name": "foo:bar", + "kind": "Implicit", "interfaces": {}, "worlds": { "xo": 0, diff --git a/crates/wit-parser/tests/ui/union-fuzz-2.wit.json b/crates/wit-parser/tests/ui/union-fuzz-2.wit.json index 6d1bc4ffd4..c722de0429 100644 --- a/crates/wit-parser/tests/ui/union-fuzz-2.wit.json +++ b/crates/wit-parser/tests/ui/union-fuzz-2.wit.json @@ -62,6 +62,7 @@ "packages": [ { "name": "foo:bar", + "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0, diff --git a/crates/wit-parser/tests/ui/use-chain.wit.json b/crates/wit-parser/tests/ui/use-chain.wit.json index bd942bb8cd..824f2e646e 100644 --- a/crates/wit-parser/tests/ui/use-chain.wit.json +++ b/crates/wit-parser/tests/ui/use-chain.wit.json @@ -43,6 +43,7 @@ "packages": [ { "name": "foo:name", + "kind": "Implicit", "interfaces": { "foo": 0, "name": 1 diff --git a/crates/wit-parser/tests/ui/use.wit.json b/crates/wit-parser/tests/ui/use.wit.json index 43cc16b4a7..c3dce34734 100644 --- a/crates/wit-parser/tests/ui/use.wit.json +++ b/crates/wit-parser/tests/ui/use.wit.json @@ -157,6 +157,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "bar": 0, "foo": 1, diff --git a/crates/wit-parser/tests/ui/versions.wit.json b/crates/wit-parser/tests/ui/versions.wit.json index febd55be51..daaaf6fec0 100644 --- a/crates/wit-parser/tests/ui/versions.wit.json +++ b/crates/wit-parser/tests/ui/versions.wit.json @@ -68,6 +68,7 @@ "packages": [ { "name": "a:a@1.0.0", + "kind": "Implicit", "interfaces": { "foo": 0 }, @@ -75,6 +76,7 @@ }, { "name": "a:a@2.0.0", + "kind": "Implicit", "interfaces": { "foo": 1 }, @@ -82,6 +84,7 @@ }, { "name": "foo:versions", + "kind": "Implicit", "interfaces": { "foo": 2 }, diff --git a/crates/wit-parser/tests/ui/wasi.wit.json b/crates/wit-parser/tests/ui/wasi.wit.json index e784fbd207..c0fa5b6ba3 100644 --- a/crates/wit-parser/tests/ui/wasi.wit.json +++ b/crates/wit-parser/tests/ui/wasi.wit.json @@ -530,6 +530,7 @@ "packages": [ { "name": "wasi:filesystem", + "kind": "Implicit", "interfaces": { "wasi": 0 }, diff --git a/crates/wit-parser/tests/ui/world-diamond.wit.json b/crates/wit-parser/tests/ui/world-diamond.wit.json index 6dba45edb9..e5155cdc76 100644 --- a/crates/wit-parser/tests/ui/world-diamond.wit.json +++ b/crates/wit-parser/tests/ui/world-diamond.wit.json @@ -111,6 +111,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "shared-items": 0, "i1": 1, diff --git a/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json b/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json index fffc11557e..2417a6adae 100644 --- a/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json +++ b/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json @@ -57,6 +57,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/world-implicit-import1.wit.json b/crates/wit-parser/tests/ui/world-implicit-import1.wit.json index 708c339b40..d155847021 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import1.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import1.wit.json @@ -70,6 +70,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/world-implicit-import2.wit.json b/crates/wit-parser/tests/ui/world-implicit-import2.wit.json index 97049842a4..d48070dead 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import2.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import2.wit.json @@ -61,6 +61,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/world-implicit-import3.wit.json b/crates/wit-parser/tests/ui/world-implicit-import3.wit.json index 159bee7738..3fd6405789 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import3.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import3.wit.json @@ -62,6 +62,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/world-same-fields4.wit.json b/crates/wit-parser/tests/ui/world-same-fields4.wit.json index 3be16acf9b..b7eac1c81b 100644 --- a/crates/wit-parser/tests/ui/world-same-fields4.wit.json +++ b/crates/wit-parser/tests/ui/world-same-fields4.wit.json @@ -71,6 +71,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "shared-items": 0 }, diff --git a/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json b/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json index b7866aa12d..0c164071ec 100644 --- a/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json +++ b/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json @@ -77,6 +77,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/world-top-level-resources.wit.json b/crates/wit-parser/tests/ui/world-top-level-resources.wit.json index 794e699f45..4ba6711832 100644 --- a/crates/wit-parser/tests/ui/world-top-level-resources.wit.json +++ b/crates/wit-parser/tests/ui/world-top-level-resources.wit.json @@ -225,6 +225,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "types": 0, "handler": 1 diff --git a/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json b/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json index d1e24e97b6..e1d37c8927 100644 --- a/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json +++ b/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json @@ -94,6 +94,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "a1": 0, "a2": 1, diff --git a/crates/wit-parser/tests/ui/worlds-with-types.wit.json b/crates/wit-parser/tests/ui/worlds-with-types.wit.json index 70e857d68b..fed7ae5e77 100644 --- a/crates/wit-parser/tests/ui/worlds-with-types.wit.json +++ b/crates/wit-parser/tests/ui/worlds-with-types.wit.json @@ -191,6 +191,7 @@ "packages": [ { "name": "foo:foo", + "kind": "Implicit", "interfaces": { "disambiguate": 0 }, From 4ba14d34565785772d4daede16e027a09dc3a1fd Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Wed, 26 Jun 2024 19:35:20 -0500 Subject: [PATCH 04/16] serde --- crates/wit-parser/src/decoding.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 69038da014..810b1ce545 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -6,8 +6,11 @@ use std::mem; use std::slice; use std::{collections::HashMap, io::Read}; use wasmparser::Chunk; +#[cfg(feature = "serde")] use wasmparser::ComponentNameSectionReader; +#[cfg(feature = "serde")] use wasmparser::KnownCustom; +#[cfg(feature = "serde")] use wasmparser::NameMap; use wasmparser::{ names::{ComponentName, ComponentNameKind}, @@ -42,7 +45,7 @@ struct ExplicitPackageInfo { types: Option, /// List of all imports and exports from this component. externs: Vec<(String, Extern)>, - // / Decoded package metadata + /// Decoded package metadata package_metadata: Option, } @@ -63,6 +66,7 @@ enum WitEncodingVersion { V2, } +#[cfg(feature = "serde")] fn register_names( names: ComponentNameSectionReader, explicit: &mut ExplicitPackageInfo, @@ -98,7 +102,7 @@ impl ComponentInfo { /// Creates a new component info by parsing the given WebAssembly component bytes. fn from_reader(mut reader: impl Read) -> Result { - let mut packages = Vec::new(); + let mut _packages = Vec::new(); let mut explicit = Vec::new(); let mut cur_package = ExplicitPackageInfo::default(); let mut is_implicit = true; @@ -106,7 +110,7 @@ impl ComponentInfo { let mut externs = Vec::new(); let mut depth = 1; let mut types = None; - let mut package_metadata = None; + let mut _package_metadata = None; let mut cur = Parser::new(0); let mut eof = false; let mut stack = Vec::new(); @@ -196,14 +200,14 @@ impl ComponentInfo { #[cfg(feature = "serde")] Payload::CustomSection(s) => { if s.name() == PackageMetadata::SECTION_NAME { - package_metadata = Some(PackageMetadata::decode(s.data())?); - cur_package.package_metadata = package_metadata.clone(); + _package_metadata = Some(PackageMetadata::decode(s.data())?); + cur_package.package_metadata = _package_metadata.clone(); } else if s.name() == "component-name" { if let KnownCustom::ComponentName(reader) = s.as_known() { - packages = register_names(reader, &mut cur_package)?; - if packages.len() == explicit.len() { + _packages = register_names(reader, &mut cur_package)?; + if _packages.len() == explicit.len() { for (i, exp) in explicit.iter_mut().enumerate() { - exp.name = Some(packages[i].clone()); + exp.name = Some(_packages[i].clone()); } } } else { @@ -236,9 +240,9 @@ impl ComponentInfo { Ok(Self { types: types.unwrap(), explicit, - packages, + packages: _packages, externs, - package_metadata, + package_metadata: _package_metadata, }) } From 20f083aa25f5b8bcd835c688a811da0c8e3c318b Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 1 Jul 2024 11:23:03 -0500 Subject: [PATCH 05/16] simplify ExplicitPackageInfo for decoding multi-package wit --- crates/wit-parser/src/decoding.rs | 51 ++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 810b1ce545..cb0c531ac3 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -29,8 +29,6 @@ struct ComponentInfo { externs: Vec<(String, Extern)>, /// Packages explicit: Vec, - /// Packages - packages: Vec, /// Decoded package metadata package_metadata: Option, } @@ -102,7 +100,6 @@ impl ComponentInfo { /// Creates a new component info by parsing the given WebAssembly component bytes. fn from_reader(mut reader: impl Read) -> Result { - let mut _packages = Vec::new(); let mut explicit = Vec::new(); let mut cur_package = ExplicitPackageInfo::default(); let mut is_implicit = true; @@ -204,7 +201,7 @@ impl ComponentInfo { cur_package.package_metadata = _package_metadata.clone(); } else if s.name() == "component-name" { if let KnownCustom::ComponentName(reader) = s.as_known() { - _packages = register_names(reader, &mut cur_package)?; + let _packages = register_names(reader, &mut cur_package)?; if _packages.len() == explicit.len() { for (i, exp) in explicit.iter_mut().enumerate() { exp.name = Some(_packages[i].clone()); @@ -240,18 +237,56 @@ impl ComponentInfo { Ok(Self { types: types.unwrap(), explicit, - packages: _packages, externs, package_metadata: _package_metadata, }) } fn is_wit_package(&self) -> Option { + // Check if each explicitly defined package is wit + if !self.explicit.is_empty() { + // all wit package exports must be component types, and there must be at + // least one + for expl in self.explicit.iter() { + if expl.externs.is_empty() { + return None; + } + if !expl.externs.iter().all(|(_, item)| { + let export = match item { + Extern::Export(e) => e, + _ => return false, + }; + match export.kind { + ComponentExternalKind::Type => matches!( + expl.types + .as_ref() + .unwrap() + .component_any_type_at(export.index), + types::ComponentAnyTypeId::Component(_) + ), + _ => false, + } + }) { + return None; + } + } + // If all packages are explicit, root package will have no extern exports + if self.externs.len() == 0 { + return Some(WitEncodingVersion::V2); + } + // // The distinction between v1 and v2 encoding formats is the structure of the export + // // strings for each component. The v1 format uses ":/wit" as the name + // // for the top-level exports, while the v2 format uses the unqualified name of the encoded + // // entity. + return match ComponentName::new(&self.externs[0].0, 0).ok()?.kind() { + ComponentNameKind::Interface(name) if name.interface().as_str() == "wit" => { + Some(WitEncodingVersion::V1) + } + _ => Some(WitEncodingVersion::V2), + }; + } // all wit package exports must be component types, and there must be at // least one - if !self.packages.is_empty() { - return Some(WitEncodingVersion::V2); - } if self.externs.is_empty() { return None; } From 500ae6fc6ebb81134b09aa82da79a3de1a54474f Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 1 Jul 2024 11:26:51 -0500 Subject: [PATCH 06/16] retry From d88d268c6b71f254bec00cb15ab1266b58085aec Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 1 Jul 2024 12:06:25 -0500 Subject: [PATCH 07/16] retry From 616721e2539c680b89fcab4383b26e395ce4d129 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 1 Jul 2024 12:39:42 -0500 Subject: [PATCH 08/16] update centos version and mirror --- ci/docker/x86_64-linux/Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ci/docker/x86_64-linux/Dockerfile b/ci/docker/x86_64-linux/Dockerfile index 89ddc2b282..bb8f48099d 100644 --- a/ci/docker/x86_64-linux/Dockerfile +++ b/ci/docker/x86_64-linux/Dockerfile @@ -1,4 +1,8 @@ -FROM centos:7 +FROM centos:8 + +RUN cd /etc/yum.repos.d/ +RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* +RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* RUN yum install -y git gcc From 19301eb845a22144ae5d68f6adf065b755a31fef Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Thu, 11 Jul 2024 11:34:53 -0500 Subject: [PATCH 09/16] merge resolve per explicit package --- crates/wit-component/tests/interfaces.rs | 1 - crates/wit-parser/src/decoding.rs | 173 ++++++++++++++--------- crates/wit-parser/src/resolve.rs | 3 +- 3 files changed, 107 insertions(+), 70 deletions(-) diff --git a/crates/wit-component/tests/interfaces.rs b/crates/wit-component/tests/interfaces.rs index a4f6bc55b4..70ab329f80 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -84,7 +84,6 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { let wat2 = wasmprinter::print_bytes(&wasm)?; assert_eq!(wat, wat2, "document did not roundtrip correctly"); } - // } Ok(()) } diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index cb0c531ac3..d2840918fa 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -342,16 +342,17 @@ impl ComponentInfo { } let pkg = pkg.ok_or_else(|| anyhow!("no exported component type found"))?; - let (mut resolve, packages) = decoder.finish(&mut [pkg]); + let (mut resolve, package) = decoder.finish(pkg); if let Some(package_metadata) = &self.package_metadata { - package_metadata.inject(&mut resolve, packages[0])?; + package_metadata.inject(&mut resolve, package)?; } - Ok((resolve, packages[0])) + Ok((resolve, package)) } fn decode_wit_v2_packages(&self) -> Result<(Resolve, Vec)> { let mut decoder = WitPackageDecoder::new(&self.types); + let mut pkg_name = None; let mut pkg_names = Vec::new(); let mut fields = PackageFields { @@ -359,6 +360,7 @@ impl ComponentInfo { worlds: &mut IndexMap::new(), }; let mut pkg_ids: Vec = Vec::new(); + let mut implicit = None; for (_, item) in self.externs.iter() { let export = match item { Extern::Export(e) => e, @@ -397,35 +399,37 @@ impl ComponentInfo { } _ => unreachable!(), }; - if !pkg_names.contains(&name) { - pkg_names.push(name.clone()); - let pkg = Package { - name: name.clone(), - kind: PackageKind::Implicit, - docs: Docs::default(), - interfaces: fields.interfaces.clone(), - worlds: fields.worlds.clone(), - }; - if !pkg_ids.contains(&pkg) { - pkg_ids.push(pkg); + if let Some(pkg_name) = pkg_name.as_ref() { + // TODO: when we have fully switched to the v2 format, we should switch to parsing + // multiple wit documents instead of bailing. + if pkg_name != &name { + bail!("item defined with mismatched package name") } } else { - let pkg = Package { - name: name.clone(), - kind: PackageKind::Implicit, - docs: Docs::default(), - interfaces: fields.interfaces.clone(), - worlds: fields.worlds.clone(), - }; - let pkg_id = pkg_ids.iter_mut().find(|p| p.name == pkg.name).unwrap(); - pkg_id.interfaces = pkg.interfaces; - pkg_id.worlds = pkg.worlds; + pkg_name.replace(name); } } + let mut resolve = if let Some(name) = pkg_name { + let pkg = Package { + name, + kind: PackageKind::Implicit, + docs: Docs::default(), + interfaces: fields.interfaces.clone(), + worlds: fields.worlds.clone(), + }; + let (resolve, package) = decoder.finish(pkg); + implicit = Some(package); + resolve + } else { + Resolve::new() + }; + for explicit in &self.explicit { + let mut cur_decoder = WitPackageDecoder::new(explicit.types.as_ref().unwrap()); fields.interfaces.clear(); fields.worlds.clear(); + pkg_name = None; for (_, item) in explicit.externs.iter() { let export = match item { Extern::Export(e) => e, @@ -449,22 +453,33 @@ impl ComponentInfo { let name = match component.exports[name] { types::ComponentEntityType::Component(ty) => { - let package_name = - decoder.decode_world(name.as_str(), &self.types[ty], &mut fields)?; + let package_name = cur_decoder.decode_world( + name.as_str(), + &explicit.types.as_ref().unwrap()[ty], + &mut fields, + )?; package_name } types::ComponentEntityType::Instance(ty) => { - let package_name = decoder.decode_interface( + let package_name = cur_decoder.decode_interface( name.as_str(), &component.imports, - &self.types[ty], + &explicit.types.as_ref().unwrap()[ty], &mut fields, )?; package_name } _ => unreachable!(), }; - + if let Some(pkg_name) = pkg_name.as_ref() { + // TODO: when we have fully switched to the v2 format, we should switch to parsing + // multiple wit documents instead of bailing. + if pkg_name != &name { + bail!("item defined with mismatched package name") + } + } else { + pkg_name.replace(name.clone()); + } if !pkg_names.contains(&name) { pkg_names.push(name.clone()); let pkg = Package { @@ -488,11 +503,29 @@ impl ComponentInfo { let pkg_id = pkg_ids.iter_mut().find(|p| p.name == pkg.name).unwrap(); pkg_id.interfaces = pkg.interfaces; pkg_id.worlds = pkg.worlds; - } + }; } + let pkg = if let Some(name) = pkg_name { + Package { + name: name.clone(), + kind: PackageKind::Implicit, + docs: Docs::default(), + interfaces: fields.interfaces.clone(), + worlds: fields.worlds.clone(), + } + } else { + Package { + name: explicit.name.as_ref().unwrap().clone(), + kind: PackageKind::Implicit, + docs: Docs::default(), + interfaces: fields.interfaces.clone(), + worlds: fields.worlds.clone(), + } + }; + let (cur_resolve, _) = cur_decoder.finish(pkg); + resolve.merge(cur_resolve)?; } - let (mut resolve, packages) = decoder.finish(&mut pkg_ids); let copy = resolve.clone(); for package in &self.explicit { if let Some(package_metadata) = &package.package_metadata { @@ -506,12 +539,20 @@ impl ComponentInfo { // For now this is a sufficient condition to know that we're working with // an implicit package declaration. This will need to be reworked when // mixed package declarations are supported - if self.explicit.len() == 0 { - let package = packages[0]; + if let Some(package) = implicit { if let Some(package_metadata) = &self.package_metadata { package_metadata.inject(&mut resolve, package)?; } } + let packages = if let Some(package) = implicit { + vec![package] + } else { + resolve + .package_names + .iter() + .map(|(_, v)| v.to_owned()) + .collect() + }; Ok((resolve, packages)) } @@ -563,7 +604,7 @@ impl ComponentInfo { } } - let (resolve, _) = decoder.finish(&mut [package]); + let (resolve, _) = decoder.finish(package); Ok((resolve, world)) } } @@ -704,16 +745,16 @@ pub fn decode_world(wasm: &[u8]) -> Result<(Resolve, WorldId)> { worlds: &mut worlds, }; let name = decoder.decode_world(name, &types[ty], &mut fields)?; - let (resolve, pkgs) = decoder.finish(&mut [Package { + let (resolve, pkg) = decoder.finish(Package { name, kind: PackageKind::Implicit, interfaces: fields.interfaces.clone(), worlds: fields.worlds.clone(), docs: Default::default(), - }]); + }); // The package decoded here should only have a single world so extract that // here to return. - let world = *resolve.packages[pkgs[0]].worlds.iter().next().unwrap().1; + let world = *resolve.packages[pkg].worlds.iter().next().unwrap().1; Ok((resolve, world)) } @@ -1658,44 +1699,40 @@ impl WitPackageDecoder<'_> { /// their topological ordering within the returned `Resolve`. /// /// Takes the root package as an argument to insert. - fn finish(mut self, packages: &mut [Package]) -> (Resolve, Vec) { - let mut resolved = Vec::new(); - for package in packages { - // Build a topological ordering is then calculated by visiting all the - // transitive dependencies of packages. - let mut order = IndexSet::new(); - for i in 0..self.foreign_packages.len() { - self.visit_package(i, &mut order); - } - // Using the topological ordering create a temporary map from - // index-in-`foreign_packages` to index-in-`order` - let mut idx_to_pos = vec![0; self.foreign_packages.len()]; - for (pos, idx) in order.iter().enumerate() { - idx_to_pos[*idx] = pos; - } - // .. and then using `idx_to_pos` sort the `foreign_packages` array based - // on the position it's at in the topological ordering - let mut deps = mem::take(&mut self.foreign_packages) - .into_iter() - .enumerate() - .collect::>(); - deps.sort_by_key(|(idx, _)| idx_to_pos[*idx]); - - // .. and finally insert the packages, in their final topological - // ordering, into the returned array. - for (_idx, (_url, pkg)) in deps { - self.insert_package(pkg); - } - let id = self.insert_package(package.clone()); - resolved.push(id); + fn finish(mut self, package: Package) -> (Resolve, PackageId) { + // Build a topological ordering is then calculated by visiting all the + // transitive dependencies of packages. + let mut order = IndexSet::new(); + for i in 0..self.foreign_packages.len() { + self.visit_package(i, &mut order); + } + // Using the topological ordering create a temporary map from + // index-in-`foreign_packages` to index-in-`order` + let mut idx_to_pos = vec![0; self.foreign_packages.len()]; + for (pos, idx) in order.iter().enumerate() { + idx_to_pos[*idx] = pos; + } + // .. and then using `idx_to_pos` sort the `foreign_packages` array based + // on the position it's at in the topological ordering + let mut deps = mem::take(&mut self.foreign_packages) + .into_iter() + .enumerate() + .collect::>(); + deps.sort_by_key(|(idx, _)| idx_to_pos[*idx]); + + // .. and finally insert the packages, in their final topological + // ordering, into the returned array. + for (_idx, (_url, pkg)) in deps { + self.insert_package(pkg); } + let id = self.insert_package(package); assert!(self.resolve.worlds.iter().all(|(_, w)| w.package.is_some())); assert!(self .resolve .interfaces .iter() .all(|(_, i)| i.package.is_some())); - (self.resolve, resolved) + (self.resolve, id) } fn insert_package(&mut self, package: Package) -> PackageId { diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index 838700e426..d7ef0f4956 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -387,7 +387,7 @@ impl Resolve { pub fn push_file(&mut self, path: impl AsRef) -> Result> { match self._push_file(path.as_ref())? { #[cfg(feature = "decoding")] - ParsedFile::Package(id) => Ok(vec![id]), + ParsedFile::Package(ids) => Ok(vec![ids]), ParsedFile::Unresolved(pkgs) => self.push_group(pkgs), } } @@ -1047,6 +1047,7 @@ impl Resolve { }, }; let pkg = &self.packages[pkg]; + pkg.worlds .get(&world_name) .copied() From 5d98d8cfb92fce700803f945413d3b0ec1721be3 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Thu, 11 Jul 2024 11:50:03 -0500 Subject: [PATCH 10/16] remove package kind --- crates/wit-component/src/encoding/wit/v2.rs | 2 +- crates/wit-component/src/metadata.rs | 3 +- crates/wit-parser/src/ast.rs | 7 +--- crates/wit-parser/src/ast/resolve.rs | 39 +++++++------------ crates/wit-parser/src/decoding.rs | 12 ------ crates/wit-parser/src/lib.rs | 5 +-- crates/wit-parser/src/resolve.rs | 11 ------ crates/wit-parser/tests/ui/comments.wit.json | 1 - .../tests/ui/complex-include.wit.json | 3 -- .../tests/ui/cross-package-resource.wit.json | 2 - crates/wit-parser/tests/ui/diamond1.wit.json | 3 -- .../tests/ui/disambiguate-diamond.wit.json | 1 - crates/wit-parser/tests/ui/empty.wit.json | 1 - .../tests/ui/feature-gates.wit.json | 1 - .../tests/ui/foreign-deps-union.wit.json | 7 ---- .../wit-parser/tests/ui/foreign-deps.wit.json | 7 ---- crates/wit-parser/tests/ui/functions.wit.json | 1 - .../tests/ui/ignore-files-deps.wit.json | 2 - .../tests/ui/import-export-overlap1.wit.json | 1 - .../tests/ui/import-export-overlap2.wit.json | 1 - .../wit-parser/tests/ui/include-reps.wit.json | 1 - .../tests/ui/kebab-name-include-with.wit.json | 1 - .../tests/ui/kinds-of-deps.wit.json | 5 --- .../wit-parser/tests/ui/many-names.wit.json | 1 - .../ui/multi-file-multi-package.wit.json | 4 -- .../wit-parser/tests/ui/multi-file.wit.json | 1 - .../ui/multi-package-shared-deps.wit.json | 4 -- .../ui/multi-package-transitive-deps.wit.json | 4 -- .../ui/name-both-resource-and-type.wit.json | 2 - .../tests/ui/package-syntax1.wit.json | 1 - .../tests/ui/package-syntax3.wit.json | 1 - .../tests/ui/package-syntax4.wit.json | 1 - ...ges-explicit-colliding-decl-names.wit.json | 2 - ...ages-explicit-internal-references.wit.json | 2 - .../ui/packages-explicit-with-semver.wit.json | 2 - .../ui/packages-multiple-explicit.wit.json | 2 - .../ui/packages-single-explicit.wit.json | 1 - crates/wit-parser/tests/ui/random.wit.json | 1 - .../tests/ui/resources-empty.wit.json | 1 - .../resources-multiple-returns-own.wit.json | 1 - .../tests/ui/resources-multiple.wit.json | 1 - .../tests/ui/resources-return-own.wit.json | 1 - crates/wit-parser/tests/ui/resources.wit.json | 1 - .../wit-parser/tests/ui/resources1.wit.json | 1 - .../tests/ui/same-name-import-export.wit.json | 1 - .../wit-parser/tests/ui/shared-types.wit.json | 1 - .../tests/ui/simple-wasm-text.wit.json | 1 - .../tests/ui/since-and-unstable.wit.json | 1 - .../tests/ui/stress-export-elaborate.wit.json | 1 - .../tests/ui/type-then-eof.wit.json | 1 - crates/wit-parser/tests/ui/types.wit.json | 1 - .../wit-parser/tests/ui/union-fuzz-1.wit.json | 1 - .../wit-parser/tests/ui/union-fuzz-2.wit.json | 1 - crates/wit-parser/tests/ui/use-chain.wit.json | 1 - crates/wit-parser/tests/ui/use.wit.json | 1 - crates/wit-parser/tests/ui/versions.wit.json | 3 -- crates/wit-parser/tests/ui/wasi.wit.json | 1 - .../tests/ui/world-diamond.wit.json | 1 - .../tests/ui/world-iface-no-collide.wit.json | 1 - .../tests/ui/world-implicit-import1.wit.json | 1 - .../tests/ui/world-implicit-import2.wit.json | 1 - .../tests/ui/world-implicit-import3.wit.json | 1 - .../tests/ui/world-same-fields4.wit.json | 1 - .../tests/ui/world-top-level-funcs.wit.json | 1 - .../ui/world-top-level-resources.wit.json | 1 - .../tests/ui/worlds-union-dedup.wit.json | 1 - .../tests/ui/worlds-with-types.wit.json | 1 - 67 files changed, 19 insertions(+), 158 deletions(-) diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index a9fb022dce..7476dfd094 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -54,7 +54,7 @@ impl Encoder<'_> { let mut names = NameMap::new(); for pkg in self.packages { let package = &self.resolve.packages[*pkg]; - if let PackageKind::Explicit = package.kind { + if self.packages.len() > 1 { let mut sub_encoder = Encoder { component: ComponentBuilder::default(), resolve: self.resolve, diff --git a/crates/wit-component/src/metadata.rs b/crates/wit-component/src/metadata.rs index 61cc78fa36..f3e43614a7 100644 --- a/crates/wit-component/src/metadata.rs +++ b/crates/wit-component/src/metadata.rs @@ -51,7 +51,7 @@ use wasm_encoder::{ }; use wasm_metadata::Producers; use wasmparser::{BinaryReader, Encoding, Parser, Payload, WasmFeatures}; -use wit_parser::{Package, PackageKind, PackageName, Resolve, World, WorldId, WorldItem}; +use wit_parser::{Package, PackageName, Resolve, World, WorldId, WorldItem}; const CURRENT_VERSION: u8 = 0x04; const CUSTOM_SECTION_NAME: &str = "wit-component-encoding"; @@ -80,7 +80,6 @@ impl Default for Bindgen { name: "root".to_string(), version: None, }, - kind: PackageKind::Implicit, docs: Default::default(), interfaces: Default::default(), worlds: Default::default(), diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index d9dfbdbb18..d65d5fed4f 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -1592,11 +1592,6 @@ enum ResolverKind<'a> { PartialImplicit(Resolver<'a>), } -pub(crate) enum ResolverKindTag { - Explicit, - Implicit, -} - fn parse_package( unparsed_pkgs: Vec, src: &Source, @@ -1729,7 +1724,7 @@ impl SourceMap { match resolver_kind { ResolverKind::Unknown => bail!("No WIT packages found in the supplied source"), ResolverKind::Explicit(pkgs) => Ok(pkgs), - ResolverKind::PartialImplicit(mut resolver) => match resolver.resolve(ResolverKindTag::Implicit)? { + ResolverKind::PartialImplicit(mut resolver) => match resolver.resolve()? { Some(pkg) => Ok(vec![pkg]), None => bail!("No WIT packages found in the supplied source"), }, diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index 6fc6dc9e63..0d3465a602 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -1,4 +1,4 @@ -use super::{ParamList, ResolverKindTag, ResultList, WorldOrInterface}; +use super::{ParamList, ResultList, WorldOrInterface}; use crate::ast::toposort::toposort; use crate::*; use anyhow::bail; @@ -14,7 +14,7 @@ pub struct Resolver<'a> { package_docs: Docs, /// All non-`package` WIT decls are going to be resolved together. - decl_lists: Vec<(PackageKind, ast::DeclList<'a>)>, + decl_lists: Vec>, // Arenas that get plumbed to the final `UnresolvedPackage` types: Arena, @@ -140,12 +140,11 @@ impl<'a> Resolver<'a> { self.package_docs = docs; } } - self.decl_lists - .push((PackageKind::Implicit, partial.decl_list)); + self.decl_lists.push(partial.decl_list); Ok(()) } - pub(crate) fn resolve(&mut self, kind: ResolverKindTag) -> Result> { + pub(crate) fn resolve(&mut self) -> Result> { // At least one of the WIT files must have a `package` annotation. let name = match &self.package_name { Some(name) => name.clone(), @@ -172,7 +171,7 @@ impl<'a> Resolver<'a> { let mut iface_id_to_ast = IndexMap::new(); let mut world_id_to_ast = IndexMap::new(); for (i, decl_list) in decl_lists.iter().enumerate() { - for item in decl_list.1.items.iter() { + for item in decl_list.items.iter() { match item { ast::AstItem::Interface(iface) => { let id = match self.ast_items[i][iface.name.name] { @@ -207,10 +206,6 @@ impl<'a> Resolver<'a> { Ok(Some(UnresolvedPackage { name, - kind: match kind { - ResolverKindTag::Explicit => PackageKind::Explicit, - ResolverKindTag::Implicit => PackageKind::Implicit, - }, docs: mem::take(&mut self.package_docs), worlds: mem::take(&mut self.worlds), types: mem::take(&mut self.types), @@ -242,21 +237,20 @@ impl<'a> Resolver<'a> { ) -> Result> { self.package_name = Some(package.package_id.package_name()); self.docs(&package.package_id.docs); - self.decl_lists = vec![(PackageKind::Explicit, package.decl_list)]; - self.resolve(ResolverKindTag::Explicit) + self.decl_lists = vec![package.decl_list]; + self.resolve() } /// Registers all foreign dependencies made within the ASTs provided. /// /// This will populate the `self.foreign_{deps,interfaces,worlds}` maps with all /// `UsePath::Package` entries. - fn populate_foreign_deps(&mut self, decl_lists: &[(PackageKind, ast::DeclList<'a>)]) { + fn populate_foreign_deps(&mut self, decl_lists: &[ast::DeclList<'a>]) { let mut foreign_deps = mem::take(&mut self.foreign_deps); let mut foreign_interfaces = mem::take(&mut self.foreign_interfaces); let mut foreign_worlds = mem::take(&mut self.foreign_worlds); for decl_list in decl_lists { decl_list - .1 .for_each_path(|_, path, _names, world_or_iface| { let (id, name) = match path { ast::UsePath::Package { id, name } => (id, name), @@ -344,7 +338,7 @@ impl<'a> Resolver<'a> { /// generated for resolving use-paths later on. fn populate_ast_items( &mut self, - decl_lists: &[(PackageKind, ast::DeclList<'a>)], + decl_lists: &[ast::DeclList<'a>], ) -> Result<(Vec, Vec)> { let mut package_items = IndexMap::new(); @@ -355,7 +349,7 @@ impl<'a> Resolver<'a> { let mut order = IndexMap::new(); for decl_list in decl_lists { let mut decl_list_ns = IndexMap::new(); - for item in decl_list.1.items.iter() { + for item in decl_list.items.iter() { match item { ast::AstItem::Interface(i) => { if package_items.insert(i.name.name, i.name.span).is_some() { @@ -407,7 +401,7 @@ impl<'a> Resolver<'a> { // package or foreign items. Foreign deps are ignored for // topological ordering. let mut decl_list_ns = IndexMap::new(); - for item in decl_list.1.items.iter() { + for item in decl_list.items.iter() { let (name, src) = match item { ast::AstItem::Use(u) => { let name = u.as_.as_ref().unwrap_or(u.item.name()); @@ -430,7 +424,7 @@ impl<'a> Resolver<'a> { // With this file's namespace information look at all `use` paths // and record dependencies between interfaces. - decl_list.1.for_each_path(|iface, path, _names, _| { + decl_list.for_each_path(|iface, path, _names, _| { // If this import isn't contained within an interface then it's // in a world and it doesn't need to participate in our // topo-sort. @@ -496,7 +490,7 @@ impl<'a> Resolver<'a> { } for decl_list in decl_lists { let mut items = IndexMap::new(); - for item in decl_list.1.items.iter() { + for item in decl_list.items.iter() { let (name, ast_item) = match item { ast::AstItem::Use(u) => { if !u.attributes.is_empty() { @@ -554,13 +548,10 @@ impl<'a> Resolver<'a> { /// This is done after all interfaces are generated so `self.resolve_path` /// can be used to determine if what's being imported from is a foreign /// interface or not. - fn populate_foreign_types( - &mut self, - decl_lists: &[(PackageKind, ast::DeclList<'a>)], - ) -> Result<()> { + fn populate_foreign_types(&mut self, decl_lists: &[ast::DeclList<'a>]) -> Result<()> { for (i, decl_list) in decl_lists.iter().enumerate() { self.cur_ast_index = i; - decl_list.1.for_each_path(|_, path, names, _| { + decl_list.for_each_path(|_, path, names, _| { let names = match names { Some(names) => names, None => return Ok(()), diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index d2840918fa..51385b27d0 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -1,4 +1,3 @@ -use crate::resolve::PackageKind; use crate::*; use anyhow::{anyhow, bail}; use indexmap::IndexSet; @@ -413,7 +412,6 @@ impl ComponentInfo { let mut resolve = if let Some(name) = pkg_name { let pkg = Package { name, - kind: PackageKind::Implicit, docs: Docs::default(), interfaces: fields.interfaces.clone(), worlds: fields.worlds.clone(), @@ -484,7 +482,6 @@ impl ComponentInfo { pkg_names.push(name.clone()); let pkg = Package { name: name.clone(), - kind: PackageKind::Implicit, docs: Docs::default(), interfaces: fields.interfaces.clone(), worlds: fields.worlds.clone(), @@ -495,7 +492,6 @@ impl ComponentInfo { } else { let pkg = Package { name: name.clone(), - kind: PackageKind::Implicit, docs: Docs::default(), interfaces: fields.interfaces.clone(), worlds: fields.worlds.clone(), @@ -508,7 +504,6 @@ impl ComponentInfo { let pkg = if let Some(name) = pkg_name { Package { name: name.clone(), - kind: PackageKind::Implicit, docs: Docs::default(), interfaces: fields.interfaces.clone(), worlds: fields.worlds.clone(), @@ -516,7 +511,6 @@ impl ComponentInfo { } else { Package { name: explicit.name.as_ref().unwrap().clone(), - kind: PackageKind::Implicit, docs: Docs::default(), interfaces: fields.interfaces.clone(), worlds: fields.worlds.clone(), @@ -582,7 +576,6 @@ impl ComponentInfo { version: None, name: "component".to_string(), }, - kind: PackageKind::Implicit, docs: Default::default(), worlds: [(world_name.to_string(), world)].into_iter().collect(), interfaces: Default::default(), @@ -747,7 +740,6 @@ pub fn decode_world(wasm: &[u8]) -> Result<(Resolve, WorldId)> { let name = decoder.decode_world(name, &types[ty], &mut fields)?; let (resolve, pkg) = decoder.finish(Package { name, - kind: PackageKind::Implicit, interfaces: fields.interfaces.clone(), worlds: fields.worlds.clone(), docs: Default::default(), @@ -822,7 +814,6 @@ impl WitPackageDecoder<'_> { } _ => bail!("package name is not a valid id: {name}"), }, - kind: PackageKind::Implicit, docs: Default::default(), interfaces: Default::default(), worlds: Default::default(), @@ -1151,7 +1142,6 @@ impl WitPackageDecoder<'_> { .entry(package_name.to_string()) .or_insert_with(|| Package { name: package_name.clone(), - kind: PackageKind::Implicit, docs: Default::default(), interfaces: Default::default(), worlds: Default::default(), @@ -1738,7 +1728,6 @@ impl WitPackageDecoder<'_> { fn insert_package(&mut self, package: Package) -> PackageId { let Package { name, - kind, interfaces, worlds, docs, @@ -1757,7 +1746,6 @@ impl WitPackageDecoder<'_> { .unwrap_or_else(|| { let id = self.resolve.packages.alloc(Package { name: name.clone(), - kind: kind.clone(), interfaces: Default::default(), worlds: Default::default(), docs, diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 9cf496e907..2ab630cc69 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -21,7 +21,7 @@ pub use ast::{parse_use_path, ParsedUsePath}; mod sizealign; pub use sizealign::*; mod resolve; -pub use resolve::{Package, PackageId, PackageKind, Remap, Resolve}; +pub use resolve::{Package, PackageId, Remap, Resolve}; mod live; pub use live::LiveTypes; @@ -72,9 +72,6 @@ pub struct UnresolvedPackage { /// The namespace, name, and version information for this package. pub name: PackageName, - /// Kind - pub kind: PackageKind, - /// All worlds from all documents within this package. /// /// Each world lists the document that it is from. diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index d7ef0f4956..a789c09d29 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -80,13 +80,6 @@ pub struct Resolve { pub all_features: bool, } -#[derive(PartialEq, Clone, Debug)] -#[cfg_attr(feature = "serde", derive(Serialize))] -pub enum PackageKind { - Explicit, - Implicit, -} - /// A WIT package within a `Resolve`. /// /// A package is a collection of interfaces and worlds. Packages additionally @@ -98,9 +91,6 @@ pub struct Package { /// A unique name corresponding to this package. pub name: PackageName, - /// Kind - pub kind: PackageKind, - /// Documentation associated with this package. #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, @@ -1398,7 +1388,6 @@ impl Remap { let pkgid = resolve.packages.alloc(Package { name: unresolved.name.clone(), - kind: unresolved.kind.clone(), docs: unresolved.docs.clone(), interfaces: Default::default(), worlds: Default::default(), diff --git a/crates/wit-parser/tests/ui/comments.wit.json b/crates/wit-parser/tests/ui/comments.wit.json index 2f18314f36..f2a04f9d39 100644 --- a/crates/wit-parser/tests/ui/comments.wit.json +++ b/crates/wit-parser/tests/ui/comments.wit.json @@ -40,7 +40,6 @@ "packages": [ { "name": "foo:comments", - "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/complex-include.wit.json b/crates/wit-parser/tests/ui/complex-include.wit.json index 38b0df2172..f9b17f2792 100644 --- a/crates/wit-parser/tests/ui/complex-include.wit.json +++ b/crates/wit-parser/tests/ui/complex-include.wit.json @@ -165,7 +165,6 @@ "packages": [ { "name": "foo:bar", - "kind": "Implicit", "interfaces": { "a": 0, "b": 1 @@ -176,7 +175,6 @@ }, { "name": "foo:baz", - "kind": "Implicit", "interfaces": { "a": 2, "b": 3 @@ -187,7 +185,6 @@ }, { "name": "foo:root", - "kind": "Implicit", "interfaces": { "ai": 4, "bi": 5 diff --git a/crates/wit-parser/tests/ui/cross-package-resource.wit.json b/crates/wit-parser/tests/ui/cross-package-resource.wit.json index f49163415e..51ab92795d 100644 --- a/crates/wit-parser/tests/ui/cross-package-resource.wit.json +++ b/crates/wit-parser/tests/ui/cross-package-resource.wit.json @@ -51,7 +51,6 @@ "packages": [ { "name": "some:dep", - "kind": "Implicit", "interfaces": { "foo": 0 }, @@ -59,7 +58,6 @@ }, { "name": "foo:bar", - "kind": "Implicit", "interfaces": { "foo": 1 }, diff --git a/crates/wit-parser/tests/ui/diamond1.wit.json b/crates/wit-parser/tests/ui/diamond1.wit.json index c524224815..54e87e7b04 100644 --- a/crates/wit-parser/tests/ui/diamond1.wit.json +++ b/crates/wit-parser/tests/ui/diamond1.wit.json @@ -36,7 +36,6 @@ "packages": [ { "name": "foo:dep1", - "kind": "Implicit", "interfaces": { "types": 0 }, @@ -44,7 +43,6 @@ }, { "name": "foo:dep2", - "kind": "Implicit", "interfaces": { "types": 1 }, @@ -52,7 +50,6 @@ }, { "name": "foo:foo", - "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json b/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json index 47bfdbe8a2..34f675da51 100644 --- a/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json +++ b/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json @@ -103,7 +103,6 @@ "packages": [ { "name": "foo:diamond", - "kind": "Implicit", "interfaces": { "shared1": 0, "shared2": 1 diff --git a/crates/wit-parser/tests/ui/empty.wit.json b/crates/wit-parser/tests/ui/empty.wit.json index 00ba7a6102..092abe5e1d 100644 --- a/crates/wit-parser/tests/ui/empty.wit.json +++ b/crates/wit-parser/tests/ui/empty.wit.json @@ -5,7 +5,6 @@ "packages": [ { "name": "foo:empty", - "kind": "Implicit", "interfaces": {}, "worlds": {} } diff --git a/crates/wit-parser/tests/ui/feature-gates.wit.json b/crates/wit-parser/tests/ui/feature-gates.wit.json index 45490954b2..9cdb44c582 100644 --- a/crates/wit-parser/tests/ui/feature-gates.wit.json +++ b/crates/wit-parser/tests/ui/feature-gates.wit.json @@ -291,7 +291,6 @@ "packages": [ { "name": "a:b", - "kind": "Implicit", "interfaces": { "ungated": 0, "ungated2": 1, diff --git a/crates/wit-parser/tests/ui/foreign-deps-union.wit.json b/crates/wit-parser/tests/ui/foreign-deps-union.wit.json index 32cbd8e6fa..419d62e7ce 100644 --- a/crates/wit-parser/tests/ui/foreign-deps-union.wit.json +++ b/crates/wit-parser/tests/ui/foreign-deps-union.wit.json @@ -346,7 +346,6 @@ "packages": [ { "name": "foo:another-pkg", - "kind": "Implicit", "interfaces": { "other-interface": 0 }, @@ -354,7 +353,6 @@ }, { "name": "foo:corp", - "kind": "Implicit", "interfaces": { "saas": 1 }, @@ -362,7 +360,6 @@ }, { "name": "foo:different-pkg", - "kind": "Implicit", "interfaces": { "i": 2 }, @@ -370,7 +367,6 @@ }, { "name": "foo:foreign-pkg", - "kind": "Implicit", "interfaces": { "the-default": 3 }, @@ -378,7 +374,6 @@ }, { "name": "foo:some-pkg", - "kind": "Implicit", "interfaces": { "the-default": 4, "some-interface": 5, @@ -388,7 +383,6 @@ }, { "name": "foo:wasi", - "kind": "Implicit", "interfaces": { "clocks": 7, "filesystem": 8 @@ -399,7 +393,6 @@ }, { "name": "foo:root", - "kind": "Implicit", "interfaces": { "foo": 9, "bar": 10, diff --git a/crates/wit-parser/tests/ui/foreign-deps.wit.json b/crates/wit-parser/tests/ui/foreign-deps.wit.json index 132fcdd301..503c1b32c9 100644 --- a/crates/wit-parser/tests/ui/foreign-deps.wit.json +++ b/crates/wit-parser/tests/ui/foreign-deps.wit.json @@ -301,7 +301,6 @@ "packages": [ { "name": "foo:another-pkg", - "kind": "Implicit", "interfaces": { "other-interface": 0 }, @@ -309,7 +308,6 @@ }, { "name": "foo:corp", - "kind": "Implicit", "interfaces": { "saas": 1 }, @@ -317,7 +315,6 @@ }, { "name": "foo:different-pkg", - "kind": "Implicit", "interfaces": { "i": 2 }, @@ -325,7 +322,6 @@ }, { "name": "foo:foreign-pkg", - "kind": "Implicit", "interfaces": { "the-default": 3 }, @@ -333,7 +329,6 @@ }, { "name": "foo:some-pkg", - "kind": "Implicit", "interfaces": { "the-default": 4, "some-interface": 5, @@ -343,7 +338,6 @@ }, { "name": "foo:wasi", - "kind": "Implicit", "interfaces": { "clocks": 7, "filesystem": 8 @@ -352,7 +346,6 @@ }, { "name": "foo:root", - "kind": "Implicit", "interfaces": { "foo": 9, "bar": 10, diff --git a/crates/wit-parser/tests/ui/functions.wit.json b/crates/wit-parser/tests/ui/functions.wit.json index a5afc8eb43..902fe35709 100644 --- a/crates/wit-parser/tests/ui/functions.wit.json +++ b/crates/wit-parser/tests/ui/functions.wit.json @@ -157,7 +157,6 @@ "packages": [ { "name": "foo:functions", - "kind": "Implicit", "interfaces": { "functions": 0 }, diff --git a/crates/wit-parser/tests/ui/ignore-files-deps.wit.json b/crates/wit-parser/tests/ui/ignore-files-deps.wit.json index 27805e9468..7a812dbecf 100644 --- a/crates/wit-parser/tests/ui/ignore-files-deps.wit.json +++ b/crates/wit-parser/tests/ui/ignore-files-deps.wit.json @@ -25,7 +25,6 @@ "packages": [ { "name": "foo:bar", - "kind": "Implicit", "interfaces": { "types": 0 }, @@ -33,7 +32,6 @@ }, { "name": "foo:foo", - "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/import-export-overlap1.wit.json b/crates/wit-parser/tests/ui/import-export-overlap1.wit.json index 77d0c44838..0c88a177b2 100644 --- a/crates/wit-parser/tests/ui/import-export-overlap1.wit.json +++ b/crates/wit-parser/tests/ui/import-export-overlap1.wit.json @@ -30,7 +30,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/import-export-overlap2.wit.json b/crates/wit-parser/tests/ui/import-export-overlap2.wit.json index 046c5af444..0c991269c5 100644 --- a/crates/wit-parser/tests/ui/import-export-overlap2.wit.json +++ b/crates/wit-parser/tests/ui/import-export-overlap2.wit.json @@ -34,7 +34,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/include-reps.wit.json b/crates/wit-parser/tests/ui/include-reps.wit.json index b77c9a640e..21debef2fe 100644 --- a/crates/wit-parser/tests/ui/include-reps.wit.json +++ b/crates/wit-parser/tests/ui/include-reps.wit.json @@ -55,7 +55,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "a": 0, "b": 1 diff --git a/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json b/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json index 64c15833dd..14c6d0d048 100644 --- a/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json +++ b/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json @@ -59,7 +59,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0, diff --git a/crates/wit-parser/tests/ui/kinds-of-deps.wit.json b/crates/wit-parser/tests/ui/kinds-of-deps.wit.json index d044bdd3ab..f13afe6301 100644 --- a/crates/wit-parser/tests/ui/kinds-of-deps.wit.json +++ b/crates/wit-parser/tests/ui/kinds-of-deps.wit.json @@ -58,7 +58,6 @@ "packages": [ { "name": "d:d", - "kind": "Implicit", "interfaces": { "d": 0 }, @@ -66,7 +65,6 @@ }, { "name": "e:e", - "kind": "Implicit", "interfaces": { "e": 1 }, @@ -74,7 +72,6 @@ }, { "name": "b:b", - "kind": "Implicit", "interfaces": { "b": 2 }, @@ -82,7 +79,6 @@ }, { "name": "c:c", - "kind": "Implicit", "interfaces": { "c": 3 }, @@ -90,7 +86,6 @@ }, { "name": "a:a", - "kind": "Implicit", "interfaces": {}, "worlds": { "a": 0 diff --git a/crates/wit-parser/tests/ui/many-names.wit.json b/crates/wit-parser/tests/ui/many-names.wit.json index 1c47f69f77..590c68471a 100644 --- a/crates/wit-parser/tests/ui/many-names.wit.json +++ b/crates/wit-parser/tests/ui/many-names.wit.json @@ -31,7 +31,6 @@ "packages": [ { "name": "foo:name", - "kind": "Implicit", "interfaces": { "x": 0 }, diff --git a/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json b/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json index a20a0c78ec..a2dea31719 100644 --- a/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json +++ b/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json @@ -212,7 +212,6 @@ "packages": [ { "name": "bar:name", - "kind": "Explicit", "interfaces": { "i2": 0 }, @@ -222,7 +221,6 @@ }, { "name": "baz:name", - "kind": "Explicit", "interfaces": { "i3": 2 }, @@ -232,7 +230,6 @@ }, { "name": "foo:name", - "kind": "Explicit", "interfaces": { "i1": 4 }, @@ -242,7 +239,6 @@ }, { "name": "qux:name", - "kind": "Explicit", "interfaces": { "i4": 6 }, diff --git a/crates/wit-parser/tests/ui/multi-file.wit.json b/crates/wit-parser/tests/ui/multi-file.wit.json index 6da127c468..45af0f5d32 100644 --- a/crates/wit-parser/tests/ui/multi-file.wit.json +++ b/crates/wit-parser/tests/ui/multi-file.wit.json @@ -283,7 +283,6 @@ "packages": [ { "name": "foo:multi-file", - "kind": "Implicit", "interfaces": { "irrelevant-name": 0, "depend-on-me": 1, diff --git a/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json b/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json index 43cd97987a..d3434da0c5 100644 --- a/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json +++ b/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json @@ -53,7 +53,6 @@ "packages": [ { "name": "foo:dep1", - "kind": "Implicit", "interfaces": { "types": 0 }, @@ -61,7 +60,6 @@ }, { "name": "foo:dep2", - "kind": "Implicit", "interfaces": { "types": 1 }, @@ -69,7 +67,6 @@ }, { "name": "foo:bar", - "kind": "Explicit", "interfaces": {}, "worlds": { "w-bar": 0 @@ -77,7 +74,6 @@ }, { "name": "foo:qux", - "kind": "Explicit", "interfaces": {}, "worlds": { "w-qux": 1 diff --git a/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json b/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json index 0fdb4dd5a9..ece4cdade9 100644 --- a/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json +++ b/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json @@ -86,7 +86,6 @@ "packages": [ { "name": "foo:dep2", - "kind": "Implicit", "interfaces": { "types": 0 }, @@ -94,7 +93,6 @@ }, { "name": "foo:dep1", - "kind": "Implicit", "interfaces": { "types": 1 }, @@ -102,7 +100,6 @@ }, { "name": "foo:bar", - "kind": "Explicit", "interfaces": {}, "worlds": { "w-bar": 0 @@ -110,7 +107,6 @@ }, { "name": "foo:qux", - "kind": "Explicit", "interfaces": {}, "worlds": { "w-qux": 1 diff --git a/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json b/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json index ac93129918..1dad5120b5 100644 --- a/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json +++ b/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json @@ -73,7 +73,6 @@ "packages": [ { "name": "some:dep", - "kind": "Implicit", "interfaces": { "foo": 0 }, @@ -81,7 +80,6 @@ }, { "name": "foo:bar", - "kind": "Implicit", "interfaces": { "foo": 1 }, diff --git a/crates/wit-parser/tests/ui/package-syntax1.wit.json b/crates/wit-parser/tests/ui/package-syntax1.wit.json index 5bf7712909..80af0cbb3b 100644 --- a/crates/wit-parser/tests/ui/package-syntax1.wit.json +++ b/crates/wit-parser/tests/ui/package-syntax1.wit.json @@ -5,7 +5,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": {}, "worlds": {} } diff --git a/crates/wit-parser/tests/ui/package-syntax3.wit.json b/crates/wit-parser/tests/ui/package-syntax3.wit.json index 8ff0a1ea2a..e0323c511b 100644 --- a/crates/wit-parser/tests/ui/package-syntax3.wit.json +++ b/crates/wit-parser/tests/ui/package-syntax3.wit.json @@ -5,7 +5,6 @@ "packages": [ { "name": "foo:bar", - "kind": "Implicit", "interfaces": {}, "worlds": {} } diff --git a/crates/wit-parser/tests/ui/package-syntax4.wit.json b/crates/wit-parser/tests/ui/package-syntax4.wit.json index 280678ddd5..7bacc86f03 100644 --- a/crates/wit-parser/tests/ui/package-syntax4.wit.json +++ b/crates/wit-parser/tests/ui/package-syntax4.wit.json @@ -5,7 +5,6 @@ "packages": [ { "name": "foo:bar@2.0.0", - "kind": "Implicit", "interfaces": {}, "worlds": {} } diff --git a/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json b/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json index 3b925bee34..f4c9e7055c 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json @@ -110,7 +110,6 @@ "packages": [ { "name": "bar:name", - "kind": "Explicit", "interfaces": { "i": 0 }, @@ -120,7 +119,6 @@ }, { "name": "foo:name", - "kind": "Explicit", "interfaces": { "i": 2 }, diff --git a/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json b/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json index 8edc232bd2..1d59a07e3a 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json @@ -71,7 +71,6 @@ "packages": [ { "name": "foo:name", - "kind": "Explicit", "interfaces": { "i1": 0 }, @@ -79,7 +78,6 @@ }, { "name": "bar:name", - "kind": "Explicit", "interfaces": {}, "worlds": { "w1": 0 diff --git a/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json b/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json index fdbd064be1..23a10462aa 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json @@ -110,7 +110,6 @@ "packages": [ { "name": "foo:name@1.0.0", - "kind": "Explicit", "interfaces": { "i1": 0 }, @@ -120,7 +119,6 @@ }, { "name": "foo:name@1.0.1", - "kind": "Explicit", "interfaces": { "i1": 2 }, diff --git a/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json b/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json index c770931d30..de73c51de3 100644 --- a/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json +++ b/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json @@ -110,7 +110,6 @@ "packages": [ { "name": "bar:name", - "kind": "Explicit", "interfaces": { "i2": 0 }, @@ -120,7 +119,6 @@ }, { "name": "foo:name", - "kind": "Explicit", "interfaces": { "i1": 2 }, diff --git a/crates/wit-parser/tests/ui/packages-single-explicit.wit.json b/crates/wit-parser/tests/ui/packages-single-explicit.wit.json index 667eadfdaa..3eefde2810 100644 --- a/crates/wit-parser/tests/ui/packages-single-explicit.wit.json +++ b/crates/wit-parser/tests/ui/packages-single-explicit.wit.json @@ -59,7 +59,6 @@ "packages": [ { "name": "foo:name", - "kind": "Explicit", "interfaces": { "i1": 0 }, diff --git a/crates/wit-parser/tests/ui/random.wit.json b/crates/wit-parser/tests/ui/random.wit.json index 977e824bb1..5d46664671 100644 --- a/crates/wit-parser/tests/ui/random.wit.json +++ b/crates/wit-parser/tests/ui/random.wit.json @@ -55,7 +55,6 @@ "packages": [ { "name": "wasi:random", - "kind": "Implicit", "interfaces": { "random": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-empty.wit.json b/crates/wit-parser/tests/ui/resources-empty.wit.json index 2adbff8138..6c40c61dfc 100644 --- a/crates/wit-parser/tests/ui/resources-empty.wit.json +++ b/crates/wit-parser/tests/ui/resources-empty.wit.json @@ -63,7 +63,6 @@ "packages": [ { "name": "foo:resources-empty", - "kind": "Implicit", "interfaces": { "resources-empty": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json b/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json index f0bd001ecc..e7234f7ed8 100644 --- a/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json +++ b/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json @@ -74,7 +74,6 @@ "packages": [ { "name": "foo:resources1", - "kind": "Implicit", "interfaces": { "resources1": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-multiple.wit.json b/crates/wit-parser/tests/ui/resources-multiple.wit.json index 9a54b96240..2914ab99d9 100644 --- a/crates/wit-parser/tests/ui/resources-multiple.wit.json +++ b/crates/wit-parser/tests/ui/resources-multiple.wit.json @@ -272,7 +272,6 @@ "packages": [ { "name": "foo:resources-multiple", - "kind": "Implicit", "interfaces": { "resources-multiple": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-return-own.wit.json b/crates/wit-parser/tests/ui/resources-return-own.wit.json index 0b2f0103d8..3e44fc106c 100644 --- a/crates/wit-parser/tests/ui/resources-return-own.wit.json +++ b/crates/wit-parser/tests/ui/resources-return-own.wit.json @@ -69,7 +69,6 @@ "packages": [ { "name": "foo:resources1", - "kind": "Implicit", "interfaces": { "resources1": 0 }, diff --git a/crates/wit-parser/tests/ui/resources.wit.json b/crates/wit-parser/tests/ui/resources.wit.json index 28b845fd30..169fb02110 100644 --- a/crates/wit-parser/tests/ui/resources.wit.json +++ b/crates/wit-parser/tests/ui/resources.wit.json @@ -328,7 +328,6 @@ "packages": [ { "name": "foo:bar", - "kind": "Implicit", "interfaces": { "foo": 0, "i": 1 diff --git a/crates/wit-parser/tests/ui/resources1.wit.json b/crates/wit-parser/tests/ui/resources1.wit.json index 1883d6f6ef..447e5ec2b9 100644 --- a/crates/wit-parser/tests/ui/resources1.wit.json +++ b/crates/wit-parser/tests/ui/resources1.wit.json @@ -87,7 +87,6 @@ "packages": [ { "name": "foo:resources1", - "kind": "Implicit", "interfaces": { "resources1": 0 }, diff --git a/crates/wit-parser/tests/ui/same-name-import-export.wit.json b/crates/wit-parser/tests/ui/same-name-import-export.wit.json index a440e633d3..3cf1798d93 100644 --- a/crates/wit-parser/tests/ui/same-name-import-export.wit.json +++ b/crates/wit-parser/tests/ui/same-name-import-export.wit.json @@ -38,7 +38,6 @@ "packages": [ { "name": "demo:greeter", - "kind": "Implicit", "interfaces": {}, "worlds": { "greeter": 0 diff --git a/crates/wit-parser/tests/ui/shared-types.wit.json b/crates/wit-parser/tests/ui/shared-types.wit.json index 7a0875d84e..569f6d3861 100644 --- a/crates/wit-parser/tests/ui/shared-types.wit.json +++ b/crates/wit-parser/tests/ui/shared-types.wit.json @@ -78,7 +78,6 @@ "packages": [ { "name": "foo:shared-items", - "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/simple-wasm-text.wit.json b/crates/wit-parser/tests/ui/simple-wasm-text.wit.json index 5e575b16c1..969c13a506 100644 --- a/crates/wit-parser/tests/ui/simple-wasm-text.wit.json +++ b/crates/wit-parser/tests/ui/simple-wasm-text.wit.json @@ -12,7 +12,6 @@ "packages": [ { "name": "a:b", - "kind": "Implicit", "interfaces": { "x": 0 }, diff --git a/crates/wit-parser/tests/ui/since-and-unstable.wit.json b/crates/wit-parser/tests/ui/since-and-unstable.wit.json index c27dad3e43..31c4fa0c4b 100644 --- a/crates/wit-parser/tests/ui/since-and-unstable.wit.json +++ b/crates/wit-parser/tests/ui/since-and-unstable.wit.json @@ -566,7 +566,6 @@ "packages": [ { "name": "a:b@1.0.1", - "kind": "Implicit", "interfaces": { "foo1": 0, "foo2": 1, diff --git a/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json b/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json index 55d6f68e0b..d77812776b 100644 --- a/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json +++ b/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json @@ -1136,7 +1136,6 @@ "packages": [ { "name": "foo:bar", - "kind": "Implicit", "interfaces": { "i1": 0, "i2": 1, diff --git a/crates/wit-parser/tests/ui/type-then-eof.wit.json b/crates/wit-parser/tests/ui/type-then-eof.wit.json index 85a5688d5e..beb0a7adca 100644 --- a/crates/wit-parser/tests/ui/type-then-eof.wit.json +++ b/crates/wit-parser/tests/ui/type-then-eof.wit.json @@ -23,7 +23,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/types.wit.json b/crates/wit-parser/tests/ui/types.wit.json index d428f7f671..8874e3541d 100644 --- a/crates/wit-parser/tests/ui/types.wit.json +++ b/crates/wit-parser/tests/ui/types.wit.json @@ -765,7 +765,6 @@ "packages": [ { "name": "foo:types", - "kind": "Implicit", "interfaces": { "types": 0 }, diff --git a/crates/wit-parser/tests/ui/union-fuzz-1.wit.json b/crates/wit-parser/tests/ui/union-fuzz-1.wit.json index 705cd46134..31c7c82687 100644 --- a/crates/wit-parser/tests/ui/union-fuzz-1.wit.json +++ b/crates/wit-parser/tests/ui/union-fuzz-1.wit.json @@ -24,7 +24,6 @@ "packages": [ { "name": "foo:bar", - "kind": "Implicit", "interfaces": {}, "worlds": { "xo": 0, diff --git a/crates/wit-parser/tests/ui/union-fuzz-2.wit.json b/crates/wit-parser/tests/ui/union-fuzz-2.wit.json index c722de0429..6d1bc4ffd4 100644 --- a/crates/wit-parser/tests/ui/union-fuzz-2.wit.json +++ b/crates/wit-parser/tests/ui/union-fuzz-2.wit.json @@ -62,7 +62,6 @@ "packages": [ { "name": "foo:bar", - "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0, diff --git a/crates/wit-parser/tests/ui/use-chain.wit.json b/crates/wit-parser/tests/ui/use-chain.wit.json index 824f2e646e..bd942bb8cd 100644 --- a/crates/wit-parser/tests/ui/use-chain.wit.json +++ b/crates/wit-parser/tests/ui/use-chain.wit.json @@ -43,7 +43,6 @@ "packages": [ { "name": "foo:name", - "kind": "Implicit", "interfaces": { "foo": 0, "name": 1 diff --git a/crates/wit-parser/tests/ui/use.wit.json b/crates/wit-parser/tests/ui/use.wit.json index c3dce34734..43cc16b4a7 100644 --- a/crates/wit-parser/tests/ui/use.wit.json +++ b/crates/wit-parser/tests/ui/use.wit.json @@ -157,7 +157,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "bar": 0, "foo": 1, diff --git a/crates/wit-parser/tests/ui/versions.wit.json b/crates/wit-parser/tests/ui/versions.wit.json index daaaf6fec0..febd55be51 100644 --- a/crates/wit-parser/tests/ui/versions.wit.json +++ b/crates/wit-parser/tests/ui/versions.wit.json @@ -68,7 +68,6 @@ "packages": [ { "name": "a:a@1.0.0", - "kind": "Implicit", "interfaces": { "foo": 0 }, @@ -76,7 +75,6 @@ }, { "name": "a:a@2.0.0", - "kind": "Implicit", "interfaces": { "foo": 1 }, @@ -84,7 +82,6 @@ }, { "name": "foo:versions", - "kind": "Implicit", "interfaces": { "foo": 2 }, diff --git a/crates/wit-parser/tests/ui/wasi.wit.json b/crates/wit-parser/tests/ui/wasi.wit.json index c0fa5b6ba3..e784fbd207 100644 --- a/crates/wit-parser/tests/ui/wasi.wit.json +++ b/crates/wit-parser/tests/ui/wasi.wit.json @@ -530,7 +530,6 @@ "packages": [ { "name": "wasi:filesystem", - "kind": "Implicit", "interfaces": { "wasi": 0 }, diff --git a/crates/wit-parser/tests/ui/world-diamond.wit.json b/crates/wit-parser/tests/ui/world-diamond.wit.json index e5155cdc76..6dba45edb9 100644 --- a/crates/wit-parser/tests/ui/world-diamond.wit.json +++ b/crates/wit-parser/tests/ui/world-diamond.wit.json @@ -111,7 +111,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "shared-items": 0, "i1": 1, diff --git a/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json b/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json index 2417a6adae..fffc11557e 100644 --- a/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json +++ b/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json @@ -57,7 +57,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/world-implicit-import1.wit.json b/crates/wit-parser/tests/ui/world-implicit-import1.wit.json index d155847021..708c339b40 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import1.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import1.wit.json @@ -70,7 +70,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/world-implicit-import2.wit.json b/crates/wit-parser/tests/ui/world-implicit-import2.wit.json index d48070dead..97049842a4 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import2.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import2.wit.json @@ -61,7 +61,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/world-implicit-import3.wit.json b/crates/wit-parser/tests/ui/world-implicit-import3.wit.json index 3fd6405789..159bee7738 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import3.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import3.wit.json @@ -62,7 +62,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "foo": 0 }, diff --git a/crates/wit-parser/tests/ui/world-same-fields4.wit.json b/crates/wit-parser/tests/ui/world-same-fields4.wit.json index b7eac1c81b..3be16acf9b 100644 --- a/crates/wit-parser/tests/ui/world-same-fields4.wit.json +++ b/crates/wit-parser/tests/ui/world-same-fields4.wit.json @@ -71,7 +71,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "shared-items": 0 }, diff --git a/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json b/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json index 0c164071ec..b7866aa12d 100644 --- a/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json +++ b/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json @@ -77,7 +77,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": {}, "worlds": { "foo": 0 diff --git a/crates/wit-parser/tests/ui/world-top-level-resources.wit.json b/crates/wit-parser/tests/ui/world-top-level-resources.wit.json index 4ba6711832..794e699f45 100644 --- a/crates/wit-parser/tests/ui/world-top-level-resources.wit.json +++ b/crates/wit-parser/tests/ui/world-top-level-resources.wit.json @@ -225,7 +225,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "types": 0, "handler": 1 diff --git a/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json b/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json index e1d37c8927..d1e24e97b6 100644 --- a/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json +++ b/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json @@ -94,7 +94,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "a1": 0, "a2": 1, diff --git a/crates/wit-parser/tests/ui/worlds-with-types.wit.json b/crates/wit-parser/tests/ui/worlds-with-types.wit.json index fed7ae5e77..70e857d68b 100644 --- a/crates/wit-parser/tests/ui/worlds-with-types.wit.json +++ b/crates/wit-parser/tests/ui/worlds-with-types.wit.json @@ -191,7 +191,6 @@ "packages": [ { "name": "foo:foo", - "kind": "Implicit", "interfaces": { "disambiguate": 0 }, From a13b38762bca254dc56bca6d13f1507d2f418f61 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Thu, 11 Jul 2024 12:31:46 -0500 Subject: [PATCH 11/16] common function for encoding packages --- crates/wit-component/src/encoding/wit/v2.rs | 134 +++++++++++--------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index 7476dfd094..2723f00511 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -53,73 +53,15 @@ impl Encoder<'_> { // roundtripping. let mut names = NameMap::new(); for pkg in self.packages { - let package = &self.resolve.packages[*pkg]; if self.packages.len() > 1 { - let mut sub_encoder = Encoder { + let sub_encoder = Encoder { component: ComponentBuilder::default(), resolve: self.resolve, packages: self.packages, }; - let package_metadata = PackageMetadata::extract(self.resolve, *pkg); - sub_encoder.component.custom_section(&CustomSection { - name: PackageMetadata::SECTION_NAME.into(), - data: package_metadata.encode()?.into(), - }); - for (name, &id) in self.resolve.packages[*pkg].interfaces.iter() { - let component_ty = sub_encoder.encode_interface(id, pkg)?; - let ty = sub_encoder.component.type_component(&component_ty); - sub_encoder.component.export( - name.as_ref(), - ComponentExportKind::Type, - ty, - None, - ); - } - for (name, &world) in self.resolve.packages[*pkg].worlds.iter() { - // Encode the `world` directly as a component, then create a wrapper - // component that exports that component. - let component_ty = super::encode_world(self.resolve, world)?; - - let world = &sub_encoder.resolve.worlds[world]; - let mut wrapper = ComponentType::new(); - wrapper.ty().component(&component_ty); - let pkg = &sub_encoder.resolve.packages[world.package.unwrap()]; - wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); - let ty = sub_encoder.component.type_component(&wrapper); - sub_encoder.component.export( - name.as_ref(), - ComponentExportKind::Type, - ty, - None, - ); - } - let sub = self.component.component(sub_encoder.component); - names.append(sub, &package.name.to_string()); + self.encode_package(&mut names, pkg, Some(sub_encoder))?; } else { - for (name, &id) in self.resolve.packages[*pkg].interfaces.iter() { - let component_ty = self.encode_interface(id, pkg)?; - let ty = self.component.type_component(&component_ty); - self.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); - } - for (name, &world) in self.resolve.packages[*pkg].worlds.iter() { - // Encode the `world` directly as a component, then create a wrapper - // component that exports that component. - let component_ty = super::encode_world(self.resolve, world)?; - let world = &self.resolve.worlds[world]; - let mut wrapper = ComponentType::new(); - wrapper.ty().component(&component_ty); - let pkg = &self.resolve.packages[world.package.unwrap()]; - wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); - let ty = self.component.type_component(&wrapper); - self.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); - } - let package_metadata = PackageMetadata::extract(self.resolve, *pkg); - self.component.custom_section(&CustomSection { - name: PackageMetadata::SECTION_NAME.into(), - data: package_metadata.encode()?.into(), - }); + self.encode_package(&mut names, pkg, None)?; } } let mut final_names = ComponentNameSection::new(); @@ -129,6 +71,76 @@ impl Encoder<'_> { Ok(()) } + fn encode_package( + &mut self, + names: &mut NameMap, + pkg: &PackageId, + mut sub_encoder: Option, + ) -> Result<()> { + let package = &self.resolve.packages[*pkg]; + if let Some(ref mut sub) = sub_encoder { + let package_metadata = PackageMetadata::extract(self.resolve, *pkg); + sub.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); + } + + for (name, &id) in self.resolve.packages[*pkg].interfaces.iter() { + if let Some(ref mut sub) = sub_encoder { + let component_ty = sub.encode_interface(id, pkg)?; + let ty = sub.component.type_component(&component_ty); + sub.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); + } else { + let component_ty = self.encode_interface(id, pkg)?; + let ty = self.component.type_component(&component_ty); + self.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); + } + } + for (name, &world) in self.resolve.packages[*pkg].worlds.iter() { + if let Some(ref mut sub) = sub_encoder { + // Encode the `world` directly as a component, then create a wrapper + // component that exports that component. + let component_ty = super::encode_world(self.resolve, world)?; + + let world = &sub.resolve.worlds[world]; + let mut wrapper = ComponentType::new(); + wrapper.ty().component(&component_ty); + let pkg = &sub.resolve.packages[world.package.unwrap()]; + wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); + let ty = sub.component.type_component(&wrapper); + sub.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); + } else { + // Encode the `world` directly as a component, then create a wrapper + // component that exports that component. + let component_ty = super::encode_world(self.resolve, world)?; + let world = &self.resolve.worlds[world]; + let mut wrapper = ComponentType::new(); + wrapper.ty().component(&component_ty); + let pkg = &self.resolve.packages[world.package.unwrap()]; + wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); + let ty = self.component.type_component(&wrapper); + self.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); + } + } + if sub_encoder.is_none() { + let package_metadata = PackageMetadata::extract(self.resolve, *pkg); + self.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); + } + if let Some(sub_encoder) = sub_encoder { + let sub = self.component.component(sub_encoder.component); + names.append(sub, &package.name.to_string()); + } + Ok(()) + } + fn encode_interface(&mut self, id: InterfaceId, pkg: &PackageId) -> Result { // Build a set of interfaces reachable from this document, including the // interfaces in the document itself. This is used to import instances From d3d2d2c15e50947c6b3fc7ed05770935e57652d3 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Fri, 12 Jul 2024 13:04:31 -0500 Subject: [PATCH 12/16] fix interfaces test assertion --- crates/wit-component/src/encoding/wit/v2.rs | 26 ++++--- crates/wit-component/tests/interfaces.rs | 4 +- crates/wit-parser/src/decoding.rs | 78 ++++++++++++--------- crates/wit-parser/src/metadata.rs | 2 +- 4 files changed, 63 insertions(+), 47 deletions(-) diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index 2723f00511..df6a155208 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -64,6 +64,14 @@ impl Encoder<'_> { self.encode_package(&mut names, pkg, None)?; } } + if self.packages.len() == 1 { + // let pkg = self.packages[0]; + let package_metadata = PackageMetadata::extract(self.resolve, self.packages[0]); + self.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); + } let mut final_names = ComponentNameSection::new(); final_names.components(&names); self.component.names(&final_names); @@ -86,7 +94,7 @@ impl Encoder<'_> { }); } - for (name, &id) in self.resolve.packages[*pkg].interfaces.iter() { + for (name, &id) in package.interfaces.iter() { if let Some(ref mut sub) = sub_encoder { let component_ty = sub.encode_interface(id, pkg)?; let ty = sub.component.type_component(&component_ty); @@ -99,7 +107,7 @@ impl Encoder<'_> { .export(name.as_ref(), ComponentExportKind::Type, ty, None); } } - for (name, &world) in self.resolve.packages[*pkg].worlds.iter() { + for (name, &world) in package.worlds.iter() { if let Some(ref mut sub) = sub_encoder { // Encode the `world` directly as a component, then create a wrapper // component that exports that component. @@ -120,20 +128,16 @@ impl Encoder<'_> { let world = &self.resolve.worlds[world]; let mut wrapper = ComponentType::new(); wrapper.ty().component(&component_ty); - let pkg = &self.resolve.packages[world.package.unwrap()]; - wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); + let package = &self.resolve.packages[world.package.unwrap()]; + wrapper.export( + &package.name.interface_id(name), + ComponentTypeRef::Component(0), + ); let ty = self.component.type_component(&wrapper); self.component .export(name.as_ref(), ComponentExportKind::Type, ty, None); } } - if sub_encoder.is_none() { - let package_metadata = PackageMetadata::extract(self.resolve, *pkg); - self.component.custom_section(&CustomSection { - name: PackageMetadata::SECTION_NAME.into(), - data: package_metadata.encode()?.into(), - }); - } if let Some(sub_encoder) = sub_encoder { let sub = self.component.component(sub_encoder.component); names.append(sub, &package.name.to_string()); diff --git a/crates/wit-component/tests/interfaces.rs b/crates/wit-component/tests/interfaces.rs index 70ab329f80..df857ebb96 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -72,14 +72,14 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { // this package's documents and assert they all match the expectations. let decoded = wit_component::decode(&wasm)?; - let decoded_package = decoded.packages()[0]; + let decoded_package = decoded.packages(); let resolve = decoded.resolve(); assert_print(resolve, decoded.packages(), path, is_dir)?; // Finally convert the decoded package to wasm again and make sure it // matches the prior wasm. - let wasm2 = wit_component::encode(Some(true), resolve, &[decoded_package])?; + let wasm2 = wit_component::encode(Some(true), resolve, &decoded_package)?; if wasm != wasm2 { let wat2 = wasmprinter::print_bytes(&wasm)?; assert_eq!(wat, wat2, "document did not roundtrip correctly"); diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 51385b27d0..801b556402 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -354,9 +354,11 @@ impl ComponentInfo { let mut pkg_name = None; let mut pkg_names = Vec::new(); + let mut interfaces = IndexMap::new(); + let mut worlds = IndexMap::new(); let mut fields = PackageFields { - interfaces: &mut IndexMap::new(), - worlds: &mut IndexMap::new(), + interfaces: &mut interfaces, + worlds: &mut worlds, }; let mut pkg_ids: Vec = Vec::new(); let mut implicit = None; @@ -413,11 +415,18 @@ impl ComponentInfo { let pkg = Package { name, docs: Docs::default(), - interfaces: fields.interfaces.clone(), - worlds: fields.worlds.clone(), + interfaces: interfaces.clone(), + worlds, }; - let (resolve, package) = decoder.finish(pkg); + let (mut resolve, package) = decoder.finish(pkg); implicit = Some(package); + // For now this is a sufficient condition to know that we're working with + // an implicit package declaration. This will need to be reworked when + // mixed package declarations are supported + // if let Some(package) = implicit { + if let Some(package_metadata) = &self.package_metadata { + package_metadata.inject(&mut resolve, package)?; + } resolve } else { Resolve::new() @@ -425,8 +434,12 @@ impl ComponentInfo { for explicit in &self.explicit { let mut cur_decoder = WitPackageDecoder::new(explicit.types.as_ref().unwrap()); - fields.interfaces.clear(); - fields.worlds.clear(); + let mut interfaces = IndexMap::new(); + let mut worlds = IndexMap::new(); + let mut fields = PackageFields { + interfaces: &mut interfaces, + worlds: &mut worlds, + }; pkg_name = None; for (_, item) in explicit.externs.iter() { let export = match item { @@ -469,6 +482,7 @@ impl ComponentInfo { } _ => unreachable!(), }; + if let Some(pkg_name) = pkg_name.as_ref() { // TODO: when we have fully switched to the v2 format, we should switch to parsing // multiple wit documents instead of bailing. @@ -505,15 +519,15 @@ impl ComponentInfo { Package { name: name.clone(), docs: Docs::default(), - interfaces: fields.interfaces.clone(), - worlds: fields.worlds.clone(), + interfaces, + worlds, } } else { Package { name: explicit.name.as_ref().unwrap().clone(), docs: Docs::default(), - interfaces: fields.interfaces.clone(), - worlds: fields.worlds.clone(), + interfaces, + worlds, } }; let (cur_resolve, _) = cur_decoder.finish(pkg); @@ -530,14 +544,7 @@ impl ComponentInfo { } } } - // For now this is a sufficient condition to know that we're working with - // an implicit package declaration. This will need to be reworked when - // mixed package declarations are supported - if let Some(package) = implicit { - if let Some(package_metadata) = &self.package_metadata { - package_metadata.inject(&mut resolve, package)?; - } - } + let packages = if let Some(package) = implicit { vec![package] } else { @@ -733,15 +740,18 @@ pub fn decode_world(wasm: &[u8]) -> Result<(Resolve, WorldId)> { types::ComponentEntityType::Component(ty) => ty, _ => unreachable!(), }; - let mut fields = PackageFields { - interfaces: &mut interfaces, - worlds: &mut worlds, - }; - let name = decoder.decode_world(name, &types[ty], &mut fields)?; + let name = decoder.decode_world( + name, + &types[ty], + &mut PackageFields { + interfaces: &mut interfaces, + worlds: &mut worlds, + }, + )?; let (resolve, pkg) = decoder.finish(Package { name, - interfaces: fields.interfaces.clone(), - worlds: fields.worlds.clone(), + interfaces, + worlds, docs: Default::default(), }); // The package decoded here should only have a single world so extract that @@ -839,7 +849,7 @@ impl WitPackageDecoder<'_> { _ => bail!("component export `{name}` is not an instance or component"), } } - Ok(package.clone()) + Ok(package) } fn decode_interface<'a>( @@ -847,7 +857,7 @@ impl WitPackageDecoder<'_> { name: &str, imports: &wasmparser::collections::IndexMap, ty: &types::ComponentInstanceType, - fields: &mut PackageFields, + fields: &mut PackageFields<'a>, ) -> Result { let component_name = self .parse_component_name(name) @@ -876,7 +886,7 @@ impl WitPackageDecoder<'_> { &mut self, name: &str, ty: &types::ComponentType, - fields: &mut PackageFields, + fields: &mut PackageFields<'a>, ) -> Result { let kebab_name = self .parse_component_name(name) @@ -896,7 +906,7 @@ impl WitPackageDecoder<'_> { &mut self, name: &str, world: WorldId, - package: &mut PackageFields, + package: &mut PackageFields<'a>, ) -> Result<()> { log::debug!("decoding component import `{name}`"); let ty = self.types.component_entity_type_of_import(name).unwrap(); @@ -946,7 +956,7 @@ impl WitPackageDecoder<'_> { &mut self, export: &DecodingExport, world: WorldId, - package: &mut PackageFields, + package: &mut PackageFields<'a>, ) -> Result<()> { let name = &export.name; log::debug!("decoding component export `{name}`"); @@ -1190,7 +1200,7 @@ impl WitPackageDecoder<'_> { &mut self, name: &str, ty: &types::ComponentInstanceType, - package: &mut PackageFields, + package: &mut PackageFields<'a>, ) -> Result<(WorldKey, InterfaceId)> { // If this interface's name is already known then that means this is an // interface that's both imported and exported. Use `register_import` @@ -1331,7 +1341,7 @@ impl WitPackageDecoder<'_> { &mut self, name: &str, ty: &types::ComponentType, - package: &mut PackageFields, + package: &mut PackageFields<'a>, ) -> Result { let name = self .extract_interface_name_from_component_name(name)? @@ -1696,6 +1706,7 @@ impl WitPackageDecoder<'_> { for i in 0..self.foreign_packages.len() { self.visit_package(i, &mut order); } + // Using the topological ordering create a temporary map from // index-in-`foreign_packages` to index-in-`order` let mut idx_to_pos = vec![0; self.foreign_packages.len()]; @@ -1715,6 +1726,7 @@ impl WitPackageDecoder<'_> { for (_idx, (_url, pkg)) in deps { self.insert_package(pkg); } + let id = self.insert_package(package); assert!(self.resolve.worlds.iter().all(|(_, w)| w.package.is_some())); assert!(self diff --git a/crates/wit-parser/src/metadata.rs b/crates/wit-parser/src/metadata.rs index 78554a3ad0..a9a6967d88 100644 --- a/crates/wit-parser/src/metadata.rs +++ b/crates/wit-parser/src/metadata.rs @@ -530,7 +530,7 @@ impl InterfaceMetadata { fn extract(resolve: &Resolve, id: InterfaceId) -> Self { let interface = &resolve.interfaces[id]; - let funcs = interface + let funcs: IndexMap = interface .functions .iter() .map(|(name, func)| (name.to_string(), FunctionMetadata::extract(func))) From 476e3f5581ea2d3b41dfa701461a41a4b21cd4f7 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 15 Jul 2024 13:22:06 -0500 Subject: [PATCH 13/16] simplify decoding --- crates/wit-component/tests/interfaces.rs | 4 +-- .../tests/interfaces/doc-comments.wat | 2 +- .../tests/interfaces/multiple-packages.wat | 15 ++++++++++ .../tests/interfaces/multiple-packages.wit | 8 +++++ .../interfaces/multiple-packages.wit.print | 10 +++++++ .../tests/interfaces/wasi-http.wat | 2 +- crates/wit-parser/src/decoding.rs | 29 ++----------------- crates/wit-parser/src/metadata.rs | 3 +- 8 files changed, 42 insertions(+), 31 deletions(-) diff --git a/crates/wit-component/tests/interfaces.rs b/crates/wit-component/tests/interfaces.rs index 26a2192dde..ac301cf647 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -79,9 +79,9 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { // Finally convert the decoded package to wasm again and make sure it // matches the prior wasm. - let wasm2 = wit_component::encode(Some(true), resolve, &decoded_package)?; + let wasm2 = wit_component::encode(Some(true), resolve, decoded_package)?; if wasm != wasm2 { - let wat2 = wasmprinter::print_bytes(&wasm)?; + let wat2 = wasmprinter::print_bytes(&wasm2)?; assert_eq!(wat, wat2, "document did not roundtrip correctly"); } diff --git a/crates/wit-component/tests/interfaces/doc-comments.wat b/crates/wit-component/tests/interfaces/doc-comments.wat index 6d61c7f477..b043389556 100644 --- a/crates/wit-component/tests/interfaces/doc-comments.wat +++ b/crates/wit-component/tests/interfaces/doc-comments.wat @@ -66,7 +66,7 @@ ) ) (export (;5;) "coverage-world" (type 4)) - (@custom "package-docs" "\00{\22docs\22:\22package docs;\22,\22worlds\22:{\22coverage-world\22:{\22docs\22:\22world docs\22,\22interfaces\22:{\22i\22:{\22docs\22:\22world inline interface docs\22,\22funcs\22:{\22f\22:\22inline interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22inline interface typedef docs\22}}}},\22types\22:{\22t\22:{\22docs\22:\22world typedef docs\22}},\22funcs\22:{\22imp\22:\22world func import docs\22,\22exp\22:\22world func export docs\22}}},\22interfaces\22:{\22coverage-iface\22:{\22docs\22:\22interface docs\22,\22funcs\22:{\22[constructor]res\22:\22constructor docs\22,\22[method]res.m\22:\22method docs\22,\22[static]res.s\22:\22static func docs\22,\22f\22:\22interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22basic typedef docs\22},\22r\22:{\22docs\22:\22record typedef docs\22,\22items\22:{\22f1\22:\22record field docs\22}},\22fl\22:{\22items\22:{\22f1\22:\22flag docs\22}},\22v\22:{\22items\22:{\22c1\22:\22variant case docs\22}},\22e\22:{\22items\22:{\22c1\22:\22enum case docs\22}}}},\22other-comment-forms\22:{\22docs\22:\22other comment forms\5cn multi-line block\22,\22funcs\22:{\22multiple-lines-split\22:\22one doc line\5cnnon-doc in the middle\5cnanother doc line\22,\22mixed-forms\22:\22mixed forms; line doc\5cnplus block doc\5cn multi-line\22}}}}") + (@custom "package-docs" "\00{\22docs\22:\22package docs;\22,\22worlds\22:{\22coverage-world\22:{\22docs\22:\22world docs\22,\22interfaces\22:{\22i\22:{\22docs\22:\22world inline interface docs\22,\22funcs\22:{\22f\22:\22inline interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22inline interface typedef docs\22}}}},\22types\22:{\22t\22:{\22docs\22:\22world typedef docs\22}},\22funcs\22:{\22imp\22:\22world func import docs\22,\22exp\22:\22world func export docs\22}}},\22interfaces\22:{\22coverage-iface\22:{\22docs\22:\22interface docs\22,\22funcs\22:{\22[constructor]res\22:\22constructor docs\22,\22[method]res.m\22:\22method docs\22,\22[static]res.s\22:\22static func docs\22,\22f\22:\22interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22basic typedef docs\22},\22r\22:{\22docs\22:\22record typedef docs\22,\22items\22:{\22f1\22:\22record field docs\22}},\22fl\22:{\22items\22:{\22f1\22:\22flag docs\22}},\22v\22:{\22items\22:{\22c1\22:\22variant case docs\22}},\22e\22:{\22items\22:{\22c1\22:\22enum case docs\22}}}},\22other-comment-forms\22:{\22docs\22:\22other comment forms\5cn multi-line block\22,\22funcs\22:{\22mixed-forms\22:\22mixed forms; line doc\5cnplus block doc\5cn multi-line\22,\22multiple-lines-split\22:\22one doc line\5cnnon-doc in the middle\5cnanother doc line\22}}}}") (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-component/tests/interfaces/multiple-packages.wat b/crates/wit-component/tests/interfaces/multiple-packages.wat index 66cbafbff7..d6e251daae 100644 --- a/crates/wit-component/tests/interfaces/multiple-packages.wat +++ b/crates/wit-component/tests/interfaces/multiple-packages.wat @@ -56,6 +56,21 @@ ) (export (;1;) "this-world" (type 0)) ) + (component $fourth:thing (;3;) + (@custom "package-docs" "\00{}") + (type (;0;) + (component + (type (;0;) + (instance + (type (;0;) (record (field "some" string))) + (export (;1;) "foo" (type (eq 0))) + ) + ) + (export (;0;) "fourth:thing/boo" (instance (type 0))) + ) + ) + (export (;1;) "boo" (type 0)) + ) (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-component/tests/interfaces/multiple-packages.wit b/crates/wit-component/tests/interfaces/multiple-packages.wit index fb9b0e933a..d592f93e55 100644 --- a/crates/wit-component/tests/interfaces/multiple-packages.wit +++ b/crates/wit-component/tests/interfaces/multiple-packages.wit @@ -23,4 +23,12 @@ package third:pkg { foo: string } } +} + +package fourth:thing { + interface boo { + record foo { + some: string + } + } } \ No newline at end of file diff --git a/crates/wit-component/tests/interfaces/multiple-packages.wit.print b/crates/wit-component/tests/interfaces/multiple-packages.wit.print index f992861f81..f825e71fbf 100644 --- a/crates/wit-component/tests/interfaces/multiple-packages.wit.print +++ b/crates/wit-component/tests/interfaces/multiple-packages.wit.print @@ -28,3 +28,13 @@ package foo:bar { import third:pkg/things; } } + + +package fourth:thing { + interface boo { + record foo { + some: string, + } + } + +} diff --git a/crates/wit-component/tests/interfaces/wasi-http.wat b/crates/wit-component/tests/interfaces/wasi-http.wat index e75781c785..43e610f369 100644 --- a/crates/wit-component/tests/interfaces/wasi-http.wat +++ b/crates/wit-component/tests/interfaces/wasi-http.wat @@ -874,7 +874,7 @@ ) ) (export (;7;) "proxy" (type 6)) - (@custom "package-docs" "\00{\22worlds\22:{\22proxy\22:{\22docs\22:\22The `wasi:http/proxy` world captures a widely-implementable intersection of\5cnhosts that includes HTTP forward and reverse proxies. Components targeting\5cnthis world may concurrently stream in and out any number of incoming and\5cnoutgoing HTTP requests.\22}},\22interfaces\22:{\22types\22:{\22docs\22:\22This interface defines all of the types and methods for implementing\5cnHTTP Requests and Responses, both incoming and outgoing, as well as\5cntheir headers, trailers, and bodies.\22,\22funcs\22:{\22http-error-code\22:\22Attempts to extract a http-related `error` from the wasi:io `error`\5cnprovided.\5cn\5cnStream operations which return\5cn`wasi:io/stream/stream-error::last-operation-failed` have a payload of\5cntype `wasi:io/error/error` with more information about the operation\5cnthat failed. This payload can be passed through to this function to see\5cnif there's http-related information about the error to return.\5cn\5cnNote that this function is fallible because not all io-errors are\5cnhttp-related errors.\22,\22[constructor]fields\22:\22Construct an empty HTTP Fields.\5cn\5cnThe resulting `fields` is mutable.\22,\22[static]fields.from-list\22:\22Construct an HTTP Fields.\5cn\5cnThe resulting `fields` is mutable.\5cn\5cnThe list represents each key-value pair in the Fields. Keys\5cnwhich have multiple values are represented by multiple entries in this\5cnlist with the same key.\5cn\5cnThe tuple is a pair of the field key, represented as a string, and\5cnValue, represented as a list of bytes. In a valid Fields, all keys\5cnand values are valid UTF-8 strings. However, values are not always\5cnwell-formed, so they are represented as a raw list of bytes.\5cn\5cnAn error result will be returned if any header or value was\5cnsyntactically invalid, or if a header was forbidden.\22,\22[method]fields.get\22:\22Get all of the values corresponding to a key. If the key is not present\5cnin this `fields`, an empty list is returned. However, if the key is\5cnpresent but empty, this is represented by a list with one or more\5cnempty field-values present.\22,\22[method]fields.has\22:\22Returns `true` when the key is present in this `fields`. If the key is\5cnsyntactically invalid, `false` is returned.\22,\22[method]fields.set\22:\22Set all of the values for a key. Clears any existing values for that\5cnkey, if they have been set.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.delete\22:\22Delete all values for a key. Does nothing if no values for the key\5cnexist.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.append\22:\22Append a value for a key. Does not change or delete any existing\5cnvalues for that key.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.entries\22:\22Retrieve the full set of keys and values in the Fields. Like the\5cnconstructor, the list represents each key-value pair.\5cn\5cnThe outer list represents each key-value pair in the Fields. Keys\5cnwhich have multiple values are represented by multiple entries in this\5cnlist with the same key.\22,\22[method]fields.clone\22:\22Make a deep copy of the Fields. Equivelant in behavior to calling the\5cn`fields` constructor on the return value of `entries`. The resulting\5cn`fields` is mutable.\22,\22[method]incoming-request.method\22:\22Returns the method of the incoming request.\22,\22[method]incoming-request.path-with-query\22:\22Returns the path with query parameters from the request, as a string.\22,\22[method]incoming-request.scheme\22:\22Returns the protocol scheme from the request.\22,\22[method]incoming-request.authority\22:\22Returns the authority from the request, if it was present.\22,\22[method]incoming-request.headers\22:\22Get the `headers` associated with the request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThe `headers` returned are a child resource: it must be dropped before\5cnthe parent `incoming-request` is dropped. Dropping this\5cn`incoming-request` before all children are dropped will trap.\22,\22[method]incoming-request.consume\22:\22Gives the `incoming-body` associated with this request. Will only\5cnreturn success at most once, and subsequent calls will return error.\22,\22[constructor]outgoing-request\22:\22Construct a new `outgoing-request` with a default `method` of `GET`, and\5cn`none` values for `path-with-query`, `scheme`, and `authority`.\5cn\5cn* `headers` is the HTTP Headers for the Request.\5cn\5cnIt is possible to construct, or manipulate with the accessor functions\5cnbelow, an `outgoing-request` with an invalid combination of `scheme`\5cnand `authority`, or `headers` which are not permitted to be sent.\5cnIt is the obligation of the `outgoing-handler.handle` implementation\5cnto reject invalid constructions of `outgoing-request`.\22,\22[method]outgoing-request.body\22:\22Returns the resource corresponding to the outgoing Body for this\5cnRequest.\5cn\5cnReturns success on the first call: the `outgoing-body` resource for\5cnthis `outgoing-request` can be retrieved at most once. Subsequent\5cncalls will return error.\22,\22[method]outgoing-request.method\22:\22Get the Method for the Request.\22,\22[method]outgoing-request.set-method\22:\22Set the Method for the Request. Fails if the string present in a\5cn`method.other` argument is not a syntactically valid method.\22,\22[method]outgoing-request.path-with-query\22:\22Get the combination of the HTTP Path and Query for the Request.\5cnWhen `none`, this represents an empty Path and empty Query.\22,\22[method]outgoing-request.set-path-with-query\22:\22Set the combination of the HTTP Path and Query for the Request.\5cnWhen `none`, this represents an empty Path and empty Query. Fails is the\5cnstring given is not a syntactically valid path and query uri component.\22,\22[method]outgoing-request.scheme\22:\22Get the HTTP Related Scheme for the Request. When `none`, the\5cnimplementation may choose an appropriate default scheme.\22,\22[method]outgoing-request.set-scheme\22:\22Set the HTTP Related Scheme for the Request. When `none`, the\5cnimplementation may choose an appropriate default scheme. Fails if the\5cnstring given is not a syntactically valid uri scheme.\22,\22[method]outgoing-request.authority\22:\22Get the HTTP Authority for the Request. A value of `none` may be used\5cnwith Related Schemes which do not require an Authority. The HTTP and\5cnHTTPS schemes always require an authority.\22,\22[method]outgoing-request.set-authority\22:\22Set the HTTP Authority for the Request. A value of `none` may be used\5cnwith Related Schemes which do not require an Authority. The HTTP and\5cnHTTPS schemes always require an authority. Fails if the string given is\5cnnot a syntactically valid uri authority.\22,\22[method]outgoing-request.headers\22:\22Get the headers associated with the Request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`outgoing-request` is dropped, or its ownership is transfered to\5cnanother component by e.g. `outgoing-handler.handle`.\22,\22[constructor]request-options\22:\22Construct a default `request-options` value.\22,\22[method]request-options.connect-timeout\22:\22The timeout for the initial connect to the HTTP Server.\22,\22[method]request-options.set-connect-timeout\22:\22Set the timeout for the initial connect to the HTTP Server. An error\5cnreturn value indicates that this timeout is not supported.\22,\22[method]request-options.first-byte-timeout\22:\22The timeout for receiving the first byte of the Response body.\22,\22[method]request-options.set-first-byte-timeout\22:\22Set the timeout for receiving the first byte of the Response body. An\5cnerror return value indicates that this timeout is not supported.\22,\22[method]request-options.between-bytes-timeout\22:\22The timeout for receiving subsequent chunks of bytes in the Response\5cnbody stream.\22,\22[method]request-options.set-between-bytes-timeout\22:\22Set the timeout for receiving subsequent chunks of bytes in the Response\5cnbody stream. An error return value indicates that this timeout is not\5cnsupported.\22,\22[static]response-outparam.set\22:\22Set the value of the `response-outparam` to either send a response,\5cnor indicate an error.\5cn\5cnThis method consumes the `response-outparam` to ensure that it is\5cncalled at most once. If it is never called, the implementation\5cnwill respond with an error.\5cn\5cnThe user may provide an `error` to `response` to allow the\5cnimplementation determine how to respond with an HTTP error response.\22,\22[method]incoming-response.status\22:\22Returns the status code from the incoming response.\22,\22[method]incoming-response.headers\22:\22Returns the headers from the incoming response.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`incoming-response` is dropped.\22,\22[method]incoming-response.consume\22:\22Returns the incoming body. May be called at most once. Returns error\5cnif called additional times.\22,\22[method]incoming-body.stream\22:\22Returns the contents of the body, as a stream of bytes.\5cn\5cnReturns success on first call: the stream representing the contents\5cncan be retrieved at most once. Subsequent calls will return error.\5cn\5cnThe returned `input-stream` resource is a child: it must be dropped\5cnbefore the parent `incoming-body` is dropped, or consumed by\5cn`incoming-body.finish`.\5cn\5cnThis invariant ensures that the implementation can determine whether\5cnthe user is consuming the contents of the body, waiting on the\5cn`future-trailers` to be ready, or neither. This allows for network\5cnbackpressure is to be applied when the user is consuming the body,\5cnand for that backpressure to not inhibit delivery of the trailers if\5cnthe user does not read the entire body.\22,\22[static]incoming-body.finish\22:\22Takes ownership of `incoming-body`, and returns a `future-trailers`.\5cnThis function will trap if the `input-stream` child is still alive.\22,\22[method]future-trailers.subscribe\22:\22Returns a pollable which becomes ready when either the trailers have\5cnbeen received, or an error has occured. When this pollable is ready,\5cnthe `get` method will return `some`.\22,\22[method]future-trailers.get\22:\22Returns the contents of the trailers, or an error which occured,\5cnonce the future is ready.\5cn\5cnThe outer `option` represents future readiness. Users can wait on this\5cn`option` to become `some` using the `subscribe` method.\5cn\5cnThe outer `result` is used to retrieve the trailers or error at most\5cnonce. It will be success on the first call in which the outer option\5cnis `some`, and error on subsequent calls.\5cn\5cnThe inner `result` represents that either the HTTP Request or Response\5cnbody, as well as any trailers, were received successfully, or that an\5cnerror occured receiving them. The optional `trailers` indicates whether\5cnor not trailers were present in the body.\5cn\5cnWhen some `trailers` are returned by this method, the `trailers`\5cnresource is immutable, and a child. Use of the `set`, `append`, or\5cn`delete` methods will return an error, and the resource must be\5cndropped before the parent `future-trailers` is dropped.\22,\22[constructor]outgoing-response\22:\22Construct an `outgoing-response`, with a default `status-code` of `200`.\5cnIf a different `status-code` is needed, it must be set via the\5cn`set-status-code` method.\5cn\5cn* `headers` is the HTTP Headers for the Response.\22,\22[method]outgoing-response.status-code\22:\22Get the HTTP Status Code for the Response.\22,\22[method]outgoing-response.set-status-code\22:\22Set the HTTP Status Code for the Response. Fails if the status-code\5cngiven is not a valid http status code.\22,\22[method]outgoing-response.headers\22:\22Get the headers associated with the Request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`outgoing-request` is dropped, or its ownership is transfered to\5cnanother component by e.g. `outgoing-handler.handle`.\22,\22[method]outgoing-response.body\22:\22Returns the resource corresponding to the outgoing Body for this Response.\5cn\5cnReturns success on the first call: the `outgoing-body` resource for\5cnthis `outgoing-response` can be retrieved at most once. Subsequent\5cncalls will return error.\22,\22[method]outgoing-body.write\22:\22Returns a stream for writing the body contents.\5cn\5cnThe returned `output-stream` is a child resource: it must be dropped\5cnbefore the parent `outgoing-body` resource is dropped (or finished),\5cnotherwise the `outgoing-body` drop or `finish` will trap.\5cn\5cnReturns success on the first call: the `output-stream` resource for\5cnthis `outgoing-body` may be retrieved at most once. Subsequent calls\5cnwill return error.\22,\22[static]outgoing-body.finish\22:\22Finalize an outgoing body, optionally providing trailers. This must be\5cncalled to signal that the response is complete. If the `outgoing-body`\5cnis dropped without calling `outgoing-body.finalize`, the implementation\5cnshould treat the body as corrupted.\5cn\5cnFails if the body's `outgoing-request` or `outgoing-response` was\5cnconstructed with a Content-Length header, and the contents written\5cnto the body (via `write`) does not match the value given in the\5cnContent-Length.\22,\22[method]future-incoming-response.subscribe\22:\22Returns a pollable which becomes ready when either the Response has\5cnbeen received, or an error has occured. When this pollable is ready,\5cnthe `get` method will return `some`.\22,\22[method]future-incoming-response.get\22:\22Returns the incoming HTTP Response, or an error, once one is ready.\5cn\5cnThe outer `option` represents future readiness. Users can wait on this\5cn`option` to become `some` using the `subscribe` method.\5cn\5cnThe outer `result` is used to retrieve the response or error at most\5cnonce. It will be success on the first call in which the outer option\5cnis `some`, and error on subsequent calls.\5cn\5cnThe inner `result` represents that either the incoming HTTP Response\5cnstatus and headers have recieved successfully, or that an error\5cnoccured. Errors may also occur while consuming the response body,\5cnbut those will be reported by the `incoming-body` and its\5cn`output-stream` child.\22},\22types\22:{\22method\22:{\22docs\22:\22This type corresponds to HTTP standard Methods.\22},\22scheme\22:{\22docs\22:\22This type corresponds to HTTP standard Related Schemes.\22},\22DNS-error-payload\22:{\22docs\22:\22Defines the case payload type for `DNS-error` above:\22},\22TLS-alert-received-payload\22:{\22docs\22:\22Defines the case payload type for `TLS-alert-received` above:\22},\22field-size-payload\22:{\22docs\22:\22Defines the case payload type for `HTTP-response-{header,trailer}-size` above:\22},\22error-code\22:{\22docs\22:\22These cases are inspired by the IANA HTTP Proxy Error Types:\5cnhttps://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types\22,\22items\22:{\22internal-error\22:\22This is a catch-all error for anything that doesn't fit cleanly into a\5cnmore specific case. It also includes an optional string for an\5cnunstructured description of the error. Users should not depend on the\5cnstring for diagnosing errors, as it's not required to be consistent\5cnbetween implementations.\22}},\22header-error\22:{\22docs\22:\22This type enumerates the different kinds of errors that may occur when\5cnsetting or appending to a `fields` resource.\22,\22items\22:{\22invalid-syntax\22:\22This error indicates that a `field-key` or `field-value` was\5cnsyntactically invalid when used with an operation that sets headers in a\5cn`fields`.\22,\22forbidden\22:\22This error indicates that a forbidden `field-key` was used when trying\5cnto set a header in a `fields`.\22,\22immutable\22:\22This error indicates that the operation on the `fields` was not\5cnpermitted because the fields are immutable.\22}},\22field-key\22:{\22docs\22:\22Field keys are always strings.\22},\22field-value\22:{\22docs\22:\22Field values should always be ASCII strings. However, in\5cnreality, HTTP implementations often have to interpret malformed values,\5cnso they are provided as a list of bytes.\22},\22fields\22:{\22docs\22:\22This following block defines the `fields` resource which corresponds to\5cnHTTP standard Fields. Fields are a common representation used for both\5cnHeaders and Trailers.\5cn\5cnA `fields` may be mutable or immutable. A `fields` created using the\5cnconstructor, `from-list`, or `clone` will be mutable, but a `fields`\5cnresource given by other means (including, but not limited to,\5cn`incoming-request.headers`, `outgoing-request.headers`) might be be\5cnimmutable. In an immutable fields, the `set`, `append`, and `delete`\5cnoperations will fail with `header-error.immutable`.\22},\22headers\22:{\22docs\22:\22Headers is an alias for Fields.\22},\22trailers\22:{\22docs\22:\22Trailers is an alias for Fields.\22},\22incoming-request\22:{\22docs\22:\22Represents an incoming HTTP Request.\22},\22outgoing-request\22:{\22docs\22:\22Represents an outgoing HTTP Request.\22},\22request-options\22:{\22docs\22:\22Parameters for making an HTTP Request. Each of these parameters is\5cncurrently an optional timeout applicable to the transport layer of the\5cnHTTP protocol.\5cn\5cnThese timeouts are separate from any the user may use to bound a\5cnblocking call to `wasi:io/poll.poll`.\22},\22response-outparam\22:{\22docs\22:\22Represents the ability to send an HTTP Response.\5cn\5cnThis resource is used by the `wasi:http/incoming-handler` interface to\5cnallow a Response to be sent corresponding to the Request provided as the\5cnother argument to `incoming-handler.handle`.\22},\22status-code\22:{\22docs\22:\22This type corresponds to the HTTP standard Status Code.\22},\22incoming-response\22:{\22docs\22:\22Represents an incoming HTTP Response.\22},\22incoming-body\22:{\22docs\22:\22Represents an incoming HTTP Request or Response's Body.\5cn\5cnA body has both its contents - a stream of bytes - and a (possibly\5cnempty) set of trailers, indicating that the full contents of the\5cnbody have been received. This resource represents the contents as\5cnan `input-stream` and the delivery of trailers as a `future-trailers`,\5cnand ensures that the user of this interface may only be consuming either\5cnthe body contents or waiting on trailers at any given time.\22},\22future-trailers\22:{\22docs\22:\22Represents a future which may eventaully return trailers, or an error.\5cn\5cnIn the case that the incoming HTTP Request or Response did not have any\5cntrailers, this future will resolve to the empty set of trailers once the\5cncomplete Request or Response body has been received.\22},\22outgoing-response\22:{\22docs\22:\22Represents an outgoing HTTP Response.\22},\22outgoing-body\22:{\22docs\22:\22Represents an outgoing HTTP Request or Response's Body.\5cn\5cnA body has both its contents - a stream of bytes - and a (possibly\5cnempty) set of trailers, inducating the full contents of the body\5cnhave been sent. This resource represents the contents as an\5cn`output-stream` child resource, and the completion of the body (with\5cnoptional trailers) with a static function that consumes the\5cn`outgoing-body` resource, and ensures that the user of this interface\5cnmay not write to the body contents after the body has been finished.\5cn\5cnIf the user code drops this resource, as opposed to calling the static\5cnmethod `finish`, the implementation should treat the body as incomplete,\5cnand that an error has occured. The implementation should propogate this\5cnerror to the HTTP protocol by whatever means it has available,\5cnincluding: corrupting the body on the wire, aborting the associated\5cnRequest, or sending a late status code for the Response.\22},\22future-incoming-response\22:{\22docs\22:\22Represents a future which may eventaully return an incoming HTTP\5cnResponse, or an error.\5cn\5cnThis resource is returned by the `wasi:http/outgoing-handler` interface to\5cnprovide the HTTP Response corresponding to the sent Request.\22}}},\22incoming-handler\22:{\22docs\22:\22This interface defines a handler of incoming HTTP Requests. It should\5cnbe exported by components which can respond to HTTP Requests.\22,\22funcs\22:{\22handle\22:\22This function is invoked with an incoming HTTP Request, and a resource\5cn`response-outparam` which provides the capability to reply with an HTTP\5cnResponse. The response is sent by calling the `response-outparam.set`\5cnmethod, which allows execution to continue after the response has been\5cnsent. This enables both streaming to the response body, and performing other\5cnwork.\5cn\5cnThe implementor of this function must write a response to the\5cn`response-outparam` before returning, or else the caller will respond\5cnwith an error on its behalf.\22}},\22outgoing-handler\22:{\22docs\22:\22This interface defines a handler of outgoing HTTP Requests. It should be\5cnimported by components which wish to make HTTP Requests.\22,\22funcs\22:{\22handle\22:\22This function is invoked with an outgoing HTTP Request, and it returns\5cna resource `future-incoming-response` which represents an HTTP Response\5cnwhich may arrive in the future.\5cn\5cnThe `options` argument accepts optional parameters for the HTTP\5cnprotocol's transport layer.\5cn\5cnThis function may return an error if the `outgoing-request` is invalid\5cnor not allowed to be made. Otherwise, protocol errors are reported\5cnthrough the `future-incoming-response`.\22}}}}") + (@custom "package-docs" "\00{\22worlds\22:{\22proxy\22:{\22docs\22:\22The `wasi:http/proxy` world captures a widely-implementable intersection of\5cnhosts that includes HTTP forward and reverse proxies. Components targeting\5cnthis world may concurrently stream in and out any number of incoming and\5cnoutgoing HTTP requests.\22}},\22interfaces\22:{\22types\22:{\22docs\22:\22This interface defines all of the types and methods for implementing\5cnHTTP Requests and Responses, both incoming and outgoing, as well as\5cntheir headers, trailers, and bodies.\22,\22funcs\22:{\22[constructor]fields\22:\22Construct an empty HTTP Fields.\5cn\5cnThe resulting `fields` is mutable.\22,\22[constructor]outgoing-request\22:\22Construct a new `outgoing-request` with a default `method` of `GET`, and\5cn`none` values for `path-with-query`, `scheme`, and `authority`.\5cn\5cn* `headers` is the HTTP Headers for the Request.\5cn\5cnIt is possible to construct, or manipulate with the accessor functions\5cnbelow, an `outgoing-request` with an invalid combination of `scheme`\5cnand `authority`, or `headers` which are not permitted to be sent.\5cnIt is the obligation of the `outgoing-handler.handle` implementation\5cnto reject invalid constructions of `outgoing-request`.\22,\22[constructor]outgoing-response\22:\22Construct an `outgoing-response`, with a default `status-code` of `200`.\5cnIf a different `status-code` is needed, it must be set via the\5cn`set-status-code` method.\5cn\5cn* `headers` is the HTTP Headers for the Response.\22,\22[constructor]request-options\22:\22Construct a default `request-options` value.\22,\22[method]fields.append\22:\22Append a value for a key. Does not change or delete any existing\5cnvalues for that key.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.clone\22:\22Make a deep copy of the Fields. Equivelant in behavior to calling the\5cn`fields` constructor on the return value of `entries`. The resulting\5cn`fields` is mutable.\22,\22[method]fields.delete\22:\22Delete all values for a key. Does nothing if no values for the key\5cnexist.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.entries\22:\22Retrieve the full set of keys and values in the Fields. Like the\5cnconstructor, the list represents each key-value pair.\5cn\5cnThe outer list represents each key-value pair in the Fields. Keys\5cnwhich have multiple values are represented by multiple entries in this\5cnlist with the same key.\22,\22[method]fields.get\22:\22Get all of the values corresponding to a key. If the key is not present\5cnin this `fields`, an empty list is returned. However, if the key is\5cnpresent but empty, this is represented by a list with one or more\5cnempty field-values present.\22,\22[method]fields.has\22:\22Returns `true` when the key is present in this `fields`. If the key is\5cnsyntactically invalid, `false` is returned.\22,\22[method]fields.set\22:\22Set all of the values for a key. Clears any existing values for that\5cnkey, if they have been set.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]future-incoming-response.get\22:\22Returns the incoming HTTP Response, or an error, once one is ready.\5cn\5cnThe outer `option` represents future readiness. Users can wait on this\5cn`option` to become `some` using the `subscribe` method.\5cn\5cnThe outer `result` is used to retrieve the response or error at most\5cnonce. It will be success on the first call in which the outer option\5cnis `some`, and error on subsequent calls.\5cn\5cnThe inner `result` represents that either the incoming HTTP Response\5cnstatus and headers have recieved successfully, or that an error\5cnoccured. Errors may also occur while consuming the response body,\5cnbut those will be reported by the `incoming-body` and its\5cn`output-stream` child.\22,\22[method]future-incoming-response.subscribe\22:\22Returns a pollable which becomes ready when either the Response has\5cnbeen received, or an error has occured. When this pollable is ready,\5cnthe `get` method will return `some`.\22,\22[method]future-trailers.get\22:\22Returns the contents of the trailers, or an error which occured,\5cnonce the future is ready.\5cn\5cnThe outer `option` represents future readiness. Users can wait on this\5cn`option` to become `some` using the `subscribe` method.\5cn\5cnThe outer `result` is used to retrieve the trailers or error at most\5cnonce. It will be success on the first call in which the outer option\5cnis `some`, and error on subsequent calls.\5cn\5cnThe inner `result` represents that either the HTTP Request or Response\5cnbody, as well as any trailers, were received successfully, or that an\5cnerror occured receiving them. The optional `trailers` indicates whether\5cnor not trailers were present in the body.\5cn\5cnWhen some `trailers` are returned by this method, the `trailers`\5cnresource is immutable, and a child. Use of the `set`, `append`, or\5cn`delete` methods will return an error, and the resource must be\5cndropped before the parent `future-trailers` is dropped.\22,\22[method]future-trailers.subscribe\22:\22Returns a pollable which becomes ready when either the trailers have\5cnbeen received, or an error has occured. When this pollable is ready,\5cnthe `get` method will return `some`.\22,\22[method]incoming-body.stream\22:\22Returns the contents of the body, as a stream of bytes.\5cn\5cnReturns success on first call: the stream representing the contents\5cncan be retrieved at most once. Subsequent calls will return error.\5cn\5cnThe returned `input-stream` resource is a child: it must be dropped\5cnbefore the parent `incoming-body` is dropped, or consumed by\5cn`incoming-body.finish`.\5cn\5cnThis invariant ensures that the implementation can determine whether\5cnthe user is consuming the contents of the body, waiting on the\5cn`future-trailers` to be ready, or neither. This allows for network\5cnbackpressure is to be applied when the user is consuming the body,\5cnand for that backpressure to not inhibit delivery of the trailers if\5cnthe user does not read the entire body.\22,\22[method]incoming-request.authority\22:\22Returns the authority from the request, if it was present.\22,\22[method]incoming-request.consume\22:\22Gives the `incoming-body` associated with this request. Will only\5cnreturn success at most once, and subsequent calls will return error.\22,\22[method]incoming-request.headers\22:\22Get the `headers` associated with the request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThe `headers` returned are a child resource: it must be dropped before\5cnthe parent `incoming-request` is dropped. Dropping this\5cn`incoming-request` before all children are dropped will trap.\22,\22[method]incoming-request.method\22:\22Returns the method of the incoming request.\22,\22[method]incoming-request.path-with-query\22:\22Returns the path with query parameters from the request, as a string.\22,\22[method]incoming-request.scheme\22:\22Returns the protocol scheme from the request.\22,\22[method]incoming-response.consume\22:\22Returns the incoming body. May be called at most once. Returns error\5cnif called additional times.\22,\22[method]incoming-response.headers\22:\22Returns the headers from the incoming response.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`incoming-response` is dropped.\22,\22[method]incoming-response.status\22:\22Returns the status code from the incoming response.\22,\22[method]outgoing-body.write\22:\22Returns a stream for writing the body contents.\5cn\5cnThe returned `output-stream` is a child resource: it must be dropped\5cnbefore the parent `outgoing-body` resource is dropped (or finished),\5cnotherwise the `outgoing-body` drop or `finish` will trap.\5cn\5cnReturns success on the first call: the `output-stream` resource for\5cnthis `outgoing-body` may be retrieved at most once. Subsequent calls\5cnwill return error.\22,\22[method]outgoing-request.authority\22:\22Get the HTTP Authority for the Request. A value of `none` may be used\5cnwith Related Schemes which do not require an Authority. The HTTP and\5cnHTTPS schemes always require an authority.\22,\22[method]outgoing-request.body\22:\22Returns the resource corresponding to the outgoing Body for this\5cnRequest.\5cn\5cnReturns success on the first call: the `outgoing-body` resource for\5cnthis `outgoing-request` can be retrieved at most once. Subsequent\5cncalls will return error.\22,\22[method]outgoing-request.headers\22:\22Get the headers associated with the Request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`outgoing-request` is dropped, or its ownership is transfered to\5cnanother component by e.g. `outgoing-handler.handle`.\22,\22[method]outgoing-request.method\22:\22Get the Method for the Request.\22,\22[method]outgoing-request.path-with-query\22:\22Get the combination of the HTTP Path and Query for the Request.\5cnWhen `none`, this represents an empty Path and empty Query.\22,\22[method]outgoing-request.scheme\22:\22Get the HTTP Related Scheme for the Request. When `none`, the\5cnimplementation may choose an appropriate default scheme.\22,\22[method]outgoing-request.set-authority\22:\22Set the HTTP Authority for the Request. A value of `none` may be used\5cnwith Related Schemes which do not require an Authority. The HTTP and\5cnHTTPS schemes always require an authority. Fails if the string given is\5cnnot a syntactically valid uri authority.\22,\22[method]outgoing-request.set-method\22:\22Set the Method for the Request. Fails if the string present in a\5cn`method.other` argument is not a syntactically valid method.\22,\22[method]outgoing-request.set-path-with-query\22:\22Set the combination of the HTTP Path and Query for the Request.\5cnWhen `none`, this represents an empty Path and empty Query. Fails is the\5cnstring given is not a syntactically valid path and query uri component.\22,\22[method]outgoing-request.set-scheme\22:\22Set the HTTP Related Scheme for the Request. When `none`, the\5cnimplementation may choose an appropriate default scheme. Fails if the\5cnstring given is not a syntactically valid uri scheme.\22,\22[method]outgoing-response.body\22:\22Returns the resource corresponding to the outgoing Body for this Response.\5cn\5cnReturns success on the first call: the `outgoing-body` resource for\5cnthis `outgoing-response` can be retrieved at most once. Subsequent\5cncalls will return error.\22,\22[method]outgoing-response.headers\22:\22Get the headers associated with the Request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`outgoing-request` is dropped, or its ownership is transfered to\5cnanother component by e.g. `outgoing-handler.handle`.\22,\22[method]outgoing-response.set-status-code\22:\22Set the HTTP Status Code for the Response. Fails if the status-code\5cngiven is not a valid http status code.\22,\22[method]outgoing-response.status-code\22:\22Get the HTTP Status Code for the Response.\22,\22[method]request-options.between-bytes-timeout\22:\22The timeout for receiving subsequent chunks of bytes in the Response\5cnbody stream.\22,\22[method]request-options.connect-timeout\22:\22The timeout for the initial connect to the HTTP Server.\22,\22[method]request-options.first-byte-timeout\22:\22The timeout for receiving the first byte of the Response body.\22,\22[method]request-options.set-between-bytes-timeout\22:\22Set the timeout for receiving subsequent chunks of bytes in the Response\5cnbody stream. An error return value indicates that this timeout is not\5cnsupported.\22,\22[method]request-options.set-connect-timeout\22:\22Set the timeout for the initial connect to the HTTP Server. An error\5cnreturn value indicates that this timeout is not supported.\22,\22[method]request-options.set-first-byte-timeout\22:\22Set the timeout for receiving the first byte of the Response body. An\5cnerror return value indicates that this timeout is not supported.\22,\22[static]fields.from-list\22:\22Construct an HTTP Fields.\5cn\5cnThe resulting `fields` is mutable.\5cn\5cnThe list represents each key-value pair in the Fields. Keys\5cnwhich have multiple values are represented by multiple entries in this\5cnlist with the same key.\5cn\5cnThe tuple is a pair of the field key, represented as a string, and\5cnValue, represented as a list of bytes. In a valid Fields, all keys\5cnand values are valid UTF-8 strings. However, values are not always\5cnwell-formed, so they are represented as a raw list of bytes.\5cn\5cnAn error result will be returned if any header or value was\5cnsyntactically invalid, or if a header was forbidden.\22,\22[static]incoming-body.finish\22:\22Takes ownership of `incoming-body`, and returns a `future-trailers`.\5cnThis function will trap if the `input-stream` child is still alive.\22,\22[static]outgoing-body.finish\22:\22Finalize an outgoing body, optionally providing trailers. This must be\5cncalled to signal that the response is complete. If the `outgoing-body`\5cnis dropped without calling `outgoing-body.finalize`, the implementation\5cnshould treat the body as corrupted.\5cn\5cnFails if the body's `outgoing-request` or `outgoing-response` was\5cnconstructed with a Content-Length header, and the contents written\5cnto the body (via `write`) does not match the value given in the\5cnContent-Length.\22,\22[static]response-outparam.set\22:\22Set the value of the `response-outparam` to either send a response,\5cnor indicate an error.\5cn\5cnThis method consumes the `response-outparam` to ensure that it is\5cncalled at most once. If it is never called, the implementation\5cnwill respond with an error.\5cn\5cnThe user may provide an `error` to `response` to allow the\5cnimplementation determine how to respond with an HTTP error response.\22,\22http-error-code\22:\22Attempts to extract a http-related `error` from the wasi:io `error`\5cnprovided.\5cn\5cnStream operations which return\5cn`wasi:io/stream/stream-error::last-operation-failed` have a payload of\5cntype `wasi:io/error/error` with more information about the operation\5cnthat failed. This payload can be passed through to this function to see\5cnif there's http-related information about the error to return.\5cn\5cnNote that this function is fallible because not all io-errors are\5cnhttp-related errors.\22},\22types\22:{\22method\22:{\22docs\22:\22This type corresponds to HTTP standard Methods.\22},\22scheme\22:{\22docs\22:\22This type corresponds to HTTP standard Related Schemes.\22},\22DNS-error-payload\22:{\22docs\22:\22Defines the case payload type for `DNS-error` above:\22},\22TLS-alert-received-payload\22:{\22docs\22:\22Defines the case payload type for `TLS-alert-received` above:\22},\22field-size-payload\22:{\22docs\22:\22Defines the case payload type for `HTTP-response-{header,trailer}-size` above:\22},\22error-code\22:{\22docs\22:\22These cases are inspired by the IANA HTTP Proxy Error Types:\5cnhttps://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types\22,\22items\22:{\22internal-error\22:\22This is a catch-all error for anything that doesn't fit cleanly into a\5cnmore specific case. It also includes an optional string for an\5cnunstructured description of the error. Users should not depend on the\5cnstring for diagnosing errors, as it's not required to be consistent\5cnbetween implementations.\22}},\22header-error\22:{\22docs\22:\22This type enumerates the different kinds of errors that may occur when\5cnsetting or appending to a `fields` resource.\22,\22items\22:{\22invalid-syntax\22:\22This error indicates that a `field-key` or `field-value` was\5cnsyntactically invalid when used with an operation that sets headers in a\5cn`fields`.\22,\22forbidden\22:\22This error indicates that a forbidden `field-key` was used when trying\5cnto set a header in a `fields`.\22,\22immutable\22:\22This error indicates that the operation on the `fields` was not\5cnpermitted because the fields are immutable.\22}},\22field-key\22:{\22docs\22:\22Field keys are always strings.\22},\22field-value\22:{\22docs\22:\22Field values should always be ASCII strings. However, in\5cnreality, HTTP implementations often have to interpret malformed values,\5cnso they are provided as a list of bytes.\22},\22fields\22:{\22docs\22:\22This following block defines the `fields` resource which corresponds to\5cnHTTP standard Fields. Fields are a common representation used for both\5cnHeaders and Trailers.\5cn\5cnA `fields` may be mutable or immutable. A `fields` created using the\5cnconstructor, `from-list`, or `clone` will be mutable, but a `fields`\5cnresource given by other means (including, but not limited to,\5cn`incoming-request.headers`, `outgoing-request.headers`) might be be\5cnimmutable. In an immutable fields, the `set`, `append`, and `delete`\5cnoperations will fail with `header-error.immutable`.\22},\22headers\22:{\22docs\22:\22Headers is an alias for Fields.\22},\22trailers\22:{\22docs\22:\22Trailers is an alias for Fields.\22},\22incoming-request\22:{\22docs\22:\22Represents an incoming HTTP Request.\22},\22outgoing-request\22:{\22docs\22:\22Represents an outgoing HTTP Request.\22},\22request-options\22:{\22docs\22:\22Parameters for making an HTTP Request. Each of these parameters is\5cncurrently an optional timeout applicable to the transport layer of the\5cnHTTP protocol.\5cn\5cnThese timeouts are separate from any the user may use to bound a\5cnblocking call to `wasi:io/poll.poll`.\22},\22response-outparam\22:{\22docs\22:\22Represents the ability to send an HTTP Response.\5cn\5cnThis resource is used by the `wasi:http/incoming-handler` interface to\5cnallow a Response to be sent corresponding to the Request provided as the\5cnother argument to `incoming-handler.handle`.\22},\22status-code\22:{\22docs\22:\22This type corresponds to the HTTP standard Status Code.\22},\22incoming-response\22:{\22docs\22:\22Represents an incoming HTTP Response.\22},\22incoming-body\22:{\22docs\22:\22Represents an incoming HTTP Request or Response's Body.\5cn\5cnA body has both its contents - a stream of bytes - and a (possibly\5cnempty) set of trailers, indicating that the full contents of the\5cnbody have been received. This resource represents the contents as\5cnan `input-stream` and the delivery of trailers as a `future-trailers`,\5cnand ensures that the user of this interface may only be consuming either\5cnthe body contents or waiting on trailers at any given time.\22},\22future-trailers\22:{\22docs\22:\22Represents a future which may eventaully return trailers, or an error.\5cn\5cnIn the case that the incoming HTTP Request or Response did not have any\5cntrailers, this future will resolve to the empty set of trailers once the\5cncomplete Request or Response body has been received.\22},\22outgoing-response\22:{\22docs\22:\22Represents an outgoing HTTP Response.\22},\22outgoing-body\22:{\22docs\22:\22Represents an outgoing HTTP Request or Response's Body.\5cn\5cnA body has both its contents - a stream of bytes - and a (possibly\5cnempty) set of trailers, inducating the full contents of the body\5cnhave been sent. This resource represents the contents as an\5cn`output-stream` child resource, and the completion of the body (with\5cnoptional trailers) with a static function that consumes the\5cn`outgoing-body` resource, and ensures that the user of this interface\5cnmay not write to the body contents after the body has been finished.\5cn\5cnIf the user code drops this resource, as opposed to calling the static\5cnmethod `finish`, the implementation should treat the body as incomplete,\5cnand that an error has occured. The implementation should propogate this\5cnerror to the HTTP protocol by whatever means it has available,\5cnincluding: corrupting the body on the wire, aborting the associated\5cnRequest, or sending a late status code for the Response.\22},\22future-incoming-response\22:{\22docs\22:\22Represents a future which may eventaully return an incoming HTTP\5cnResponse, or an error.\5cn\5cnThis resource is returned by the `wasi:http/outgoing-handler` interface to\5cnprovide the HTTP Response corresponding to the sent Request.\22}}},\22incoming-handler\22:{\22docs\22:\22This interface defines a handler of incoming HTTP Requests. It should\5cnbe exported by components which can respond to HTTP Requests.\22,\22funcs\22:{\22handle\22:\22This function is invoked with an incoming HTTP Request, and a resource\5cn`response-outparam` which provides the capability to reply with an HTTP\5cnResponse. The response is sent by calling the `response-outparam.set`\5cnmethod, which allows execution to continue after the response has been\5cnsent. This enables both streaming to the response body, and performing other\5cnwork.\5cn\5cnThe implementor of this function must write a response to the\5cn`response-outparam` before returning, or else the caller will respond\5cnwith an error on its behalf.\22}},\22outgoing-handler\22:{\22docs\22:\22This interface defines a handler of outgoing HTTP Requests. It should be\5cnimported by components which wish to make HTTP Requests.\22,\22funcs\22:{\22handle\22:\22This function is invoked with an outgoing HTTP Request, and it returns\5cna resource `future-incoming-response` which represents an HTTP Response\5cnwhich may arrive in the future.\5cn\5cnThe `options` argument accepts optional parameters for the HTTP\5cnprotocol's transport layer.\5cn\5cnThis function may return an error if the `outgoing-request` is invalid\5cnor not allowed to be made. Otherwise, protocol errors are reported\5cnthrough the `future-incoming-response`.\22}}}}") (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 801b556402..d339968068 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -352,7 +352,6 @@ impl ComponentInfo { let mut decoder = WitPackageDecoder::new(&self.types); let mut pkg_name = None; - let mut pkg_names = Vec::new(); let mut interfaces = IndexMap::new(); let mut worlds = IndexMap::new(); @@ -360,7 +359,6 @@ impl ComponentInfo { interfaces: &mut interfaces, worlds: &mut worlds, }; - let mut pkg_ids: Vec = Vec::new(); let mut implicit = None; for (_, item) in self.externs.iter() { let export = match item { @@ -400,6 +398,7 @@ impl ComponentInfo { } _ => unreachable!(), }; + if let Some(pkg_name) = pkg_name.as_ref() { // TODO: when we have fully switched to the v2 format, we should switch to parsing // multiple wit documents instead of bailing. @@ -492,28 +491,6 @@ impl ComponentInfo { } else { pkg_name.replace(name.clone()); } - if !pkg_names.contains(&name) { - pkg_names.push(name.clone()); - let pkg = Package { - name: name.clone(), - docs: Docs::default(), - interfaces: fields.interfaces.clone(), - worlds: fields.worlds.clone(), - }; - if !pkg_ids.contains(&pkg) { - pkg_ids.push(pkg); - } - } else { - let pkg = Package { - name: name.clone(), - docs: Docs::default(), - interfaces: fields.interfaces.clone(), - worlds: fields.worlds.clone(), - }; - let pkg_id = pkg_ids.iter_mut().find(|p| p.name == pkg.name).unwrap(); - pkg_id.interfaces = pkg.interfaces; - pkg_id.worlds = pkg.worlds; - }; } let pkg = if let Some(name) = pkg_name { Package { @@ -534,11 +511,11 @@ impl ComponentInfo { resolve.merge(cur_resolve)?; } - let copy = resolve.clone(); + let package_names = resolve.package_names.clone(); for package in &self.explicit { if let Some(package_metadata) = &package.package_metadata { let name = package.name.as_ref().unwrap(); - let pkg = copy.package_names.get(&name.clone()); + let pkg = package_names.get(&name.clone()); if let Some(pkg) = pkg { package_metadata.inject(&mut resolve, *pkg)?; } diff --git a/crates/wit-parser/src/metadata.rs b/crates/wit-parser/src/metadata.rs index 78554a3ad0..be49258e03 100644 --- a/crates/wit-parser/src/metadata.rs +++ b/crates/wit-parser/src/metadata.rs @@ -530,12 +530,13 @@ impl InterfaceMetadata { fn extract(resolve: &Resolve, id: InterfaceId) -> Self { let interface = &resolve.interfaces[id]; - let funcs = interface + let mut funcs: IndexMap = interface .functions .iter() .map(|(name, func)| (name.to_string(), FunctionMetadata::extract(func))) .filter(|(_, item)| !item.is_empty()) .collect(); + funcs.sort_keys(); let types = interface .types .iter() From cde809ea6051dd794ce78db9f43f7bf4edddf47b Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 15 Jul 2024 15:39:38 -0500 Subject: [PATCH 14/16] combine ComponentInfo with ExplicitPackageInfo and dont pass sub_encoder around --- ci/docker/x86_64-linux/Dockerfile | 2 +- crates/wit-component/src/encoding/wit/v2.rs | 83 ++++------ crates/wit-parser/src/decoding.rs | 161 +++++++------------- 3 files changed, 86 insertions(+), 160 deletions(-) diff --git a/ci/docker/x86_64-linux/Dockerfile b/ci/docker/x86_64-linux/Dockerfile index 2cb8385d85..7b4ac6b2e3 100644 --- a/ci/docker/x86_64-linux/Dockerfile +++ b/ci/docker/x86_64-linux/Dockerfile @@ -2,4 +2,4 @@ FROM almalinux:8 RUN dnf install -y git gcc -ENV PATH=$PATH:/rust/bin \ No newline at end of file +ENV PATH=$PATH:/rust/bin diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index df6a155208..ce49f29c83 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -54,18 +54,20 @@ impl Encoder<'_> { let mut names = NameMap::new(); for pkg in self.packages { if self.packages.len() > 1 { - let sub_encoder = Encoder { + let mut sub_encoder = Encoder { component: ComponentBuilder::default(), resolve: self.resolve, packages: self.packages, }; - self.encode_package(&mut names, pkg, Some(sub_encoder))?; + sub_encoder.encode_package(true, pkg)?; + let name = &self.resolve.packages[*pkg]; + let sub = self.component.component(sub_encoder.component); + names.append(sub, &name.name.to_string()); } else { - self.encode_package(&mut names, pkg, None)?; + self.encode_package(false, pkg)?; } } if self.packages.len() == 1 { - // let pkg = self.packages[0]; let package_metadata = PackageMetadata::extract(self.resolve, self.packages[0]); self.component.custom_section(&CustomSection { name: PackageMetadata::SECTION_NAME.into(), @@ -79,68 +81,37 @@ impl Encoder<'_> { Ok(()) } - fn encode_package( - &mut self, - names: &mut NameMap, - pkg: &PackageId, - mut sub_encoder: Option, - ) -> Result<()> { + fn encode_package(&mut self, is_sub: bool, pkg: &PackageId) -> Result<()> { let package = &self.resolve.packages[*pkg]; - if let Some(ref mut sub) = sub_encoder { + if is_sub { let package_metadata = PackageMetadata::extract(self.resolve, *pkg); - sub.component.custom_section(&CustomSection { + self.component.custom_section(&CustomSection { name: PackageMetadata::SECTION_NAME.into(), data: package_metadata.encode()?.into(), }); } for (name, &id) in package.interfaces.iter() { - if let Some(ref mut sub) = sub_encoder { - let component_ty = sub.encode_interface(id, pkg)?; - let ty = sub.component.type_component(&component_ty); - sub.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); - } else { - let component_ty = self.encode_interface(id, pkg)?; - let ty = self.component.type_component(&component_ty); - self.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); - } + let component_ty = self.encode_interface(id, pkg)?; + let ty = self.component.type_component(&component_ty); + self.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); } for (name, &world) in package.worlds.iter() { - if let Some(ref mut sub) = sub_encoder { - // Encode the `world` directly as a component, then create a wrapper - // component that exports that component. - let component_ty = super::encode_world(self.resolve, world)?; - - let world = &sub.resolve.worlds[world]; - let mut wrapper = ComponentType::new(); - wrapper.ty().component(&component_ty); - let pkg = &sub.resolve.packages[world.package.unwrap()]; - wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0)); - let ty = sub.component.type_component(&wrapper); - sub.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); - } else { - // Encode the `world` directly as a component, then create a wrapper - // component that exports that component. - let component_ty = super::encode_world(self.resolve, world)?; - let world = &self.resolve.worlds[world]; - let mut wrapper = ComponentType::new(); - wrapper.ty().component(&component_ty); - let package = &self.resolve.packages[world.package.unwrap()]; - wrapper.export( - &package.name.interface_id(name), - ComponentTypeRef::Component(0), - ); - let ty = self.component.type_component(&wrapper); - self.component - .export(name.as_ref(), ComponentExportKind::Type, ty, None); - } - } - if let Some(sub_encoder) = sub_encoder { - let sub = self.component.component(sub_encoder.component); - names.append(sub, &package.name.to_string()); + // Encode the `world` directly as a component, then create a wrapper + // component that exports that component. + let component_ty = super::encode_world(self.resolve, world)?; + let world = &self.resolve.worlds[world]; + let mut wrapper = ComponentType::new(); + wrapper.ty().component(&component_ty); + let package = &self.resolve.packages[world.package.unwrap()]; + wrapper.export( + &package.name.interface_id(name), + ComponentTypeRef::Component(0), + ); + let ty = self.component.type_component(&wrapper); + self.component + .export(name.as_ref(), ComponentExportKind::Type, ty, None); } Ok(()) } diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index d339968068..0ccc13dc4d 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -19,22 +19,9 @@ use wasmparser::{ WasmFeatures, }; -/// Represents information about a decoded WebAssembly component. -struct ComponentInfo { - /// Wasmparser-defined type information learned after a component is fully - /// validated. - types: types::Types, - /// List of all imports and exports from this component. - externs: Vec<(String, Extern)>, - /// Packages - explicit: Vec, - /// Decoded package metadata - package_metadata: Option, -} - /// Represents information about a decoded WebAssembly component. #[derive(Default)] -struct ExplicitPackageInfo { +struct ComponentInfo { /// Package name name: Option, /// Wasmparser-defined type information learned after a component is fully @@ -42,6 +29,8 @@ struct ExplicitPackageInfo { types: Option, /// List of all imports and exports from this component. externs: Vec<(String, Extern)>, + /// Packages + explicit: Vec, /// Decoded package metadata package_metadata: Option, } @@ -66,7 +55,7 @@ enum WitEncodingVersion { #[cfg(feature = "serde")] fn register_names( names: ComponentNameSectionReader, - explicit: &mut ExplicitPackageInfo, + explicit: &mut ComponentInfo, ) -> Result> { let mut packages = Vec::new(); for section in names { @@ -100,7 +89,8 @@ impl ComponentInfo { fn from_reader(mut reader: impl Read) -> Result { let mut explicit = Vec::new(); - let mut cur_package = ExplicitPackageInfo::default(); + // let mut cur_package = ExplicitPackageInfo::default(); + let mut cur_package = ComponentInfo::default(); let mut is_implicit = true; let mut validator = Validator::new_with_features(WasmFeatures::all()); let mut externs = Vec::new(); @@ -145,7 +135,6 @@ impl ComponentInfo { } else if depth == 0 { types = Some(t); } else { - cur_package.types = Some(t); explicit.push(mem::take(&mut cur_package)); } } @@ -214,7 +203,7 @@ impl ComponentInfo { Payload::ModuleSection { parser, .. } | Payload::ComponentSection { parser, .. } => { is_implicit = false; - cur_package = ExplicitPackageInfo::default(); + cur_package = ComponentInfo::default(); stack.push(cur.clone()); cur = parser.clone(); } @@ -234,7 +223,8 @@ impl ComponentInfo { } Ok(Self { - types: types.unwrap(), + name: None, + types, explicit, externs, package_metadata: _package_metadata, @@ -297,7 +287,10 @@ impl ComponentInfo { }; match export.kind { ComponentExternalKind::Type => matches!( - self.types.component_any_type_at(export.index), + self.types + .as_ref() + .unwrap() + .component_any_type_at(export.index), types::ComponentAnyTypeId::Component(_) ), _ => false, @@ -319,7 +312,7 @@ impl ComponentInfo { } fn decode_wit_v1_package(&self) -> Result<(Resolve, PackageId)> { - let mut decoder = WitPackageDecoder::new(&self.types); + let mut decoder = WitPackageDecoder::new(&self.types.as_ref().unwrap()); let mut pkg = None; for (name, item) in self.externs.iter() { @@ -327,8 +320,8 @@ impl ComponentInfo { Extern::Export(e) => e, _ => unreachable!(), }; - let id = self.types.component_type_at(export.index); - let ty = &self.types[id]; + let id = self.types.as_ref().unwrap().component_type_at(export.index); + let ty = &self.types.as_ref().unwrap()[id]; if pkg.is_some() { bail!("more than one top-level exported component type found"); } @@ -348,27 +341,21 @@ impl ComponentInfo { Ok((resolve, package)) } - fn decode_wit_v2_packages(&self) -> Result<(Resolve, Vec)> { - let mut decoder = WitPackageDecoder::new(&self.types); - - let mut pkg_name = None; - - let mut interfaces = IndexMap::new(); - let mut worlds = IndexMap::new(); - let mut fields = PackageFields { - interfaces: &mut interfaces, - worlds: &mut worlds, - }; - let mut implicit = None; - for (_, item) in self.externs.iter() { + fn decode_externs( + info: &ComponentInfo, + decoder: &mut WitPackageDecoder, + pkg_name: &mut Option, + mut fields: &mut PackageFields, + ) -> Result<()> { + for (_, item) in info.externs.iter() { let export = match item { Extern::Export(e) => e, _ => unreachable!(), }; let index = export.index; - let id = self.types.component_type_at(index); - let component = &self.types[id]; + let id = info.types.as_ref().unwrap().component_type_at(index); + let component = &info.types.as_ref().unwrap()[id]; // The single export of this component will determine if it's a world or an interface: // worlds export a component, while interfaces export an instance. @@ -379,22 +366,25 @@ impl ComponentInfo { ); } - let name = component.exports.keys().nth(0).unwrap(); + let key_name = component.exports.keys().nth(0).unwrap(); - let name = match component.exports[name] { + let name = match component.exports[key_name] { types::ComponentEntityType::Component(ty) => { - let package_name = - decoder.decode_world(name.as_str(), &self.types[ty], &mut fields)?; - package_name + let package_name = decoder.decode_world( + key_name.as_str(), + &info.types.as_ref().unwrap()[ty], + &mut fields, + )?; + Some(package_name) } types::ComponentEntityType::Instance(ty) => { let package_name = decoder.decode_interface( - name.as_str(), + key_name.as_str(), &component.imports, - &self.types[ty], + &info.types.as_ref().unwrap()[ty], &mut fields, )?; - package_name + Some(package_name) } _ => unreachable!(), }; @@ -402,17 +392,33 @@ impl ComponentInfo { if let Some(pkg_name) = pkg_name.as_ref() { // TODO: when we have fully switched to the v2 format, we should switch to parsing // multiple wit documents instead of bailing. - if pkg_name != &name { + if *pkg_name != name.unwrap() { bail!("item defined with mismatched package name") } } else { - pkg_name.replace(name); + pkg_name.replace(name.unwrap()); } } + Ok(()) + } + + fn decode_wit_v2_packages(&self) -> Result<(Resolve, Vec)> { + let mut decoder = WitPackageDecoder::new(&self.types.as_ref().unwrap()); - let mut resolve = if let Some(name) = pkg_name { + let mut pkg_name = None; + + let mut interfaces = IndexMap::new(); + let mut worlds = IndexMap::new(); + let mut fields = PackageFields { + interfaces: &mut interfaces, + worlds: &mut worlds, + }; + let mut implicit = None; + ComponentInfo::decode_externs(self, &mut decoder, &mut pkg_name, &mut fields)?; + + let mut resolve = if let Some(name) = &pkg_name { let pkg = Package { - name, + name: name.clone(), docs: Docs::default(), interfaces: interfaces.clone(), worlds, @@ -440,58 +446,7 @@ impl ComponentInfo { worlds: &mut worlds, }; pkg_name = None; - for (_, item) in explicit.externs.iter() { - let export = match item { - Extern::Export(e) => e, - _ => unreachable!(), - }; - - let index = export.index; - let id = explicit.types.as_ref().unwrap().component_type_at(index); - let component = &explicit.types.as_ref().unwrap()[id]; - - // The single export of this component will determine if it's a world or an interface: - // worlds export a component, while interfaces export an instance. - if component.exports.len() != 1 { - bail!( - "Expected a single export, but found {} instead", - component.exports.len() - ); - } - - let name = component.exports.keys().nth(0).unwrap(); - - let name = match component.exports[name] { - types::ComponentEntityType::Component(ty) => { - let package_name = cur_decoder.decode_world( - name.as_str(), - &explicit.types.as_ref().unwrap()[ty], - &mut fields, - )?; - package_name - } - types::ComponentEntityType::Instance(ty) => { - let package_name = cur_decoder.decode_interface( - name.as_str(), - &component.imports, - &explicit.types.as_ref().unwrap()[ty], - &mut fields, - )?; - package_name - } - _ => unreachable!(), - }; - - if let Some(pkg_name) = pkg_name.as_ref() { - // TODO: when we have fully switched to the v2 format, we should switch to parsing - // multiple wit documents instead of bailing. - if pkg_name != &name { - bail!("item defined with mismatched package name") - } - } else { - pkg_name.replace(name.clone()); - } - } + ComponentInfo::decode_externs(explicit, &mut cur_decoder, &mut pkg_name, &mut fields)?; let pkg = if let Some(name) = pkg_name { Package { name: name.clone(), @@ -536,7 +491,7 @@ impl ComponentInfo { fn decode_component(&self) -> Result<(Resolve, WorldId)> { assert!(self.is_wit_package().is_none()); - let mut decoder = WitPackageDecoder::new(&self.types); + let mut decoder = WitPackageDecoder::new(&self.types.as_ref().unwrap()); // Note that this name is arbitrarily chosen. We may one day perhaps // want to encode this in the component binary format itself, but for // now it shouldn't be an issue to have a defaulted name here. From 9ba171aac34351ff96a8cdc3e7719ff08f438b48 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 15 Jul 2024 16:11:55 -0500 Subject: [PATCH 15/16] encode metadata via helper --- crates/wit-component/src/encoding/wit/v2.rs | 29 ++++++++----------- .../tests/interfaces/multiple-packages.wat | 8 ++--- crates/wit-parser/src/decoding.rs | 1 - 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index ce49f29c83..65e1ae4bd2 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -42,6 +42,14 @@ struct Encoder<'a> { } impl Encoder<'_> { + fn encode_metadata(&mut self, pkg: &PackageId) -> Result<()> { + let package_metadata = PackageMetadata::extract(self.resolve, *pkg); + self.component.custom_section(&CustomSection { + name: PackageMetadata::SECTION_NAME.into(), + data: package_metadata.encode()?.into(), + }); + Ok(()) + } fn run(&mut self) -> Result<()> { // Build a set of interfaces reachable from this document, including the // interfaces in the document itself. This is used to import instances @@ -59,21 +67,14 @@ impl Encoder<'_> { resolve: self.resolve, packages: self.packages, }; - sub_encoder.encode_package(true, pkg)?; + sub_encoder.encode_package(pkg)?; let name = &self.resolve.packages[*pkg]; let sub = self.component.component(sub_encoder.component); names.append(sub, &name.name.to_string()); } else { - self.encode_package(false, pkg)?; + self.encode_package(pkg)?; } } - if self.packages.len() == 1 { - let package_metadata = PackageMetadata::extract(self.resolve, self.packages[0]); - self.component.custom_section(&CustomSection { - name: PackageMetadata::SECTION_NAME.into(), - data: package_metadata.encode()?.into(), - }); - } let mut final_names = ComponentNameSection::new(); final_names.components(&names); self.component.names(&final_names); @@ -81,15 +82,8 @@ impl Encoder<'_> { Ok(()) } - fn encode_package(&mut self, is_sub: bool, pkg: &PackageId) -> Result<()> { + fn encode_package(&mut self, pkg: &PackageId) -> Result<()> { let package = &self.resolve.packages[*pkg]; - if is_sub { - let package_metadata = PackageMetadata::extract(self.resolve, *pkg); - self.component.custom_section(&CustomSection { - name: PackageMetadata::SECTION_NAME.into(), - data: package_metadata.encode()?.into(), - }); - } for (name, &id) in package.interfaces.iter() { let component_ty = self.encode_interface(id, pkg)?; @@ -113,6 +107,7 @@ impl Encoder<'_> { self.component .export(name.as_ref(), ComponentExportKind::Type, ty, None); } + self.encode_metadata(pkg)?; Ok(()) } diff --git a/crates/wit-component/tests/interfaces/multiple-packages.wat b/crates/wit-component/tests/interfaces/multiple-packages.wat index d6e251daae..db2adf54a0 100644 --- a/crates/wit-component/tests/interfaces/multiple-packages.wat +++ b/crates/wit-component/tests/interfaces/multiple-packages.wat @@ -1,6 +1,5 @@ (component (component $another:thing (;0;) - (@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22docs\22:\22documenting an interface\22,\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.2.3\22}}}}}}}") (type (;0;) (component (type (;0;) @@ -13,9 +12,9 @@ ) ) (export (;1;) "something" (type 0)) + (@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22docs\22:\22documenting an interface\22,\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.2.3\22}}}}}}}") ) (component $third:pkg (;1;) - (@custom "package-docs" "\01{\22interfaces\22:{\22things\22:{\22stability\22:{\22stable\22:{\22since\22:\221.2.3\22}},\22types\22:{\22other-record\22:{\22docs\22:\22documenting an type\22}}}}}") (type (;0;) (component (type (;0;) @@ -28,9 +27,9 @@ ) ) (export (;1;) "things" (type 0)) + (@custom "package-docs" "\01{\22interfaces\22:{\22things\22:{\22stability\22:{\22stable\22:{\22since\22:\221.2.3\22}},\22types\22:{\22other-record\22:{\22docs\22:\22documenting an type\22}}}}}") ) (component $foo:bar (;2;) - (@custom "package-docs" "\00{}") (type (;0;) (component (type (;0;) @@ -55,9 +54,9 @@ ) ) (export (;1;) "this-world" (type 0)) + (@custom "package-docs" "\00{}") ) (component $fourth:thing (;3;) - (@custom "package-docs" "\00{}") (type (;0;) (component (type (;0;) @@ -70,6 +69,7 @@ ) ) (export (;1;) "boo" (type 0)) + (@custom "package-docs" "\00{}") ) (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 0ccc13dc4d..5344fb196f 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -89,7 +89,6 @@ impl ComponentInfo { fn from_reader(mut reader: impl Read) -> Result { let mut explicit = Vec::new(); - // let mut cur_package = ExplicitPackageInfo::default(); let mut cur_package = ComponentInfo::default(); let mut is_implicit = true; let mut validator = Validator::new_with_features(WasmFeatures::all()); From 04dfc6aba224aa6a6cb0f1fdef6d90a3ad5d9a3a Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Tue, 16 Jul 2024 10:03:18 -0500 Subject: [PATCH 16/16] v3 env var and const, additional error --- crates/wit-component/src/encoding/wit/mod.rs | 5 ++++- crates/wit-component/src/encoding/wit/v2.rs | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/wit-component/src/encoding/wit/mod.rs b/crates/wit-component/src/encoding/wit/mod.rs index 71c2b4f54c..f8693cf913 100644 --- a/crates/wit-component/src/encoding/wit/mod.rs +++ b/crates/wit-component/src/encoding/wit/mod.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use wasm_encoder::{ComponentBuilder, ComponentType}; use wit_parser::{PackageId, Resolve, WorldId}; @@ -48,6 +48,9 @@ pub fn encode_component( if use_v2.unwrap_or_else(use_v2_encoding) { v2::encode_component(resolve, packages) } else { + if packages.len() > 1 { + bail!("Only encoding a single package, when multiple are present") + } v1::encode_component(resolve, packages[0]) } } diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index 65e1ae4bd2..44a98d9176 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -6,6 +6,11 @@ use std::mem; use wasm_encoder::*; use wit_parser::*; +/// If set to 1, wit packages will be encoded +/// using V3 style, where all packages are wrapped in +/// a subcomponent +const USE_V3: u8 = 0; + /// Encodes the given `package` within `resolve` to a binary WebAssembly /// representation. /// @@ -61,7 +66,7 @@ impl Encoder<'_> { // roundtripping. let mut names = NameMap::new(); for pkg in self.packages { - if self.packages.len() > 1 { + if self.packages.len() > 1 || std::env::var_os("USE_V3").is_some() || USE_V3 == 1 { let mut sub_encoder = Encoder { component: ComponentBuilder::default(), resolve: self.resolve,