Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove v1 encoding support for WIT documents/worlds #1781

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@ use std::mem;
use wasm_encoder::*;
use wit_parser::*;

/// Encodes the given `package` within `resolve` to a binary WebAssembly
/// representation.
///
/// This function is the root of the implementation of serializing a WIT package
/// into a WebAssembly representation. The wasm representation serves two
/// purposes:
///
/// * One is to be a binary encoding of a WIT document which is ideally more
/// stable than the WIT textual format itself.
/// * Another is to provide a clear mapping of all WIT features into the
/// component model through use of its binary representation.
///
/// The `resolve` provided is a set of packages and types and such and the
/// `package` argument is an ID within the world provided. The documents within
/// `package` will all be encoded into the binary returned.
///
/// The binary returned can be [`decode`d](crate::decode) to recover the WIT
/// package provided.
pub fn encode(resolve: &Resolve, package: PackageId) -> Result<Vec<u8>> {
let mut component = encode_component(resolve, package)?;
component.raw_custom_section(&crate::base_producers().raw_custom_section());
Ok(component.finish())
}

/// Encodes the given `package` within `resolve` to a binary WebAssembly
/// representation.
///
Expand Down Expand Up @@ -41,6 +65,82 @@ pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result<Compone
Ok(encoder.component)
}

/// Encodes a `world` as a component type.
pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentType> {
let mut component = InterfaceEncoder::new(resolve);
let world = &resolve.worlds[world_id];
log::trace!("encoding world {}", world.name);

// This sort is similar in purpose to the sort below in
// `encode_instance`, but different in its sort. The purpose here is
// to ensure that when a document is either printed as WIT or
// encoded as wasm that decoding from those artifacts produces the
// same WIT package. Namely both encoding processes should encode
// things in the same order.
//
// When printing worlds in WIT freestanding function imports are
// printed first, then types. Resource functions are attached to
// types which means that they all come last. Sort all
// resource-related functions here to the back of the `imports` list
// while keeping everything else in front, using a stable sort to
// preserve preexisting ordering.
let mut imports = world.imports.iter().collect::<Vec<_>>();
imports.sort_by_key(|(_name, import)| match import {
WorldItem::Function(f) => match f.kind {
FunctionKind::Freestanding => 0,
_ => 1,
},
_ => 0,
});

// Encode the imports
for (name, import) in imports {
let name = resolve.name_world_key(name);
log::trace!("encoding import {name}");
let ty = match import {
WorldItem::Interface { id, .. } => {
component.interface = Some(*id);
let idx = component.encode_instance(*id)?;
ComponentTypeRef::Instance(idx)
}
WorldItem::Function(f) => {
component.interface = None;
let idx = component.encode_func_type(resolve, f)?;
ComponentTypeRef::Func(idx)
}
WorldItem::Type(t) => {
component.interface = None;
component.import_types = true;
component.encode_valtype(resolve, &Type::Id(*t))?;
component.import_types = false;
continue;
}
};
component.outer.import(&name, ty);
}
// Encode the exports
for (name, export) in world.exports.iter() {
let name = resolve.name_world_key(name);
log::trace!("encoding export {name}");
let ty = match export {
WorldItem::Interface { id, .. } => {
component.interface = Some(*id);
let idx = component.encode_instance(*id)?;
ComponentTypeRef::Instance(idx)
}
WorldItem::Function(f) => {
component.interface = None;
let idx = component.encode_func_type(resolve, f)?;
ComponentTypeRef::Func(idx)
}
WorldItem::Type(_) => unreachable!(),
};
component.outer.export(&name, ty);
}

Ok(component.outer)
}

struct Encoder<'a> {
component: ComponentBuilder,
resolve: &'a Resolve,
Expand All @@ -49,6 +149,34 @@ struct Encoder<'a> {

impl Encoder<'_> {
fn run(&mut self) -> Result<()> {
// Encode all interfaces as component types and then export them.
for (name, &id) in self.resolve.packages[self.package].interfaces.iter() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the comment above this code be reworded given these changes? It seems odd that the exact same comment is duplicated below in encode_interface; it seems like a better fit there than here.

let component_ty = self.encode_interface(id)?;
let ty = self.component.type_component(&component_ty);
self.component
.export(name.as_ref(), ComponentExportKind::Type, ty, None);
}

// For each `world` encode it directly as a component and then create a
// wrapper component that exports that component.
for (name, &world) in self.resolve.packages[self.package].worlds.iter() {
let component_ty = 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);
}

Ok(())
}

fn encode_interface(&mut self, id: InterfaceId) -> Result<ComponentType> {
// Build a set of interfaces reachable from this document, including the
// interfaces in the document itself. This is used to import instances
// into the component type we're encoding. Note that entire interfaces
Expand All @@ -58,9 +186,7 @@ impl Encoder<'_> {
// notably on the order that types are defined in to assist with
// roundtripping.
let mut interfaces = IndexSet::new();
for (_, id) in self.resolve.packages[self.package].interfaces.iter() {
self.add_live_interfaces(&mut interfaces, *id);
}
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
Expand All @@ -73,23 +199,15 @@ impl Encoder<'_> {
assert!(first);
}
}
for (name, _world) in self.resolve.packages[self.package].worlds.iter() {
let first = used_names.insert(name.clone());
assert!(first);
}

// Encode all interfaces, foreign and local, into this component type.
// Local interfaces get their functions defined as well and are
// exported. Foreign interfaces are imported and only have their types
// encoded.
let mut encoder = InterfaceEncoder::new(self.resolve);
for interface in interfaces {
encoder.interface = Some(interface);
let iface = &self.resolve.interfaces[interface];
let name = self.resolve.id_of(interface).unwrap();
log::trace!("encoding interface {name}");
if iface.package == Some(self.package) {
if interface == id {
let idx = encoder.encode_instance(interface)?;
log::trace!("exporting self as {idx}");
encoder.outer.export(&name, ComponentTypeRef::Instance(idx));
} else {
encoder.push_instance();
Expand All @@ -104,21 +222,10 @@ impl Encoder<'_> {
encoder.outer.import(&name, ComponentTypeRef::Instance(idx));
}
}
encoder.interface = None;

for (name, world) in self.resolve.packages[self.package].worlds.iter() {
let component_ty = encode_world(self.resolve, *world)?;
let idx = encoder.outer.type_count();
encoder.outer.ty().component(&component_ty);
let id = self.resolve.packages[self.package].name.interface_id(name);
encoder.outer.export(&id, ComponentTypeRef::Component(idx));
}
encoder.interface = None;

let ty = self.component.type_component(&encoder.outer);
let id = self.resolve.packages[self.package].name.interface_id("wit");
self.component
.export(&id, ComponentExportKind::Type, ty, None);
Ok(())
Ok(encoder.outer)
}

/// Recursively add all live interfaces reachable from `id` into the
Expand Down Expand Up @@ -317,79 +424,3 @@ impl<'a> ValtypeEncoder<'a> for InterfaceEncoder<'a> {
&mut self.func_type_map
}
}

/// Encodes a `world` as a component type.
pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentType> {
let mut component = InterfaceEncoder::new(resolve);
let world = &resolve.worlds[world_id];
log::trace!("encoding world {}", world.name);

// This sort is similar in purpose to the sort below in
// `encode_instance`, but different in its sort. The purpose here is
// to ensure that when a document is either printed as WIT or
// encoded as wasm that decoding from those artifacts produces the
// same WIT package. Namely both encoding processes should encode
// things in the same order.
//
// When printing worlds in WIT freestanding function imports are
// printed first, then types. Resource functions are attached to
// types which means that they all come last. Sort all
// resource-related functions here to the back of the `imports` list
// while keeping everything else in front, using a stable sort to
// preserve preexisting ordering.
let mut imports = world.imports.iter().collect::<Vec<_>>();
imports.sort_by_key(|(_name, import)| match import {
WorldItem::Function(f) => match f.kind {
FunctionKind::Freestanding => 0,
_ => 1,
},
_ => 0,
});

// Encode the imports
for (name, import) in imports {
let name = resolve.name_world_key(name);
log::trace!("encoding import {name}");
let ty = match import {
WorldItem::Interface { id, .. } => {
component.interface = Some(*id);
let idx = component.encode_instance(*id)?;
ComponentTypeRef::Instance(idx)
}
WorldItem::Function(f) => {
component.interface = None;
let idx = component.encode_func_type(resolve, f)?;
ComponentTypeRef::Func(idx)
}
WorldItem::Type(t) => {
component.interface = None;
component.import_types = true;
component.encode_valtype(resolve, &Type::Id(*t))?;
component.import_types = false;
continue;
}
};
component.outer.import(&name, ty);
}
// Encode the exports
for (name, export) in world.exports.iter() {
let name = resolve.name_world_key(name);
log::trace!("encoding export {name}");
let ty = match export {
WorldItem::Interface { id, .. } => {
component.interface = Some(*id);
let idx = component.encode_instance(*id)?;
ComponentTypeRef::Instance(idx)
}
WorldItem::Function(f) => {
component.interface = None;
let idx = component.encode_func_type(resolve, f)?;
ComponentTypeRef::Func(idx)
}
WorldItem::Type(_) => unreachable!(),
};
component.outer.export(&name, ty);
}

Ok(component.outer)
}
58 changes: 0 additions & 58 deletions crates/wit-component/src/encoding/wit/mod.rs

This file was deleted.

Loading