Skip to content

Commit

Permalink
Remove v1 encoding support for WIT documents/worlds (#1781)
Browse files Browse the repository at this point in the history
* Remove v1 encoding support for WIT documents/worlds

This commit removes support for the "v1" encoding of WIT packages into
WebAssembly. This was originally migrated to v2 in #1252 and v2 was
enabled by default in #1274. This finishes the transition by removing
support for creating the old encoding.

* Fix fuzzer build

* Update outdated comments
  • Loading branch information
alexcrichton authored Sep 12, 2024
1 parent de9369c commit 564157e
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 500 deletions.
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() {
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

0 comments on commit 564157e

Please sign in to comment.