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 the wit_component::ComponentInterfaces type #833

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 9 additions & 56 deletions crates/wit-component/src/decoding.rs
Expand Up @@ -85,68 +85,19 @@ struct InterfaceDecoder<'a> {
name_map: IndexMap<PtrHash<'a, types::Type>, &'a str>,
}

/// Parsed representation of interfaces found within a component.
///
/// This is more-or-less a "world" and will likely be replaced one day with a
/// `wit-parser` representation of a world.
#[derive(Clone, Default)]
pub struct ComponentInterfaces {
/// The "default export" which is the interface directly exported from the
/// component at the top level.
pub default: Option<Interface>,
/// Imported interfaces, keyed by name, of the component.
pub imports: IndexMap<String, Interface>,
/// Exported interfaces, keyed by name, of the component.
pub exports: IndexMap<String, Interface>,
}

impl ComponentInterfaces {
/// Returns an iterator which visits all the exported interfaces, both named
/// and default. The second entry in each pair the export name of the
/// interface, or `None` if it's the default export interface.
pub fn exports(&self) -> impl Iterator<Item = (&Interface, Option<&str>)> + '_ {
self.exports
.iter()
.map(|(name, i)| (i, Some(name.as_str())))
.chain(self.default.iter().map(|i| (i, None)))
}

/// Converts back into a `wit_parser::World`
pub fn into_world(self, name: &str) -> wit_parser::World {
wit_parser::World {
imports: self.imports,
exports: self.exports,
default: self.default,
name: name.to_string(),
docs: Default::default(),
}
}
}

impl From<wit_parser::World> for ComponentInterfaces {
fn from(world: wit_parser::World) -> ComponentInterfaces {
ComponentInterfaces {
exports: world.exports,
imports: world.imports,
default: world.default,
}
}
}

/// Decode the interfaces imported and exported by a component.
/// Decode the world described by the given component bytes.
///
/// This function takes a binary component as input and will infer the
/// `Interface` representation of its imports and exports. More-or-less this
/// will infer the "world" from a binary component. The binary component at this
/// time is either a "types only" component produced by `wit-component` or an
/// actual output of `wit-component`.
/// `World` representation of its imports and exports. The binary component at
/// this time is either a "types only" component produced by `wit-component` or
/// an actual output of `wit-component`.
///
/// The returned interfaces represent the description of imports and exports
/// The returned world represents the description of imports and exports
/// from the component.
///
/// This can fail if the input component is invalid or otherwise isn't of the
/// expected shape. At this time not all component shapes are supported here.
pub fn decode_component_interfaces(bytes: &[u8]) -> Result<ComponentInterfaces> {
pub fn decode_world(name: &str, bytes: &[u8]) -> Result<World> {
let info = ComponentInfo::new(bytes)?;
let mut imports = IndexMap::new();
let mut exports = IndexMap::new();
Expand Down Expand Up @@ -224,7 +175,9 @@ pub fn decode_component_interfaces(bytes: &[u8]) -> Result<ComponentInterfaces>
Some(InterfaceDecoder::new(&info).decode(default.iter().map(|(n, t)| (*n, *t)))?)
};

Ok(ComponentInterfaces {
Ok(World {
name: name.to_string(),
docs: Default::default(),
imports,
exports,
default,
Expand Down
37 changes: 14 additions & 23 deletions crates/wit-component/src/encoding.rs
Expand Up @@ -58,7 +58,7 @@ use crate::{
validate_adapter_module, validate_module, ValidatedAdapter, ValidatedModule,
MAIN_MODULE_IMPORT_NAME,
},
ComponentInterfaces, StringEncoding,
StringEncoding,
};
use anyhow::{anyhow, bail, Context, Result};
use indexmap::{map::Entry, IndexMap, IndexSet};
Expand All @@ -69,7 +69,7 @@ use wasmparser::{FuncType, Validator, WasmFeatures};
use wit_parser::{
abi::{AbiVariant, WasmSignature, WasmType},
Enum, Flags, Function, FunctionKind, Interface, Params, Record, Result_, Results, Tuple, Type,
TypeDef, TypeDefKind, Union, Variant,
TypeDef, TypeDefKind, Union, Variant, World,
};

const INDIRECT_TABLE_NAME: &str = "$imports";
Expand Down Expand Up @@ -1300,7 +1300,7 @@ impl<'a> EncodingState<'a> {
instance_index: u32,
realloc_index: Option<u32>,
) -> Result<()> {
for (export, export_name) in metadata.interfaces.exports() {
for (export, export_name) in metadata.world.exports() {
let mut interface_exports = Vec::new();

// Make sure all named types are present in the exported instance
Expand Down Expand Up @@ -2167,15 +2167,10 @@ impl ComponentEncoder {
/// Add a "world" of interfaces (exports/imports/default) to this encoder
/// to configure what's being imported/exported.
///
/// The string encoding of the specified interface set is supplied here as
/// The string encoding of the specified world is supplied here as
/// well.
pub fn interfaces(
mut self,
interfaces: ComponentInterfaces,
encoding: StringEncoding,
) -> Result<Self> {
self.metadata
.merge(BindgenMetadata::new(interfaces, encoding))?;
pub fn world(mut self, world: World, encoding: StringEncoding) -> Result<Self> {
self.metadata.merge(BindgenMetadata::new(world, encoding))?;
Ok(self)
}

Expand Down Expand Up @@ -2225,15 +2220,11 @@ impl ComponentEncoder {
let mut state = EncodingState::default();
let mut types = TypeEncoder::default();
let mut imports = ImportEncoder::default();
types.encode_func_types(self.metadata.interfaces.exports().map(|p| p.0))?;
types.encode_instance_imports(
&self.metadata.interfaces.imports,
info.as_ref(),
&mut imports,
)?;
types.encode_func_types(self.metadata.world.exports().map(|p| p.0))?;
types.encode_instance_imports(&self.metadata.world.imports, info.as_ref(), &mut imports)?;

for (_name, (_, metadata)) in self.adapters.iter() {
types.encode_func_types(metadata.interfaces.exports().map(|p| p.0))?;
types.encode_func_types(metadata.world.exports().map(|p| p.0))?;
}

if self.types_only {
Expand All @@ -2248,7 +2239,7 @@ impl ComponentEncoder {
// itself.
let mut raw_exports = Vec::new();
let mut default_exports = Vec::new();
for (interface, name) in self.metadata.interfaces.exports() {
for (interface, name) in self.metadata.world.exports() {
match name {
Some(name) => {
let index = types
Expand Down Expand Up @@ -2310,13 +2301,13 @@ impl ComponentEncoder {
.context("failed to validate the imports of the minimized adapter module")?;
state.encode_core_adapter_module(name, &wasm);
for (name, required) in info.required_imports.iter() {
let interface = &metadata.interfaces.imports[*name];
let interface = &metadata.world.imports[*name];
types.encode_instance_import(name, interface, Some(required), &mut imports)?;
}
imports.adapters.insert(name, info);
}

for (interface, _default) in self.metadata.interfaces.exports() {
for (interface, _default) in self.metadata.world.exports() {
types.encode_interface_named_types(interface)?;
}

Expand All @@ -2332,7 +2323,7 @@ impl ComponentEncoder {
state.realloc_index,
)?;
for (name, (_, metadata)) in self.adapters.iter() {
if metadata.interfaces.exports().count() == 0 {
if metadata.world.exports().count() == 0 {
continue;
}
state.encode_exports(
Expand Down Expand Up @@ -2373,7 +2364,7 @@ fn required_adapter_exports(
required.insert(name.to_string(), ty.clone());
}
}
for (interface, name) in metadata.interfaces.exports() {
for (interface, name) in metadata.world.exports() {
for func in interface.functions.iter() {
let name = func.core_export_name(name);
let ty = interface.wasm_signature(AbiVariant::GuestExport, func);
Expand Down
2 changes: 1 addition & 1 deletion crates/wit-component/src/lib.rs
Expand Up @@ -13,7 +13,7 @@ mod gc;
mod printing;
mod validation;

pub use decoding::{decode_component_interfaces, ComponentInterfaces};
pub use decoding::decode_world;
pub use encoding::*;
pub use printing::*;

Expand Down
70 changes: 32 additions & 38 deletions crates/wit-component/src/metadata.rs
Expand Up @@ -16,22 +16,25 @@
//! per-language-binding-generation and consumed by slurping up all the
//! sections during the component creation process.
//!
//! The custom section here contains `ComponentInterfaces`, the interpretation
//! of a "world" of a component, along with how strings are encoded for all the
//! specified interfaces. Currently the encoding is:
//! The custom section here contains `World`, the interpretation of a "world"
//! of a component, along with how strings are encoded for all the specified
//! interfaces. Currently the encoding is:
//!
//! * First, a version byte (`CURRENT_VERSION`). This is intended to detect
//! mismatches between different versions of the binding generator and
//! `wit-component` which may or may not become a problem over time.
//!
//! * Next a string encoding byte.
//!
//! * Afterwards a "types only" component encoding of a `ComponentInterfaces`
//! * Afterwards a "types only" component encoding of a `World`
//! package through the `ComponentEncoder::types_only` configuration.

use crate::{decode_component_interfaces, ComponentEncoder, ComponentInterfaces, StringEncoding};
use anyhow::{anyhow, bail, Context, Result};
use crate::{decode_world, ComponentEncoder, StringEncoding};
use anyhow::{bail, Context, Result};
use indexmap::IndexMap;
use wasm_encoder::Encode;
use wasmparser::BinaryReader;
use wit_parser::World;

const CURRENT_VERSION: u8 = 0x01;

Expand All @@ -40,9 +43,8 @@ const CURRENT_VERSION: u8 = 0x01;
/// This structure is returned by the [`extract_module_interfaces`] function.
#[derive(Default)]
pub struct BindgenMetadata {
/// All interfaces found within a module, merged together into one set of
/// `ComponentInterfaces`.
pub interfaces: ComponentInterfaces,
/// All interfaces found within a module, merged together into one `World`.
pub world: World,

/// Per-function options imported into the core wasm module, currently only
/// related to string encoding.
Expand Down Expand Up @@ -97,10 +99,10 @@ pub fn decode(wasm: &[u8]) -> Result<(Vec<u8>, BindgenMetadata)> {
/// into the final core wasm binary. The core wasm binary is later fed
/// through `wit-component` to produce the actual component where this returned
/// section will be decoded.
pub fn encode(interfaces: &ComponentInterfaces, encoding: StringEncoding) -> Vec<u8> {
pub fn encode(world: &World, encoding: StringEncoding) -> Vec<u8> {
let component = ComponentEncoder::default()
.types_only(true)
.interfaces(interfaces.clone(), encoding)
.world(world.clone(), encoding)
.unwrap()
.encode()
.unwrap();
Expand All @@ -112,60 +114,57 @@ pub fn encode(interfaces: &ComponentInterfaces, encoding: StringEncoding) -> Vec
StringEncoding::UTF16 => 0x01,
StringEncoding::CompactUTF16 => 0x02,
});
world.name.encode(&mut ret);
ret.extend(component);
ret
}

impl BindgenMetadata {
fn decode(data: &[u8]) -> Result<BindgenMetadata> {
let mut data = data.iter();
let version = *data
.next()
.ok_or_else(|| anyhow!("component-type metadata section"))?;
let mut reader = BinaryReader::new(data);
let version = reader.read_u8()?;
if version != CURRENT_VERSION {
bail!("component-type version {version} does not match supported version {CURRENT_VERSION}");
}
let encoding = match data
.next()
.ok_or_else(|| anyhow!("component-type metadata section"))?
{
let encoding = match reader.read_u8()? {
0x00 => StringEncoding::UTF8,
0x01 => StringEncoding::UTF16,
0x02 => StringEncoding::CompactUTF16,
byte => bail!("invalid string encoding {byte:#x}"),
};
let name = reader.read_string()?;

Ok(BindgenMetadata::new(
decode_component_interfaces(data.as_slice())?,
decode_world(name, &data[reader.original_position()..])?,
encoding,
))
}

/// Creates a new `BindgenMetadata` instance holding the given set of
/// interfaces which are expected to all use the `encoding` specified.
pub fn new(interfaces: ComponentInterfaces, encoding: StringEncoding) -> BindgenMetadata {
pub fn new(world: World, encoding: StringEncoding) -> BindgenMetadata {
let mut ret = BindgenMetadata {
interfaces,
world,
import_encodings: Default::default(),
export_encodings: Default::default(),
};

if let Some(iface) = &ret.interfaces.default {
if let Some(iface) = &ret.world.default {
for func in iface.functions.iter() {
let name = func.core_export_name(None);
let prev = ret.export_encodings.insert(name.to_string(), encoding);
assert!(prev.is_none());
}
}

for (name, import) in ret.interfaces.imports.iter() {
for (name, import) in ret.world.imports.iter() {
for func in import.functions.iter() {
let key = (name.clone(), func.name.clone());
let prev = ret.import_encodings.insert(key, encoding);
assert!(prev.is_none());
}
}
for (name, export) in ret.interfaces.exports.iter() {
for (name, export) in ret.world.exports.iter() {
for func in export.functions.iter() {
let name = func.core_export_name(Some(name));
let prev = ret.export_encodings.insert(name.to_string(), encoding);
Expand All @@ -186,36 +185,31 @@ impl BindgenMetadata {
/// between metadata.
pub fn merge(&mut self, other: BindgenMetadata) -> Result<()> {
let BindgenMetadata {
interfaces:
ComponentInterfaces {
imports,
exports,
default,
},
world,
import_encodings,
export_encodings,
} = other;

// TODO: instead of returning an error here on duplicate interfaces
// this should merge the two interfaces. This probably requires world
// files to exist first though.
for (name, import) in imports {
let prev = self.interfaces.imports.insert(name.clone(), import);
for (name, import) in world.imports {
let prev = self.world.imports.insert(name.clone(), import);
if prev.is_some() {
bail!("import interface `{name}` specified twice");
}
}
for (name, export) in exports {
let prev = self.interfaces.exports.insert(name.clone(), export);
for (name, export) in world.exports {
let prev = self.world.exports.insert(name.clone(), export);
if prev.is_some() {
bail!("export interface `{name}` specified twice");
}
}
if let Some(default) = default {
if self.interfaces.default.is_some() {
if let Some(default) = world.default {
if self.world.default.is_some() {
bail!("default export interface specified twice");
}
self.interfaces.default = Some(default);
self.world.default = Some(default);
}

for (name, encoding) in export_encodings {
Expand Down