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

Fix some issues with future encodings #1270

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
17 changes: 14 additions & 3 deletions crates/wit-component/src/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,18 +346,29 @@ pub fn decode(bytes: &[u8]) -> Result<DecodedWasm> {

/// Decodes the single component type `world` specified as a WIT world.
///
/// The `name` provided should be a full ID such as `foo:bar/baz`.
/// The `world` should be an exported component type. The `world` must have been
/// previously created via `encode_world` meaning that it is a component that
/// itself imports nothing and exports a single component, and the single
/// component export represents the world. The name of the export is also the
/// name of the package/world/etc.
pub(crate) fn decode_world(
types: &types::Types,
name: &str,
world: types::ComponentTypeId,
) -> Result<(Resolve, WorldId)> {
let mut decoder = WitPackageDecoder::new(types);
let mut interfaces = IndexMap::new();
let mut worlds = IndexMap::new();
let ty = &types[world];
assert_eq!(ty.imports.len(), 0);
assert_eq!(ty.exports.len(), 1);
let name = ty.exports.keys().nth(0).unwrap();
let ty = match ty.exports[0] {
types::ComponentEntityType::Component(ty) => ty,
_ => unreachable!(),
};
let name = decoder.decode_world(
name,
&types[world],
&types[ty],
&mut PackageFields {
interfaces: &mut interfaces,
worlds: &mut worlds,
Expand Down
20 changes: 5 additions & 15 deletions crates/wit-component/src/encoding/wit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ fn use_v2_encoding() -> bool {
/// The binary returned can be [`decode`d](crate::decode) to recover the WIT
/// package provided.
pub fn encode(use_v2: Option<bool>, resolve: &Resolve, package: PackageId) -> Result<Vec<u8>> {
if use_v2.unwrap_or_else(use_v2_encoding) {
v2::encode(resolve, package)
} else {
v1::encode(resolve, package)
}
let mut component = encode_component(use_v2, resolve, package)?;
component.raw_custom_section(&crate::base_producers().raw_custom_section());
Ok(component.finish())
}

/// Exactly like `encode`, except gives an unfinished `ComponentBuilder` in case you need
Expand All @@ -55,14 +53,6 @@ pub fn encode_component(
}

/// Encodes a `world` as a component type.
pub fn encode_world(
use_v2: Option<bool>,
resolve: &Resolve,
world_id: WorldId,
) -> Result<ComponentType> {
if use_v2.unwrap_or_else(use_v2_encoding) {
v2::encode_world(resolve, world_id)
} else {
v1::encode_world(resolve, world_id)
}
pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentType> {
v1::encode_world(resolve, world_id)
}
8 changes: 0 additions & 8 deletions crates/wit-component/src/encoding/wit/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,6 @@ use wit_parser::*;
///
/// 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())
}

/// Exactly like `encode`, except gives an unfinished `ComponentBuilder` in case you need
/// to append anything else before finishing.
pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result<ComponentBuilder> {
let mut encoder = Encoder {
component: ComponentBuilder::default(),
Expand Down
104 changes: 11 additions & 93 deletions crates/wit-component/src/encoding/wit/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,6 @@ use wit_parser::*;
///
/// 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())
}

/// Exactly like `encode`, except gives an unfinished `ComponentBuilder` in case you need
/// to append anything else before finishing.
pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result<ComponentBuilder> {
let mut encoder = Encoder {
component: ComponentBuilder::default(),
Expand Down Expand Up @@ -75,8 +67,17 @@ impl Encoder<'_> {
}

for (name, &world) in self.resolve.packages[self.package].worlds.iter() {
let component_ty = encode_world(self.resolve, world)?;
let ty = self.component.type_component(&component_ty);
// 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);
}
Expand Down Expand Up @@ -332,86 +333,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 encoder = 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(i) => {
encoder.interface = Some(*i);
let idx = encoder.encode_instance(*i)?;
ComponentTypeRef::Instance(idx)
}
WorldItem::Function(f) => {
encoder.interface = None;
let idx = encoder.encode_func_type(resolve, f)?;
ComponentTypeRef::Func(idx)
}
WorldItem::Type(t) => {
encoder.interface = None;
encoder.import_types = true;
encoder.encode_valtype(resolve, &Type::Id(*t))?;
encoder.import_types = false;
continue;
}
};
encoder.outer.import(&name, ty);
}
// Encode the exports
for (name, export) in world.exports.iter() {
let name = resolve.name_world_key(name);
let ty = match export {
WorldItem::Interface(i) => {
encoder.interface = Some(*i);
let idx = encoder.encode_instance(*i)?;
ComponentTypeRef::Instance(idx)
}
WorldItem::Function(f) => {
encoder.interface = None;
let idx = encoder.encode_func_type(resolve, f)?;
ComponentTypeRef::Func(idx)
}
WorldItem::Type(_) => unreachable!(),
};
encoder.outer.export(&name, ty);
}

let mut component = ComponentType::new();
component.ty().component(&encoder.outer);

let name = match world.package {
Some(id) => resolve.packages[id].name.interface_id(&world.name),
None => world.name.clone(),
};
component.export(&name, ComponentTypeRef::Component(0));
Ok(component)
}
27 changes: 16 additions & 11 deletions crates/wit-component/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ use crate::{DecodedWasm, StringEncoding};
use anyhow::{bail, Context, Result};
use indexmap::IndexMap;
use std::borrow::Cow;
use wasm_encoder::{ComponentBuilder, ComponentExportKind, CustomSection, Encode};
use wasm_encoder::{
ComponentBuilder, ComponentExportKind, ComponentType, ComponentTypeRef, CustomSection, Encode,
};
use wasm_metadata::Producers;
use wasmparser::types::ComponentAnyTypeId;
use wasmparser::{
Expand Down Expand Up @@ -225,7 +227,15 @@ pub fn encode(
ret
}
EncodingFormat::Next => {
let ty = crate::encoding::encode_world(None, resolve, world)?;
let ty = crate::encoding::encode_world(resolve, world)?;

let world = &resolve.worlds[world];
let mut outer_ty = ComponentType::new();
outer_ty.ty().component(&ty);
outer_ty.export(
&resolve.id_of_name(world.package.unwrap(), &world.name),
ComponentTypeRef::Component(0),
);

let mut builder = ComponentBuilder::default();

Expand All @@ -234,14 +244,9 @@ pub fn encode(
name: CUSTOM_SECTION_NAME.into(),
data: Cow::Borrowed(&[CURRENT_VERSION, string_encoding]),
});
let ty = builder.type_component(&ty);
let world = &resolve.worlds[world];
builder.export(
&resolve.id_of_name(world.package.unwrap(), &world.name),
ComponentExportKind::Type,
ty,
None,
);

let ty = builder.type_component(&outer_ty);
builder.export(&world.name, ComponentExportKind::Type, ty, None);

let mut producers = crate::base_producers();
if let Some(p) = extra_producers {
Expand Down Expand Up @@ -313,7 +318,7 @@ fn decode_custom_section(wasm: &[u8]) -> Result<(Resolve, WorldId, StringEncodin
_ => bail!("expected an exported component type"),
};

let (resolve, world) = crate::decoding::decode_world(types, exports[0].name.0, ty)?;
let (resolve, world) = crate::decoding::decode_world(types, ty)?;
Ok((resolve, world, string_encoding))
}

Expand Down
2 changes: 1 addition & 1 deletion crates/wit-component/src/targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn targets(resolve: &Resolve, world: WorldId, component_to_test: &[u8]) -> R
// (2) Encode the world to a component type and embed a new component which
// imports the encoded component type.
let test_component_idx = {
let component_ty = encode_world(None, resolve, world)?;
let component_ty = encode_world(resolve, world)?;
let mut component = ComponentBuilder::default();
let component_ty_idx = component.type_component(&component_ty);
component.import(
Expand Down