From 5be459e57fec1cd636cd0fbb2ebb887927b55ac9 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 24 Oct 2022 07:15:46 -0700 Subject: [PATCH] Some clean-up work. (#391) * Fix some typos and linter warnings. This commit fixes some random comment typos and various linter warnings. * wit-component: rename `decode_interface_component`. This commit renames `decode_interface_component` to `decode_component_interfaces` to better describe what the function does. Previously it was meant to decode an "interface-only" component, but now it returns all the interfaces (i.e. the "world") of the component. * wit-component: implement multi-value return printing. This commit implements printing of multi-value returns of interfaces decoded from a component. * Remove `module` field from `Interface`. The `module` field was originally used by `cargo component` but is no longer necessary and otherwise unused from `wit-bindgen` itself. * Remove `symbol_namespace` from Rust generator. This field is no longer used anywhere in the wit-bindgen repo. * Add method to `Interface` for determining core export names. This commit extracts the expected core export name out of `wit-component` and into `Interface`, allowing generators to conform to the name expected by `wit-component`. --- build.rs | 6 ++- crates/bindgen-core/src/component.rs | 2 +- crates/bindgen-core/src/lib.rs | 12 +++--- crates/gen-guest-c/src/lib.rs | 10 +++-- crates/gen-guest-rust/src/lib.rs | 37 +++++------------ crates/gen-guest-teavm-java/src/lib.rs | 11 ++--- crates/wit-component/src/cli.rs | 8 ++-- crates/wit-component/src/decoding.rs | 10 ++--- crates/wit-component/src/encoding.rs | 23 ++++------- crates/wit-component/src/extract.rs | 23 +++++------ crates/wit-component/src/gc.rs | 16 +++++--- crates/wit-component/src/lib.rs | 2 +- crates/wit-component/src/printing.rs | 12 +++++- crates/wit-component/src/validation.rs | 41 ++++++++----------- crates/wit-component/tests/interfaces.rs | 2 +- .../tests/interfaces/integers/component.wat | 8 ++-- .../interfaces/integers/import-integers.wit | 2 +- .../tests/interfaces/integers/types_only.wat | 8 ++-- crates/wit-parser/src/ast/resolve.rs | 3 +- crates/wit-parser/src/lib.rs | 18 ++++---- 20 files changed, 123 insertions(+), 131 deletions(-) diff --git a/build.rs b/build.rs index 5b94b9613..707ae43fe 100644 --- a/build.rs +++ b/build.rs @@ -26,7 +26,9 @@ fn commit_info() { let mut next = || parts.next().unwrap(); println!("cargo:rustc-env=CARGO_GIT_HASH={}", next()); println!( - "cargo:rustc-env=CARGO_VERSION_INFO={}", - format!("{} ({} {})", env!("CARGO_PKG_VERSION"), next(), next()) + "cargo:rustc-env=CARGO_VERSION_INFO={} ({} {})", + env!("CARGO_PKG_VERSION"), + next(), + next() ); } diff --git a/crates/bindgen-core/src/component.rs b/crates/bindgen-core/src/component.rs index e940a2c23..8ba62ec5a 100644 --- a/crates/bindgen-core/src/component.rs +++ b/crates/bindgen-core/src/component.rs @@ -31,7 +31,7 @@ pub fn generate( // will likely change as worlds are iterated on in the component model // standard. Regardless though this is the step where types are learned // and `Interface`s are constructed for further code generation below. - let interfaces = wit_component::decode_interface_component(binary) + let interfaces = wit_component::decode_component_interfaces(binary) .context("failed to extract interface information from component")?; // Components are complicated, there's no real way around that. To diff --git a/crates/bindgen-core/src/lib.rs b/crates/bindgen-core/src/lib.rs index 5ef839864..02e1bb307 100644 --- a/crates/bindgen-core/src/lib.rs +++ b/crates/bindgen-core/src/lib.rs @@ -139,8 +139,8 @@ pub trait Generator { for f in iface.functions.iter() { match dir { - Direction::Import => self.import(iface, &f), - Direction::Export => self.export(iface, &f), + Direction::Import => self.import(iface, f), + Direction::Export => self.export(iface, f), } } @@ -266,7 +266,7 @@ impl Types { } } self.type_info.insert(ty, info); - return info; + info } pub fn type_info(&mut self, iface: &Interface, ty: &Type) -> TypeInfo { @@ -415,7 +415,7 @@ impl Source { let lines = src.lines().collect::>(); for (i, line) in lines.iter().enumerate() { let trimmed = line.trim(); - if trimmed.starts_with("}") && self.s.ends_with(" ") { + if trimmed.starts_with('}') && self.s.ends_with(" ") { self.s.pop(); self.s.pop(); } @@ -434,7 +434,7 @@ impl Source { // looking at the source code rather than getting a panic. self.indent = self.indent.saturating_sub(1); } - if i != lines.len() - 1 || src.ends_with("\n") { + if i != lines.len() - 1 || src.ends_with('\n') { self.newline(); } } @@ -449,7 +449,7 @@ impl Source { } fn newline(&mut self) { - self.s.push_str("\n"); + self.s.push('\n'); for _ in 0..self.indent { self.s.push_str(" "); } diff --git a/crates/gen-guest-c/src/lib.rs b/crates/gen-guest-c/src/lib.rs index 9c55df44f..d49427f0a 100644 --- a/crates/gen-guest-c/src/lib.rs +++ b/crates/gen-guest-c/src/lib.rs @@ -1098,6 +1098,10 @@ impl Generator for C { let sig = iface.wasm_signature(AbiVariant::GuestExport, func); + // Currently the C generator always emits default exports + // This needs to change once the generator works from a world + let export_name = iface.core_export_name(true, func); + // Print the actual header for this function into the header file, and // it's what we'll be calling. let c_sig = self.print_sig(iface, func); @@ -1106,8 +1110,7 @@ impl Generator for C { // canonical ABI. uwriteln!( self.src.c_adapters, - "\n__attribute__((export_name(\"{}\")))", - func.name + "__attribute__((export_name(\"{export_name}\")))" ); let import_name = self.names.tmp(&format!( "__wasm_export_{}_{}", @@ -1151,8 +1154,7 @@ impl Generator for C { if iface.guest_export_needs_post_return(func) { uwriteln!( self.src.c_fns, - "\n__attribute__((weak, export_name(\"cabi_post_{}\")))", - func.name + "__attribute__((export_name(\"cabi_post_{export_name}\")))" ); uwrite!(self.src.c_fns, "void {import_name}_post_return("); diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index 4d1d53d5b..25dc8ba47 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -32,11 +32,6 @@ pub struct Opts { #[cfg_attr(feature = "clap", arg(long))] pub unchecked: bool, - /// A prefix to prepend to all exported symbols. Note that this is only - /// intended for testing because it breaks the general form of the ABI. - #[cfg_attr(feature = "clap", arg(skip))] - pub symbol_namespace: String, - /// If true, code generation should avoid any features that depend on `std`. #[cfg_attr(feature = "clap", arg(long))] pub no_std: bool, @@ -106,12 +101,12 @@ impl WorldGenerator for RustWasm { fn export(&mut self, name: &str, iface: &Interface, _files: &mut Files) { self.interface(iface, TypeMode::Owned, false) - .generate_exports(name); + .generate_exports(name, false); } fn export_default(&mut self, name: &str, iface: &Interface, _files: &mut Files) { self.interface(iface, TypeMode::Owned, false) - .generate_exports(name); + .generate_exports(name, true); } fn finish(&mut self, name: &str, interfaces: &ComponentInterfaces, files: &mut Files) { @@ -214,7 +209,7 @@ struct InterfaceGenerator<'a> { } impl InterfaceGenerator<'_> { - fn generate_exports(mut self, name: &str) { + fn generate_exports(mut self, name: &str, default_export: bool) { self.types(); let camel = name.to_upper_camel_case(); @@ -228,7 +223,7 @@ impl InterfaceGenerator<'_> { uwriteln!(self.src, "}}"); for func in self.iface.functions.iter() { - self.generate_guest_export(name, func); + self.generate_guest_export(name, func, default_export); } self.append_submodule(name); @@ -301,18 +296,12 @@ impl InterfaceGenerator<'_> { } } - fn generate_guest_export(&mut self, module_name: &str, func: &Function) { + fn generate_guest_export(&mut self, module_name: &str, func: &Function, default_export: bool) { let module_name = module_name.to_snake_case(); let trait_bound = module_name.to_upper_camel_case(); let iface_snake = self.iface.name.to_snake_case(); let name_snake = func.name.to_snake_case(); - let name = match &self.iface.module { - Some(module) => { - format!("{module}#{}", func.name) - } - None => format!("{}{}", self.gen.opts.symbol_namespace, func.name), - }; - + let export_name = self.iface.core_export_name(default_export, func); let mut macro_src = Source::default(); // Generate, simultaneously, the actual lifting/lowering function within @@ -333,7 +322,7 @@ impl InterfaceGenerator<'_> { uwrite!( macro_src, " - #[export_name = \"{name}\"] + #[export_name = \"{export_name}\"] unsafe extern \"C\" fn export_{iface_snake}_{name_snake}(\ ", ); @@ -397,11 +386,9 @@ impl InterfaceGenerator<'_> { uwrite!( macro_src, " - #[export_name = \"{}cabi_post_{}\"] + #[export_name = \"cabi_post_{export_name}\"] pub unsafe extern \"C\" fn post_return_{iface_snake}_{name_snake}(\ - ", - self.gen.opts.symbol_namespace, - func.name, + " ); let mut params = Vec::new(); for (i, result) in sig.results.iter().enumerate() { @@ -620,18 +607,16 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { params: &[WasmType], results: &[WasmType], ) -> String { - let module = iface.module.as_deref().unwrap_or(&iface.name); - // Define the actual function we're calling inline self.push_str("#[link(wasm_import_module = \""); - self.push_str(module); + self.push_str(&iface.name); self.push_str("\")]\n"); self.push_str("extern \"C\" {\n"); self.push_str("#[cfg_attr(target_arch = \"wasm32\", link_name = \""); self.push_str(name); self.push_str("\")]\n"); self.push_str("#[cfg_attr(not(target_arch = \"wasm32\"), link_name = \""); - self.push_str(module); + self.push_str(&iface.name); self.push_str("_"); self.push_str(name); self.push_str("\")]\n"); diff --git a/crates/gen-guest-teavm-java/src/lib.rs b/crates/gen-guest-teavm-java/src/lib.rs index 4589aad63..8d2031884 100644 --- a/crates/gen-guest-teavm-java/src/lib.rs +++ b/crates/gen-guest-teavm-java/src/lib.rs @@ -645,6 +645,10 @@ impl Generator for TeaVmJava { fn export(&mut self, iface: &Interface, func: &Function) { let sig = iface.wasm_signature(AbiVariant::GuestExport, func); + // Currently the Java generator always emits default exports + // This needs to change once the generator works from a world + let export_name = iface.core_export_name(true, func); + let mut bindgen = FunctionBindgen::new( self, &func.name, @@ -661,7 +665,6 @@ impl Generator for TeaVmJava { assert!(!bindgen.needs_cleanup_list); let src = bindgen.src; - let name = &func.name; let result_type = match &sig.results[..] { [] => "void", @@ -685,7 +688,7 @@ impl Generator for TeaVmJava { uwrite!( self.src, r#" - @Export(name = "{name}") + @Export(name = "{export_name}") private static {result_type} wasmExport{camel_name}({params}) {{ {src} }} @@ -693,8 +696,6 @@ impl Generator for TeaVmJava { ); if iface.guest_export_needs_post_return(func) { - let name = &func.name; - let params = sig .results .iter() @@ -719,7 +720,7 @@ impl Generator for TeaVmJava { uwrite!( self.src, r#" - @Export(name = "cabi_post_{name}") + @Export(name = "cabi_post_{export_name}") private static void wasmExport{camel_name}PostReturn({params}) {{ {src} }} diff --git a/crates/wit-component/src/cli.rs b/crates/wit-component/src/cli.rs index 6db816103..53ccb835c 100644 --- a/crates/wit-component/src/cli.rs +++ b/crates/wit-component/src/cli.rs @@ -1,10 +1,8 @@ //! The WebAssembly component tool command line interface. -#![deny(missing_docs)] - use crate::extract::{extract_module_interfaces, ModuleInterfaces}; use crate::{ - decode_interface_component, ComponentEncoder, ComponentInterfaces, InterfacePrinter, + decode_component_interfaces, ComponentEncoder, ComponentInterfaces, InterfacePrinter, StringEncoding, }; use anyhow::{anyhow, bail, Context, Result}; @@ -71,7 +69,7 @@ fn parse_adapter(s: &str) -> Result<(String, Vec, Interface)> { default, }, } = extract_module_interfaces(&wasm)?; - if exports.len() > 0 || default.is_some() { + if !exports.is_empty() || default.is_some() { bail!("adapter modules cannot have an exported interface"); } let import = match imports.len() { @@ -233,7 +231,7 @@ impl WasmToWitApp { let bytes = wat::parse_file(&self.component) .with_context(|| format!("failed to parse component `{}`", self.component.display()))?; - let interfaces = decode_interface_component(&bytes).with_context(|| { + let interfaces = decode_component_interfaces(&bytes).with_context(|| { format!("failed to decode component `{}`", self.component.display()) })?; let which = match &self.import { diff --git a/crates/wit-component/src/decoding.rs b/crates/wit-component/src/decoding.rs index e80585b8b..974bff716 100644 --- a/crates/wit-component/src/decoding.rs +++ b/crates/wit-component/src/decoding.rs @@ -91,11 +91,11 @@ struct InterfaceDecoder<'a> { #[derive(Default)] pub struct ComponentInterfaces { /// The "default export" which is the interface directly exported from the - /// component at the first level. + /// component at the top level. pub default: Option, - /// Imported interfaces, keyed by name, to the component. + /// Imported interfaces, keyed by name, of the component. pub imports: IndexMap, - /// Exported interfaces, keyed by name, to the component. + /// Exported interfaces, keyed by name, of the component. pub exports: IndexMap, } @@ -112,7 +112,7 @@ pub struct ComponentInterfaces { /// /// 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_interface_component(bytes: &[u8]) -> Result { +pub fn decode_component_interfaces(bytes: &[u8]) -> Result { let info = ComponentInfo::new(bytes)?; let mut imports = IndexMap::new(); let mut exports = IndexMap::new(); @@ -224,7 +224,7 @@ impl<'a> InterfaceDecoder<'a> { } // Iterate over all exports an interpret them as defined items within - // the interface, either functiosn or types at this time. + // the interface, either functions or types at this time. for (name, ty) in map { match ty { types::ComponentEntityType::Func(ty) => { diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index ad99b8b12..c82802665 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -54,10 +54,7 @@ use crate::extract::{extract_module_interfaces, ModuleInterfaces}; use crate::{ - validation::{ - expected_export_name, validate_adapter_module, validate_module, ValidatedAdapter, - ValidatedModule, - }, + validation::{validate_adapter_module, validate_module, ValidatedAdapter, ValidatedModule}, ComponentInterfaces, StringEncoding, }; use anyhow::{anyhow, bail, Context, Result}; @@ -1311,9 +1308,7 @@ impl<'a> EncodingState<'a> { // Alias the exports from the core module for func in &export.functions { - let name = - expected_export_name((!is_default).then(|| export.name.as_str()), &func.name); - + let name = export.core_export_name(is_default, func); let core_func_index = self.component.alias_core_item( core_instance_index, ExportKind::Func, @@ -1424,7 +1419,7 @@ impl<'a> EncodingState<'a> { } for shim in ret.list.iter() { - ret.shim_names.insert(shim.kind.clone(), shim.name.clone()); + ret.shim_names.insert(shim.kind, shim.name.clone()); } assert!(self.shim_instance_index.is_none()); @@ -1572,7 +1567,7 @@ impl<'a> EncodingState<'a> { let (instance_index, _, interface) = imports.map.get_full(interface).unwrap(); let func_index = self.component.alias_func( instance_index as u32, - &interface.indirect[*indirect_index].name, + interface.indirect[*indirect_index].name, ); let realloc = match realloc { @@ -2143,7 +2138,7 @@ impl ComponentEncoder { if self.interface.is_some() { bail!("default interface cannot be specified twice"); } - self.interface = Some(interface.clone()); + self.interface = Some(interface); Ok(self) } @@ -2151,7 +2146,7 @@ impl ComponentEncoder { pub fn imports(mut self, imports: impl IntoIterator) -> Result { for i in imports { if self.imports.contains_key(&i.name) { - bail!("cannot specifiy import interface for `{}` twice", i.name); + bail!("cannot specify import interface for `{}` twice", i.name); } self.imports.insert(i.name.clone(), i); } @@ -2162,7 +2157,7 @@ impl ComponentEncoder { pub fn exports(mut self, exports: impl IntoIterator) -> Result { for i in exports { if self.exports.contains_key(&i.name) { - bail!("cannot specifiy export interface for `{}` twice", i.name); + bail!("cannot specify export interface for `{}` twice", i.name); } self.exports.insert(i.name.clone(), i); } @@ -2214,7 +2209,7 @@ impl ComponentEncoder { default, }, } = extract_module_interfaces(&wasm)?; - if exports.len() > 0 || default.is_some() { + if !exports.is_empty() || default.is_some() { bail!("adapter modules cannot have an exported interface"); } let import = match imports.len() { @@ -2335,7 +2330,7 @@ impl ComponentEncoder { state.encode_imports(&imports); state.encode_core_module(&self.module); - state.encode_core_instantiation(self.encoding, &imports, &info)?; + state.encode_core_instantiation(self.encoding, &imports, info)?; state.encode_exports(self.encoding, exports, &types)?; } diff --git a/crates/wit-component/src/extract.rs b/crates/wit-component/src/extract.rs index 5f106c8dc..35f7b9ac3 100644 --- a/crates/wit-component/src/extract.rs +++ b/crates/wit-component/src/extract.rs @@ -1,9 +1,9 @@ -use crate::{decode_interface_component, ComponentInterfaces}; +use crate::{decode_component_interfaces, ComponentInterfaces}; use anyhow::{bail, Context, Result}; /// Result of extracting interfaces embedded within a core wasm file. /// -/// This structure is reated by the [`extract_module_interfaces`] function. +/// This structure is returned by the [`extract_module_interfaces`] function. #[derive(Default)] pub struct ModuleInterfaces { /// The core wasm binary with custom sections removed. @@ -25,19 +25,18 @@ pub fn extract_module_interfaces(wasm: &[u8]) -> Result { let mut ret = ModuleInterfaces::default(); for payload in wasmparser::Parser::new(0).parse_all(wasm) { - match payload.context("decoding item in module")? { - wasmparser::Payload::CustomSection(cs) => { - if !cs.name().starts_with("component-type") { - continue; - } - ret.decode(cs.data()) - .with_context(|| format!("decoding custom section {}", cs.name()))?; + if let wasmparser::Payload::CustomSection(cs) = + payload.context("decoding item in module")? + { + if !cs.name().starts_with("component-type") { + continue; } - _ => {} + ret.decode(cs.data()) + .with_context(|| format!("decoding custom section {}", cs.name()))?; } } - // TODO: should remove the custom setions decoded above from the wasm binary + // TODO: should remove the custom sections decoded above from the wasm binary // created here, and bytecodealliance/wasmparser#792 should help with that // to make the loop above pretty small. ret.wasm = wasm.to_vec(); @@ -51,7 +50,7 @@ impl ModuleInterfaces { default, imports, exports, - } = decode_interface_component(component)?; + } = decode_component_interfaces(component)?; if let Some(iface) = default { if self.interfaces.default.is_some() { diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index d6e7c8bc1..1a41e39e7 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -40,6 +40,9 @@ pub fn run(wasm: &[u8], required: &IndexMap<&str, FuncType>) -> Result> module.encode() } +// Represents a function called while processing a module work list. +type WorklistFunc<'a> = fn(&mut Module<'a>, u32) -> Result<()>; + // Representation of a wasm module which is used to GC a module to its minimal // set of required items necessary to implement the `exports` // @@ -69,9 +72,9 @@ struct Module<'a> { // Helper data structure used during the `liveness` path to avoid recursion. // When calculating the liveness of an item this `worklist` is pushed to and // then processed until it's empty. An item pushed onto this list represents - // a new index that has been discovered to be live and the function is wehat + // a new index that has been discovered to be live and the function is what // walks the item's definition to find other items that it references. - worklist: Vec<(u32, fn(&mut Module<'a>, u32) -> Result<()>)>, + worklist: Vec<(u32, WorklistFunc<'a>)>, } struct Table<'a> { @@ -384,7 +387,7 @@ impl<'a> Module<'a> { // Keep track of the "empty type" to see if we can reuse an // existing one or one needs to be injected if a `start` // function is calculated at the end. - if ty.params().len() == 0 && ty.results().len() == 0 { + if ty.params().is_empty() && ty.results().is_empty() { empty_type = Some(map.types.remap(i)); } } @@ -678,7 +681,7 @@ impl<'a> Module<'a> { let stack_pointers = mutable_i32_globals .iter() .filter_map(|(i, _)| { - let name = *self.global_names.get(&i)?; + let name = *self.global_names.get(i)?; if name == "__stack_pointer" { Some(*i) } else { @@ -699,7 +702,7 @@ impl<'a> Module<'a> { // special handling for all payloads of instructions to mark any referenced // items live. // -// Currently item identification happesn through the field name of the payload. +// Currently item identification happens through the field name of the payload. // While not exactly the most robust solution this should work well enough for // now. macro_rules! define_visit { @@ -811,6 +814,7 @@ impl Encoder { macro_rules! define_encode { ($(@$p:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( + #[allow(clippy::drop_copy)] fn $visit(&mut self, _offset: usize $(, $($arg: $argty),*)?) { #[allow(unused_imports)] use wasm_encoder::Instruction::*; @@ -993,6 +997,6 @@ impl Remap { fn remap(&self, old: u32) -> u32 { let ret = self.map[old as usize]; assert!(ret != u32::MAX); - return ret; + ret } } diff --git a/crates/wit-component/src/lib.rs b/crates/wit-component/src/lib.rs index 0bb2f90c6..c34e06291 100644 --- a/crates/wit-component/src/lib.rs +++ b/crates/wit-component/src/lib.rs @@ -15,7 +15,7 @@ mod gc; mod printing; mod validation; -pub use decoding::{decode_interface_component, ComponentInterfaces}; +pub use decoding::{decode_component_interfaces, ComponentInterfaces}; pub use encoding::*; pub use extract::*; pub use printing::*; diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index ae7a88896..edeff2a70 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -38,7 +38,17 @@ impl InterfacePrinter { self.output.push_str(" -> "); self.print_type_name(interface, &rs[0].1)?; } - _ => todo!("multireturn: wit component printing"), + _ => { + self.output.push_str(" -> ("); + for (i, (name, ty)) in rs.iter().enumerate() { + if i > 0 { + self.output.push_str(", "); + } + write!(&mut self.output, "{name}: ")?; + self.print_type_name(interface, ty)?; + } + self.output.push(')'); + } }, Results::Anon(ty) => { self.output.push_str(" -> "); diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs index c1ad7b3f7..eeb53dd3d 100644 --- a/crates/wit-component/src/validation.rs +++ b/crates/wit-component/src/validation.rs @@ -1,6 +1,5 @@ use anyhow::{anyhow, bail, Result}; use indexmap::{map::Entry, IndexMap, IndexSet}; -use std::borrow::Cow; use wasmparser::{ types::Types, Encoding, ExternalKind, FuncType, Parser, Payload, TypeRef, ValType, ValidPayload, Validator, @@ -14,15 +13,6 @@ fn is_canonical_function(name: &str) -> bool { name.starts_with("cabi_") } -pub fn expected_export_name<'a>(interface: Option<&str>, func: &'a str) -> Cow<'a, str> { - // TODO: wit-bindgen currently doesn't mangle its export names, so this - // only works with the default (i.e. `None`) interface. - match interface { - Some(interface) => format!("{}#{}", interface, func).into(), - None => func.into(), - } -} - fn wasm_sig_to_func_type(signature: WasmSignature) -> FuncType { fn from_wasm_type(ty: &WasmType) -> ValType { match ty { @@ -179,7 +169,7 @@ pub fn validate_module<'a>( } if let Some(interface) = interface { - validate_exported_interface(interface, None, &export_funcs, &types)?; + validate_exported_interface(interface, true, &export_funcs, &types)?; } for (name, interface) in exports { @@ -187,7 +177,7 @@ pub fn validate_module<'a>( bail!("cannot export an interface with an empty name"); } - validate_exported_interface(interface, Some(name), &export_funcs, &types)?; + validate_exported_interface(interface, false, &export_funcs, &types)?; } Ok(ret) @@ -214,7 +204,7 @@ pub struct ValidatedAdapter<'a> { /// This is the module and field name of the memory import, if one is /// specified. /// - /// Due to LLVM codegen this is typically `env::memory` as a totally separte + /// Due to LLVM codegen this is typically `env::memory` as a totally separate /// import from the `required_import` above. pub needs_memory: Option<(String, String)>, @@ -401,42 +391,43 @@ fn validate_imported_interface<'a>( fn validate_exported_interface( interface: &Interface, - name: Option<&str>, + default_export: bool, exports: &IndexMap<&str, u32>, types: &Types, ) -> Result<()> { for f in &interface.functions { - let expected_export = expected_export_name(name, &f.name); - match exports.get(expected_export.as_ref()) { + let expected_export_name = interface.core_export_name(default_export, f); + match exports.get(expected_export_name.as_ref()) { Some(func_index) => { let expected_ty = wasm_sig_to_func_type(interface.wasm_signature(AbiVariant::GuestExport, f)); let ty = types.function_at(*func_index).unwrap(); if ty != &expected_ty { - match name { - Some(name) => bail!( - "type mismatch for function `{}` from exported interface `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`", + if default_export { + bail!( + "type mismatch for default interface function `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`", f.name, - name, expected_ty.params(), expected_ty.results(), ty.params(), ty.results() - ), - None => bail!( - "type mismatch for default interface function `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`", + ); + } else { + bail!( + "type mismatch for function `{}` from exported interface `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`", f.name, + interface.name, expected_ty.params(), expected_ty.results(), ty.params(), ty.results() - ) + ); } } } None => bail!( "module does not export required function `{}`", - expected_export + expected_export_name ), } } diff --git a/crates/wit-component/tests/interfaces.rs b/crates/wit-component/tests/interfaces.rs index 88d13fde7..853f3a6af 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -73,7 +73,7 @@ fn run_test(path: &Path) -> Result<()> { ); } - let interfaces = wit_component::decode_interface_component(wasm) + let interfaces = wit_component::decode_component_interfaces(wasm) .context(format!("failed to decode bytes for test `{test_case}`"))?; if test_case == "empty" { diff --git a/crates/wit-component/tests/interfaces/integers/component.wat b/crates/wit-component/tests/interfaces/integers/component.wat index 1b2da525f..10dec0530 100644 --- a/crates/wit-component/tests/interfaces/integers/component.wat +++ b/crates/wit-component/tests/interfaces/integers/component.wat @@ -18,7 +18,8 @@ (type (;16;) (func (result s64))) (type (;17;) (tuple s64 u8)) (type (;18;) (func (result 17))) - (type (;19;) + (type (;19;) (func (result "a" s64) (result "b" u8))) + (type (;20;) (instance (alias outer 1 0 (type (;0;))) (export "a1" (func (type 0))) @@ -56,10 +57,11 @@ (export "r8" (func (type 16))) (alias outer 1 18 (type (;17;))) (export "pair-ret" (func (type 17))) - (export "multi-ret" (func (type 17))) + (alias outer 1 19 (type (;18;))) + (export "multi-ret" (func (type 18))) ) ) - (import "integers" (instance (;0;) (type 19))) + (import "integers" (instance (;0;) (type 20))) (core module (;0;) (type (;0;) (func (param i32))) (type (;1;) (func (param i64))) diff --git a/crates/wit-component/tests/interfaces/integers/import-integers.wit b/crates/wit-component/tests/interfaces/integers/import-integers.wit index dedd40e77..d40688bae 100644 --- a/crates/wit-component/tests/interfaces/integers/import-integers.wit +++ b/crates/wit-component/tests/interfaces/integers/import-integers.wit @@ -34,5 +34,5 @@ r8: func() -> s64 pair-ret: func() -> tuple -multi-ret: func() -> tuple +multi-ret: func() -> (a: s64, b: u8) diff --git a/crates/wit-component/tests/interfaces/integers/types_only.wat b/crates/wit-component/tests/interfaces/integers/types_only.wat index d2c7e36d2..e2750add9 100644 --- a/crates/wit-component/tests/interfaces/integers/types_only.wat +++ b/crates/wit-component/tests/interfaces/integers/types_only.wat @@ -18,7 +18,8 @@ (type (;16;) (func (result s64))) (type (;17;) (tuple s64 u8)) (type (;18;) (func (result 17))) - (type (;19;) + (type (;19;) (func (result "a" s64) (result "b" u8))) + (type (;20;) (instance (alias outer 1 0 (type (;0;))) (export "a1" (func (type 0))) @@ -56,8 +57,9 @@ (export "r8" (func (type 16))) (alias outer 1 18 (type (;17;))) (export "pair-ret" (func (type 17))) - (export "multi-ret" (func (type 17))) + (alias outer 1 19 (type (;18;))) + (export "multi-ret" (func (type 18))) ) ) - (import "integers" (instance (;0;) (type 19))) + (import "integers" (instance (;0;) (type 20))) ) \ No newline at end of file diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index dc946e7d3..cc2abcc6c 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -74,7 +74,6 @@ impl Resolver { Ok(Interface { name: name.to_string(), - module: None, types: mem::take(&mut self.types), type_lookup: mem::take(&mut self.type_lookup), interface_lookup: Default::default(), @@ -508,7 +507,7 @@ impl Resolver { // comment to avoid breaking on empty block comments, `/**/`. let doc = doc.strip_suffix("*/").unwrap(); - if let Some(doc) = doc.strip_prefix("*") { + if let Some(doc) = doc.strip_prefix('*') { let docs = docs.get_or_insert_with(String::new); for line in doc.lines() { docs.push_str(line); diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 0394a6009..9b08fac5a 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, bail, Context, Result}; use id_arena::{Arena, Id}; use pulldown_cmark::{CodeBlockKind, CowStr, Event, Options, Parser, Tag}; +use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::fs; use std::path::{Path, PathBuf}; @@ -19,14 +20,6 @@ pub fn validate_id(s: &str) -> Result<()> { #[derive(Debug, Clone, Default, PartialEq)] pub struct Interface { pub name: String, - /// The module name to use for bindings generation. - /// - /// If `None`, then the interface name will be used. - /// - /// If `Some`, then this value is used to format an export - /// name of `#` for exports or an import module - /// name of `` for imports. - pub module: Option, pub types: Arena, pub type_lookup: HashMap, pub interfaces: Arena, @@ -435,6 +428,15 @@ impl Interface { } } + /// Gets the core export name for the given function. + pub fn core_export_name<'a>(&self, default_export: bool, func: &'a Function) -> Cow<'a, str> { + if default_export || self.name.is_empty() { + Cow::Borrowed(&func.name) + } else { + Cow::Owned(format!("{}#{}", self.name, func.name)) + } + } + pub fn topological_types(&self) -> Vec { let mut ret = Vec::new(); let mut visited = HashSet::new();