From fd677d1e7001bde2d1389df5212c755d9b4f2706 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Wed, 26 Jun 2024 14:27:55 -0500 Subject: [PATCH] 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 | 48 +-- .../interfaces/doc-comments/foo.wit.print | 27 -- .../tests/interfaces/multiple-packages.wat | 62 +++ .../tests/interfaces/multiple-packages.wit | 26 ++ .../interfaces/multiple-packages.wit.print | 28 ++ .../tests/interfaces/resources.wit.print | 7 - .../tests/interfaces/wasi-http/http.wit.print | 358 ---------------- 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 | 12 +- 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 + .../wit-stability-in-binary-format.wit.stdout | 14 - 81 files changed, 720 insertions(+), 650 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..71a41c70f3 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -54,37 +54,33 @@ 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)?; + 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, &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")?; - // 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")?; + // 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/doc-comments/foo.wit.print b/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print index 3ef196c33e..d775884eb1 100644 --- a/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print +++ b/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print @@ -1,79 +1,52 @@ -/// package docs; package foo:foo; -/// interface docs interface coverage-iface { - /// basic typedef docs type t = u32; - /// record typedef docs record r { - /// record field docs f1: u8, f2: u8, } flags fl { - /// flag docs f1, f2, } variant v { - /// variant case docs c1(u8), c2, } enum e { - /// enum case docs c1, c2, } resource res { - /// constructor docs constructor(); - /// method docs m: func(); - /// static func docs s: static func(); } - /// interface func docs f: func(); } -/// other comment forms -/// multi-line block interface other-comment-forms { - /// one doc line - /// non-doc in the middle - /// another doc line multiple-lines-split: func(); - /// mixed forms; line doc - /// plus block doc - /// multi-line mixed-forms: func(); } -/// world docs world coverage-world { - /// world func import docs import imp: func(); - /// world typedef docs type t = u32; - /// world func export docs export exp: func(); - /// world inline interface docs export i: interface { - /// inline interface typedef docs type t = u32; - /// inline interface func docs f: func(); } } 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-component/tests/interfaces/resources.wit.print b/crates/wit-component/tests/interfaces/resources.wit.print index c52d674491..b2a22dcd50 100644 --- a/crates/wit-component/tests/interfaces/resources.wit.print +++ b/crates/wit-component/tests/interfaces/resources.wit.print @@ -31,19 +31,14 @@ interface implicit-own-handles { } interface implicit-own-handles2 { - /// the `own` return and list param should be the same `own` resource a { constructor(a: list); } - /// same as above, even when the `list` implicitly-defined `own` comes - /// before an explicitly defined `own` resource b { constructor(a: list, b: b); } - /// same as the above, the `own` argument should have the same type as the - /// return value resource c { a: static func(a: c) -> c; } @@ -69,8 +64,6 @@ world some-world { export y: func() -> bar; } world implicit-own-handles3 { - /// there should only be one `list` type despite there looking like two - /// list types here resource a { constructor(a: list, b: list); } diff --git a/crates/wit-component/tests/interfaces/wasi-http/http.wit.print b/crates/wit-component/tests/interfaces/wasi-http/http.wit.print index ff5dd9b189..d84da15b40 100644 --- a/crates/wit-component/tests/interfaces/wasi-http/http.wit.print +++ b/crates/wit-component/tests/interfaces/wasi-http/http.wit.print @@ -1,15 +1,11 @@ package wasi:http@0.2.0-rc-2023-12-05; -/// This interface defines all of the types and methods for implementing -/// HTTP Requests and Responses, both incoming and outgoing, as well as -/// their headers, trailers, and bodies. interface types { use wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10.{duration}; use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream}; use wasi:io/error@0.2.0-rc-2023-11-10.{error as io-error}; use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; - /// This type corresponds to HTTP standard Methods. variant method { get, head, @@ -23,33 +19,27 @@ interface types { other(string), } - /// This type corresponds to HTTP standard Related Schemes. variant scheme { HTTP, HTTPS, other(string), } - /// Defines the case payload type for `DNS-error` above: record DNS-error-payload { rcode: option, info-code: option, } - /// Defines the case payload type for `TLS-alert-received` above: record TLS-alert-received-payload { alert-id: option, alert-message: option, } - /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: record field-size-payload { field-name: option, field-size: option, } - /// These cases are inspired by the IANA HTTP Proxy Error Types: - /// https://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types variant error-code { DNS-timeout, DNS-error(DNS-error-payload), @@ -89,471 +79,123 @@ interface types { HTTP-protocol-error, loop-detected, configuration-error, - /// This is a catch-all error for anything that doesn't fit cleanly into a - /// more specific case. It also includes an optional string for an - /// unstructured description of the error. Users should not depend on the - /// string for diagnosing errors, as it's not required to be consistent - /// between implementations. internal-error(option), } - /// This type enumerates the different kinds of errors that may occur when - /// setting or appending to a `fields` resource. variant header-error { - /// This error indicates that a `field-key` or `field-value` was - /// syntactically invalid when used with an operation that sets headers in a - /// `fields`. invalid-syntax, - /// This error indicates that a forbidden `field-key` was used when trying - /// to set a header in a `fields`. forbidden, - /// This error indicates that the operation on the `fields` was not - /// permitted because the fields are immutable. immutable, } - /// Field keys are always strings. type field-key = string; - /// Field values should always be ASCII strings. However, in - /// reality, HTTP implementations often have to interpret malformed values, - /// so they are provided as a list of bytes. type field-value = list; - /// This following block defines the `fields` resource which corresponds to - /// HTTP standard Fields. Fields are a common representation used for both - /// Headers and Trailers. - /// - /// A `fields` may be mutable or immutable. A `fields` created using the - /// constructor, `from-list`, or `clone` will be mutable, but a `fields` - /// resource given by other means (including, but not limited to, - /// `incoming-request.headers`, `outgoing-request.headers`) might be be - /// immutable. In an immutable fields, the `set`, `append`, and `delete` - /// operations will fail with `header-error.immutable`. resource fields { - /// Construct an empty HTTP Fields. - /// - /// The resulting `fields` is mutable. constructor(); - /// Construct an HTTP Fields. - /// - /// The resulting `fields` is mutable. - /// - /// The list represents each key-value pair in the Fields. Keys - /// which have multiple values are represented by multiple entries in this - /// list with the same key. - /// - /// The tuple is a pair of the field key, represented as a string, and - /// Value, represented as a list of bytes. In a valid Fields, all keys - /// and values are valid UTF-8 strings. However, values are not always - /// well-formed, so they are represented as a raw list of bytes. - /// - /// An error result will be returned if any header or value was - /// syntactically invalid, or if a header was forbidden. from-list: static func(entries: list>) -> result; - /// Get all of the values corresponding to a key. If the key is not present - /// in this `fields`, an empty list is returned. However, if the key is - /// present but empty, this is represented by a list with one or more - /// empty field-values present. get: func(name: field-key) -> list; - /// Returns `true` when the key is present in this `fields`. If the key is - /// syntactically invalid, `false` is returned. has: func(name: field-key) -> bool; - /// Set all of the values for a key. Clears any existing values for that - /// key, if they have been set. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. set: func(name: field-key, value: list) -> result<_, header-error>; - /// Delete all values for a key. Does nothing if no values for the key - /// exist. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. delete: func(name: field-key) -> result<_, header-error>; - /// Append a value for a key. Does not change or delete any existing - /// values for that key. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. append: func(name: field-key, value: field-value) -> result<_, header-error>; - /// Retrieve the full set of keys and values in the Fields. Like the - /// constructor, the list represents each key-value pair. - /// - /// The outer list represents each key-value pair in the Fields. Keys - /// which have multiple values are represented by multiple entries in this - /// list with the same key. entries: func() -> list>; - /// Make a deep copy of the Fields. Equivelant in behavior to calling the - /// `fields` constructor on the return value of `entries`. The resulting - /// `fields` is mutable. clone: func() -> fields; } - /// Headers is an alias for Fields. type headers = fields; - /// Trailers is an alias for Fields. type trailers = fields; - /// Represents an incoming HTTP Request. resource incoming-request { - /// Returns the method of the incoming request. method: func() -> method; - /// Returns the path with query parameters from the request, as a string. path-with-query: func() -> option; - /// Returns the protocol scheme from the request. scheme: func() -> option; - /// Returns the authority from the request, if it was present. authority: func() -> option; - /// Get the `headers` associated with the request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// The `headers` returned are a child resource: it must be dropped before - /// the parent `incoming-request` is dropped. Dropping this - /// `incoming-request` before all children are dropped will trap. headers: func() -> headers; - /// Gives the `incoming-body` associated with this request. Will only - /// return success at most once, and subsequent calls will return error. consume: func() -> result; } - /// Represents an outgoing HTTP Request. resource outgoing-request { - /// Construct a new `outgoing-request` with a default `method` of `GET`, and - /// `none` values for `path-with-query`, `scheme`, and `authority`. - /// - /// * `headers` is the HTTP Headers for the Request. - /// - /// It is possible to construct, or manipulate with the accessor functions - /// below, an `outgoing-request` with an invalid combination of `scheme` - /// and `authority`, or `headers` which are not permitted to be sent. - /// It is the obligation of the `outgoing-handler.handle` implementation - /// to reject invalid constructions of `outgoing-request`. constructor(headers: headers); - /// Returns the resource corresponding to the outgoing Body for this - /// Request. - /// - /// Returns success on the first call: the `outgoing-body` resource for - /// this `outgoing-request` can be retrieved at most once. Subsequent - /// calls will return error. body: func() -> result; - /// Get the Method for the Request. method: func() -> method; - /// Set the Method for the Request. Fails if the string present in a - /// `method.other` argument is not a syntactically valid method. set-method: func(method: method) -> result; - /// Get the combination of the HTTP Path and Query for the Request. - /// When `none`, this represents an empty Path and empty Query. path-with-query: func() -> option; - /// Set the combination of the HTTP Path and Query for the Request. - /// When `none`, this represents an empty Path and empty Query. Fails is the - /// string given is not a syntactically valid path and query uri component. set-path-with-query: func(path-with-query: option) -> result; - /// Get the HTTP Related Scheme for the Request. When `none`, the - /// implementation may choose an appropriate default scheme. scheme: func() -> option; - /// Set the HTTP Related Scheme for the Request. When `none`, the - /// implementation may choose an appropriate default scheme. Fails if the - /// string given is not a syntactically valid uri scheme. set-scheme: func(scheme: option) -> result; - /// Get the HTTP Authority for the Request. A value of `none` may be used - /// with Related Schemes which do not require an Authority. The HTTP and - /// HTTPS schemes always require an authority. authority: func() -> option; - /// Set the HTTP Authority for the Request. A value of `none` may be used - /// with Related Schemes which do not require an Authority. The HTTP and - /// HTTPS schemes always require an authority. Fails if the string given is - /// not a syntactically valid uri authority. set-authority: func(authority: option) -> result; - /// Get the headers associated with the Request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `outgoing-request` is dropped, or its ownership is transfered to - /// another component by e.g. `outgoing-handler.handle`. headers: func() -> headers; } - /// Parameters for making an HTTP Request. Each of these parameters is - /// currently an optional timeout applicable to the transport layer of the - /// HTTP protocol. - /// - /// These timeouts are separate from any the user may use to bound a - /// blocking call to `wasi:io/poll.poll`. resource request-options { - /// Construct a default `request-options` value. constructor(); - /// The timeout for the initial connect to the HTTP Server. connect-timeout: func() -> option; - /// Set the timeout for the initial connect to the HTTP Server. An error - /// return value indicates that this timeout is not supported. set-connect-timeout: func(duration: option) -> result; - /// The timeout for receiving the first byte of the Response body. first-byte-timeout: func() -> option; - /// Set the timeout for receiving the first byte of the Response body. An - /// error return value indicates that this timeout is not supported. set-first-byte-timeout: func(duration: option) -> result; - /// The timeout for receiving subsequent chunks of bytes in the Response - /// body stream. between-bytes-timeout: func() -> option; - /// Set the timeout for receiving subsequent chunks of bytes in the Response - /// body stream. An error return value indicates that this timeout is not - /// supported. set-between-bytes-timeout: func(duration: option) -> result; } - /// Represents the ability to send an HTTP Response. - /// - /// This resource is used by the `wasi:http/incoming-handler` interface to - /// allow a Response to be sent corresponding to the Request provided as the - /// other argument to `incoming-handler.handle`. resource response-outparam { - /// Set the value of the `response-outparam` to either send a response, - /// or indicate an error. - /// - /// This method consumes the `response-outparam` to ensure that it is - /// called at most once. If it is never called, the implementation - /// will respond with an error. - /// - /// The user may provide an `error` to `response` to allow the - /// implementation determine how to respond with an HTTP error response. set: static func(param: response-outparam, response: result); } - /// This type corresponds to the HTTP standard Status Code. type status-code = u16; - /// Represents an incoming HTTP Response. resource incoming-response { - /// Returns the status code from the incoming response. status: func() -> status-code; - /// Returns the headers from the incoming response. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `incoming-response` is dropped. headers: func() -> headers; - /// Returns the incoming body. May be called at most once. Returns error - /// if called additional times. consume: func() -> result; } - /// Represents an incoming HTTP Request or Response's Body. - /// - /// A body has both its contents - a stream of bytes - and a (possibly - /// empty) set of trailers, indicating that the full contents of the - /// body have been received. This resource represents the contents as - /// an `input-stream` and the delivery of trailers as a `future-trailers`, - /// and ensures that the user of this interface may only be consuming either - /// the body contents or waiting on trailers at any given time. resource incoming-body { - /// Returns the contents of the body, as a stream of bytes. - /// - /// Returns success on first call: the stream representing the contents - /// can be retrieved at most once. Subsequent calls will return error. - /// - /// The returned `input-stream` resource is a child: it must be dropped - /// before the parent `incoming-body` is dropped, or consumed by - /// `incoming-body.finish`. - /// - /// This invariant ensures that the implementation can determine whether - /// the user is consuming the contents of the body, waiting on the - /// `future-trailers` to be ready, or neither. This allows for network - /// backpressure is to be applied when the user is consuming the body, - /// and for that backpressure to not inhibit delivery of the trailers if - /// the user does not read the entire body. %stream: func() -> result; - /// Takes ownership of `incoming-body`, and returns a `future-trailers`. - /// This function will trap if the `input-stream` child is still alive. finish: static func(this: incoming-body) -> future-trailers; } - /// Represents a future which may eventaully return trailers, or an error. - /// - /// In the case that the incoming HTTP Request or Response did not have any - /// trailers, this future will resolve to the empty set of trailers once the - /// complete Request or Response body has been received. resource future-trailers { - /// Returns a pollable which becomes ready when either the trailers have - /// been received, or an error has occured. When this pollable is ready, - /// the `get` method will return `some`. subscribe: func() -> pollable; - /// Returns the contents of the trailers, or an error which occured, - /// once the future is ready. - /// - /// The outer `option` represents future readiness. Users can wait on this - /// `option` to become `some` using the `subscribe` method. - /// - /// The outer `result` is used to retrieve the trailers or error at most - /// once. It will be success on the first call in which the outer option - /// is `some`, and error on subsequent calls. - /// - /// The inner `result` represents that either the HTTP Request or Response - /// body, as well as any trailers, were received successfully, or that an - /// error occured receiving them. The optional `trailers` indicates whether - /// or not trailers were present in the body. - /// - /// When some `trailers` are returned by this method, the `trailers` - /// resource is immutable, and a child. Use of the `set`, `append`, or - /// `delete` methods will return an error, and the resource must be - /// dropped before the parent `future-trailers` is dropped. get: func() -> option, error-code>>>; } - /// Represents an outgoing HTTP Response. resource outgoing-response { - /// Construct an `outgoing-response`, with a default `status-code` of `200`. - /// If a different `status-code` is needed, it must be set via the - /// `set-status-code` method. - /// - /// * `headers` is the HTTP Headers for the Response. constructor(headers: headers); - /// Get the HTTP Status Code for the Response. status-code: func() -> status-code; - /// Set the HTTP Status Code for the Response. Fails if the status-code - /// given is not a valid http status code. set-status-code: func(status-code: status-code) -> result; - /// Get the headers associated with the Request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `outgoing-request` is dropped, or its ownership is transfered to - /// another component by e.g. `outgoing-handler.handle`. headers: func() -> headers; - /// Returns the resource corresponding to the outgoing Body for this Response. - /// - /// Returns success on the first call: the `outgoing-body` resource for - /// this `outgoing-response` can be retrieved at most once. Subsequent - /// calls will return error. body: func() -> result; } - /// Represents an outgoing HTTP Request or Response's Body. - /// - /// A body has both its contents - a stream of bytes - and a (possibly - /// empty) set of trailers, inducating the full contents of the body - /// have been sent. This resource represents the contents as an - /// `output-stream` child resource, and the completion of the body (with - /// optional trailers) with a static function that consumes the - /// `outgoing-body` resource, and ensures that the user of this interface - /// may not write to the body contents after the body has been finished. - /// - /// If the user code drops this resource, as opposed to calling the static - /// method `finish`, the implementation should treat the body as incomplete, - /// and that an error has occured. The implementation should propogate this - /// error to the HTTP protocol by whatever means it has available, - /// including: corrupting the body on the wire, aborting the associated - /// Request, or sending a late status code for the Response. resource outgoing-body { - /// Returns a stream for writing the body contents. - /// - /// The returned `output-stream` is a child resource: it must be dropped - /// before the parent `outgoing-body` resource is dropped (or finished), - /// otherwise the `outgoing-body` drop or `finish` will trap. - /// - /// Returns success on the first call: the `output-stream` resource for - /// this `outgoing-body` may be retrieved at most once. Subsequent calls - /// will return error. write: func() -> result; - /// Finalize an outgoing body, optionally providing trailers. This must be - /// called to signal that the response is complete. If the `outgoing-body` - /// is dropped without calling `outgoing-body.finalize`, the implementation - /// should treat the body as corrupted. - /// - /// Fails if the body's `outgoing-request` or `outgoing-response` was - /// constructed with a Content-Length header, and the contents written - /// to the body (via `write`) does not match the value given in the - /// Content-Length. finish: static func(this: outgoing-body, trailers: option) -> result<_, error-code>; } - /// Represents a future which may eventaully return an incoming HTTP - /// Response, or an error. - /// - /// This resource is returned by the `wasi:http/outgoing-handler` interface to - /// provide the HTTP Response corresponding to the sent Request. resource future-incoming-response { - /// Returns a pollable which becomes ready when either the Response has - /// been received, or an error has occured. When this pollable is ready, - /// the `get` method will return `some`. subscribe: func() -> pollable; - /// Returns the incoming HTTP Response, or an error, once one is ready. - /// - /// The outer `option` represents future readiness. Users can wait on this - /// `option` to become `some` using the `subscribe` method. - /// - /// The outer `result` is used to retrieve the response or error at most - /// once. It will be success on the first call in which the outer option - /// is `some`, and error on subsequent calls. - /// - /// The inner `result` represents that either the incoming HTTP Response - /// status and headers have recieved successfully, or that an error - /// occured. Errors may also occur while consuming the response body, - /// but those will be reported by the `incoming-body` and its - /// `output-stream` child. get: func() -> option>>; } - /// Attempts to extract a http-related `error` from the wasi:io `error` - /// provided. - /// - /// Stream operations which return - /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of - /// type `wasi:io/error/error` with more information about the operation - /// that failed. This payload can be passed through to this function to see - /// if there's http-related information about the error to return. - /// - /// Note that this function is fallible because not all io-errors are - /// http-related errors. http-error-code: func(err: borrow) -> option; } -/// This interface defines a handler of incoming HTTP Requests. It should -/// be exported by components which can respond to HTTP Requests. interface incoming-handler { use types.{incoming-request, response-outparam}; - /// This function is invoked with an incoming HTTP Request, and a resource - /// `response-outparam` which provides the capability to reply with an HTTP - /// Response. The response is sent by calling the `response-outparam.set` - /// method, which allows execution to continue after the response has been - /// sent. This enables both streaming to the response body, and performing other - /// work. - /// - /// The implementor of this function must write a response to the - /// `response-outparam` before returning, or else the caller will respond - /// with an error on its behalf. handle: func(request: incoming-request, response-out: response-outparam); } -/// This interface defines a handler of outgoing HTTP Requests. It should be -/// imported by components which wish to make HTTP Requests. interface outgoing-handler { use types.{outgoing-request, request-options, future-incoming-response, error-code}; - /// This function is invoked with an outgoing HTTP Request, and it returns - /// a resource `future-incoming-response` which represents an HTTP Response - /// which may arrive in the future. - /// - /// The `options` argument accepts optional parameters for the HTTP - /// protocol's transport layer. - /// - /// This function may return an error if the `outgoing-request` is invalid - /// or not allowed to be made. Otherwise, protocol errors are reported - /// through the `future-incoming-response`. handle: func(request: outgoing-request, options: option) -> result; } -/// The `wasi:http/proxy` world captures a widely-implementable intersection of -/// hosts that includes HTTP forward and reverse proxies. Components targeting -/// this world may concurrently stream in and out any number of incoming and -/// outgoing HTTP requests. world proxy { import wasi:random/random@0.2.0-rc-2023-11-10; import wasi:io/error@0.2.0-rc-2023-11-10; 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..cc0482295b 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(); } @@ -142,13 +236,18 @@ impl ComponentInfo { Ok(Self { types: types.unwrap(), externs, - package_metadata: _package_metadata, + explicit, + packages, + 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!(), }; - - pkg_names.insert(name); + 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); + 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 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())?; + + 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)?; + } + } + // 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)?; + } } } 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>( @@ -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..b0ab727fae 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -76,17 +76,26 @@ pub struct Resolve { pub features: IndexSet, } +#[derive(PartialEq, Clone, Debug, 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 +1222,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 }, diff --git a/tests/cli/wit-stability-in-binary-format.wit.stdout b/tests/cli/wit-stability-in-binary-format.wit.stdout index f3191af2d4..d9bf5d5dae 100644 --- a/tests/cli/wit-stability-in-binary-format.wit.stdout +++ b/tests/cli/wit-stability-in-binary-format.wit.stdout @@ -1,39 +1,25 @@ -/// RUN: component wit % --wasm | component wit package a:b; -@since(version = 1.0.0) interface foo { - @since(version = 1.0.0) type t = u32; - @since(version = 1.0.0) resource r { - @since(version = 1.0.0) constructor(); } - @since(version = 1.0.0) f: func(); } -@since(version = 1.0.0) world w { - @since(version = 1.0.0) import foo; - @since(version = 1.0.0) import a: interface { } - @since(version = 1.0.0) import f: func(); - @since(version = 1.0.0) type t = u32; - @since(version = 1.0.0) export f: func(); - @since(version = 1.0.0) export foo; - @since(version = 1.0.0) export a: interface { } }