Skip to content

Commit

Permalink
explicit packages encoded as nested components
Browse files Browse the repository at this point in the history
  • Loading branch information
macovedj committed Jun 26, 2024
1 parent 29da207 commit 301c1a4
Show file tree
Hide file tree
Showing 74 changed files with 352 additions and 110 deletions.
4 changes: 4 additions & 0 deletions crates/wasm-encoder/src/component/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions crates/wasm-encoder/src/component/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion crates/wit-component/src/encoding/wit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
}

Expand Down
124 changes: 60 additions & 64 deletions crates/wit-component/src/encoding/wit/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,27 @@ 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<ComponentBuilder> {
pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result<ComponentBuilder> {
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)
}

struct Encoder<'a> {
component: ComponentBuilder,
resolve: &'a Resolve,
packages: &'a [PackageId],
package: PackageId,
}

impl Encoder<'_> {
Expand All @@ -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(())
}

Expand Down
97 changes: 70 additions & 27 deletions crates/wit-component/src/encoding/wit/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,6 @@ pub fn encode_component(resolve: &Resolve, packages: &[PackageId]) -> Result<Com
};
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(),
});
}

Ok(encoder.component)
}

Expand All @@ -59,30 +51,81 @@ impl Encoder<'_> {
// decoding process where everyone's view of a foreign document agrees
// notably on the order that types are defined in to assist with
// roundtripping.
let mut 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 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 world = &self.resolve.worlds[world];
let mut wrapper = ComponentType::new();
wrapper.ty().component(&component_ty);
let pkg = &self.resolve.packages[world.package.unwrap()];
wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0));
let world = &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 ty = self.component.type_component(&wrapper);
self.component
.export(name.as_ref(), ComponentExportKind::Type, ty, None);
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(())
}
Expand Down
3 changes: 2 additions & 1 deletion crates/wit-component/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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(),
Expand Down
32 changes: 32 additions & 0 deletions crates/wit-component/tests/interfaces/multiple-packages.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(component
(component $foo:bar (;0;)
(@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")
)
)
26 changes: 26 additions & 0 deletions crates/wit-component/tests/interfaces/multiple-packages.wit
Original file line number Diff line number Diff line change
@@ -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
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package root:component;

world root {
}
Loading

0 comments on commit 301c1a4

Please sign in to comment.