From 13813ddc608ff17a4fea7c26e93bcdb8b5a64361 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Nov 2026 12:21:11 -0700 Subject: [PATCH] Add cm64 support to "dummy modules" This commit plumb support for 64-bit linear memories in the `dummy.rs` support in the `wit-component` crate. This involves a new parameter in the function used to create a dummy module which is the index type of the linear memory to be generated. This is plumbed through then to the CLI to be able to test this as well. Fuzzing now generates 64-bit linear memories in the `roundtrip_wit` fuzzer, too. --- crates/wit-component/src/dummy.rs | 149 ++++++++++++------ crates/wit-component/src/encoding.rs | 7 +- crates/wit-component/src/lib.rs | 2 +- crates/wit-component/src/semver_check.rs | 7 +- crates/wit-component/src/validation.rs | 20 +-- fuzz/src/roundtrip_wit.rs | 10 +- src/bin/wasm-tools/component.rs | 61 +++---- tests/cli/dummy-cm64-import-and-export.wit | 17 ++ tests/cli/dummy-cm64.wit | 36 +++++ tests/cli/dummy-cm64.wit.embed.stdout | 67 ++++++++ .../cli/help-component-embed-short.wat.stdout | 1 + tests/cli/help-component-embed.wat.stdout | 3 +- 12 files changed, 285 insertions(+), 95 deletions(-) create mode 100644 tests/cli/dummy-cm64-import-and-export.wit create mode 100644 tests/cli/dummy-cm64.wit create mode 100644 tests/cli/dummy-cm64.wit.embed.stdout diff --git a/crates/wit-component/src/dummy.rs b/crates/wit-component/src/dummy.rs index e0ace3b1b4..f32e51b79a 100644 --- a/crates/wit-component/src/dummy.rs +++ b/crates/wit-component/src/dummy.rs @@ -1,3 +1,4 @@ +use crate::validation::IndexType; use wit_parser::abi::WasmType; use wit_parser::{ Function, FutureIntrinsic, LiftLowerAbi, ManglingAndAbi, Resolve, ResourceIntrinsic, @@ -6,18 +7,42 @@ use wit_parser::{ }; /// Generate a dummy implementation core Wasm module for a given WIT document -pub fn dummy_module(resolve: &Resolve, world_id: WorldId, mangling: ManglingAndAbi) -> Vec { +/// +/// The `index_type` argument is the index type of the generated module's +/// linear memory. +pub fn dummy_module( + resolve: &Resolve, + world_id: WorldId, + mangling: ManglingAndAbi, + index_type: IndexType, +) -> Vec { + let ptr = index_type; + + // Usage of `resolve.wasm_signature` requires a world with nominal type IDs + // to correctly handle the `rep` of `borrow`, so ensure that's done here. + let mut resolve = resolve.clone(); + resolve.generate_nominal_type_ids(world_id); + let resolve = &resolve; + let world = &resolve.worlds[world_id]; let mut wat = String::new(); wat.push_str("(module\n"); for (name, import) in world.imports.iter() { match import { WorldItem::Function(func) => { - push_imported_func(&mut wat, resolve, world_id, None, func, mangling); + push_imported_func(&mut wat, resolve, world_id, None, func, mangling, ptr); } WorldItem::Interface { id: import, .. } => { for (_, func) in resolve.interfaces[*import].functions.iter() { - push_imported_func(&mut wat, resolve, world_id, Some(name), func, mangling); + push_imported_func( + &mut wat, + resolve, + world_id, + Some(name), + func, + mangling, + ptr, + ); } for (_, ty) in resolve.interfaces[*import].types.iter() { push_imported_type_intrinsics(&mut wat, resolve, Some(name), *ty, mangling); @@ -30,7 +55,7 @@ pub fn dummy_module(resolve: &Resolve, world_id: WorldId, mangling: ManglingAndA } if mangling.is_async() { - push_root_async_intrinsics(&mut wat); + push_root_async_intrinsics(&mut wat, ptr); } // Append any intrinsics which are imported but used in exported items @@ -38,7 +63,9 @@ pub fn dummy_module(resolve: &Resolve, world_id: WorldId, mangling: ManglingAndA for (name, export) in world.exports.iter() { match export { WorldItem::Function(func) => { - push_exported_func_intrinsics(&mut wat, resolve, world_id, None, func, mangling); + push_exported_func_intrinsics( + &mut wat, resolve, world_id, None, func, mangling, ptr, + ); } WorldItem::Interface { id: export, .. } => { for (_, func) in resolve.interfaces[*export].functions.iter() { @@ -49,10 +76,18 @@ pub fn dummy_module(resolve: &Resolve, world_id: WorldId, mangling: ManglingAndA Some(name), func, mangling, + ptr, ); } for (_, ty) in resolve.interfaces[*export].types.iter() { - push_exported_type_intrinsics(&mut wat, resolve, Some(name), *ty, mangling); + push_exported_type_intrinsics( + &mut wat, + resolve, + Some(name), + *ty, + mangling, + ptr, + ); } } WorldItem::Type { .. } => {} @@ -62,14 +97,14 @@ pub fn dummy_module(resolve: &Resolve, world_id: WorldId, mangling: ManglingAndA for (name, export) in world.exports.iter() { match export { WorldItem::Function(func) => { - push_func_export(&mut wat, resolve, world_id, None, func, mangling); + push_func_export(&mut wat, resolve, world_id, None, func, mangling, ptr); } WorldItem::Interface { id: export, .. } => { for (_, func) in resolve.interfaces[*export].functions.iter() { - push_func_export(&mut wat, resolve, world_id, Some(name), func, mangling); + push_func_export(&mut wat, resolve, world_id, Some(name), func, mangling, ptr); } for (_, ty) in resolve.interfaces[*export].types.iter() { - push_exported_resource_functions(&mut wat, resolve, name, *ty, mangling); + push_exported_resource_functions(&mut wat, resolve, name, *ty, mangling, ptr); } } WorldItem::Type { .. } => {} @@ -77,10 +112,10 @@ pub fn dummy_module(resolve: &Resolve, world_id: WorldId, mangling: ManglingAndA } let memory = resolve.wasm_export_name(mangling, WasmExport::Memory); - wat.push_str(&format!("(memory (export {memory:?}) 0)\n")); + wat.push_str(&format!("(memory (export {memory:?}) {ptr} 0)\n")); let realloc = resolve.wasm_export_name(mangling, WasmExport::Realloc); wat.push_str(&format!( - "(func (export {realloc:?}) (param i32 i32 i32 i32) (result i32) unreachable)\n" + "(func (export {realloc:?}) (param {ptr} {ptr} {ptr} {ptr}) (result {ptr}) unreachable)\n" )); let initialize = resolve.wasm_export_name(mangling, WasmExport::Initialize); @@ -97,18 +132,21 @@ fn push_imported_func( interface: Option<&WorldKey>, func: &Function, mangling: ManglingAndAbi, + ptr: IndexType, ) { let mangling = mangling.for_func(func); let sig = resolve.wasm_signature(world, mangling.import_variant(), func); let (module, name) = resolve.wasm_import_name(mangling, WasmImport::Func { interface, func }); wat.push_str(&format!("(import {module:?} {name:?} (func")); - push_tys(wat, "param", &sig.params); - push_tys(wat, "result", &sig.results); + push_tys(wat, "param", &sig.params, ptr); + push_tys(wat, "result", &sig.results, ptr); wat.push_str("))\n"); if mangling.is_async() { - push_imported_future_and_stream_intrinsics(wat, resolve, mangling, false, interface, func); + push_imported_future_and_stream_intrinsics( + wat, resolve, mangling, false, interface, func, ptr, + ); } } @@ -155,6 +193,7 @@ fn push_exported_func_intrinsics( interface: Option<&WorldKey>, func: &Function, mangling: ManglingAndAbi, + ptr: IndexType, ) { if !mangling.is_async() { return; @@ -164,11 +203,11 @@ fn push_exported_func_intrinsics( let (module, name, sig) = func.task_return_import(resolve, world, interface, mangling.mangling()); wat.push_str(&format!("(import {module:?} {name:?} (func")); - push_tys(wat, "param", &sig.params); - push_tys(wat, "result", &sig.results); + push_tys(wat, "param", &sig.params, ptr); + push_tys(wat, "result", &sig.results, ptr); wat.push_str("))\n"); - push_imported_future_and_stream_intrinsics(wat, resolve, mangling, true, interface, func); + push_imported_future_and_stream_intrinsics(wat, resolve, mangling, true, interface, func, ptr); } fn push_imported_future_and_stream_intrinsics( @@ -178,6 +217,7 @@ fn push_imported_future_and_stream_intrinsics( exported: bool, interface: Option<&WorldKey>, func: &Function, + ptr: IndexType, ) { for id in func.find_futures_and_streams(resolve).into_iter() { match &resolve.types[id].kind { @@ -225,12 +265,12 @@ fn push_imported_future_and_stream_intrinsics( (import {module:?} {drop_readable:?} (func (param i32))) (import {module:?} {drop_writable:?} (func (param i32))) (import {module:?} {forward:?} (func (param i32 i32))) -(import {module:?} {async_read:?} (func (param i32 i32) (result i32))) -(import {module:?} {async_write:?} (func (param i32 i32) (result i32))) +(import {module:?} {async_read:?} (func (param i32 {ptr}) (result i32))) +(import {module:?} {async_write:?} (func (param i32 {ptr}) (result i32))) ;; deferred behind ๐Ÿš -;;(import {module:?} {read:?} (func (param i32 i32) (result i32))) -;;(import {module:?} {write:?} (func (param i32 i32) (result i32))) +;;(import {module:?} {read:?} (func (param i32 {ptr}) (result i32))) +;;(import {module:?} {write:?} (func (param i32 {ptr}) (result i32))) ;;(import {module:?} {async_cancel_read:?} (func (param i32) (result i32))) ;;(import {module:?} {async_cancel_write:?} (func (param i32) (result i32))) "# @@ -280,12 +320,12 @@ fn push_imported_future_and_stream_intrinsics( (import {module:?} {drop_readable:?} (func (param i32))) (import {module:?} {drop_writable:?} (func (param i32))) (import {module:?} {forward:?} (func (param i32 i32))) -(import {module:?} {async_read:?} (func (param i32 i32 i32) (result i32))) -(import {module:?} {async_write:?} (func (param i32 i32 i32) (result i32))) +(import {module:?} {async_read:?} (func (param i32 {ptr} {ptr}) (result {ptr}))) +(import {module:?} {async_write:?} (func (param i32 {ptr} {ptr}) (result {ptr}))) ;; deferred behind ๐Ÿš -;;(import {module:?} {read:?} (func (param i32 i32 i32) (result i32))) -;;(import {module:?} {write:?} (func (param i32 i32 i32) (result i32))) +;;(import {module:?} {read:?} (func (param i32 {ptr} {ptr}) (result {ptr}))) +;;(import {module:?} {write:?} (func (param i32 {ptr} {ptr}) (result {ptr}))) ;;(import {module:?} {async_cancel_read:?} (func (param i32) (result i32))) ;;(import {module:?} {async_cancel_write:?} (func (param i32) (result i32))) "# @@ -302,19 +342,23 @@ fn push_exported_type_intrinsics( interface: Option<&WorldKey>, resource: TypeId, mangling: ManglingAndAbi, + ptr: IndexType, ) { let ty = &resolve.types[resource]; match ty.kind { TypeDefKind::Resource => { let intrinsics = [ - (ResourceIntrinsic::ExportedDrop, "(func (param i32))"), + ( + ResourceIntrinsic::ExportedDrop, + "(func (param i32))".to_string(), + ), ( ResourceIntrinsic::ExportedNew, - "(func (param i32) (result i32))", + format!("(func (param {ptr}) (result i32))"), ), ( ResourceIntrinsic::ExportedRep, - "(func (param i32) (result i32))", + format!("(func (param i32) (result {ptr}))"), ), ]; for (intrinsic, sig) in intrinsics { @@ -342,6 +386,7 @@ fn push_exported_resource_functions( interface: &WorldKey, resource: TypeId, mangling: ManglingAndAbi, + ptr: IndexType, ) { let ty = &resolve.types[resource]; match ty.kind { @@ -357,7 +402,7 @@ fn push_exported_resource_functions( resource, }, ); - wat.push_str(&format!("(func (export {name:?}) (param i32))")); + wat.push_str(&format!("(func (export {name:?}) (param {ptr}))")); } fn push_func_export( @@ -367,6 +412,7 @@ fn push_func_export( interface: Option<&WorldKey>, func: &Function, mangling: ManglingAndAbi, + ptr: IndexType, ) { let mangling = mangling.for_func(func); let sig = resolve.wasm_signature(world, mangling.export_variant(), func); @@ -379,8 +425,8 @@ fn push_func_export( }, ); wat.push_str(&format!("(func (export \"{name}\")")); - push_tys(wat, "param", &sig.params); - push_tys(wat, "result", &sig.results); + push_tys(wat, "param", &sig.params, ptr); + push_tys(wat, "result", &sig.results, ptr); wat.push_str(" unreachable)\n"); match mangling { @@ -394,7 +440,7 @@ fn push_func_export( }, ); wat.push_str(&format!("(func (export \"{name}\")")); - push_tys(wat, "param", &sig.results); + push_tys(wat, "param", &sig.results, ptr); wat.push_str(")\n"); } ManglingAndAbi::Legacy(LiftLowerAbi::AsyncCallback) => { @@ -414,7 +460,7 @@ fn push_func_export( } } -fn push_tys(dst: &mut String, desc: &str, params: &[WasmType]) { +fn push_tys(dst: &mut String, desc: &str, params: &[WasmType], ptr: IndexType) { if params.is_empty() { return; } @@ -427,46 +473,45 @@ fn push_tys(dst: &mut String, desc: &str, params: &[WasmType]) { WasmType::I64 => dst.push_str("i64"), WasmType::F32 => dst.push_str("f32"), WasmType::F64 => dst.push_str("f64"), - WasmType::Pointer => dst.push_str("i32"), + WasmType::Pointer | WasmType::Length => dst.push_str(&ptr.to_string()), WasmType::PointerOrI64 => dst.push_str("i64"), - WasmType::Length => dst.push_str("i32"), } } dst.push(')'); } -fn push_root_async_intrinsics(dst: &mut String) { - dst.push_str( +fn push_root_async_intrinsics(dst: &mut String, ptr: IndexType) { + dst.push_str(&format!( r#" (import "[export]$root" "[task-cancel]" (func)) (import "$root" "[backpressure-inc]" (func)) (import "$root" "[backpressure-dec]" (func)) (import "$root" "[waitable-set-new]" (func (result i32))) -(import "$root" "[waitable-set-wait]" (func (param i32 i32) (result i32))) -(import "$root" "[waitable-set-poll]" (func (param i32 i32) (result i32))) +(import "$root" "[waitable-set-wait]" (func (param i32 {ptr}) (result i32))) +(import "$root" "[waitable-set-poll]" (func (param i32 {ptr}) (result i32))) (import "$root" "[waitable-set-drop]" (func (param i32))) (import "$root" "[waitable-join]" (func (param i32 i32))) (import "$root" "[thread-yield]" (func (result i32))) (import "$root" "[subtask-drop]" (func (param i32))) (import "$root" "[subtask-cancel]" (func (param i32) (result i32))) -(import "$root" "[context-get-0]" (func (result i32))) -(import "$root" "[context-set-0]" (func (param i32))) +(import "$root" "[context-get-0]" (func (result {ptr}))) +(import "$root" "[context-set-0]" (func (param {ptr}))) ;; deferred behind ๐Ÿงต upstream -;;(import "$root" "[cancellable][waitable-set-wait]" (func (param i32 i32) (result i32))) -;;(import "$root" "[cancellable][waitable-set-poll]" (func (param i32 i32) (result i32))) +;;(import "$root" "[cancellable][waitable-set-wait]" (func (param i32 {ptr}) (result i32))) +;;(import "$root" "[cancellable][waitable-set-poll]" (func (param i32 {ptr}) (result i32))) ;;(import "$root" "[cancellable][thread-yield]" (func (result i32))) -;;(import "$root" "[context-get-1]" (func (result i32))) -;;(import "$root" "[context-set-1]" (func (param i32))) +;;(import "$root" "[context-get-1]" (func (result {ptr}))) +;;(import "$root" "[context-set-1]" (func (param {ptr}))) ;; deferred behind ๐Ÿ“ upstream -;;(import "$root" "[error-context-new-utf8]" (func (param i32 i32) (result i32))) -;;(import "$root" "[error-context-new-utf16]" (func (param i32 i32) (result i32))) -;;(import "$root" "[error-context-new-latin1+utf16]" (func (param i32 i32) (result i32))) -;;(import "$root" "[error-context-debug-message-utf8]" (func (param i32 i32))) -;;(import "$root" "[error-context-debug-message-utf16]" (func (param i32 i32))) -;;(import "$root" "[error-context-debug-message-latin1+utf16]" (func (param i32 i32))) +;;(import "$root" "[error-context-new-utf8]" (func (param {ptr} {ptr}) (result i32))) +;;(import "$root" "[error-context-new-utf16]" (func (param {ptr} {ptr}) (result i32))) +;;(import "$root" "[error-context-new-latin1+utf16]" (func (param {ptr} {ptr}) (result i32))) +;;(import "$root" "[error-context-debug-message-utf8]" (func (param i32 {ptr}))) +;;(import "$root" "[error-context-debug-message-utf16]" (func (param i32 {ptr}))) +;;(import "$root" "[error-context-debug-message-latin1+utf16]" (func (param i32 {ptr}))) ;;(import "$root" "[error-context-drop]" (func (param i32))) "#, - ); + )); } diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index fdda932dfc..cf21f3df90 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -3764,7 +3764,12 @@ world test { .unwrap(); let world = resolve.select_world(&[pkg], None).unwrap(); - let mut module = dummy_module(&resolve, world, ManglingAndAbi::Standard32); + let mut module = dummy_module( + &resolve, + world, + ManglingAndAbi::Standard32, + crate::IndexType::I32, + ); embed_component_metadata(&mut module, &resolve, world, StringEncoding::UTF8, true).unwrap(); diff --git a/crates/wit-component/src/lib.rs b/crates/wit-component/src/lib.rs index 464c43ac86..7cf31ec403 100644 --- a/crates/wit-component/src/lib.rs +++ b/crates/wit-component/src/lib.rs @@ -21,7 +21,7 @@ pub use encoding::{ComponentEncoder, LibraryInfo, encode}; pub use linking::Linker; pub use printing::*; pub use targets::*; -pub use validation::AdapterModuleDidNotExport; +pub use validation::{AdapterModuleDidNotExport, IndexType}; pub use wit_parser::decoding::{DecodedWasm, decode, decode_reader}; pub mod metadata; diff --git a/crates/wit-component/src/semver_check.rs b/crates/wit-component/src/semver_check.rs index c4ed41695a..f5eb5cb9d7 100644 --- a/crates/wit-component/src/semver_check.rs +++ b/crates/wit-component/src/semver_check.rs @@ -65,7 +65,12 @@ pub fn semver_check(mut resolve: Resolve, prev: WorldId, new: WorldId) -> Result let mut root_component = ComponentBuilder::default(); // (1) above - create a dummy component which has the shape of `prev`. - let mut prev_as_module = dummy_module(&resolve, prev, ManglingAndAbi::Standard32); + let mut prev_as_module = dummy_module( + &resolve, + prev, + ManglingAndAbi::Standard32, + crate::IndexType::I32, + ); embed_component_metadata( &mut prev_as_module, &resolve, diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs index bf7b13d043..fa6a1b2f12 100644 --- a/crates/wit-component/src/validation.rs +++ b/crates/wit-component/src/validation.rs @@ -24,8 +24,10 @@ use wit_parser::{ /// index type corresponds to the component model's 64-bit (`cm64`) feature. #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] pub enum IndexType { + /// A 32-bit linear memory, where pointers and lengths are `i32`. #[default] I32, + /// A 64-bit linear memory, where pointers and lengths are `i64`. I64, } @@ -41,7 +43,7 @@ impl fmt::Display for IndexType { impl IndexType { /// Returns the index type used for canonical ABI pointers by the memory /// `ty`. - pub fn of_memory(ty: &MemoryType) -> IndexType { + pub(crate) fn of_memory(ty: &MemoryType) -> IndexType { if ty.memory64 { IndexType::I64 } else { @@ -50,7 +52,7 @@ impl IndexType { } /// Returns the core wasm type of pointers and lengths. - pub fn ptr(&self) -> ValType { + pub(crate) fn ptr(&self) -> ValType { match self { IndexType::I32 => ValType::I32, IndexType::I64 => ValType::I64, @@ -58,7 +60,7 @@ impl IndexType { } /// Returns the core wasm type that `ty` maps to with this index type. - pub fn val_type(&self, ty: &WasmType) -> ValType { + pub(crate) fn val_type(&self, ty: &WasmType) -> ValType { match ty { WasmType::I32 => ValType::I32, WasmType::I64 => ValType::I64, @@ -71,7 +73,7 @@ impl IndexType { /// Returns the core wasm function type that `signature` maps to with this /// index type. - pub fn func_type(&self, signature: &WasmSignature) -> FuncType { + pub(crate) fn func_type(&self, signature: &WasmSignature) -> FuncType { FuncType::new( signature.params.iter().map(|ty| self.val_type(ty)), signature.results.iter().map(|ty| self.val_type(ty)), @@ -79,7 +81,7 @@ impl IndexType { } /// Returns the size, in bytes, of a pointer. - pub fn ptr_size(&self) -> u32 { + pub(crate) fn ptr_size(&self) -> u32 { match self { IndexType::I32 => 4, IndexType::I64 => 8, @@ -87,12 +89,12 @@ impl IndexType { } /// Returns the log2 of the natural alignment of a pointer. - pub fn ptr_align_log2(&self) -> u32 { + pub(crate) fn ptr_align_log2(&self) -> u32 { self.ptr_size().trailing_zeros() } /// Returns a constant expression of this pointer type with the value `a`. - pub fn const_expr(&self, a: u32) -> wasm_encoder::ConstExpr { + pub(crate) fn const_expr(&self, a: u32) -> wasm_encoder::ConstExpr { match self { IndexType::I32 => wasm_encoder::ConstExpr::i32_const(a as i32), IndexType::I64 => wasm_encoder::ConstExpr::i64_const(a.into()), @@ -100,12 +102,12 @@ impl IndexType { } /// Same as [`IndexType::ptr`], but for `wasm-encoder`. - pub fn encoder_ptr(&self) -> wasm_encoder::ValType { + pub(crate) fn encoder_ptr(&self) -> wasm_encoder::ValType { self.ptr().try_into().unwrap() } /// Same as [`IndexType::val_type`], but for `wasm-encoder`. - pub fn encoder_val_type(&self, ty: &WasmType) -> wasm_encoder::ValType { + pub(crate) fn encoder_val_type(&self, ty: &WasmType) -> wasm_encoder::ValType { self.val_type(ty).try_into().unwrap() } diff --git a/fuzz/src/roundtrip_wit.rs b/fuzz/src/roundtrip_wit.rs index 855cc54bed..f09e4c8d23 100644 --- a/fuzz/src/roundtrip_wit.rs +++ b/fuzz/src/roundtrip_wit.rs @@ -47,11 +47,17 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> { 3 => ManglingAndAbi::Standard32, _ => unreachable!(), }; + // 64-bit memories are only supported with legacy names at this time. + let index_type = match mangling { + ManglingAndAbi::Legacy(_) if u.arbitrary()? => IndexType::I64, + _ => IndexType::I32, + }; log::debug!( - "embedding world {} as in a dummy module with abi {mangling:?}", + "embedding world {} as in a dummy module with abi {mangling:?} \ + and index type {index_type}", world.name ); - let mut dummy = wit_component::dummy_module(&resolve, id, mangling); + let mut dummy = wit_component::dummy_module(&resolve, id, mangling, index_type); if u.arbitrary()? { let mut dst = Module::default(); let mut reencode = RemoveImports { diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs index 39a5f1ddc5..476e4a9297 100644 --- a/src/bin/wasm-tools/component.rs +++ b/src/bin/wasm-tools/component.rs @@ -14,10 +14,10 @@ use wasmparser::types::{CoreTypeId, EntityType, Types}; use wasmparser::{Payload, ValidPayload, WasmFeatures}; use wat::Detect; use wit_component::{ - ComponentEncoder, DecodedWasm, Linker, StringEncoding, WitPrinter, embed_component_metadata, - metadata, + ComponentEncoder, DecodedWasm, IndexType, Linker, StringEncoding, WitPrinter, + embed_component_metadata, metadata, }; -use wit_parser::{LiftLowerAbi, Mangling, ManglingAndAbi, WorldItem, WorldKey}; +use wit_parser::{LiftLowerAbi, ManglingAndAbi, WorldItem, WorldKey}; /// WebAssembly wit-based component tooling. #[derive(Parser)] @@ -422,10 +422,9 @@ pub struct EmbedOpts { /// /// This flag is the same as `--dummy` where if specified a core wasm module /// is not read but is instead generated. The value of the option here is - /// the name mangling scheme to use for core wasm names generated. Current - /// options are `legacy|standard32`. + /// the name mangling scheme to use for core wasm names generated. #[clap(long, conflicts_with = "dummy")] - dummy_names: Option, + dummy_names: Option, /// With `--dummy-names legacy`, this will generate a core module such that /// all the imports are lowered using the async ABI and the exports are @@ -461,6 +460,13 @@ pub struct EmbedOpts { only_custom: bool, } +#[derive(clap::ValueEnum, Debug, Copy, Clone, PartialEq, Eq)] +enum DummyNames { + Standard32, + Legacy, + Legacy64, +} + impl EmbedOpts { fn general_opts(&self) -> &wasm_tools::GeneralOpts { self.io.general_opts() @@ -493,29 +499,28 @@ impl EmbedOpts { if self.async_callback || self.async_stackful { bail!("non-legacy mangling not yet supported when generating async dummy modules"); } - wit_component::dummy_module(&resolve, world, ManglingAndAbi::Standard32) - } else if let Some(mangling) = self.dummy_names { - wit_component::dummy_module( - &resolve, - world, - match mangling { - Mangling::Standard32 => { - if self.async_callback || self.async_stackful { - bail!( - "non-legacy mangling not yet supported when generating async dummy modules" - ); - } - ManglingAndAbi::Standard32 + wit_component::dummy_module(&resolve, world, ManglingAndAbi::Standard32, IndexType::I32) + } else if let Some(names) = self.dummy_names { + let legacy = ManglingAndAbi::Legacy(if self.async_callback { + LiftLowerAbi::AsyncCallback + } else if self.async_stackful { + LiftLowerAbi::AsyncStackful + } else { + LiftLowerAbi::Sync + }); + let (mangling, index_type) = match names { + DummyNames::Standard32 => { + if self.async_callback || self.async_stackful { + bail!( + "non-legacy mangling not yet supported when generating async dummy modules" + ); } - Mangling::Legacy => ManglingAndAbi::Legacy(if self.async_callback { - LiftLowerAbi::AsyncCallback - } else if self.async_stackful { - LiftLowerAbi::AsyncStackful - } else { - LiftLowerAbi::Sync - }), - }, - ) + (ManglingAndAbi::Standard32, IndexType::I32) + } + DummyNames::Legacy => (legacy, IndexType::I32), + DummyNames::Legacy64 => (legacy, IndexType::I64), + }; + wit_component::dummy_module(&resolve, world, mangling, index_type) } else { self.io.get_input_wasm(Some(&self.generate_dwarf))? }; diff --git a/tests/cli/dummy-cm64-import-and-export.wit b/tests/cli/dummy-cm64-import-and-export.wit new file mode 100644 index 0000000000..fe6b5ca723 --- /dev/null +++ b/tests/cli/dummy-cm64-import-and-export.wit @@ -0,0 +1,17 @@ +// RUN: component embed --dummy-names legacy64 % | \ +// component new | \ +// validate -f cm64 + +package a:b; + +interface i { + resource r { + name: func(); + } + f: func(x: borrow); +} + +world w { + import i; + export i; +} diff --git a/tests/cli/dummy-cm64.wit b/tests/cli/dummy-cm64.wit new file mode 100644 index 0000000000..859ed46d8d --- /dev/null +++ b/tests/cli/dummy-cm64.wit @@ -0,0 +1,36 @@ +// RUN[embed]: component embed -t --dummy-names legacy64 % | strip -t +// RUN[new]: component embed --dummy-names legacy64 % | \ +// component new | \ +// validate -f cm64 + +package a:b; + +interface imp { + resource r { + constructor(name: string); + name: func() -> string; + take: func(other: borrow, l: list); + } + + f: func(a: string, b: option, c: result, string>) -> tuple; + many: func(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: u32, i: u32, j: u32, k: u32, l: u32, m: u32, n: u32, o: u32, p: u32, q: u32, r: u32); +} + +interface exp { + use imp.{r as imported}; + + resource r { + constructor(name: string); + name: func() -> string; + merge: func(other: borrow, imported: borrow); + } + + f: func(a: string, b: option) -> list; +} + +world w { + import imp; + import g: func(x: string) -> string; + export exp; + export g: func(x: string) -> option; +} diff --git a/tests/cli/dummy-cm64.wit.embed.stdout b/tests/cli/dummy-cm64.wit.embed.stdout new file mode 100644 index 0000000000..ff08b0e9ad --- /dev/null +++ b/tests/cli/dummy-cm64.wit.embed.stdout @@ -0,0 +1,67 @@ +(module + (type (;0;) (func (param i64 i64) (result i32))) + (type (;1;) (func (param i32 i64))) + (type (;2;) (func (param i32 i32 i64 i64))) + (type (;3;) (func (param i64 i64 i32 i64 i64 i32 i64 i64 i64))) + (type (;4;) (func (param i64))) + (type (;5;) (func (param i32))) + (type (;6;) (func (param i64 i64 i64))) + (type (;7;) (func (param i64) (result i32))) + (type (;8;) (func (param i32) (result i64))) + (type (;9;) (func (param i64 i64) (result i64))) + (type (;10;) (func (param i64) (result i64))) + (type (;11;) (func (param i64 i64 i32))) + (type (;12;) (func)) + (type (;13;) (func (param i64 i64 i32 i64 i64) (result i64))) + (type (;14;) (func (param i64 i64 i64 i64) (result i64))) + (import "a:b/imp" "[constructor]r" (func (;0;) (type 0) (param i64 i64) (result i32))) + (import "a:b/imp" "[method]r.name" (func (;1;) (type 1) (param i32 i64))) + (import "a:b/imp" "[method]r.take" (func (;2;) (type 2) (param i32 i32 i64 i64))) + (import "a:b/imp" "f" (func (;3;) (type 3) (param i64 i64 i32 i64 i64 i32 i64 i64 i64))) + (import "a:b/imp" "many" (func (;4;) (type 4) (param i64))) + (import "a:b/imp" "[resource-drop]r" (func (;5;) (type 5) (param i32))) + (import "$root" "g" (func (;6;) (type 6) (param i64 i64 i64))) + (import "[export]a:b/exp" "[resource-drop]r" (func (;7;) (type 5) (param i32))) + (import "[export]a:b/exp" "[resource-new]r" (func (;8;) (type 7) (param i64) (result i32))) + (import "[export]a:b/exp" "[resource-rep]r" (func (;9;) (type 8) (param i32) (result i64))) + (memory (;0;) i64 0) + (export "g" (func 10)) + (export "cabi_post_g" (func 11)) + (export "a:b/exp#[constructor]r" (func 12)) + (export "cabi_post_a:b/exp#[constructor]r" (func 13)) + (export "a:b/exp#[method]r.name" (func 14)) + (export "cabi_post_a:b/exp#[method]r.name" (func 15)) + (export "a:b/exp#[method]r.merge" (func 16)) + (export "cabi_post_a:b/exp#[method]r.merge" (func 17)) + (export "a:b/exp#f" (func 18)) + (export "cabi_post_a:b/exp#f" (func 19)) + (export "a:b/exp#[dtor]r" (func 20)) + (export "memory" (memory 0)) + (export "cabi_realloc" (func 21)) + (export "_initialize" (func 22)) + (func (;10;) (type 9) (param i64 i64) (result i64) + unreachable + ) + (func (;11;) (type 4) (param i64)) + (func (;12;) (type 0) (param i64 i64) (result i32) + unreachable + ) + (func (;13;) (type 5) (param i32)) + (func (;14;) (type 10) (param i64) (result i64) + unreachable + ) + (func (;15;) (type 4) (param i64)) + (func (;16;) (type 11) (param i64 i64 i32) + unreachable + ) + (func (;17;) (type 12)) + (func (;18;) (type 13) (param i64 i64 i32 i64 i64) (result i64) + unreachable + ) + (func (;19;) (type 4) (param i64)) + (func (;20;) (type 4) (param i64)) + (func (;21;) (type 14) (param i64 i64 i64 i64) (result i64) + unreachable + ) + (func (;22;) (type 12)) +) diff --git a/tests/cli/help-component-embed-short.wat.stdout b/tests/cli/help-component-embed-short.wat.stdout index 221c799409..0a2533c455 100644 --- a/tests/cli/help-component-embed-short.wat.stdout +++ b/tests/cli/help-component-embed-short.wat.stdout @@ -34,6 +34,7 @@ Options: module as a placeholder --dummy-names Same as `--dummy`, but the style of core wasm names is specified + [possible values: standard32, legacy, legacy64] --async-callback With `--dummy-names legacy`, this will generate a core module such that all the imports are lowered using the async ABI and the exports diff --git a/tests/cli/help-component-embed.wat.stdout b/tests/cli/help-component-embed.wat.stdout index e110e77a39..555696c313 100644 --- a/tests/cli/help-component-embed.wat.stdout +++ b/tests/cli/help-component-embed.wat.stdout @@ -119,7 +119,8 @@ Options: This flag is the same as `--dummy` where if specified a core wasm module is not read but is instead generated. The value of the option here is the name mangling scheme to use for core wasm names generated. - Current options are `legacy|standard32`. + + [possible values: standard32, legacy, legacy64] --async-callback With `--dummy-names legacy`, this will generate a core module such