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 27, 2024
1 parent 29da207 commit 3e8b0f8
Show file tree
Hide file tree
Showing 77 changed files with 723 additions and 243 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
98 changes: 70 additions & 28 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,80 @@ 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 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(())
}
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
2 changes: 0 additions & 2 deletions crates/wit-component/src/printing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -98,7 +97,6 @@ impl WitPrinter {

if has_multiple_packages {
self.output.push_str("}");
self.output.indent -= 1
}
}

Expand Down
Loading

0 comments on commit 3e8b0f8

Please sign in to comment.