From 57dca934adac77a393cc943500615652003ff36e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 17 Aug 2022 11:17:34 -0500 Subject: [PATCH] Upgrade wasm-tools crates, namely the component model (#4715) * Upgrade wasm-tools crates, namely the component model This commit pulls in the latest versions of all of the `wasm-tools` family of crates. There were two major changes that happened in `wasm-tools` in the meantime: * bytecodealliance/wasm-tools#697 - this commit introduced a new API for more efficiently reading binary operators from a wasm binary. The old `Operator`-based reading was left in place, however, and continues to be what Wasmtime uses. I hope to update Wasmtime in a future PR to use this new API, but for now the biggest change is... * bytecodealliance/wasm-tools#703 - this commit was a major update to the component model AST. This commit almost entirely deals with the fallout of this change. The changes made to the component model were: 1. The `unit` type no longer exists. This was generally a simple change where the `Unit` case in a few different locations were all removed. 2. The `expected` type was renamed to `result`. This similarly was relatively lightweight and mostly just a renaming on the surface. I took this opportunity to rename `val::Result` to `val::ResultVal` and `types::Result` to `types::ResultType` to avoid clashing with the standard library types. The `Option`-based types were handled with this as well. 3. The payload type of `variant` and `result` types are now optional. This affected many locations that calculate flat type representations, ABI information, etc. The `#[derive(ComponentType)]` macro now specifically handles Rust-defined `enum` types which have no payload to the equivalent in the component model. 4. Functions can now return multiple parameters. This changed the signature of invoking component functions because the return value is now bound by `ComponentNamedList` (renamed from `ComponentParams`). This had a large effect in the tests, fuzz test case generation, etc. 5. Function types with 2-or-more parameters/results must uniquely name all parameters/results. This mostly affected the text format used throughout the tests. I haven't added specifically new tests for multi-return but I changed a number of tests to use it. Additionally I've updated the fuzzers to all exercise multi-return as well so I think we should get some good coverage with that. * Update version numbers * Use crates.io --- Cargo.lock | 34 +- Cargo.toml | 4 +- cranelift/wasm/Cargo.toml | 2 +- cranelift/wasm/src/sections_translator.rs | 12 +- crates/component-macro/src/lib.rs | 10 +- crates/cranelift/Cargo.toml | 2 +- crates/environ/Cargo.toml | 8 +- crates/environ/fuzz/Cargo.toml | 4 +- .../fuzz/fuzz_targets/fact-valid-module.rs | 4 +- crates/environ/src/component/types.rs | 173 +++---- crates/environ/src/fact/signature.rs | 6 +- crates/environ/src/fact/trampoline.rs | 161 ++++--- crates/environ/src/module_environ.rs | 12 +- crates/fuzzing/Cargo.toml | 12 +- .../fuzzing/src/generators/component_types.rs | 39 +- crates/fuzzing/src/oracles.rs | 47 +- crates/misc/component-fuzz-util/src/lib.rs | 204 +++++---- crates/misc/component-test-util/src/lib.rs | 24 +- crates/types/Cargo.toml | 2 +- crates/wasmtime/Cargo.toml | 2 +- crates/wasmtime/src/component/func.rs | 141 +++--- crates/wasmtime/src/component/func/host.rs | 92 ++-- crates/wasmtime/src/component/func/typed.rs | 106 +++-- crates/wasmtime/src/component/instance.rs | 10 +- crates/wasmtime/src/component/linker.rs | 2 +- crates/wasmtime/src/component/mod.rs | 2 +- crates/wasmtime/src/component/types.rs | 96 ++-- crates/wasmtime/src/component/values.rs | 264 ++++++----- crates/wast/Cargo.toml | 2 +- crates/wast/src/component.rs | 57 ++- crates/wast/src/spectest.rs | 6 +- crates/wast/src/wast.rs | 36 +- fuzz/build.rs | 16 +- supply-chain/audits.toml | 48 ++ tests/all/component_model/dynamic.rs | 107 +++-- tests/all/component_model/func.rs | 428 +++++++++--------- tests/all/component_model/import.rs | 134 +++--- tests/all/component_model/macros.rs | 117 +++-- tests/all/component_model/nested.rs | 7 +- tests/all/component_model/post_return.rs | 20 +- tests/all/component_model/strings.rs | 8 +- .../component-model/adapter.wast | 2 +- .../misc_testsuite/component-model/fused.wast | 56 ++- .../component-model/simple.wast | 2 +- .../misc_testsuite/component-model/types.wast | 29 +- .../threads/load-store-alignment.wast | 8 +- 46 files changed, 1425 insertions(+), 1133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8af525b846..adbbd6547e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3260,18 +3260,18 @@ checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "wasm-encoder" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8905fd25fdadeb0e7e8bf43a9f46f9f972d6291ad0c7a32573b88dd13a6cfa6b" +checksum = "d443c5a7daae71697d97ec12ad70b4fe8766d3a0f4db16158ac8b781365892f7" dependencies = [ "leb128", ] [[package]] name = "wasm-mutate" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e6de18ed96f27d3942041e5ae02177aff18e4425196a3d4b1f14145d027f71" +checksum = "f04ad5c8a18bf9d8d07ad9df8dea5e8ff701ab3472583a79350c3ab5b4766705" dependencies = [ "egg", "log", @@ -3283,9 +3283,9 @@ dependencies = [ [[package]] name = "wasm-smith" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54f72dd89c036847831ef4d3b8f7fd8618d87509422728f12b0937f96d6dd04" +checksum = "3daf8042376731e1873eae92dd609e1d0781105ffc3ffbc452f7bab719c887e2" dependencies = [ "arbitrary", "flagset", @@ -3330,18 +3330,18 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.88.0" +version = "0.89.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8cf7dd82407fe68161bedcd57fde15596f32ebf6e9b3bdbf3ae1da20e38e5e" +checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" dependencies = [ "indexmap", ] [[package]] name = "wasmprinter" -version = "0.2.38" +version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f2786f19a25211ddfa331e28b7579a6d6880f5f4b18d21253cd90274aa4c21" +checksum = "aa9e5ee2f56cc8a5da489558114e8c118e5a8416d96aefe63dcf1b5b05b858c6" dependencies = [ "anyhow", "wasmparser", @@ -3487,7 +3487,7 @@ dependencies = [ "wasmtime-wasi-crypto", "wasmtime-wasi-nn", "wasmtime-wast", - "wast 45.0.0", + "wast 46.0.0", "wat", "windows-sys", ] @@ -3745,7 +3745,7 @@ dependencies = [ "anyhow", "log", "wasmtime", - "wast 45.0.0", + "wast 46.0.0", ] [[package]] @@ -3759,9 +3759,9 @@ dependencies = [ [[package]] name = "wast" -version = "45.0.0" +version = "46.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186c474c4f9bb92756b566d592a16591b4526b1a4841171caa3f31d7fe330d96" +checksum = "ea0ab19660e3ea6891bba69167b9be40fad00fb1fe3dd39c5eebcee15607131b" dependencies = [ "leb128", "memchr", @@ -3771,11 +3771,11 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d4bc4724b4f02a482c8cab053dac5ef26410f264c06ce914958f9a42813556" +checksum = "8f775282def4d5bffd94d60d6ecd57bfe6faa46171cdbf8d32bd5458842b1e3e" dependencies = [ - "wast 45.0.0", + "wast 46.0.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9cd9ad2013..f39aff64d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,12 +51,12 @@ test-programs = { path = "crates/test-programs" } wasmtime-runtime = { path = "crates/runtime" } tokio = { version = "1.8.0", features = ["rt", "time", "macros", "rt-multi-thread"] } tracing-subscriber = "0.3.1" -wast = "45.0.0" +wast = "46.0.0" criterion = "0.3.4" num_cpus = "1.13.0" memchr = "2.4" async-trait = "0.1" -wat = "1.0.47" +wat = "1.0.48" once_cell = "1.9.0" rayon = "1.5.0" component-macro-test = { path = "crates/misc/component-macro-test" } diff --git a/cranelift/wasm/Cargo.toml b/cranelift/wasm/Cargo.toml index 0e89f9d3af..4ece1af05d 100644 --- a/cranelift/wasm/Cargo.toml +++ b/cranelift/wasm/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["webassembly", "wasm"] edition = "2021" [dependencies] -wasmparser = { version = "0.88.0", default-features = false } +wasmparser = { version = "0.89.0", default-features = false } cranelift-codegen = { path = "../codegen", version = "0.88.0", default-features = false } cranelift-entity = { path = "../entity", version = "0.88.0" } cranelift-frontend = { path = "../frontend", version = "0.88.0", default-features = false } diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs index 8ca0eeaf5e..e6a91e6dfb 100644 --- a/cranelift/wasm/src/sections_translator.rs +++ b/cranelift/wasm/src/sections_translator.rs @@ -304,10 +304,10 @@ pub fn parse_element_section<'data>( match kind { ElementKind::Active { table_index, - init_expr, + offset_expr, } => { - let mut init_expr_reader = init_expr.get_binary_reader(); - let (base, offset) = match init_expr_reader.read_operator()? { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { Operator::I32Const { value } => (None, value as u32), Operator::GlobalGet { global_index } => { (Some(GlobalIndex::from_u32(global_index)), 0) @@ -354,10 +354,10 @@ pub fn parse_data_section<'data>( match kind { DataKind::Active { memory_index, - init_expr, + offset_expr, } => { - let mut init_expr_reader = init_expr.get_binary_reader(); - let (base, offset) = match init_expr_reader.read_operator()? { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { Operator::I32Const { value } => (None, value as u64), Operator::I64Const { value } => (None, value as u64), Operator::GlobalGet { global_index } => { diff --git a/crates/component-macro/src/lib.rs b/crates/component-macro/src/lib.rs index 9c801a270f..77b9aa9934 100644 --- a/crates/component-macro/src/lib.rs +++ b/crates/component-macro/src/lib.rs @@ -753,11 +753,11 @@ impl Expander for ComponentTypeExpander { let name = rename.unwrap_or_else(|| Literal::string(&ident.to_string())); if let Some(ty) = ty { - abi_list.extend(quote!(<#ty as wasmtime::component::ComponentType>::ABI,)); + abi_list.extend(quote!(Some(<#ty as wasmtime::component::ComponentType>::ABI),)); case_names_and_checks.extend(match style { VariantStyle::Variant => { - quote!((#name, <#ty as wasmtime::component::ComponentType>::typecheck),) + quote!((#name, Some(<#ty as wasmtime::component::ComponentType>::typecheck)),) } VariantStyle::Union => { quote!(<#ty as wasmtime::component::ComponentType>::typecheck,) @@ -780,10 +780,10 @@ impl Expander for ComponentTypeExpander { unique_types.insert(ty); } else { - abi_list.extend(quote!(<() as wasmtime::component::ComponentType>::ABI,)); + abi_list.extend(quote!(None,)); case_names_and_checks.extend(match style { VariantStyle::Variant => { - quote!((#name, <() as wasmtime::component::ComponentType>::typecheck),) + quote!((#name, None),) } VariantStyle::Union => { quote!(<() as wasmtime::component::ComponentType>::typecheck,) @@ -846,7 +846,7 @@ impl Expander for ComponentTypeExpander { } unsafe impl #impl_generics #internal::ComponentVariant for #name #ty_generics #where_clause { - const CASES: &'static [#internal::CanonicalAbiInfo] = &[#abi_list]; + const CASES: &'static [Option<#internal::CanonicalAbiInfo>] = &[#abi_list]; } }; diff --git a/crates/cranelift/Cargo.toml b/crates/cranelift/Cargo.toml index 58f18084d7..8f57c2ce4c 100644 --- a/crates/cranelift/Cargo.toml +++ b/crates/cranelift/Cargo.toml @@ -19,7 +19,7 @@ cranelift-codegen = { path = "../../cranelift/codegen", version = "0.88.0" } cranelift-frontend = { path = "../../cranelift/frontend", version = "0.88.0" } cranelift-entity = { path = "../../cranelift/entity", version = "0.88.0" } cranelift-native = { path = "../../cranelift/native", version = "0.88.0" } -wasmparser = "0.88.0" +wasmparser = "0.89.0" target-lexicon = "0.12" gimli = { version = "0.26.0", default-features = false, features = ['read', 'std'] } object = { version = "0.29.0", default-features = false, features = ['write'] } diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index 412cbaeb71..f67e99d554 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -14,7 +14,7 @@ edition = "2021" anyhow = "1.0" cranelift-entity = { path = "../../cranelift/entity", version = "0.88.0" } wasmtime-types = { path = "../types", version = "0.41.0" } -wasmparser = "0.88.0" +wasmparser = "0.89.0" indexmap = { version = "1.0.2", features = ["serde-1"] } thiserror = "1.0.4" serde = { version = "1.0.94", features = ["derive"] } @@ -22,15 +22,15 @@ log = { version = "0.4.8", default-features = false } gimli = { version = "0.26.0", default-features = false, features = ['read'] } object = { version = "0.29.0", default-features = false, features = ['read_core', 'write_core', 'elf'] } target-lexicon = "0.12" -wasm-encoder = { version = "0.15.0", optional = true } -wasmprinter = { version = "0.2.38", optional = true } +wasm-encoder = { version = "0.16.0", optional = true } +wasmprinter = { version = "0.2.39", optional = true } wasmtime-component-util = { path = "../component-util", version = "=0.41.0", optional = true } [dev-dependencies] atty = "0.2.14" clap = { version = "3.2.8", features = ['derive'] } env_logger = "0.9.0" -wat = "1.0.47" +wat = "1.0.48" [[example]] name = "factc" diff --git a/crates/environ/fuzz/Cargo.toml b/crates/environ/fuzz/Cargo.toml index 1793bcc848..56f228c3e6 100644 --- a/crates/environ/fuzz/Cargo.toml +++ b/crates/environ/fuzz/Cargo.toml @@ -12,8 +12,8 @@ cargo-fuzz = true arbitrary = { version = "1.1.0", features = ["derive"] } env_logger = "0.9.0" libfuzzer-sys = "0.4" -wasmparser = "0.88.0" -wasmprinter = "0.2.37" +wasmparser = "0.89.0" +wasmprinter = "0.2.39" wat = "1.0" wasmtime-environ = { path = ".." } component-fuzz-util = { path = "../../misc/component-fuzz-util", optional = true } diff --git a/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs b/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs index 2c7fe269ed..b43f8cda6c 100644 --- a/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs +++ b/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs @@ -79,11 +79,11 @@ fn target(module: GenAdapterModule) { let wat = format!( "(component {types} - (type (func {params} {result})) + (type (func {params} {results})) )", types = wat_decls.types, params = wat_decls.params, - result = wat_decls.result, + results = wat_decls.results, ); let wasm = wat::parse_str(&wat).unwrap(); diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs index 0c4e47ee75..eab476d94d 100644 --- a/crates/environ/src/component/types.rs +++ b/crates/environ/src/component/types.rs @@ -94,9 +94,9 @@ indices! { /// Index pointing to an option type in the component model (aka a /// `Option`) pub struct TypeOptionIndex(u32); - /// Index pointing to an expected type in the component model (aka a + /// Index pointing to an result type in the component model (aka a /// `Result`) - pub struct TypeExpectedIndex(u32); + pub struct TypeResultIndex(u32); // ======================================================================== // Index types used to identify modules and components during compilation. @@ -215,7 +215,7 @@ pub struct ComponentTypes { flags: PrimaryMap, unions: PrimaryMap, options: PrimaryMap, - expecteds: PrimaryMap, + results: PrimaryMap, module_types: ModuleTypes, } @@ -229,8 +229,6 @@ impl ComponentTypes { /// Returns the canonical ABI information about the specified type. pub fn canonical_abi(&self, ty: &InterfaceType) -> &CanonicalAbiInfo { match ty { - InterfaceType::Unit => &CanonicalAbiInfo::ZERO, - InterfaceType::U8 | InterfaceType::S8 | InterfaceType::Bool => { &CanonicalAbiInfo::SCALAR1 } @@ -255,7 +253,7 @@ impl ComponentTypes { InterfaceType::Enum(i) => &self[*i].abi, InterfaceType::Union(i) => &self[*i].abi, InterfaceType::Option(i) => &self[*i].abi, - InterfaceType::Expected(i) => &self[*i].abi, + InterfaceType::Result(i) => &self[*i].abi, } } } @@ -284,7 +282,7 @@ impl_index! { impl Index for ComponentTypes { TypeFlags => flags } impl Index for ComponentTypes { TypeUnion => unions } impl Index for ComponentTypes { TypeOption => options } - impl Index for ComponentTypes { TypeExpected => expecteds } + impl Index for ComponentTypes { TypeResult => results } } // Additionally forward anything that can index `ModuleTypes` to `ModuleTypes` @@ -315,7 +313,7 @@ pub struct ComponentTypesBuilder { flags: HashMap, unions: HashMap, options: HashMap, - expecteds: HashMap, + results: HashMap, component_types: ComponentTypes, module_types: ModuleTypesBuilder, @@ -637,7 +635,11 @@ impl ComponentTypesBuilder { .iter() .map(|(name, ty)| (name.map(|s| s.to_string()), self.valtype(ty))) .collect(), - result: self.valtype(&ty.result), + results: ty + .results + .iter() + .map(|(name, ty)| (name.map(|s| s.to_string()), self.valtype(ty))) + .collect(), }; self.add_func_type(ty) } @@ -662,8 +664,8 @@ impl ComponentTypesBuilder { wasmparser::ComponentDefinedType::Option(e) => { InterfaceType::Option(self.option_type(e)) } - wasmparser::ComponentDefinedType::Expected { ok, error } => { - InterfaceType::Expected(self.expected_type(ok, error)) + wasmparser::ComponentDefinedType::Result { ok, err } => { + InterfaceType::Result(self.result_type(ok, err)) } } } @@ -707,15 +709,14 @@ impl ComponentTypesBuilder { assert!(case.refines.is_none()); VariantCase { name: case.name.to_string(), - ty: self.valtype(&case.ty), + ty: case.ty.as_ref().map(|ty| self.valtype(ty)), } }) .collect::>(); - let (info, abi) = VariantInfo::new( - cases - .iter() - .map(|c| self.component_types.canonical_abi(&c.ty)), - ); + let (info, abi) = VariantInfo::new(cases.iter().map(|c| { + c.ty.as_ref() + .map(|ty| self.component_types.canonical_abi(ty)) + })); self.add_variant_type(TypeVariant { cases, abi, info }) } @@ -742,11 +743,7 @@ impl ComponentTypesBuilder { fn enum_type(&mut self, variants: &[&str]) -> TypeEnumIndex { let names = variants.iter().map(|s| s.to_string()).collect::>(); - let (info, abi) = VariantInfo::new( - names - .iter() - .map(|_| self.component_types.canonical_abi(&InterfaceType::Unit)), - ); + let (info, abi) = VariantInfo::new(names.iter().map(|_| None)); self.add_enum_type(TypeEnum { names, abi, info }) } @@ -755,32 +752,32 @@ impl ComponentTypesBuilder { .iter() .map(|ty| self.valtype(ty)) .collect::>(); - let (info, abi) = - VariantInfo::new(types.iter().map(|t| self.component_types.canonical_abi(t))); + let (info, abi) = VariantInfo::new( + types + .iter() + .map(|t| Some(self.component_types.canonical_abi(t))), + ); self.add_union_type(TypeUnion { types, abi, info }) } fn option_type(&mut self, ty: &wasmparser::ComponentValType) -> TypeOptionIndex { let ty = self.valtype(ty); - let (info, abi) = VariantInfo::new([ - self.component_types.canonical_abi(&InterfaceType::Unit), - self.component_types.canonical_abi(&ty), - ]); + let (info, abi) = VariantInfo::new([None, Some(self.component_types.canonical_abi(&ty))]); self.add_option_type(TypeOption { ty, abi, info }) } - fn expected_type( + fn result_type( &mut self, - ok: &wasmparser::ComponentValType, - err: &wasmparser::ComponentValType, - ) -> TypeExpectedIndex { - let ok = self.valtype(ok); - let err = self.valtype(err); + ok: &Option, + err: &Option, + ) -> TypeResultIndex { + let ok = ok.as_ref().map(|ty| self.valtype(ty)); + let err = err.as_ref().map(|ty| self.valtype(ty)); let (info, abi) = VariantInfo::new([ - self.component_types.canonical_abi(&ok), - self.component_types.canonical_abi(&err), + ok.as_ref().map(|t| self.component_types.canonical_abi(t)), + err.as_ref().map(|t| self.component_types.canonical_abi(t)), ]); - self.add_expected_type(TypeExpected { ok, err, abi, info }) + self.add_result_type(TypeResult { ok, err, abi, info }) } /// Interns a new function type within this type information. @@ -823,12 +820,12 @@ impl ComponentTypesBuilder { intern_and_fill_flat_types!(self, options, ty) } - /// Interns a new expected type within this type information. - pub fn add_expected_type(&mut self, ty: TypeExpected) -> TypeExpectedIndex { - intern_and_fill_flat_types!(self, expecteds, ty) + /// Interns a new result type within this type information. + pub fn add_result_type(&mut self, ty: TypeResult) -> TypeResultIndex { + intern_and_fill_flat_types!(self, results, ty) } - /// Interns a new expected type within this type information. + /// Interns a new type within this type information. pub fn add_interface_type(&mut self, ty: InterfaceType) -> TypeInterfaceIndex { intern( &mut self.interface_types, @@ -849,7 +846,6 @@ impl ComponentTypesBuilder { /// in the canonical abi. pub fn flat_types(&self, ty: &InterfaceType) -> Option> { match ty { - InterfaceType::Unit => Some(FlatTypes::EMPTY), InterfaceType::U8 | InterfaceType::S8 | InterfaceType::Bool @@ -870,7 +866,7 @@ impl ComponentTypesBuilder { InterfaceType::Enum(i) => self.flat.enums[*i].as_flat_types(), InterfaceType::Union(i) => self.flat.unions[*i].as_flat_types(), InterfaceType::Option(i) => self.flat.options[*i].as_flat_types(), - InterfaceType::Expected(i) => self.flat.expecteds[*i].as_flat_types(), + InterfaceType::Result(i) => self.flat.results[*i].as_flat_types(), } } } @@ -973,8 +969,8 @@ pub struct TypeFunc { /// The list of optionally named parameters for this function, and their /// types. pub params: Box<[(Option, InterfaceType)]>, - /// The return value of this function. - pub result: InterfaceType, + /// The return values of this function. + pub results: Box<[(Option, InterfaceType)]>, } /// All possible interface types that values can have. @@ -986,7 +982,6 @@ pub struct TypeFunc { #[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq, Debug)] #[allow(missing_docs)] pub enum InterfaceType { - Unit, Bool, S8, U8, @@ -1008,13 +1003,12 @@ pub enum InterfaceType { Enum(TypeEnumIndex), Union(TypeUnionIndex), Option(TypeOptionIndex), - Expected(TypeExpectedIndex), + Result(TypeResultIndex), } impl From<&wasmparser::PrimitiveValType> for InterfaceType { fn from(ty: &wasmparser::PrimitiveValType) -> InterfaceType { match ty { - wasmparser::PrimitiveValType::Unit => InterfaceType::Unit, wasmparser::PrimitiveValType::Bool => InterfaceType::Bool, wasmparser::PrimitiveValType::S8 => InterfaceType::S8, wasmparser::PrimitiveValType::U8 => InterfaceType::U8, @@ -1080,7 +1074,7 @@ const fn max(a: u32, b: u32) -> u32 { impl CanonicalAbiInfo { /// ABI information for zero-sized types. - pub const ZERO: CanonicalAbiInfo = CanonicalAbiInfo { + const ZERO: CanonicalAbiInfo = CanonicalAbiInfo { size32: 0, align32: 1, size64: 0, @@ -1204,7 +1198,7 @@ impl CanonicalAbiInfo { fn variant<'a, I>(cases: I) -> CanonicalAbiInfo where - I: IntoIterator, + I: IntoIterator>, I::IntoIter: ExactSizeIterator, { // NB: this is basically a duplicate definition of @@ -1218,11 +1212,13 @@ impl CanonicalAbiInfo { let mut max_align64 = discrim_size; let mut max_case_count = Some(0); for case in cases { - max_size32 = max_size32.max(case.size32); - max_align32 = max_align32.max(case.align32); - max_size64 = max_size64.max(case.size64); - max_align64 = max_align64.max(case.align64); - max_case_count = max_flat(max_case_count, case.flat_count); + if let Some(case) = case { + max_size32 = max_size32.max(case.size32); + max_align32 = max_align32.max(case.align32); + max_size64 = max_size64.max(case.size64); + max_align64 = max_align64.max(case.align64); + max_case_count = max_flat(max_case_count, case.flat_count); + } } CanonicalAbiInfo { size32: align_to( @@ -1240,7 +1236,7 @@ impl CanonicalAbiInfo { } /// Same as `CanonicalAbiInfo::variant` but `const`-safe - pub const fn variant_static(cases: &[CanonicalAbiInfo]) -> CanonicalAbiInfo { + pub const fn variant_static(cases: &[Option]) -> CanonicalAbiInfo { // NB: this is basically a duplicate definition of // `CanonicalAbiInfo::variant`, these should be kept in sync. @@ -1256,11 +1252,13 @@ impl CanonicalAbiInfo { let mut i = 0; while i < cases.len() { let case = &cases[i]; - max_size32 = max(max_size32, case.size32); - max_align32 = max(max_align32, case.align32); - max_size64 = max(max_size64, case.size64); - max_align64 = max(max_align64, case.align64); - max_case_count = max_flat(max_case_count, case.flat_count); + if let Some(case) = case { + max_size32 = max(max_size32, case.size32); + max_align32 = max(max_align32, case.align32); + max_size64 = max(max_size64, case.size64); + max_align64 = max(max_align64, case.align64); + max_case_count = max_flat(max_case_count, case.flat_count); + } i += 1; } CanonicalAbiInfo { @@ -1309,7 +1307,7 @@ impl VariantInfo { /// cases. pub fn new<'a, I>(cases: I) -> (VariantInfo, CanonicalAbiInfo) where - I: IntoIterator, + I: IntoIterator>, I::IntoIter: ExactSizeIterator, { let cases = cases.into_iter(); @@ -1325,7 +1323,7 @@ impl VariantInfo { ) } /// TODO - pub const fn new_static(cases: &[CanonicalAbiInfo]) -> VariantInfo { + pub const fn new_static(cases: &[Option]) -> VariantInfo { let size = match DiscriminantSize::from_count(cases.len()) { Some(size) => size, None => unreachable!(), @@ -1404,8 +1402,8 @@ pub struct TypeVariant { pub struct VariantCase { /// Name of the variant, unique amongst all cases in a variant. pub name: String, - /// Type associated with this payload, maybe `Unit`. - pub ty: InterfaceType, + /// Optional type associated with this payload. + pub ty: Option, } /// Shape of a "tuple" type in interface types. @@ -1473,13 +1471,13 @@ pub struct TypeOption { pub info: VariantInfo, } -/// Shape of an "expected" interface type. +/// Shape of a "result" interface type. #[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] -pub struct TypeExpected { +pub struct TypeResult { /// The `T` in `Result` - pub ok: InterfaceType, + pub ok: Option, /// The `E` in `Result` - pub err: InterfaceType, + pub err: Option, /// Byte information about this type in the canonical ABI. pub abi: CanonicalAbiInfo, /// Byte information about this variant type. @@ -1531,7 +1529,6 @@ pub struct FlatTypes<'a> { #[allow(missing_docs)] impl FlatTypes<'_> { - pub const EMPTY: FlatTypes<'static> = FlatTypes::new(&[]); pub const I32: FlatTypes<'static> = FlatTypes::new(&[FlatType::I32]); pub const I64: FlatTypes<'static> = FlatTypes::new(&[FlatType::I64]); pub const F32: FlatTypes<'static> = FlatTypes::new(&[FlatType::F32]); @@ -1578,7 +1575,7 @@ struct FlatTypesCache { flags: PrimaryMap, unions: PrimaryMap, options: PrimaryMap, - expecteds: PrimaryMap, + results: PrimaryMap, } struct FlatTypesStorage { @@ -1662,22 +1659,33 @@ impl FlatTypesStorage { /// Builds up the flat types used to represent a `variant` which notably /// handles "join"ing types together so each case is representable as a /// single flat list of types. + /// + /// The iterator item is: + /// + /// * `None` - no payload for this case + /// * `Some(None)` - this case has a payload but can't be represented with + /// flat types + /// * `Some(Some(types))` - this case has a payload and is represented with + /// the types specified in the flat representation. fn build_variant<'a, I>(&mut self, cases: I) where - I: IntoIterator>>, + I: IntoIterator>>>, { let cases = cases.into_iter(); self.push(FlatType::I32, FlatType::I32); for ty in cases { let types = match ty { - Some(types) => types, + Some(Some(types)) => types, // If this case isn't representable with a flat list of types // then this variant also isn't representable. - None => { + Some(None) => { self.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap(); return; } + // If this case doesn't have a payload then it doesn't change + // whether this is representable or not. + None => continue, }; // If the case used all of the flat types then the discriminant // added for this variant means that this variant is no longer @@ -1739,19 +1747,26 @@ impl FlatTypesStorage { } fn variants(&mut self, types: &ComponentTypesBuilder, ty: &TypeVariant) { - self.build_variant(ty.cases.iter().map(|c| types.flat_types(&c.ty))) + self.build_variant( + ty.cases + .iter() + .map(|c| c.ty.as_ref().map(|ty| types.flat_types(ty))), + ) } fn unions(&mut self, types: &ComponentTypesBuilder, ty: &TypeUnion) { - self.build_variant(ty.types.iter().map(|t| types.flat_types(t))) + self.build_variant(ty.types.iter().map(|t| Some(types.flat_types(t)))) } - fn expecteds(&mut self, types: &ComponentTypesBuilder, ty: &TypeExpected) { - self.build_variant([types.flat_types(&ty.ok), types.flat_types(&ty.err)]); + fn results(&mut self, types: &ComponentTypesBuilder, ty: &TypeResult) { + self.build_variant([ + ty.ok.as_ref().map(|ty| types.flat_types(ty)), + ty.err.as_ref().map(|ty| types.flat_types(ty)), + ]) } fn options(&mut self, types: &ComponentTypesBuilder, ty: &TypeOption) { - self.build_variant([Some(FlatTypes::EMPTY), types.flat_types(&ty.ty)]); + self.build_variant([None, Some(types.flat_types(&ty.ty))]); } } diff --git a/crates/environ/src/fact/signature.rs b/crates/environ/src/fact/signature.rs index c93836c1fa..24a1fdd523 100644 --- a/crates/environ/src/fact/signature.rs +++ b/crates/environ/src/fact/signature.rs @@ -49,7 +49,11 @@ impl ComponentTypesBuilder { }; let mut results_indirect = false; - let results = match self.flatten_types(&options.options, MAX_FLAT_RESULTS, [ty.result]) { + let results = match self.flatten_types( + &options.options, + MAX_FLAT_RESULTS, + ty.results.iter().map(|(_, ty)| *ty), + ) { Some(list) => list, None => { results_indirect = true; diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index a058ebeb98..406bcd732a 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -17,7 +17,7 @@ use crate::component::{ CanonicalAbiInfo, ComponentTypesBuilder, InterfaceType, StringEncoding, TypeEnumIndex, - TypeExpectedIndex, TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, + TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, TypeResultIndex, TypeTupleIndex, TypeUnionIndex, TypeVariantIndex, VariantInfo, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, }; @@ -345,17 +345,19 @@ impl Compiler<'_, '_> { param_locals: &[(u32, ValType)], result_locals: &[(u32, ValType)], ) { - let src_ty = self.types[adapter.lift.ty].result; - let dst_ty = self.types[adapter.lower.ty].result; + let src_tys = &self.types[adapter.lift.ty].results; + let src_tys = src_tys.iter().map(|(_, ty)| *ty).collect::>(); + let dst_tys = &self.types[adapter.lower.ty].results; + let dst_tys = dst_tys.iter().map(|(_, ty)| *ty).collect::>(); let lift_opts = &adapter.lift.options; let lower_opts = &adapter.lower.options; - let src_flat = self - .types - .flatten_types(lift_opts, MAX_FLAT_RESULTS, [src_ty]); - let dst_flat = self - .types - .flatten_types(lower_opts, MAX_FLAT_RESULTS, [dst_ty]); + let src_flat = + self.types + .flatten_types(lift_opts, MAX_FLAT_RESULTS, src_tys.iter().copied()); + let dst_flat = + self.types + .flatten_types(lower_opts, MAX_FLAT_RESULTS, dst_tys.iter().copied()); let src = if src_flat.is_some() { Source::Stack(Stack { @@ -367,7 +369,11 @@ impl Compiler<'_, '_> { // return value of the function itself. The imported function will // return a linear memory address at which the values can be read // from. - let align = self.types.align(lift_opts, &src_ty); + let align = src_tys + .iter() + .map(|t| self.types.align(lift_opts, t)) + .max() + .unwrap_or(1); assert_eq!(result_locals.len(), 1); let (addr, ty) = result_locals[0]; assert_eq!(ty, lift_opts.ptr()); @@ -380,13 +386,25 @@ impl Compiler<'_, '_> { // This is slightly different than `translate_params` where the // return pointer was provided by the caller of this function // meaning the last parameter local is a pointer into linear memory. - let align = self.types.align(lower_opts, &dst_ty); + let align = dst_tys + .iter() + .map(|t| self.types.align(lower_opts, t)) + .max() + .unwrap_or(1); let (addr, ty) = *param_locals.last().expect("no retptr"); assert_eq!(ty, lower_opts.ptr()); Destination::Memory(self.memory_operand(lower_opts, TempLocal::new(addr, ty), align)) }; - self.translate(&src_ty, &src, &dst_ty, &dst); + let srcs = src + .record_field_srcs(self.types, src_tys.iter().copied()) + .zip(src_tys.iter()); + let dsts = dst + .record_field_dsts(self.types, dst_tys.iter().copied()) + .zip(dst_tys.iter()); + for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) { + self.translate(&src_ty, &src, &dst_ty, &dst); + } } fn translate( @@ -406,8 +424,7 @@ impl Compiler<'_, '_> { // Classify the source type as "primitive" or not as a heuristic to // whether the translation should be split out into a helper function. let src_primitive = match src_ty { - InterfaceType::Unit - | InterfaceType::Bool + InterfaceType::Bool | InterfaceType::U8 | InterfaceType::S8 | InterfaceType::U16 @@ -429,7 +446,7 @@ impl Compiler<'_, '_> { | InterfaceType::Union(_) | InterfaceType::Enum(_) | InterfaceType::Option(_) - | InterfaceType::Expected(_) => false, + | InterfaceType::Result(_) => false, }; let top_level = mem::replace(&mut self.top_level_translate, false); @@ -440,10 +457,10 @@ impl Compiler<'_, '_> { // were translated inline then this could get arbitrarily large // // (type $level0 (list u8)) - // (type $level1 (expected $level0 $level0)) - // (type $level2 (expected $level1 $level1)) - // (type $level3 (expected $level2 $level2)) - // (type $level4 (expected $level3 $level3)) + // (type $level1 (result $level0 $level0)) + // (type $level2 (result $level1 $level1)) + // (type $level3 (result $level2 $level2)) + // (type $level4 (result $level3 $level3)) // ;; ... // // If everything we inlined then translation of `$level0` would appear @@ -505,7 +522,6 @@ impl Compiler<'_, '_> { } } match src_ty { - InterfaceType::Unit => self.translate_unit(src, dst_ty, dst), InterfaceType::Bool => self.translate_bool(src, dst_ty, dst), InterfaceType::U8 => self.translate_u8(src, dst_ty, dst), InterfaceType::S8 => self.translate_s8(src, dst_ty, dst), @@ -527,18 +543,12 @@ impl Compiler<'_, '_> { InterfaceType::Union(u) => self.translate_union(*u, src, dst_ty, dst), InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst), InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst), - InterfaceType::Expected(t) => self.translate_expected(*t, src, dst_ty, dst), + InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst), } self.top_level_translate = top_level; } - fn translate_unit(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) { - // TODO: subtyping - assert!(matches!(dst_ty, InterfaceType::Unit)); - drop((src, dst)); - } - fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) { // TODO: subtyping assert!(matches!(dst_ty, InterfaceType::Bool)); @@ -1996,8 +2006,8 @@ impl Compiler<'_, '_> { _ => panic!("expected a variant"), }; - let src_info = variant_info(self.types, src_ty.cases.iter().map(|c| c.ty)); - let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|c| c.ty)); + let src_info = variant_info(self.types, src_ty.cases.iter().map(|c| c.ty.as_ref())); + let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|c| c.ty.as_ref())); let iter = src_ty.cases.iter().enumerate().map(|(src_i, src_case)| { let dst_i = dst_ty @@ -2010,9 +2020,9 @@ impl Compiler<'_, '_> { let dst_i = u32::try_from(dst_i).unwrap(); VariantCase { src_i, - src_ty: &src_case.ty, + src_ty: src_case.ty.as_ref(), dst_i, - dst_ty: &dst_case.ty, + dst_ty: dst_case.ty.as_ref(), } }); self.convert_variant(src, &src_info, dst, &dst_info, iter); @@ -2031,8 +2041,8 @@ impl Compiler<'_, '_> { _ => panic!("expected an option"), }; assert_eq!(src_ty.types.len(), dst_ty.types.len()); - let src_info = variant_info(self.types, src_ty.types.iter().copied()); - let dst_info = variant_info(self.types, dst_ty.types.iter().copied()); + let src_info = variant_info(self.types, src_ty.types.iter().map(Some)); + let dst_info = variant_info(self.types, dst_ty.types.iter().map(Some)); self.convert_variant( src, @@ -2049,8 +2059,8 @@ impl Compiler<'_, '_> { VariantCase { src_i: i, dst_i: i, - src_ty, - dst_ty, + src_ty: Some(src_ty), + dst_ty: Some(dst_ty), } }), ); @@ -2068,10 +2078,9 @@ impl Compiler<'_, '_> { InterfaceType::Enum(t) => &self.types[*t], _ => panic!("expected an option"), }; - let src_info = variant_info(self.types, src_ty.names.iter().map(|_| InterfaceType::Unit)); - let dst_info = variant_info(self.types, dst_ty.names.iter().map(|_| InterfaceType::Unit)); + let src_info = variant_info(self.types, src_ty.names.iter().map(|_| None)); + let dst_info = variant_info(self.types, dst_ty.names.iter().map(|_| None)); - let unit = &InterfaceType::Unit; self.convert_variant( src, &src_info, @@ -2084,8 +2093,8 @@ impl Compiler<'_, '_> { VariantCase { src_i, dst_i, - src_ty: unit, - dst_ty: unit, + src_ty: None, + dst_ty: None, } }), ); @@ -2103,9 +2112,11 @@ impl Compiler<'_, '_> { InterfaceType::Option(t) => &self.types[*t].ty, _ => panic!("expected an option"), }; + let src_ty = Some(src_ty); + let dst_ty = Some(dst_ty); - let src_info = variant_info(self.types, [InterfaceType::Unit, *src_ty]); - let dst_info = variant_info(self.types, [InterfaceType::Unit, *dst_ty]); + let src_info = variant_info(self.types, [None, src_ty]); + let dst_info = variant_info(self.types, [None, dst_ty]); self.convert_variant( src, @@ -2116,8 +2127,8 @@ impl Compiler<'_, '_> { VariantCase { src_i: 0, dst_i: 0, - src_ty: &InterfaceType::Unit, - dst_ty: &InterfaceType::Unit, + src_ty: None, + dst_ty: None, }, VariantCase { src_i: 1, @@ -2130,21 +2141,21 @@ impl Compiler<'_, '_> { ); } - fn translate_expected( + fn translate_result( &mut self, - src_ty: TypeExpectedIndex, + src_ty: TypeResultIndex, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination, ) { let src_ty = &self.types[src_ty]; let dst_ty = match dst_ty { - InterfaceType::Expected(t) => &self.types[*t], - _ => panic!("expected an expected"), + InterfaceType::Result(t) => &self.types[*t], + _ => panic!("expected a result"), }; - let src_info = variant_info(self.types, [src_ty.ok, src_ty.err]); - let dst_info = variant_info(self.types, [dst_ty.ok, dst_ty.err]); + let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]); + let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]); self.convert_variant( src, @@ -2155,14 +2166,14 @@ impl Compiler<'_, '_> { VariantCase { src_i: 0, dst_i: 0, - src_ty: &src_ty.ok, - dst_ty: &dst_ty.ok, + src_ty: src_ty.ok.as_ref(), + dst_ty: dst_ty.ok.as_ref(), }, VariantCase { src_i: 1, dst_i: 1, - src_ty: &src_ty.err, - dst_ty: &dst_ty.err, + src_ty: src_ty.err.as_ref(), + dst_ty: dst_ty.err.as_ref(), }, ] .into_iter(), @@ -2254,11 +2265,18 @@ impl Compiler<'_, '_> { }, } - // Translate the payload of this case using the various types from - // the dst/src. let src_payload = src.payload_src(self.types, src_info, src_ty); let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty); - self.translate(src_ty, &src_payload, dst_ty, &dst_payload); + + // Translate the payload of this case using the various types from + // the dst/src. + match (src_ty, dst_ty) { + (Some(src_ty), Some(dst_ty)) => { + self.translate(src_ty, &src_payload, dst_ty, &dst_payload); + } + (None, None) => {} + _ => unimplemented!(), + } // If the results of this translation were placed on the stack then // the stack values may need to be padded with more zeros due to @@ -2824,11 +2842,14 @@ impl<'a> Source<'a> { &self, types: &ComponentTypesBuilder, info: &VariantInfo, - case: &InterfaceType, + case: Option<&InterfaceType>, ) -> Source<'a> { match self { Source::Stack(s) => { - let flat_len = types.flat_types(case).unwrap().len(); + let flat_len = match case { + Some(case) => types.flat_types(case).unwrap().len(), + None => 0, + }; Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len)) } Source::Memory(mem) => { @@ -2879,11 +2900,14 @@ impl<'a> Destination<'a> { &self, types: &ComponentTypesBuilder, info: &VariantInfo, - case: &InterfaceType, + case: Option<&InterfaceType>, ) -> Destination { match self { Destination::Stack(s, opts) => { - let flat_len = types.flat_types(case).unwrap().len(); + let flat_len = match case { + Some(case) => types.flat_types(case).unwrap().len(), + None => 0, + }; Destination::Stack(&s[1..][..flat_len], opts) } Destination::Memory(mem) => { @@ -2949,17 +2973,22 @@ impl<'a> Stack<'a> { struct VariantCase<'a> { src_i: u32, - src_ty: &'a InterfaceType, + src_ty: Option<&'a InterfaceType>, dst_i: u32, - dst_ty: &'a InterfaceType, + dst_ty: Option<&'a InterfaceType>, } -fn variant_info(types: &ComponentTypesBuilder, cases: I) -> VariantInfo +fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo where - I: IntoIterator, + I: IntoIterator>, I::IntoIter: ExactSizeIterator, { - VariantInfo::new(cases.into_iter().map(|i| types.canonical_abi(&i))).0 + VariantInfo::new( + cases + .into_iter() + .map(|ty| ty.map(|ty| types.canonical_abi(ty))), + ) + .0 } enum MallocSize { diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs index 7429a348ab..b029084aeb 100644 --- a/crates/environ/src/module_environ.rs +++ b/crates/environ/src/module_environ.rs @@ -429,11 +429,11 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { match kind { ElementKind::Active { table_index, - init_expr, + offset_expr, } => { let table_index = TableIndex::from_u32(table_index); - let mut init_expr_reader = init_expr.get_binary_reader(); - let (base, offset) = match init_expr_reader.read_operator()? { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { Operator::I32Const { value } => (None, value as u32), Operator::GlobalGet { global_index } => { (Some(GlobalIndex::from_u32(global_index)), 0) @@ -547,12 +547,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { match kind { DataKind::Active { memory_index, - init_expr, + offset_expr, } => { let range = mk_range(&mut self.result.total_data)?; let memory_index = MemoryIndex::from_u32(memory_index); - let mut init_expr_reader = init_expr.get_binary_reader(); - let (base, offset) = match init_expr_reader.read_operator()? { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { Operator::I32Const { value } => (None, value as u64), Operator::I64Const { value } => (None, value as u64), Operator::GlobalGet { global_index } => { diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index d1dc70130d..823aaf5337 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -17,13 +17,13 @@ log = "0.4.8" rayon = "1.2.1" target-lexicon = "0.12.3" tempfile = "3.3.0" -wasmparser = "0.88.0" -wasmprinter = "0.2.38" +wasmparser = "0.89.0" +wasmprinter = "0.2.39" wasmtime = { path = "../wasmtime" } wasmtime-wast = { path = "../wast" } -wasm-encoder = "0.15.0" -wasm-smith = "0.11.3" -wasm-mutate = "0.2.6" +wasm-encoder = "0.16.0" +wasm-smith = "0.11.4" +wasm-mutate = "0.2.7" wasm-spec-interpreter = { path = "./wasm-spec-interpreter", optional = true } wasmi = "0.11.0" @@ -35,7 +35,7 @@ wasmi = "0.11.0" v8 = "0.44.3" [dev-dependencies] -wat = "1.0.47" +wat = "1.0.48" rand = { version = "0.8.0", features = ["small_rng"] } # Only enable the `build-libinterpret` feature when fuzzing is enabled, enabling diff --git a/crates/fuzzing/src/generators/component_types.rs b/crates/fuzzing/src/generators/component_types.rs index 1e78645a9d..133b990fc2 100644 --- a/crates/fuzzing/src/generators/component_types.rs +++ b/crates/fuzzing/src/generators/component_types.rs @@ -11,7 +11,7 @@ use component_fuzz_util::{Declarations, EXPORT_FUNCTION, IMPORT_FUNCTION}; use std::any::Any; use std::fmt::Debug; use std::ops::ControlFlow; -use wasmtime::component::{self, Component, Lift, Linker, Lower, Val}; +use wasmtime::component::{self, Component, ComponentNamedList, Lift, Linker, Lower, Val}; use wasmtime::{Config, Engine, Store, StoreContextMut}; /// Minimum length of an arbitrary list value generated for a test case @@ -25,7 +25,6 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar use component::Type; Ok(match ty { - Type::Unit => Val::Unit, Type::Bool => Val::Bool(input.arbitrary()?), Type::S8 => Val::S8(input.arbitrary()?), Type::U8 => Val::U8(input.arbitrary()?), @@ -66,18 +65,18 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar ) .unwrap(), Type::Variant(variant) => { - let mut cases = variant.cases(); - let discriminant = input.int_in_range(0..=cases.len() - 1)?; - variant - .new_val( - &format!("C{discriminant}"), - arbitrary_val(&cases.nth(discriminant).unwrap().ty, input)?, - ) - .unwrap() + let cases = variant.cases().collect::>(); + let case = input.choose(&cases)?; + let payload = match &case.ty { + Some(ty) => Some(arbitrary_val(ty, input)?), + None => None, + }; + variant.new_val(case.name, payload).unwrap() } Type::Enum(en) => { - let discriminant = input.int_in_range(0..=en.names().len() - 1)?; - en.new_val(&format!("C{discriminant}")).unwrap() + let names = en.names().collect::>(); + let name = input.choose(&names)?; + en.new_val(name).unwrap() } Type::Union(un) => { let mut types = un.types(); @@ -98,12 +97,18 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar }) .unwrap() } - Type::Expected(expected) => { + Type::Result(result) => { let discriminant = input.int_in_range(0..=1)?; - expected + result .new_val(match discriminant { - 0 => Ok(arbitrary_val(&expected.ok(), input)?), - 1 => Err(arbitrary_val(&expected.err(), input)?), + 0 => Ok(match result.ok() { + Some(ty) => Some(arbitrary_val(&ty, input)?), + None => None, + }), + 1 => Err(match result.err() { + Some(ty) => Some(arbitrary_val(&ty, input)?), + None => None, + }), _ => unreachable!(), }) .unwrap() @@ -135,7 +140,7 @@ macro_rules! define_static_api_test { ) -> arbitrary::Result<()> where $($param: Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static,)* - R: Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static + R: ComponentNamedList + Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static { crate::init_fuzzing(); diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index 0bf6617495..2ddc0c31e5 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -1090,7 +1090,7 @@ pub fn dynamic_component_api_target(input: &mut arbitrary::Unstructured) -> arbi let mut config = component_test_util::config(); config.debug_adapter_modules(input.arbitrary()?); let engine = Engine::new(&config).unwrap(); - let mut store = Store::new(&engine, (Box::new([]) as Box<[Val]>, None)); + let mut store = Store::new(&engine, (Vec::new(), None)); let wat = case.declarations().make_component(); let wat = wat.as_bytes(); log_wasm(wat); @@ -1100,39 +1100,46 @@ pub fn dynamic_component_api_target(input: &mut arbitrary::Unstructured) -> arbi linker .root() .func_new(&component, IMPORT_FUNCTION, { - move |cx: StoreContextMut<'_, (Box<[Val]>, Option)>, args: &[Val]| -> Result { - log::trace!("received arguments {args:?}"); - let (expected_args, result) = cx.data(); - assert_eq!(args.len(), expected_args.len()); - for (expected, actual) in expected_args.iter().zip(args) { + move |mut cx: StoreContextMut<'_, (Vec, Option>)>, + params: &[Val], + results: &mut [Val]| + -> Result<()> { + log::trace!("received params {params:?}"); + let (expected_args, expected_results) = cx.data_mut(); + assert_eq!(params.len(), expected_args.len()); + for (expected, actual) in expected_args.iter().zip(params) { assert_eq!(expected, actual); } - let result = result.as_ref().unwrap().clone(); - log::trace!("returning result {result:?}"); - Ok(result) + results.clone_from_slice(&expected_results.take().unwrap()); + log::trace!("returning results {results:?}"); + Ok(()) } }) .unwrap(); let instance = linker.instantiate(&mut store, &component).unwrap(); let func = instance.get_func(&mut store, EXPORT_FUNCTION).unwrap(); - let params = func.params(&store); - let result = func.result(&store); + let param_tys = func.params(&store); + let result_tys = func.results(&store); while input.arbitrary()? { - let args = params + let params = param_tys .iter() .map(|ty| component_types::arbitrary_val(ty, input)) - .collect::>>()?; + .collect::>>()?; + let results = result_tys + .iter() + .map(|ty| component_types::arbitrary_val(ty, input)) + .collect::>>()?; - let result = component_types::arbitrary_val(&result, input)?; + *store.data_mut() = (params.clone(), Some(results.clone())); - *store.data_mut() = (args.clone(), Some(result.clone())); - - log::trace!("passing args {args:?}"); - let actual = func.call_and_post_return(&mut store, &args).unwrap(); - log::trace!("received return {actual:?}"); - assert_eq!(actual, result); + log::trace!("passing params {params:?}"); + let mut actual = vec![Val::Bool(false); results.len()]; + func.call_and_post_return(&mut store, ¶ms, &mut actual) + .unwrap(); + log::trace!("received results {actual:?}"); + assert_eq!(actual, results); } Ok(()) diff --git a/crates/misc/component-fuzz-util/src/lib.rs b/crates/misc/component-fuzz-util/src/lib.rs index 8387d29d9c..7c6509d404 100644 --- a/crates/misc/component-fuzz-util/src/lib.rs +++ b/crates/misc/component-fuzz-util/src/lib.rs @@ -17,7 +17,7 @@ use wasmtime_component_util::{DiscriminantSize, FlagsSize, REALLOC_AND_FREE}; const MAX_FLAT_PARAMS: usize = 16; const MAX_FLAT_RESULTS: usize = 1; -const MAX_ARITY: usize = 5; +const MAX_ARITY: u32 = 5; /// The name of the imported host function which the generated component will call pub const IMPORT_FUNCTION: &str = "echo"; @@ -57,7 +57,7 @@ impl fmt::Display for CoreType { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UsizeInRange(usize); impl UsizeInRange { @@ -74,7 +74,7 @@ impl<'a, const L: usize, const H: usize> Arbitrary<'a> for UsizeInRange { /// Wraps a `Box<[T]>` and provides an `Arbitrary` implementation that always generates slices of length less than /// or equal to the longest tuple for which Wasmtime generates a `ComponentType` impl -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct VecInRange(Vec); impl<'a, T: Arbitrary<'a>, const L: u32, const H: u32> Arbitrary<'a> for VecInRange { @@ -98,9 +98,8 @@ impl Deref for VecInRange { /// Represents a component model interface type #[allow(missing_docs)] -#[derive(Arbitrary, Debug)] +#[derive(Arbitrary, Debug, Clone)] pub enum Type { - Unit, Bool, S8, U8, @@ -129,12 +128,15 @@ pub enum Type { // Like records, allow a good number of variants, but variants require at // least one case. - Variant(VecInRange), + Variant(VecInRange, 1, 200>), Enum(UsizeInRange<1, 257>), Union(VecInRange), Option(Box), - Expected { ok: Box, err: Box }, + Result { + ok: Option>, + err: Option>, + }, // Generate 0 flags all the way up to 65 flags which exercises the 0 to // 3 x u32 cases. @@ -147,10 +149,14 @@ fn lower_record<'a>(types: impl Iterator, vec: &mut Vec(types: impl Iterator, vec: &mut Vec) { +fn lower_variant<'a>(types: impl Iterator>, vec: &mut Vec) { vec.push(CoreType::I32); let offset = vec.len(); for ty in types { + let ty = match ty { + Some(ty) => ty, + None => continue, + }; for (index, ty) in ty.lowered().iter().enumerate() { let index = offset + index; if index < vec.len() { @@ -184,7 +190,6 @@ impl Type { fn lower(&self, vec: &mut Vec) { match self { - Type::Unit => (), Type::Bool | Type::U8 | Type::S8 @@ -203,9 +208,12 @@ impl Type { } Type::Record(types) => lower_record(types.iter(), vec), Type::Tuple(types) => lower_record(types.0.iter(), vec), - Type::Variant(types) | Type::Union(types) => lower_variant(types.0.iter(), vec), - Type::Option(ty) => lower_variant([&Type::Unit, ty].into_iter(), vec), - Type::Expected { ok, err } => lower_variant([ok.deref(), err].into_iter(), vec), + Type::Variant(types) => lower_variant(types.0.iter().map(|t| t.as_ref()), vec), + Type::Union(types) => lower_variant(types.0.iter().map(Some), vec), + Type::Option(ty) => lower_variant([None, Some(&**ty)].into_iter(), vec), + Type::Result { ok, err } => { + lower_variant([ok.as_deref(), err.as_deref()].into_iter(), vec) + } Type::Flags(count) => { vec.extend(iter::repeat(CoreType::I32).take(u32_count_from_flag_count(count.0))) } @@ -214,11 +222,6 @@ impl Type { fn size_and_alignment(&self) -> SizeAndAlignment { match self { - Type::Unit => SizeAndAlignment { - size: 0, - alignment: 1, - }, - Type::Bool | Type::S8 | Type::U8 => SizeAndAlignment { size: 1, alignment: 1, @@ -248,13 +251,16 @@ impl Type { Type::Tuple(types) => record_size_and_alignment(types.0.iter()), - Type::Variant(types) | Type::Union(types) => variant_size_and_alignment(types.0.iter()), + Type::Variant(types) => variant_size_and_alignment(types.0.iter().map(|t| t.as_ref())), + Type::Union(types) => variant_size_and_alignment(types.0.iter().map(Some)), - Type::Enum(count) => variant_size_and_alignment((0..count.0).map(|_| &Type::Unit)), + Type::Enum(count) => variant_size_and_alignment((0..count.0).map(|_| None)), - Type::Option(ty) => variant_size_and_alignment([&Type::Unit, ty].into_iter()), + Type::Option(ty) => variant_size_and_alignment([None, Some(&**ty)].into_iter()), - Type::Expected { ok, err } => variant_size_and_alignment([ok.deref(), err].into_iter()), + Type::Result { ok, err } => { + variant_size_and_alignment([ok.as_deref(), err.as_deref()].into_iter()) + } Type::Flags(count) => match FlagsSize::from_count(count.0) { FlagsSize::Size0 => SizeAndAlignment { @@ -299,15 +305,17 @@ fn record_size_and_alignment<'a>(types: impl Iterator) -> SizeA } fn variant_size_and_alignment<'a>( - types: impl ExactSizeIterator, + types: impl ExactSizeIterator>, ) -> SizeAndAlignment { let discriminant_size = DiscriminantSize::from_count(types.len()).unwrap(); let mut alignment = u32::from(discriminant_size); let mut size = 0; for ty in types { - let size_and_alignment = ty.size_and_alignment(); - alignment = alignment.max(size_and_alignment.alignment); - size = size.max(size_and_alignment.size); + if let Some(ty) = ty { + let size_and_alignment = ty.size_and_alignment(); + alignment = alignment.max(size_and_alignment.alignment); + size = size.max(size_and_alignment.size); + } } SizeAndAlignment { @@ -319,12 +327,15 @@ fn variant_size_and_alignment<'a>( } } -fn make_import_and_export(params: &[Type], result: &Type) -> String { +fn make_import_and_export(params: &[Type], results: &[Type]) -> String { let params_lowered = params .iter() .flat_map(|ty| ty.lowered()) .collect::>(); - let result_lowered = result.lowered(); + let results_lowered = results + .iter() + .flat_map(|ty| ty.lowered()) + .collect::>(); let mut core_params = String::new(); let mut gets = String::new(); @@ -345,13 +356,13 @@ fn make_import_and_export(params: &[Type], result: &Type) -> String { format!("(param{core_params})") }; - if result_lowered.len() <= MAX_FLAT_RESULTS { + if results_lowered.len() <= MAX_FLAT_RESULTS { let mut core_results = String::new(); - for result in result_lowered.iter() { + for result in results_lowered.iter() { write!(&mut core_results, " {result}").unwrap(); } - let maybe_core_results = if result_lowered.is_empty() { + let maybe_core_results = if results_lowered.is_empty() { String::new() } else { format!("(result{core_results})") @@ -368,7 +379,8 @@ fn make_import_and_export(params: &[Type], result: &Type) -> String { )"# ) } else { - let SizeAndAlignment { size, alignment } = result.size_and_alignment(); + let SizeAndAlignment { size, alignment } = + Type::Record(VecInRange(results.to_vec())).size_and_alignment(); format!( r#" @@ -405,7 +417,6 @@ fn make_rust_name(name_counter: &mut u32) -> Ident { /// parameter is used to accumulate declarations for each recursively visited type. pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStream) -> TokenStream { match ty { - Type::Unit => quote!(()), Type::Bool => quote!(bool), Type::S8 => quote!(i8), Type::U8 => quote!(u8), @@ -458,29 +469,51 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre quote!((#fields)) } - Type::Variant(types) | Type::Union(types) => { + Type::Variant(types) => { let cases = types .0 .iter() .enumerate() .map(|(index, ty)| { let name = format_ident!("C{index}"); - let ty = rust_type(ty, name_counter, declarations); - quote!(#name(#ty),) + let ty = match ty { + Some(ty) => { + let ty = rust_type(ty, name_counter, declarations); + quote!((#ty)) + } + None => quote!(), + }; + quote!(#name #ty,) }) .collect::(); let name = make_rust_name(name_counter); + declarations.extend(quote! { + #[derive(ComponentType, Lift, Lower, PartialEq, Debug, Clone, Arbitrary)] + #[component(variant)] + enum #name { + #cases + } + }); - let which = if let Type::Variant(_) = ty { - quote!(variant) - } else { - quote!(union) - }; + quote!(#name) + } + Type::Union(types) => { + let cases = types + .0 + .iter() + .enumerate() + .map(|(index, ty)| { + let name = format_ident!("U{index}"); + let ty = rust_type(ty, name_counter, declarations); + quote!(#name(#ty),) + }) + .collect::(); + let name = make_rust_name(name_counter); declarations.extend(quote! { #[derive(ComponentType, Lift, Lower, PartialEq, Debug, Clone, Arbitrary)] - #[component(#which)] + #[component(union)] enum #name { #cases } @@ -491,7 +524,7 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre Type::Enum(count) => { let cases = (0..count.0) .map(|index| { - let name = format_ident!("C{index}"); + let name = format_ident!("E{index}"); quote!(#name,) }) .collect::(); @@ -512,9 +545,15 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre let ty = rust_type(ty, name_counter, declarations); quote!(Option<#ty>) } - Type::Expected { ok, err } => { - let ok = rust_type(ok, name_counter, declarations); - let err = rust_type(err, name_counter, declarations); + Type::Result { ok, err } => { + let ok = match ok { + Some(ok) => rust_type(ok, name_counter, declarations), + None => quote!(()), + }; + let err = match err { + Some(err) => rust_type(err, name_counter, declarations), + None => quote!(()), + }; quote!(Result<#ok, #err>) } Type::Flags(count) => { @@ -564,7 +603,6 @@ impl<'a> TypesBuilder<'a> { fn write_ref(&mut self, ty: &'a Type, dst: &mut String) { match ty { // Primitive types can be referenced directly - Type::Unit => dst.push_str("unit"), Type::Bool => dst.push_str("bool"), Type::S8 => dst.push_str("s8"), Type::U8 => dst.push_str("u8"), @@ -588,7 +626,7 @@ impl<'a> TypesBuilder<'a> { | Type::Enum(_) | Type::Union(_) | Type::Option(_) - | Type::Expected { .. } + | Type::Result { .. } | Type::Flags(_) => { let idx = self.next; self.next += 1; @@ -601,8 +639,7 @@ impl<'a> TypesBuilder<'a> { fn write_decl(&mut self, idx: u32, ty: &'a Type) -> String { let mut decl = format!("(type $t{idx} "); match ty { - Type::Unit - | Type::Bool + Type::Bool | Type::S8 | Type::U8 | Type::S16 @@ -641,8 +678,11 @@ impl<'a> TypesBuilder<'a> { Type::Variant(types) => { decl.push_str("(variant"); for (index, ty) in types.iter().enumerate() { - write!(decl, r#" (case "C{index}" "#).unwrap(); - self.write_ref(ty, &mut decl); + write!(decl, r#" (case "C{index}""#).unwrap(); + if let Some(ty) = ty { + decl.push_str(" "); + self.write_ref(ty, &mut decl); + } decl.push_str(")"); } decl.push_str(")"); @@ -667,11 +707,17 @@ impl<'a> TypesBuilder<'a> { self.write_ref(ty, &mut decl); decl.push_str(")"); } - Type::Expected { ok, err } => { - decl.push_str("(expected "); - self.write_ref(ok, &mut decl); - decl.push_str(" "); - self.write_ref(err, &mut decl); + Type::Result { ok, err } => { + decl.push_str("(result"); + if let Some(ok) = ok { + decl.push_str(" "); + self.write_ref(ok, &mut decl); + } + if let Some(err) = err { + decl.push_str(" (error "); + self.write_ref(err, &mut decl); + decl.push_str(")"); + } decl.push_str(")"); } Type::Flags(count) => { @@ -695,7 +741,7 @@ pub struct Declarations { /// Parameter declarations used for the imported and exported functions pub params: Cow<'static, str>, /// Result declaration used for the imported and exported functions - pub result: Cow<'static, str>, + pub results: Cow<'static, str>, /// A WAT fragment representing the core function import and export to use for testing pub import_and_export: Cow<'static, str>, /// String encoding to use for host -> component @@ -710,7 +756,7 @@ impl Declarations { let Self { types, params, - result, + results, import_and_export, encoding1, encoding2, @@ -768,7 +814,7 @@ impl Declarations { {types} - (type $sig (func {params} {result})) + (type $sig (func {params} {results})) (import "{IMPORT_FUNCTION}" (func $f (type $sig))) {c1} @@ -783,12 +829,12 @@ impl Declarations { } /// Represents a test case for calling a component function -#[derive(Debug)] +#[derive(Arbitrary, Debug)] pub struct TestCase { /// The types of parameters to pass to the function - pub params: Box<[Type]>, - /// The type of the result to be returned by the function - pub result: Type, + pub params: VecInRange, + /// The result types of the the function + pub results: VecInRange, /// String encoding to use from host-to-component. pub encoding1: StringEncoding, /// String encoding to use from component-to-host. @@ -801,17 +847,20 @@ impl TestCase { let mut builder = TypesBuilder::default(); let mut params = String::new(); - for ty in self.params.iter() { - params.push_str(" (param "); + for (i, ty) in self.params.iter().enumerate() { + params.push_str(&format!(" (param \"p{i}\" ")); builder.write_ref(ty, &mut params); params.push_str(")"); } - let mut result = String::from("(result "); - builder.write_ref(&self.result, &mut result); - result.push_str(")"); + let mut results = String::new(); + for (i, ty) in self.results.iter().enumerate() { + results.push_str(&format!(" (result \"r{i}\" ")); + builder.write_ref(ty, &mut results); + results.push_str(")"); + } - let import_and_export = make_import_and_export(&self.params, &self.result); + let import_and_export = make_import_and_export(&self.params, &self.results); let mut type_decls = Vec::new(); while let Some((idx, ty)) = builder.worklist.pop() { @@ -830,7 +879,7 @@ impl TestCase { Declarations { types: types.into(), params: params.into(), - result: result.into(), + results: results.into(), import_and_export: import_and_export.into(), encoding1: self.encoding1, encoding2: self.encoding2, @@ -838,21 +887,6 @@ impl TestCase { } } -impl<'a> Arbitrary<'a> for TestCase { - /// Generate an arbitrary [`TestCase`]. - fn arbitrary(input: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self { - params: input - .arbitrary_iter()? - .take(MAX_ARITY) - .collect::>>()?, - result: input.arbitrary()?, - encoding1: input.arbitrary()?, - encoding2: input.arbitrary()?, - }) - } -} - #[derive(Copy, Clone, Debug, Arbitrary)] pub enum StringEncoding { Utf8, diff --git a/crates/misc/component-test-util/src/lib.rs b/crates/misc/component-test-util/src/lib.rs index c5a4970e6f..6636dc7d50 100644 --- a/crates/misc/component-test-util/src/lib.rs +++ b/crates/misc/component-test-util/src/lib.rs @@ -4,7 +4,7 @@ use std::mem::MaybeUninit; use wasmtime::component::__internal::{ CanonicalAbiInfo, ComponentTypes, InterfaceType, Memory, MemoryMut, Options, StoreOpaque, }; -use wasmtime::component::{ComponentParams, ComponentType, Func, Lift, Lower, TypedFunc, Val}; +use wasmtime::component::{ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, Val}; use wasmtime::{AsContextMut, Config, Engine, StoreContextMut}; pub trait TypedFuncExt { @@ -13,8 +13,8 @@ pub trait TypedFuncExt { impl TypedFuncExt for TypedFunc where - P: ComponentParams + Lower, - R: Lift, + P: ComponentNamedList + Lower, + R: ComponentNamedList + Lift, { fn call_and_post_return(&self, mut store: impl AsContextMut, params: P) -> Result { let result = self.call(&mut store, params)?; @@ -24,14 +24,24 @@ where } pub trait FuncExt { - fn call_and_post_return(&self, store: impl AsContextMut, args: &[Val]) -> Result; + fn call_and_post_return( + &self, + store: impl AsContextMut, + params: &[Val], + results: &mut [Val], + ) -> Result<()>; } impl FuncExt for Func { - fn call_and_post_return(&self, mut store: impl AsContextMut, args: &[Val]) -> Result { - let result = self.call(&mut store, args)?; + fn call_and_post_return( + &self, + mut store: impl AsContextMut, + params: &[Val], + results: &mut [Val], + ) -> Result<()> { + self.call(&mut store, params, results)?; self.post_return(&mut store)?; - Ok(result) + Ok(()) } } diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 6cacf58509..6d1d24f6f3 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -12,4 +12,4 @@ edition = "2021" cranelift-entity = { path = "../../cranelift/entity", version = "0.88.0", features = ['enable-serde'] } serde = { version = "1.0.94", features = ["derive"] } thiserror = "1.0.4" -wasmparser = { version = "0.88.0", default-features = false } +wasmparser = { version = "0.89.0", default-features = false } diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index 18472c9198..297a683045 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -22,7 +22,7 @@ wasmtime-cranelift = { path = "../cranelift", version = "=0.41.0", optional = tr wasmtime-component-macro = { path = "../component-macro", version = "=0.41.0", optional = true } wasmtime-component-util = { path = "../component-util", version = "=0.41.0", optional = true } target-lexicon = { version = "0.12.0", default-features = false } -wasmparser = "0.88.0" +wasmparser = "0.89.0" anyhow = "1.0.19" libc = "0.2" cfg-if = "1.0" diff --git a/crates/wasmtime/src/component/func.rs b/crates/wasmtime/src/component/func.rs index ce00716377..f62df418a9 100644 --- a/crates/wasmtime/src/component/func.rs +++ b/crates/wasmtime/src/component/func.rs @@ -198,8 +198,8 @@ impl Func { /// # use wasmtime::component::Func; /// # use wasmtime::Store; /// # fn foo(func: &Func, mut store: Store<()>) -> anyhow::Result<()> { - /// let typed = func.typed::<(&str,), String, _>(&store)?; - /// let ret = typed.call(&mut store, ("Hello, ",))?; + /// let typed = func.typed::<(&str,), (String,), _>(&store)?; + /// let ret = typed.call(&mut store, ("Hello, ",))?.0; /// println!("returned string was: {}", ret); /// # Ok(()) /// # } @@ -211,16 +211,16 @@ impl Func { /// # use wasmtime::component::Func; /// # use wasmtime::Store; /// # fn foo(func: &Func, mut store: Store<()>) -> anyhow::Result<()> { - /// let typed = func.typed::<(u32, Option<&str>, &[u8]), bool, _>(&store)?; - /// let ok: bool = typed.call(&mut store, (1, Some("hello"), b"bytes!"))?; + /// let typed = func.typed::<(u32, Option<&str>, &[u8]), (bool,), _>(&store)?; + /// let ok: bool = typed.call(&mut store, (1, Some("hello"), b"bytes!"))?.0; /// println!("return value was: {ok}"); /// # Ok(()) /// # } /// ``` pub fn typed(&self, store: S) -> Result> where - Params: ComponentParams + Lower, - Return: Lift, + Params: ComponentNamedList + Lower, + Return: ComponentNamedList + Lift, S: AsContext, { self._typed(store.as_context().0) @@ -231,8 +231,8 @@ impl Func { store: &StoreOpaque, ) -> Result> where - Params: ComponentParams + Lower, - Return: Lift, + Params: ComponentNamedList + Lower, + Return: ComponentNamedList + Lift, { self.typecheck::(store)?; unsafe { Ok(TypedFunc::new_unchecked(*self)) } @@ -240,15 +240,16 @@ impl Func { fn typecheck(&self, store: &StoreOpaque) -> Result<()> where - Params: ComponentParams + Lower, - Return: Lift, + Params: ComponentNamedList + Lower, + Return: ComponentNamedList + Lift, { let data = &store[self.0]; let ty = &data.types[data.ty]; - Params::typecheck_params(&ty.params, &data.types) + Params::typecheck_named_list(&ty.params, &data.types) .context("type mismatch with parameters")?; - Return::typecheck(&ty.result, &data.types).context("type mismatch with result")?; + Return::typecheck_named_list(&ty.results, &data.types) + .context("type mismatch with results")?; Ok(()) } @@ -263,10 +264,14 @@ impl Func { .collect() } - /// Get the result type for this function. - pub fn result(&self, store: impl AsContext) -> Type { + /// Get the result types for this function. + pub fn results(&self, store: impl AsContext) -> Box<[Type]> { let data = &store.as_context()[self.0]; - Type::from(&data.types[data.ty].result, &data.types) + data.types[data.ty] + .results + .iter() + .map(|(_, ty)| Type::from(ty, &data.types)) + .collect() } /// Invokes this function with the `params` given and returns the result. @@ -274,64 +279,71 @@ impl Func { /// The `params` here must match the type signature of this `Func`, or this will return an error. If a trap /// occurs while executing this function, then an error will also be returned. // TODO: say more -- most of the docs for `TypedFunc::call` apply here, too - pub fn call(&self, mut store: impl AsContextMut, args: &[Val]) -> Result { + pub fn call( + &self, + mut store: impl AsContextMut, + params: &[Val], + results: &mut [Val], + ) -> Result<()> { let store = &mut store.as_context_mut(); - let params; - let result; + let param_tys = self.params(&store); + let result_tys = self.results(&store); - { - let data = &store[self.0]; - let ty = &data.types[data.ty]; - - if ty.params.len() != args.len() { - bail!( - "expected {} argument(s), got {}", - ty.params.len(), - args.len() - ); - } - - params = ty - .params - .iter() - .zip(args) - .map(|((_, ty), arg)| { - let ty = Type::from(ty, &data.types); - - ty.check(arg).context("type mismatch with parameters")?; - - Ok(ty) - }) - .collect::>>()?; - - result = Type::from(&ty.result, &data.types); + if param_tys.len() != params.len() { + bail!( + "expected {} argument(s), got {}", + param_tys.len(), + params.len() + ); + } + if result_tys.len() != results.len() { + bail!( + "expected {} results(s), got {}", + result_tys.len(), + results.len() + ); } - let param_abi = CanonicalAbiInfo::record(params.iter().map(|t| t.canonical_abi())); - let result_count = result.canonical_abi().flat_count(MAX_FLAT_RESULTS); + for (param, ty) in params.iter().zip(param_tys.iter()) { + ty.check(param).context("type mismatch with parameters")?; + } + + let param_abi = CanonicalAbiInfo::record(param_tys.iter().map(|t| t.canonical_abi())); + let result_abi = CanonicalAbiInfo::record(result_tys.iter().map(|t| t.canonical_abi())); self.call_raw( store, - args, - |store, options, args, dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>| { - if param_abi.flat_count(MAX_FLAT_PARAMS).is_none() { - self.store_args(store, &options, ¶m_abi, ¶ms, args, dst) - } else { + params, + |store, options, params, dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>| { + if param_abi.flat_count(MAX_FLAT_PARAMS).is_some() { let dst = &mut unsafe { mem::transmute::<_, &mut [MaybeUninit; MAX_FLAT_PARAMS]>(dst) } .iter_mut(); - args.iter() - .try_for_each(|arg| arg.lower(store, &options, dst)) + params + .iter() + .try_for_each(|param| param.lower(store, &options, dst)) + } else { + self.store_args(store, &options, ¶m_abi, ¶m_tys, params, dst) } }, |store, options, src: &[ValRaw; MAX_FLAT_RESULTS]| { - if result_count.is_none() { - Self::load_result(&Memory::new(store, &options), &result, &mut src.iter()) + if result_abi.flat_count(MAX_FLAT_RESULTS).is_some() { + let mut flat = src.iter(); + for (ty, slot) in result_tys.iter().zip(results) { + *slot = Val::lift(ty, store, &options, &mut flat)?; + } + Ok(()) } else { - Val::lift(&result, store, &options, &mut src.iter()) + Self::load_results( + &Memory::new(store, &options), + &result_abi, + &result_tys, + results, + &mut src.iter(), + ) } }, ) @@ -567,12 +579,13 @@ impl Func { Ok(()) } - fn load_result<'a>( + fn load_results<'a>( mem: &Memory, - ty: &Type, + abi: &CanonicalAbiInfo, + result_tys: &[Type], + results: &mut [Val], src: &mut std::slice::Iter<'_, ValRaw>, - ) -> Result { - let abi = ty.canonical_abi(); + ) -> Result<()> { // FIXME: needs to read an i64 for memory64 let ptr = usize::try_from(src.next().unwrap().get_u32())?; if ptr % usize::try_from(abi.align32)? != 0 { @@ -585,6 +598,12 @@ impl Func { .and_then(|b| b.get(..usize::try_from(abi.size32).unwrap())) .ok_or_else(|| anyhow::anyhow!("pointer out of bounds of memory"))?; - Val::load(ty, mem, bytes) + let mut offset = 0; + for (ty, slot) in result_tys.iter().zip(results) { + let abi = ty.canonical_abi(); + let offset = abi.next_field32_size(&mut offset); + *slot = Val::load(ty, mem, &bytes[offset..][..abi.size32 as usize])?; + } + Ok(()) } } diff --git a/crates/wasmtime/src/component/func/host.rs b/crates/wasmtime/src/component/func/host.rs index b7301e2698..e3c5c520e3 100644 --- a/crates/wasmtime/src/component/func/host.rs +++ b/crates/wasmtime/src/component/func/host.rs @@ -1,6 +1,6 @@ use crate::component::func::{Memory, MemoryMut, Options}; use crate::component::storage::slice_to_storage_mut; -use crate::component::{ComponentParams, ComponentType, Lift, Lower, Type, Val}; +use crate::component::{ComponentNamedList, ComponentType, Lift, Lower, Type, Val}; use crate::{AsContextMut, StoreContextMut, ValRaw}; use anyhow::{anyhow, bail, Context, Result}; use std::any::Any; @@ -53,8 +53,8 @@ impl HostFunc { fn new(func: F, entrypoint: VMLoweringCallee) -> Arc where F: Send + Sync + 'static, - P: ComponentParams + Lift + 'static, - R: Lower + 'static, + P: ComponentNamedList + Lift + 'static, + R: ComponentNamedList + Lower + 'static, { Arc::new(HostFunc { entrypoint, @@ -63,14 +63,14 @@ impl HostFunc { }) } - pub(crate) fn new_dynamic< - T, - F: Fn(StoreContextMut<'_, T>, &[Val]) -> Result + Send + Sync + 'static, - >( + pub(crate) fn new_dynamic( func: F, index: TypeFuncIndex, types: &Arc, - ) -> Arc { + ) -> Arc + where + F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, + { let ty = &types[index]; Arc::new(HostFunc { @@ -94,7 +94,11 @@ impl HostFunc { .iter() .map(|(_, ty)| Type::from(ty, types)) .collect(), - result: Type::from(&ty.result, types), + results: ty + .results + .iter() + .map(|(_, ty)| Type::from(ty, types)) + .collect(), }, }), }) @@ -115,12 +119,12 @@ impl HostFunc { fn typecheck(ty: TypeFuncIndex, types: &Arc) -> Result<()> where - P: ComponentParams + Lift, - R: Lower, + P: ComponentNamedList + Lift, + R: ComponentNamedList + Lower, { let ty = &types[ty]; - P::typecheck_params(&ty.params, types).context("type mismatch with parameters")?; - R::typecheck(&ty.result, types).context("type mismatch with result")?; + P::typecheck_named_list(&ty.params, types).context("type mismatch with parameters")?; + R::typecheck_named_list(&ty.results, types).context("type mismatch with results")?; Ok(()) } @@ -281,8 +285,8 @@ macro_rules! impl_into_component_func { impl IntoComponentFunc for F where F: Fn($($args),*) -> Result + Send + Sync + 'static, - ($($args,)*): ComponentParams + Lift + 'static, - R: Lower + 'static, + ($($args,)*): ComponentNamedList + Lift + 'static, + R: ComponentNamedList + Lower + 'static, { extern "C" fn entrypoint( cx: *mut VMOpaqueContext, @@ -319,8 +323,8 @@ macro_rules! impl_into_component_func { impl IntoComponentFunc, $($args,)*), R> for F where F: Fn(StoreContextMut<'_, T>, $($args),*) -> Result + Send + Sync + 'static, - ($($args,)*): ComponentParams + Lift + 'static, - R: Lower + 'static, + ($($args,)*): ComponentNamedList + Lift + 'static, + R: ComponentNamedList + Lower + 'static, { extern "C" fn entrypoint( cx: *mut VMOpaqueContext, @@ -357,7 +361,7 @@ macro_rules! impl_into_component_func { for_each_function_signature!(impl_into_component_func); unsafe fn call_host_dynamic( - Types { params, result }: &Types, + Types { params, results }: &Types, cx: *mut VMOpaqueContext, mut flags: InstanceFlags, memory: *mut VMMemoryDefinition, @@ -367,7 +371,7 @@ unsafe fn call_host_dynamic( closure: F, ) -> Result<()> where - F: FnOnce(StoreContextMut<'_, T>, &[Val]) -> Result, + F: FnOnce(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()>, { let cx = VMComponentContext::from_opaque(cx); let instance = (*cx).instance(); @@ -391,15 +395,7 @@ where let ret_index; let param_abi = CanonicalAbiInfo::record(params.iter().map(|t| t.canonical_abi())); - let param_count = param_abi.flat_count.and_then(|i| { - let i = usize::from(i); - if i > MAX_FLAT_PARAMS { - None - } else { - Some(i) - } - }); - if let Some(param_count) = param_count { + if let Some(param_count) = param_abi.flat_count(MAX_FLAT_PARAMS) { let iter = &mut storage.iter(); args = params .iter() @@ -424,20 +420,31 @@ where ret_index = 1; }; - let ret = closure(cx.as_context_mut(), &args)?; + let mut result_vals = Vec::with_capacity(results.len()); + for _ in results.iter() { + result_vals.push(Val::Bool(false)); + } + closure(cx.as_context_mut(), &args, &mut result_vals)?; flags.set_may_leave(false); - result.check(&ret)?; + for (val, ty) in result_vals.iter().zip(results.iter()) { + ty.check(val)?; + } - let result_count = result.canonical_abi().flat_count(MAX_FLAT_RESULTS); - if result_count.is_some() { + let result_abi = CanonicalAbiInfo::record(results.iter().map(|t| t.canonical_abi())); + if result_abi.flat_count(MAX_FLAT_RESULTS).is_some() { let dst = mem::transmute::<&mut [ValRaw], &mut [MaybeUninit]>(storage); - ret.lower(&mut cx, &options, &mut dst.iter_mut())?; + let mut dst = dst.iter_mut(); + for val in result_vals.iter() { + val.lower(&mut cx, &options, &mut dst)?; + } } else { let ret_ptr = &storage[ret_index]; let mut memory = MemoryMut::new(cx.as_context_mut(), &options); - let ptr = - validate_inbounds_dynamic(result.canonical_abi(), memory.as_slice_mut(), ret_ptr)?; - ret.store(&mut memory, ptr)?; + let mut ptr = validate_inbounds_dynamic(&result_abi, memory.as_slice_mut(), ret_ptr)?; + for (val, ty) in result_vals.iter().zip(results.iter()) { + let offset = ty.canonical_abi().next_field32_size(&mut ptr); + val.store(&mut memory, offset)?; + } } flags.set_may_leave(true); @@ -463,7 +470,7 @@ fn validate_inbounds_dynamic(abi: &CanonicalAbiInfo, memory: &[u8], ptr: &ValRaw struct Types { params: Box<[Type]>, - result: Type, + results: Box<[Type]>, } struct DynamicContext { @@ -471,10 +478,7 @@ struct DynamicContext { types: Types, } -extern "C" fn dynamic_entrypoint< - T, - F: Fn(StoreContextMut<'_, T>, &[Val]) -> Result + Send + Sync + 'static, ->( +extern "C" fn dynamic_entrypoint( cx: *mut VMOpaqueContext, data: *mut u8, flags: InstanceFlags, @@ -483,7 +487,9 @@ extern "C" fn dynamic_entrypoint< string_encoding: StringEncoding, storage: *mut ValRaw, storage_len: usize, -) { +) where + F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, +{ let data = data as *const DynamicContext; unsafe { handle_result(|| { @@ -495,7 +501,7 @@ extern "C" fn dynamic_entrypoint< realloc, string_encoding, std::slice::from_raw_parts_mut(storage, storage_len), - |store, values| ((*data).func)(store, values), + |store, params, results| ((*data).func)(store, params, results), ) }) } diff --git a/crates/wasmtime/src/component/func/typed.rs b/crates/wasmtime/src/component/func/typed.rs index ae7019e881..bc88fb0ab2 100644 --- a/crates/wasmtime/src/component/func/typed.rs +++ b/crates/wasmtime/src/component/func/typed.rs @@ -60,7 +60,7 @@ impl Clone for TypedFunc { impl TypedFunc where - Params: ComponentParams + Lower, + Params: ComponentNamedList + Lower, Return: Lift, { /// Creates a new [`TypedFunc`] from the provided component [`Func`], @@ -288,8 +288,8 @@ where } } -/// A trait representing a static list of parameters that can be passed to a -/// [`TypedFunc`]. +/// A trait representing a static list of named types that can be passed to or +/// returned from a [`TypedFunc`]. /// /// This trait is implemented for a number of tuple types and is not expected /// to be implemented externally. The contents of this trait are hidden as it's @@ -304,11 +304,11 @@ where // would not be memory safe. The main reason this is `unsafe` is the // `typecheck` function which must operate correctly relative to the `AsTuple` // interpretation of the implementor. -pub unsafe trait ComponentParams: ComponentType { - /// Performs a typecheck to ensure that this `ComponentParams` implementor - /// matches the types of the types in `params`. +pub unsafe trait ComponentNamedList: ComponentType { + /// Performs a typecheck to ensure that this `ComponentNamedList` + /// implementor matches the types of the types in `params`. #[doc(hidden)] - fn typecheck_params( + fn typecheck_named_list( params: &[(Option, InterfaceType)], types: &ComponentTypes, ) -> Result<()>; @@ -374,6 +374,9 @@ pub unsafe trait ComponentType { #[doc(hidden)] const ALIGN32: u32 = Self::ABI.align32; + #[doc(hidden)] + const IS_RUST_UNIT_TYPE: bool = false; + /// Returns the number of core wasm abi values will be used to represent /// this type in its lowered form. /// @@ -393,7 +396,7 @@ pub unsafe trait ComponentType { #[doc(hidden)] pub unsafe trait ComponentVariant: ComponentType { - const CASES: &'static [CanonicalAbiInfo]; + const CASES: &'static [Option]; const INFO: VariantInfo = VariantInfo::new_static(Self::CASES); const PAYLOAD_OFFSET32: usize = Self::INFO.payload_offset32 as usize; } @@ -1352,16 +1355,9 @@ fn typecheck_tuple( expected: &[fn(&InterfaceType, &ComponentTypes) -> Result<()>], ) -> Result<()> { match ty { - InterfaceType::Unit if expected.len() == 0 => Ok(()), InterfaceType::Tuple(t) => { let tuple = &types[*t]; if tuple.types.len() != expected.len() { - if expected.len() == 0 { - bail!( - "expected unit or 0-tuple, found {}-tuple", - tuple.types.len(), - ); - } bail!( "expected {}-tuple, found {}-tuple", expected.len(), @@ -1373,9 +1369,6 @@ fn typecheck_tuple( } Ok(()) } - other if expected.len() == 0 => { - bail!("expected `unit` or 0-tuple found `{}`", desc(other)) - } other => bail!("expected `tuple` found `{}`", desc(other)), } } @@ -1419,7 +1412,10 @@ pub fn typecheck_record( pub fn typecheck_variant( ty: &InterfaceType, types: &ComponentTypes, - expected: &[(&str, fn(&InterfaceType, &ComponentTypes) -> Result<()>)], + expected: &[( + &str, + Option Result<()>>, + )], ) -> Result<()> { match ty { InterfaceType::Variant(index) => { @@ -1434,11 +1430,20 @@ pub fn typecheck_variant( } for (case, &(name, check)) in cases.iter().zip(expected) { - check(&case.ty, types) - .with_context(|| format!("type mismatch for case {}", name))?; - if case.name != name { - bail!("expected variant case named {}, found {}", name, case.name); + bail!("expected variant case named {name}, found {}", case.name); + } + + match (check, &case.ty) { + (Some(check), Some(ty)) => check(ty, types) + .with_context(|| format!("type mismatch for case {name}"))?, + (None, None) => {} + (Some(_), None) => { + bail!("case `{name}` has no type but one was expected") + } + (None, Some(_)) => { + bail!("case `{name}` has a type but none was expected") + } } } @@ -1558,8 +1563,7 @@ where { type Lower = TupleLower2<::Lower, T::Lower>; - const ABI: CanonicalAbiInfo = - CanonicalAbiInfo::variant_static(&[<() as ComponentType>::ABI, T::ABI]); + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[None, Some(T::ABI)]); fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> { match ty { @@ -1573,7 +1577,7 @@ unsafe impl ComponentVariant for Option where T: ComponentType, { - const CASES: &'static [CanonicalAbiInfo] = &[<() as ComponentType>::ABI, T::ABI]; + const CASES: &'static [Option] = &[None, Some(T::ABI)]; } unsafe impl Lower for Option @@ -1667,17 +1671,25 @@ where { type Lower = ResultLower; - const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[T::ABI, E::ABI]); + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[Some(T::ABI), Some(E::ABI)]); fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> { match ty { - InterfaceType::Expected(r) => { - let expected = &types[*r]; - T::typecheck(&expected.ok, types)?; - E::typecheck(&expected.err, types)?; + InterfaceType::Result(r) => { + let result = &types[*r]; + match &result.ok { + Some(ty) => T::typecheck(ty, types)?, + None if T::IS_RUST_UNIT_TYPE => {} + None => bail!("expected no `ok` type"), + } + match &result.err { + Some(ty) => E::typecheck(ty, types)?, + None if E::IS_RUST_UNIT_TYPE => {} + None => bail!("expected no `err` type"), + } Ok(()) } - other => bail!("expected `expected` found `{}`", desc(other)), + other => bail!("expected `result` found `{}`", desc(other)), } } } @@ -1715,7 +1727,7 @@ where T: ComponentType, E: ComponentType, { - const CASES: &'static [CanonicalAbiInfo] = &[T::ABI, E::ABI]; + const CASES: &'static [Option] = &[Some(T::ABI), Some(E::ABI)]; } unsafe impl Lower for Result @@ -1896,6 +1908,15 @@ macro_rules! impl_component_ty_for_tuples { $($t::ABI),* ]); + const IS_RUST_UNIT_TYPE: bool = { + let mut _is_unit = true; + $( + let _anything_to_bind_the_macro_variable = $t::IS_RUST_UNIT_TYPE; + _is_unit = false; + )* + _is_unit + }; + fn typecheck( ty: &InterfaceType, types: &ComponentTypes, @@ -1944,19 +1965,19 @@ macro_rules! impl_component_ty_for_tuples { } #[allow(non_snake_case)] - unsafe impl<$($t,)*> ComponentParams for ($($t,)*) + unsafe impl<$($t,)*> ComponentNamedList for ($($t,)*) where $($t: ComponentType),* { - fn typecheck_params( - params: &[(Option, InterfaceType)], + fn typecheck_named_list( + names: &[(Option, InterfaceType)], _types: &ComponentTypes, ) -> Result<()> { - if params.len() != $n { - bail!("expected {} types, found {}", $n, params.len()); + if names.len() != $n { + bail!("expected {} types, found {}", $n, names.len()); } - let mut params = params.iter().map(|i| &i.1); - $($t::typecheck(params.next().unwrap(), _types)?;)* - debug_assert!(params.next().is_none()); + let mut names = names.iter().map(|i| &i.1); + $($t::typecheck(names.next().unwrap(), _types)?;)* + debug_assert!(names.next().is_none()); Ok(()) } } @@ -1978,14 +1999,13 @@ fn desc(ty: &InterfaceType) -> &'static str { InterfaceType::S64 => "s64", InterfaceType::Float32 => "f32", InterfaceType::Float64 => "f64", - InterfaceType::Unit => "unit", InterfaceType::Bool => "bool", InterfaceType::Char => "char", InterfaceType::String => "string", InterfaceType::List(_) => "list", InterfaceType::Tuple(_) => "tuple", InterfaceType::Option(_) => "option", - InterfaceType::Expected(_) => "expected", + InterfaceType::Result(_) => "result", InterfaceType::Record(_) => "record", InterfaceType::Variant(_) => "variant", diff --git a/crates/wasmtime/src/component/instance.rs b/crates/wasmtime/src/component/instance.rs index 7abc81bfea..0fcb62fbe6 100644 --- a/crates/wasmtime/src/component/instance.rs +++ b/crates/wasmtime/src/component/instance.rs @@ -1,5 +1,5 @@ use crate::component::func::HostFunc; -use crate::component::{Component, ComponentParams, Func, Lift, Lower, TypedFunc}; +use crate::component::{Component, ComponentNamedList, Func, Lift, Lower, TypedFunc}; use crate::instance::OwnedImports; use crate::store::{StoreOpaque, Stored}; use crate::{AsContextMut, Module, StoreContextMut}; @@ -90,8 +90,8 @@ impl Instance { name: &str, ) -> Result> where - Params: ComponentParams + Lower, - Results: Lift, + Params: ComponentNamedList + Lower, + Results: ComponentNamedList + Lift, S: AsContextMut, { let f = self @@ -585,8 +585,8 @@ impl<'a, 'store> ExportInstance<'a, 'store> { /// Same as [`Instance::get_typed_func`] pub fn typed_func(&mut self, name: &str) -> Result> where - Params: ComponentParams + Lower, - Results: Lift, + Params: ComponentNamedList + Lower, + Results: ComponentNamedList + Lift, { let func = self .func(name) diff --git a/crates/wasmtime/src/component/linker.rs b/crates/wasmtime/src/component/linker.rs index 1289deb632..8978abb0d4 100644 --- a/crates/wasmtime/src/component/linker.rs +++ b/crates/wasmtime/src/component/linker.rs @@ -238,7 +238,7 @@ impl LinkerInstance<'_, T> { /// called, which must return a `Val` which is an instance of the result /// type of the import. pub fn func_new< - F: Fn(StoreContextMut<'_, T>, &[Val]) -> Result + Send + Sync + 'static, + F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, >( &mut self, component: &Component, diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index bb9f3923b6..1b49ca0e3e 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -14,7 +14,7 @@ pub mod types; mod values; pub use self::component::Component; pub use self::func::{ - ComponentParams, ComponentType, Func, IntoComponentFunc, Lift, Lower, TypedFunc, WasmList, + ComponentNamedList, ComponentType, Func, IntoComponentFunc, Lift, Lower, TypedFunc, WasmList, WasmStr, }; pub use self::instance::{ExportInstance, Exports, Instance, InstancePre}; diff --git a/crates/wasmtime/src/component/types.rs b/crates/wasmtime/src/component/types.rs index 1b30135ae5..a1dacacbc8 100644 --- a/crates/wasmtime/src/component/types.rs +++ b/crates/wasmtime/src/component/types.rs @@ -7,8 +7,8 @@ use std::mem; use std::ops::Deref; use std::sync::Arc; use wasmtime_environ::component::{ - CanonicalAbiInfo, ComponentTypes, InterfaceType, TypeEnumIndex, TypeExpectedIndex, - TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, TypeTupleIndex, + CanonicalAbiInfo, ComponentTypes, InterfaceType, TypeEnumIndex, TypeFlagsIndex, + TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, TypeResultIndex, TypeTupleIndex, TypeUnionIndex, TypeVariantIndex, VariantInfo, }; @@ -111,8 +111,8 @@ impl Tuple { pub struct Case<'a> { /// The name of the case pub name: &'a str, - /// The type of the case - pub ty: Type, + /// The optional payload type of the case + pub ty: Option, } /// A `variant` interface type @@ -121,7 +121,7 @@ pub struct Variant(Handle); impl Variant { /// Instantiate this type with the specified case `name` and `value`. - pub fn new_val(&self, name: &str, value: Val) -> Result { + pub fn new_val(&self, name: &str, value: Option) -> Result { Ok(Val::Variant(values::Variant::new(self, name, value)?)) } @@ -129,7 +129,7 @@ impl Variant { pub fn cases(&self) -> impl ExactSizeIterator { self.0.types[self.0.index].cases.iter().map(|case| Case { name: &case.name, - ty: Type::from(&case.ty, &self.0.types), + ty: case.ty.as_ref().map(|ty| Type::from(ty, &self.0.types)), }) } @@ -198,12 +198,12 @@ impl Union { /// An `option` interface type #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Option(Handle); +pub struct OptionType(Handle); -impl Option { +impl OptionType { /// Instantiate this type with the specified `value`. - pub fn new_val(&self, value: std::option::Option) -> Result { - Ok(Val::Option(values::Option::new(self, value)?)) + pub fn new_val(&self, value: Option) -> Result { + Ok(Val::Option(values::OptionVal::new(self, value)?)) } /// Retrieve the type parameter for this `option`. @@ -222,22 +222,28 @@ impl Option { /// An `expected` interface type #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Expected(Handle); +pub struct ResultType(Handle); -impl Expected { +impl ResultType { /// Instantiate this type with the specified `value`. - pub fn new_val(&self, value: Result) -> Result { - Ok(Val::Expected(values::Expected::new(self, value)?)) + pub fn new_val(&self, value: Result, Option>) -> Result { + Ok(Val::Result(values::ResultVal::new(self, value)?)) } /// Retrieve the `ok` type parameter for this `option`. - pub fn ok(&self) -> Type { - Type::from(&self.0.types[self.0.index].ok, &self.0.types) + pub fn ok(&self) -> Option { + Some(Type::from( + self.0.types[self.0.index].ok.as_ref()?, + &self.0.types, + )) } /// Retrieve the `err` type parameter for this `option`. - pub fn err(&self) -> Type { - Type::from(&self.0.types[self.0.index].err, &self.0.types) + pub fn err(&self) -> Option { + Some(Type::from( + self.0.types[self.0.index].err.as_ref()?, + &self.0.types, + )) } pub(crate) fn variant_info(&self) -> &VariantInfo { @@ -274,52 +280,29 @@ impl Flags { /// Represents a component model interface type #[derive(Clone, PartialEq, Eq, Debug)] +#[allow(missing_docs)] pub enum Type { - /// Unit - Unit, - /// Boolean Bool, - /// Signed 8-bit integer S8, - /// Unsigned 8-bit integer U8, - /// Signed 16-bit integer S16, - /// Unsigned 16-bit integer U16, - /// Signed 32-bit integer S32, - /// Unsigned 32-bit integer U32, - /// Signed 64-bit integer S64, - /// Unsigned 64-bit integer U64, - /// 64-bit floating point value Float32, - /// 64-bit floating point value Float64, - /// 32-bit character Char, - /// Character string String, - /// List of values List(List), - /// Record Record(Record), - /// Tuple Tuple(Tuple), - /// Variant Variant(Variant), - /// Enum Enum(Enum), - /// Union Union(Union), - /// Option - Option(Option), - /// Expected - Expected(Expected), - /// Bit flags + Option(OptionType), + Result(ResultType), Flags(Flags), } @@ -402,12 +385,12 @@ impl Type { } } - /// Retrieve the inner [`Option`] of a [`Type::Option`]. + /// Retrieve the inner [`OptionType`] of a [`Type::Option`]. /// /// # Panics /// /// This will panic if `self` is not a [`Type::Option`]. - pub fn unwrap_option(&self) -> &Option { + pub fn unwrap_option(&self) -> &OptionType { if let Type::Option(handle) = self { &handle } else { @@ -415,16 +398,16 @@ impl Type { } } - /// Retrieve the inner [`Expected`] of a [`Type::Expected`]. + /// Retrieve the inner [`ResultType`] of a [`Type::Result`]. /// /// # Panics /// - /// This will panic if `self` is not a [`Type::Expected`]. - pub fn unwrap_expected(&self) -> &Expected { - if let Type::Expected(handle) = self { + /// This will panic if `self` is not a [`Type::Result`]. + pub fn unwrap_result(&self) -> &ResultType { + if let Type::Result(handle) = self { &handle } else { - panic!("attempted to unwrap a {} as a expected", self.desc()) + panic!("attempted to unwrap a {} as a result", self.desc()) } } @@ -462,7 +445,6 @@ impl Type { /// Convert the specified `InterfaceType` to a `Type`. pub(crate) fn from(ty: &InterfaceType, types: &Arc) -> Self { match ty { - InterfaceType::Unit => Type::Unit, InterfaceType::Bool => Type::Bool, InterfaceType::S8 => Type::S8, InterfaceType::U8 => Type::U8, @@ -500,11 +482,11 @@ impl Type { index: *index, types: types.clone(), })), - InterfaceType::Option(index) => Type::Option(Option(Handle { + InterfaceType::Option(index) => Type::Option(OptionType(Handle { index: *index, types: types.clone(), })), - InterfaceType::Expected(index) => Type::Expected(Expected(Handle { + InterfaceType::Result(index) => Type::Result(ResultType(Handle { index: *index, types: types.clone(), })), @@ -517,7 +499,6 @@ impl Type { fn desc(&self) -> &'static str { match self { - Type::Unit => "unit", Type::Bool => "bool", Type::S8 => "s8", Type::U8 => "u8", @@ -538,7 +519,7 @@ impl Type { Type::Enum(_) => "enum", Type::Union(_) => "union", Type::Option(_) => "option", - Type::Expected(_) => "expected", + Type::Result(_) => "result", Type::Flags(_) => "flags", } } @@ -546,7 +527,6 @@ impl Type { /// Calculate the size and alignment requirements for the specified type. pub(crate) fn canonical_abi(&self) -> &CanonicalAbiInfo { match self { - Type::Unit => &CanonicalAbiInfo::ZERO, Type::Bool | Type::S8 | Type::U8 => &CanonicalAbiInfo::SCALAR1, Type::S16 | Type::U16 => &CanonicalAbiInfo::SCALAR2, Type::S32 | Type::U32 | Type::Char | Type::Float32 => &CanonicalAbiInfo::SCALAR4, @@ -558,7 +538,7 @@ impl Type { Type::Enum(handle) => handle.canonical_abi(), Type::Union(handle) => handle.canonical_abi(), Type::Option(handle) => handle.canonical_abi(), - Type::Expected(handle) => handle.canonical_abi(), + Type::Result(handle) => handle.canonical_abi(), Type::Flags(handle) => handle.canonical_abi(), } } diff --git a/crates/wasmtime/src/component/values.rs b/crates/wasmtime/src/component/values.rs index adfe6bc53d..9f87ba1a5c 100644 --- a/crates/wasmtime/src/component/values.rs +++ b/crates/wasmtime/src/component/values.rs @@ -180,12 +180,12 @@ impl fmt::Debug for Tuple { pub struct Variant { ty: types::Variant, discriminant: u32, - value: Box, + value: Option>, } impl Variant { /// Instantiate the specified type with the specified case `name` and `value`. - pub fn new(ty: &types::Variant, name: &str, value: Val) -> Result { + pub fn new(ty: &types::Variant, name: &str, value: Option) -> Result { let (discriminant, case_type) = ty .cases() .enumerate() @@ -198,14 +198,12 @@ impl Variant { }) .ok_or_else(|| anyhow!("unknown variant case: {name}"))?; - case_type - .check(&value) - .with_context(|| format!("type mismatch for case {name} of variant"))?; + typecheck_payload(name, case_type.as_ref(), value.as_ref())?; Ok(Self { ty: ty.clone(), discriminant: u32::try_from(discriminant)?, - value: Box::new(value), + value: value.map(Box::new), }) } @@ -224,15 +222,26 @@ impl Variant { } /// Returns the payload value for this variant. - pub fn payload(&self) -> &Val { - &self.value + pub fn payload(&self) -> Option<&Val> { + self.value.as_deref() + } +} + +fn typecheck_payload(name: &str, case_type: Option<&Type>, value: Option<&Val>) -> Result<()> { + match (case_type, value) { + (Some(expected), Some(actual)) => expected + .check(&actual) + .with_context(|| format!("type mismatch for case {name} of variant")), + (None, None) => Ok(()), + (Some(_), None) => bail!("expected a payload for case `{name}`"), + (None, Some(_)) => bail!("did not expect payload for case `{name}`"), } } impl fmt::Debug for Variant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(self.discriminant()) - .field(self.payload()) + .field(&self.payload()) .finish() } } @@ -279,7 +288,7 @@ impl fmt::Debug for Enum { pub struct Union { ty: types::Union, discriminant: u32, - value: Box, + value: Option>, } impl Union { @@ -293,7 +302,7 @@ impl Union { Ok(Self { ty: ty.clone(), discriminant, - value: Box::new(value), + value: Some(Box::new(value)), }) } else { Err(anyhow!( @@ -315,7 +324,7 @@ impl Union { /// Returns the payload value for this union. pub fn payload(&self) -> &Val { - &self.value + self.value.as_ref().unwrap() } } @@ -328,15 +337,15 @@ impl fmt::Debug for Union { } #[derive(PartialEq, Eq, Clone)] -pub struct Option { - ty: types::Option, +pub struct OptionVal { + ty: types::OptionType, discriminant: u32, - value: Box, + value: Option>, } -impl Option { +impl OptionVal { /// Instantiate the specified type with the specified `value`. - pub fn new(ty: &types::Option, value: std::option::Option) -> Result { + pub fn new(ty: &types::OptionType, value: Option) -> Result { let value = value .map(|value| { ty.ty().check(&value).context("type mismatch for option")?; @@ -348,77 +357,69 @@ impl Option { Ok(Self { ty: ty.clone(), discriminant: if value.is_none() { 0 } else { 1 }, - value: Box::new(value.unwrap_or(Val::Unit)), + value: value.map(Box::new), }) } /// Returns the type of this value. - pub fn ty(&self) -> &types::Option { + pub fn ty(&self) -> &types::OptionType { &self.ty } /// Returns the optional value contained within. - pub fn value(&self) -> std::option::Option<&Val> { - if self.discriminant == 0 { - None - } else { - Some(&self.value) - } + pub fn value(&self) -> Option<&Val> { + self.value.as_deref() } } -impl fmt::Debug for Option { +impl fmt::Debug for OptionVal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.value().fmt(f) } } #[derive(PartialEq, Eq, Clone)] -pub struct Expected { - ty: types::Expected, +pub struct ResultVal { + ty: types::ResultType, discriminant: u32, - value: Box, + value: Option>, } -impl Expected { +impl ResultVal { /// Instantiate the specified type with the specified `value`. - pub fn new(ty: &types::Expected, value: Result) -> Result { + pub fn new(ty: &types::ResultType, value: Result, Option>) -> Result { Ok(Self { ty: ty.clone(), discriminant: if value.is_ok() { 0 } else { 1 }, - value: Box::new(match value { + value: match value { Ok(value) => { - ty.ok() - .check(&value) - .context("type mismatch for ok case of expected")?; - value + typecheck_payload("ok", ty.ok().as_ref(), value.as_ref())?; + value.map(Box::new) } Err(value) => { - ty.err() - .check(&value) - .context("type mismatch for err case of expected")?; - value + typecheck_payload("err", ty.err().as_ref(), value.as_ref())?; + value.map(Box::new) } - }), + }, }) } /// Returns the type of this value. - pub fn ty(&self) -> &types::Expected { + pub fn ty(&self) -> &types::ResultType { &self.ty } /// Returns the result value contained within. - pub fn value(&self) -> Result<&Val, &Val> { + pub fn value(&self) -> Result, Option<&Val>> { if self.discriminant == 0 { - Ok(&self.value) + Ok(self.value.as_deref()) } else { - Err(&self.value) + Err(self.value.as_deref()) } } } -impl fmt::Debug for Expected { +impl fmt::Debug for ResultVal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.value().fmt(f) } @@ -487,52 +488,29 @@ impl fmt::Debug for Flags { /// Represents possible runtime values which a component function can either consume or produce #[derive(Debug, PartialEq, Eq, Clone)] +#[allow(missing_docs)] pub enum Val { - /// Unit - Unit, - /// Boolean Bool(bool), - /// Signed 8-bit integer S8(i8), - /// Unsigned 8-bit integer U8(u8), - /// Signed 16-bit integer S16(i16), - /// Unsigned 16-bit integer U16(u16), - /// Signed 32-bit integer S32(i32), - /// Unsigned 32-bit integer U32(u32), - /// Signed 64-bit integer S64(i64), - /// Unsigned 64-bit integer U64(u64), - /// 32-bit floating point value Float32(u32), - /// 64-bit floating point value Float64(u64), - /// 32-bit character Char(char), - /// Character string String(Box), - /// List of values List(List), - /// Record Record(Record), - /// Tuple Tuple(Tuple), - /// Variant Variant(Variant), - /// Enum Enum(Enum), - /// Union Union(Union), - /// Option - Option(Option), - /// Expected - Expected(Expected), - /// Bit flags + Option(OptionVal), + Result(ResultVal), Flags(Flags), } @@ -540,7 +518,6 @@ impl Val { /// Retrieve the [`Type`] of this value. pub fn ty(&self) -> Type { match self { - Val::Unit => Type::Unit, Val::Bool(_) => Type::Bool, Val::S8(_) => Type::S8, Val::U8(_) => Type::U8, @@ -560,8 +537,8 @@ impl Val { Val::Variant(Variant { ty, .. }) => Type::Variant(ty.clone()), Val::Enum(Enum { ty, .. }) => Type::Enum(ty.clone()), Val::Union(Union { ty, .. }) => Type::Union(ty.clone()), - Val::Option(Option { ty, .. }) => Type::Option(ty.clone()), - Val::Expected(Expected { ty, .. }) => Type::Expected(ty.clone()), + Val::Option(OptionVal { ty, .. }) => Type::Option(ty.clone()), + Val::Result(ResultVal { ty, .. }) => Type::Result(ty.clone()), Val::Flags(Flags { ty, .. }) => Type::Flags(ty.clone()), } } @@ -574,7 +551,6 @@ impl Val { src: &mut std::slice::Iter<'_, ValRaw>, ) -> Result { Ok(match ty { - Type::Unit => Val::Unit, Type::Bool => Val::Bool(bool::lift(store, options, next(src))?), Type::S8 => Val::S8(i8::lift(store, options, next(src))?), Type::U8 => Val::U8(u8::lift(store, options, next(src))?), @@ -622,13 +598,13 @@ impl Val { Val::Variant(Variant { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Enum(handle) => { let (discriminant, _) = lift_variant( handle.canonical_abi().flat_count(usize::MAX).unwrap(), - handle.names().map(|_| Type::Unit), + handle.names().map(|_| None), store, options, src, @@ -642,7 +618,7 @@ impl Val { Type::Union(handle) => { let (discriminant, value) = lift_variant( handle.canonical_abi().flat_count(usize::MAX).unwrap(), - handle.types(), + handle.types().map(Some), store, options, src, @@ -651,25 +627,25 @@ impl Val { Val::Union(Union { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Option(handle) => { let (discriminant, value) = lift_variant( handle.canonical_abi().flat_count(usize::MAX).unwrap(), - [Type::Unit, handle.ty()].into_iter(), + [None, Some(handle.ty())].into_iter(), store, options, src, )?; - Val::Option(Option { + Val::Option(OptionVal { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } - Type::Expected(handle) => { + Type::Result(handle) => { let (discriminant, value) = lift_variant( handle.canonical_abi().flat_count(usize::MAX).unwrap(), [handle.ok(), handle.err()].into_iter(), @@ -678,10 +654,10 @@ impl Val { src, )?; - Val::Expected(Expected { + Val::Result(ResultVal { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Flags(handle) => { @@ -703,7 +679,6 @@ impl Val { /// Deserialize a value of this type from the heap. pub(crate) fn load(ty: &Type, mem: &Memory, bytes: &[u8]) -> Result { Ok(match ty { - Type::Unit => Val::Unit, Type::Bool => Val::Bool(bool::load(mem, bytes)?), Type::S8 => Val::S8(i8::load(mem, bytes)?), Type::U8 => Val::U8(u8::load(mem, bytes)?), @@ -742,13 +717,13 @@ impl Val { Val::Variant(Variant { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Enum(handle) => { let (discriminant, _) = load_variant( handle.variant_info(), - handle.names().map(|_| Type::Unit), + handle.names().map(|_| None), mem, bytes, )?; @@ -760,29 +735,29 @@ impl Val { } Type::Union(handle) => { let (discriminant, value) = - load_variant(handle.variant_info(), handle.types(), mem, bytes)?; + load_variant(handle.variant_info(), handle.types().map(Some), mem, bytes)?; Val::Union(Union { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Option(handle) => { let (discriminant, value) = load_variant( handle.variant_info(), - [Type::Unit, handle.ty()].into_iter(), + [None, Some(handle.ty())].into_iter(), mem, bytes, )?; - Val::Option(Option { + Val::Option(OptionVal { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } - Type::Expected(handle) => { + Type::Result(handle) => { let (discriminant, value) = load_variant( handle.variant_info(), [handle.ok(), handle.err()].into_iter(), @@ -790,10 +765,10 @@ impl Val { bytes, )?; - Val::Expected(Expected { + Val::Result(ResultVal { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Flags(handle) => Val::Flags(Flags { @@ -819,7 +794,6 @@ impl Val { dst: &mut std::slice::IterMut<'_, MaybeUninit>, ) -> Result<()> { match self { - Val::Unit => (), Val::Bool(value) => value.lower(store, options, next_mut(dst))?, Val::S8(value) => value.lower(store, options, next_mut(dst))?, Val::U8(value) => value.lower(store, options, next_mut(dst))?, @@ -863,23 +837,28 @@ impl Val { value, .. }) - | Val::Option(Option { + | Val::Option(OptionVal { discriminant, value, .. }) - | Val::Expected(Expected { + | Val::Result(ResultVal { discriminant, value, .. }) => { next_mut(dst).write(ValRaw::u32(*discriminant)); - value.lower(store, options, dst)?; // For the remaining lowered representation of this variant that // the payload didn't write we write out zeros here to ensure // the entire variant is written. - let value_flat = value.ty().canonical_abi().flat_count(usize::MAX).unwrap(); + let value_flat = match value { + Some(value) => { + value.lower(store, options, dst)?; + value.ty().canonical_abi().flat_count(usize::MAX).unwrap() + } + None => 0, + }; let variant_flat = self.ty().canonical_abi().flat_count(usize::MAX).unwrap(); for _ in (1 + value_flat)..variant_flat { next_mut(dst).write(ValRaw::u64(0)); @@ -903,7 +882,6 @@ impl Val { debug_assert!(offset % usize::try_from(self.ty().canonical_abi().align32)? == 0); match self { - Val::Unit => (), Val::Bool(value) => value.store(mem, offset)?, Val::S8(value) => value.store(mem, offset)?, Val::U8(value) => value.store(mem, offset)?, @@ -936,29 +914,53 @@ impl Val { discriminant, value, ty, - }) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?, + }) => self.store_variant( + *discriminant, + value.as_deref(), + ty.variant_info(), + mem, + offset, + )?, Val::Enum(Enum { discriminant, ty }) => { - self.store_variant(*discriminant, &Val::Unit, ty.variant_info(), mem, offset)? + self.store_variant(*discriminant, None, ty.variant_info(), mem, offset)? } Val::Union(Union { discriminant, value, ty, - }) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?, + }) => self.store_variant( + *discriminant, + value.as_deref(), + ty.variant_info(), + mem, + offset, + )?, - Val::Option(Option { + Val::Option(OptionVal { discriminant, value, ty, - }) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?, + }) => self.store_variant( + *discriminant, + value.as_deref(), + ty.variant_info(), + mem, + offset, + )?, - Val::Expected(Expected { + Val::Result(ResultVal { discriminant, value, ty, - }) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?, + }) => self.store_variant( + *discriminant, + value.as_deref(), + ty.variant_info(), + mem, + offset, + )?, Val::Flags(Flags { count, value, .. }) => { match FlagsSize::from_count(*count as usize) { @@ -982,7 +984,7 @@ impl Val { fn store_variant( &self, discriminant: u32, - value: &Val, + value: Option<&Val>, info: &VariantInfo, mem: &mut MemoryMut<'_, T>, offset: usize, @@ -993,8 +995,12 @@ impl Val { DiscriminantSize::Size4 => discriminant.store(mem, offset)?, } - let offset = offset + usize::try_from(info.payload_offset32).unwrap(); - value.store(mem, offset) + if let Some(value) = value { + let offset = offset + usize::try_from(info.payload_offset32).unwrap(); + value.store(mem, offset)?; + } + + Ok(()) } } @@ -1048,10 +1054,10 @@ fn load_record( fn load_variant( info: &VariantInfo, - mut types: impl ExactSizeIterator, + mut types: impl ExactSizeIterator>, mem: &Memory, bytes: &[u8], -) -> Result<(u32, Val)> { +) -> Result<(u32, Option>)> { let discriminant = match info.size { DiscriminantSize::Size1 => u32::from(u8::load(mem, &bytes[..1])?), DiscriminantSize::Size2 => u32::from(u16::load(mem, &bytes[..2])?), @@ -1064,26 +1070,40 @@ fn load_variant( types.len() ) })?; - let payload_offset = usize::try_from(info.payload_offset32).unwrap(); - let case_size = usize::try_from(case_ty.canonical_abi().size32).unwrap(); - let value = Val::load(&case_ty, mem, &bytes[payload_offset..][..case_size])?; + let value = match case_ty { + Some(case_ty) => { + let payload_offset = usize::try_from(info.payload_offset32).unwrap(); + let case_size = usize::try_from(case_ty.canonical_abi().size32).unwrap(); + Some(Box::new(Val::load( + &case_ty, + mem, + &bytes[payload_offset..][..case_size], + )?)) + } + None => None, + }; Ok((discriminant, value)) } fn lift_variant<'a>( flatten_count: usize, - mut types: impl ExactSizeIterator, + mut types: impl ExactSizeIterator>, store: &StoreOpaque, options: &Options, src: &mut std::slice::Iter<'_, ValRaw>, -) -> Result<(u32, Val)> { +) -> Result<(u32, Option>)> { let len = types.len(); let discriminant = next(src).get_u32(); let ty = types .nth(discriminant as usize) .ok_or_else(|| anyhow!("discriminant {} out of range [0..{})", discriminant, len))?; - let value = Val::lift(&ty, store, options, src)?; - let value_flat = ty.canonical_abi().flat_count(usize::MAX).unwrap(); + let (value, value_flat) = match ty { + Some(ty) => ( + Some(Box::new(Val::lift(&ty, store, options, src)?)), + ty.canonical_abi().flat_count(usize::MAX).unwrap(), + ), + None => (None, 0), + }; for _ in (1 + value_flat)..flatten_count { next(src); } diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index ac71e4ccc6..ecf9b39086 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" [dependencies] anyhow = "1.0.19" wasmtime = { path = "../wasmtime", version = "0.41.0", default-features = false, features = ['cranelift'] } -wast = "45.0.0" +wast = "46.0.0" log = "0.4" [badges] diff --git a/crates/wast/src/component.rs b/crates/wast/src/component.rs index d2ee07c705..8a88538bb0 100644 --- a/crates/wast/src/component.rs +++ b/crates/wast/src/component.rs @@ -9,7 +9,6 @@ pub use wasmtime::component::*; pub fn val(v: &WastVal<'_>, ty: &Type) -> Result { Ok(match v { - WastVal::Unit => Val::Unit, WastVal::Bool(b) => Val::Bool(*b), WastVal::U8(b) => Val::U8(*b), WastVal::S8(b) => Val::S8(*b), @@ -80,10 +79,10 @@ pub fn val(v: &WastVal<'_>, ty: &Type) -> Result { WastVal::Variant(name, payload) => match ty { Type::Variant(t) => { let case = match t.cases().find(|c| c.name == *name) { - Some(case) => case.ty, + Some(case) => case, None => bail!("no case named `{}", name), }; - let payload = val(payload, &case)?; + let payload = payload_val(case.name, payload.as_deref(), case.ty.as_ref())?; t.new_val(name, payload)? } _ => bail!("expected a variant value"), @@ -109,11 +108,11 @@ pub fn val(v: &WastVal<'_>, ty: &Type) -> Result { } _ => bail!("expected an option value"), }, - WastVal::Expected(v) => match ty { - Type::Expected(t) => { + WastVal::Result(v) => match ty { + Type::Result(t) => { let v = match v { - Ok(v) => Ok(val(v, &t.ok())?), - Err(v) => Err(val(v, &t.err())?), + Ok(v) => Ok(payload_val("ok", v.as_deref(), t.ok().as_ref())?), + Err(v) => Err(payload_val("err", v.as_deref(), t.err().as_ref())?), }; t.new_val(v)? } @@ -126,12 +125,17 @@ pub fn val(v: &WastVal<'_>, ty: &Type) -> Result { }) } +fn payload_val(name: &str, v: Option<&WastVal<'_>>, ty: Option<&Type>) -> Result> { + match (v, ty) { + (Some(v), Some(ty)) => Ok(Some(val(v, ty)?)), + (None, None) => Ok(None), + (Some(_), None) => bail!("expected payload for case `{name}`"), + (None, Some(_)) => bail!("unexpected payload for case `{name}`"), + } +} + pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { match expected { - WastVal::Unit => match actual { - Val::Unit => Ok(()), - _ => mismatch(expected, actual), - }, WastVal::Bool(e) => match actual { Val::Bool(a) => match_debug(a, e), _ => mismatch(expected, actual), @@ -242,7 +246,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { if a.discriminant() != *name { bail!("expected discriminant `{name}` got `{}`", a.discriminant()); } - match_val(e, a.payload()) + match_payload_val(name, e.as_deref(), a.payload()) } _ => mismatch(expected, actual), }, @@ -274,12 +278,12 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { }, _ => mismatch(expected, actual), }, - WastVal::Expected(e) => match actual { - Val::Expected(a) => match (e, a.value()) { + WastVal::Result(e) => match actual { + Val::Result(a) => match (e, a.value()) { (Ok(_), Err(_)) => bail!("expected `ok`, found `err`"), (Err(_), Ok(_)) => bail!("expected `err`, found `ok`"), - (Err(e), Err(a)) => match_val(e, a), - (Ok(e), Ok(a)) => match_val(e, a), + (Err(e), Err(a)) => match_payload_val("err", e.as_deref(), a), + (Ok(e), Ok(a)) => match_payload_val("ok", e.as_deref(), a), }, _ => mismatch(expected, actual), }, @@ -294,6 +298,21 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { } } +fn match_payload_val( + name: &str, + expected: Option<&WastVal<'_>>, + actual: Option<&Val>, +) -> Result<()> { + match (expected, actual) { + (Some(e), Some(a)) => { + match_val(e, a).with_context(|| format!("failed to match case `{name}`")) + } + (None, None) => Ok(()), + (Some(_), None) => bail!("expected payload for case `{name}`"), + (None, Some(_)) => bail!("unexpected payload for case `{name}`"), + } +} + fn match_debug(actual: &T, expected: &T) -> Result<()> where T: Eq + Debug + ?Sized, @@ -311,7 +330,6 @@ where fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> { let expected = match expected { - WastVal::Unit => "unit", WastVal::Bool(..) => "bool", WastVal::U8(..) => "u8", WastVal::S8(..) => "s8", @@ -332,11 +350,10 @@ fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> { WastVal::Variant(..) => "variant", WastVal::Union(..) => "union", WastVal::Option(..) => "option", - WastVal::Expected(..) => "expected", + WastVal::Result(..) => "result", WastVal::Flags(..) => "flags", }; let actual = match actual { - Val::Unit => "unit", Val::Bool(..) => "bool", Val::U8(..) => "u8", Val::S8(..) => "s8", @@ -357,7 +374,7 @@ fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> { Val::Variant(..) => "variant", Val::Union(..) => "union", Val::Option(..) => "option", - Val::Expected(..) => "expected", + Val::Result(..) => "result", Val::Flags(..) => "flags", }; bail!("expected `{expected}` got `{actual}`") diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index 74905d02dc..a02d86970f 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -48,11 +48,11 @@ pub fn link_spectest(linker: &mut Linker, store: &mut Store) -> Result< #[cfg(feature = "component-model")] pub fn link_component_spectest(linker: &mut component::Linker) -> Result<()> { let engine = linker.engine().clone(); - linker.root().func_wrap("host-return-two", || Ok(2u32))?; + linker.root().func_wrap("host-return-two", || Ok((2u32,)))?; let mut i = linker.instance("host")?; - i.func_wrap("return-three", || Ok(3u32))?; + i.func_wrap("return-three", || Ok((3u32,)))?; i.instance("nested")? - .func_wrap("return-four", || Ok(4u32))?; + .func_wrap("return-four", || Ok((4u32,)))?; let module = Module::new( &engine, diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index d9f885acff..6980a0317d 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -47,7 +47,7 @@ impl Outcome { enum Results { Core(Vec), #[cfg(feature = "component-model")] - Component(component::Val), + Component(Vec), } enum InstanceKind { @@ -148,7 +148,7 @@ impl WastContext { #[cfg(feature = "component-model")] Wat::Component(m) => self .instantiate_component(&m.encode()?)? - .map(|_| Results::Component(component::Val::Unit)), + .map(|_| Results::Component(Vec::new())), #[cfg(not(feature = "component-model"))] Wat::Component(_) => bail!("component-model support not enabled"), }), @@ -193,8 +193,10 @@ impl WastContext { }) .collect::>>()?; - Ok(match func.call(&mut self.store, &values) { - Ok(results) => { + let mut results = + vec![component::Val::Bool(false); func.results(&self.store).len()]; + Ok(match func.call(&mut self.store, &values, &mut results) { + Ok(()) => { func.post_return(&mut self.store)?; Outcome::Ok(Results::Component(results.into())) } @@ -290,6 +292,9 @@ impl WastContext { fn assert_return(&self, result: Outcome, results: &[WastRet<'_>]) -> Result<()> { match result.into_result()? { Results::Core(values) => { + if values.len() != results.len() { + bail!("expected {} results found {}", results.len(), values.len()); + } for (i, (v, e)) in values.iter().zip(results).enumerate() { let e = match e { WastRet::Core(core) => core, @@ -301,17 +306,20 @@ impl WastContext { } } #[cfg(feature = "component-model")] - Results::Component(value) => { - if results.len() != 1 { - bail!("expected one result value assertion"); + Results::Component(values) => { + if values.len() != results.len() { + bail!("expected {} results found {}", results.len(), values.len()); + } + for (i, (v, e)) in values.iter().zip(results).enumerate() { + let e = match e { + WastRet::Core(_) => { + bail!("expected component value found core value") + } + WastRet::Component(val) => val, + }; + component::match_val(e, v) + .with_context(|| format!("result {} didn't match", i))?; } - let result = match &results[0] { - WastRet::Component(ret) => ret, - WastRet::Core(_) => { - bail!("expected core value found component value") - } - }; - component::match_val(&result, &value)?; } } Ok(()) diff --git a/fuzz/build.rs b/fuzz/build.rs index 97c6e64ddf..9e737857c4 100644 --- a/fuzz/build.rs +++ b/fuzz/build.rs @@ -75,7 +75,7 @@ mod component { let Declarations { types, params, - result, + results, import_and_export, encoding1, encoding2, @@ -92,16 +92,22 @@ mod component { }) .collect::(); - let rust_result = - component_fuzz_util::rust_type(&case.result, name_counter, &mut declarations); + let rust_results = case + .results + .iter() + .map(|ty| { + let ty = component_fuzz_util::rust_type(&ty, name_counter, &mut declarations); + quote!(#ty,) + }) + .collect::(); - let test = quote!(#index => component_types::#test::<#rust_params #rust_result>( + let test = quote!(#index => component_types::#test::<#rust_params (#rust_results)>( input, { static DECLS: Declarations = Declarations { types: Cow::Borrowed(#types), params: Cow::Borrowed(#params), - result: Cow::Borrowed(#result), + results: Cow::Borrowed(#results), import_and_export: Cow::Borrowed(#import_and_export), encoding1: #encoding1, encoding2: #encoding2, diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 841e41faef..29fc774cad 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -68,6 +68,12 @@ criteria = "safe-to-deploy" version = "0.15.0" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasm-encoder]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.16.0" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wasm-mutate]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -80,6 +86,12 @@ criteria = "safe-to-deploy" version = "0.2.6" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasm-mutate]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.2.7" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wasm-smith]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -92,6 +104,12 @@ criteria = "safe-to-deploy" version = "0.11.3" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasm-smith]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.11.4" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wasmparser]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -104,6 +122,18 @@ criteria = "safe-to-deploy" version = "0.88.0" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasmparser]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.89.0" +notes = "The Bytecode Alliance is the author of this crate." + +[[audits.wasmparser]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.89.1" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wasmprinter]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -116,6 +146,12 @@ criteria = "safe-to-deploy" version = "0.2.38" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasmprinter]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.2.39" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wast]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -134,6 +170,12 @@ criteria = "safe-to-deploy" version = "45.0.0" notes = "The Bytecode Alliance is the author of this crate" +[[audits.wast]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "46.0.0" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wat]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -146,3 +188,9 @@ criteria = "safe-to-deploy" version = "1.0.47" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wat]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "1.0.48" +notes = "The Bytecode Alliance is the author of this crate." + diff --git a/tests/all/component_model/dynamic.rs b/tests/all/component_model/dynamic.rs index f9ac63fdb8..fd9e19c5c4 100644 --- a/tests/all/component_model/dynamic.rs +++ b/tests/all/component_model/dynamic.rs @@ -8,6 +8,7 @@ use wasmtime::Store; fn primitives() -> Result<()> { let engine = super::engine(); let mut store = Store::new(&engine, ()); + let mut output = [Val::Bool(false)]; for (input, ty, param) in [ (Val::Bool(true), "bool", Param(Type::U8, Some(0))), @@ -34,9 +35,9 @@ fn primitives() -> Result<()> { let component = Component::new(&engine, make_echo_component_with_params(ty, &[param]))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); } // Sad path: type mismatch @@ -48,7 +49,7 @@ fn primitives() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = func - .call_and_post_return(&mut store, &[Val::U64(42)]) + .call_and_post_return(&mut store, &[Val::U64(42)], &mut output) .unwrap_err(); assert!(err.to_string().contains("type mismatch"), "{err}"); @@ -62,6 +63,7 @@ fn primitives() -> Result<()> { Val::Float64(3.14159265_f64.to_bits()), Val::Float64(3.14159265_f64.to_bits()), ], + &mut output, ) .unwrap_err(); @@ -72,13 +74,22 @@ fn primitives() -> Result<()> { // Sad path: arity mismatch (too few) - let err = func.call_and_post_return(&mut store, &[]).unwrap_err(); - + let err = func + .call_and_post_return(&mut store, &[], &mut output) + .unwrap_err(); assert!( err.to_string().contains("expected 1 argument(s), got 0"), "{err}" ); + let err = func + .call_and_post_return(&mut store, &output, &mut []) + .unwrap_err(); + assert!( + err.to_string().contains("expected 1 results(s), got 0"), + "{err}" + ); + Ok(()) } @@ -91,9 +102,9 @@ fn strings() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input = Val::String(Box::from("hello, component!")); - let output = func.call_and_post_return(&mut store, &[input.clone()])?; - - assert_eq!(input, output); + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; + assert_eq!(input, output[0]); Ok(()) } @@ -112,9 +123,10 @@ fn lists() -> Result<()> { Val::U32(79023439), Val::U32(2084037802), ]))?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Sad path: type mismatch @@ -163,9 +175,10 @@ fn records() -> Result<()> { .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?, ), ])?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Sad path: type mismatch @@ -198,7 +211,7 @@ fn records() -> Result<()> { .unwrap_record() .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?, ), - ("F", Val::Unit), + ("F", Val::Bool(true)), ]) .unwrap_err(); @@ -246,54 +259,62 @@ fn variants() -> Result<()> { let ty = &func.params(&store)[0]; let input = ty .unwrap_variant() - .new_val("B", Val::Float64(3.14159265_f64.to_bits()))?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + .new_val("B", Some(Val::Float64(3.14159265_f64.to_bits())))?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Do it again, this time using case "C" let component = Component::new( &engine, - dbg!(make_echo_component_with_params( + make_echo_component_with_params( r#"(variant (case "A" u32) (case "B" float64) (case "C" (record (field "D" bool) (field "E" u32))))"#, &[ Param(Type::U8, Some(0)), Param(Type::I64, Some(8)), Param(Type::I32, Some(12)), ], - )), + ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let ty = &func.params(&store)[0]; - let c_type = &ty.unwrap_variant().cases().nth(2).unwrap().ty; + let c_type = &ty.unwrap_variant().cases().nth(2).unwrap().ty.unwrap(); let input = ty.unwrap_variant().new_val( "C", - c_type - .unwrap_record() - .new_val([("D", Val::Bool(true)), ("E", Val::U32(314159265))])?, + Some( + c_type + .unwrap_record() + .new_val([("D", Val::Bool(true)), ("E", Val::U32(314159265))])?, + ), )?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Sad path: type mismatch let err = ty .unwrap_variant() - .new_val("B", Val::U64(314159265)) + .new_val("B", Some(Val::U64(314159265))) .unwrap_err(); - assert!(err.to_string().contains("type mismatch"), "{err}"); + let err = ty.unwrap_variant().new_val("B", None).unwrap_err(); + assert!( + err.to_string().contains("expected a payload for case `B`"), + "{err}" + ); // Sad path: unknown case let err = ty .unwrap_variant() - .new_val("D", Val::U64(314159265)) + .new_val("D", Some(Val::U64(314159265))) .unwrap_err(); - + assert!(err.to_string().contains("unknown variant case"), "{err}"); + let err = ty.unwrap_variant().new_val("D", None).unwrap_err(); assert!(err.to_string().contains("unknown variant case"), "{err}"); // Make sure we lift variants which have cases of different sizes with the correct alignment @@ -323,13 +344,15 @@ fn variants() -> Result<()> { let input = ty.unwrap_record().new_val([ ( "A", - a_type.unwrap_variant().new_val("A", Val::U32(314159265))?, + a_type + .unwrap_variant() + .new_val("A", Some(Val::U32(314159265)))?, ), ("B", Val::U32(628318530)), ])?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); Ok(()) } @@ -350,9 +373,10 @@ fn flags() -> Result<()> { let func = instance.get_func(&mut store, "echo").unwrap(); let ty = &func.params(&store)[0]; let input = ty.unwrap_flags().new_val(&["B", "D"])?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Sad path: unknown flags @@ -391,11 +415,10 @@ fn everything() -> Result<()> { (field "U" float64) (field "V" string) (field "W" char) - (field "X" unit) (field "Y" (tuple u32 u32)) (field "Z" (union u32 float64)) (field "AA" (option u32)) - (field "BB" (expected string string)) + (field "BB" (result string (error string))) )"#, &[ Param(Type::I32, Some(0)), @@ -436,7 +459,7 @@ fn everything() -> Result<()> { .map(|field| field.ty) .collect::>(); let (b_type, c_type, f_type, j_type, y_type, z_type, aa_type, bb_type) = ( - &types[1], &types[2], &types[3], &types[4], &types[14], &types[15], &types[16], &types[17], + &types[1], &types[2], &types[3], &types[4], &types[13], &types[14], &types[15], &types[16], ); let f_element_type = &f_type.unwrap_list().ty(); let input = ty.unwrap_record().new_val([ @@ -458,7 +481,7 @@ fn everything() -> Result<()> { "J", j_type .unwrap_variant() - .new_val("L", Val::Float64(3.14159265_f64.to_bits()))?, + .new_val("L", Some(Val::Float64(3.14159265_f64.to_bits())))?, ), ("P", Val::S8(42)), ("Q", Val::S16(4242)), @@ -468,7 +491,6 @@ fn everything() -> Result<()> { ("U", Val::Float64(3.14159265_f64.to_bits())), ("V", Val::String(Box::from("wow, nice types"))), ("W", Val::Char('🦀')), - ("X", Val::Unit), ( "Y", y_type @@ -488,13 +510,14 @@ fn everything() -> Result<()> { ( "BB", bb_type - .unwrap_expected() - .new_val(Ok(Val::String(Box::from("no problem"))))?, + .unwrap_result() + .new_val(Ok(Some(Val::String(Box::from("no problem")))))?, ), ])?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); Ok(()) } diff --git a/tests/all/component_model/func.rs b/tests/all/component_model/func.rs index 6c30bd985d..cf28ea5c50 100644 --- a/tests/all/component_model/func.rs +++ b/tests/all/component_model/func.rs @@ -66,10 +66,10 @@ fn typecheck() -> Result<()> { (func (export "take-string") (param string) (canon lift (core func $i "take-string") (memory $i "memory") (realloc (func $i "realloc"))) ) - (func (export "take-two-args") (param s32) (param (list u8)) + (func (export "take-two-args") (param "a" s32) (param "b" (list u8)) (canon lift (core func $i "two-args") (memory $i "memory") (realloc (func $i "realloc"))) ) - (func (export "ret-tuple") (result (tuple u8 s8)) + (func (export "ret-tuple") (result "a" u8) (result "b" s8) (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) ) (func (export "ret-tuple1") (result (tuple u32)) @@ -96,29 +96,34 @@ fn typecheck() -> Result<()> { let ret_tuple1 = instance.get_func(&mut store, "ret-tuple1").unwrap(); let ret_string = instance.get_func(&mut store, "ret-string").unwrap(); let ret_list_u8 = instance.get_func(&mut store, "ret-list-u8").unwrap(); - assert!(thunk.typed::<(), u32, _>(&store).is_err()); + assert!(thunk.typed::<(), (u32,), _>(&store).is_err()); assert!(thunk.typed::<(u32,), (), _>(&store).is_err()); assert!(thunk.typed::<(), (), _>(&store).is_ok()); assert!(tuple_thunk.typed::<(), (), _>(&store).is_err()); - assert!(tuple_thunk.typed::<((),), (), _>(&store).is_ok()); + assert!(tuple_thunk.typed::<((),), (), _>(&store).is_err()); + assert!(tuple_thunk.typed::<((),), ((),), _>(&store).is_ok()); assert!(take_string.typed::<(), (), _>(&store).is_err()); assert!(take_string.typed::<(String,), (), _>(&store).is_ok()); assert!(take_string.typed::<(&str,), (), _>(&store).is_ok()); assert!(take_string.typed::<(&[u8],), (), _>(&store).is_err()); assert!(take_two_args.typed::<(), (), _>(&store).is_err()); - assert!(take_two_args.typed::<(i32, &[u8]), u32, _>(&store).is_err()); + assert!(take_two_args + .typed::<(i32, &[u8]), (u32,), _>(&store) + .is_err()); assert!(take_two_args.typed::<(u32, &[u8]), (), _>(&store).is_err()); assert!(take_two_args.typed::<(i32, &[u8]), (), _>(&store).is_ok()); assert!(ret_tuple.typed::<(), (), _>(&store).is_err()); assert!(ret_tuple.typed::<(), (u8,), _>(&store).is_err()); assert!(ret_tuple.typed::<(), (u8, i8), _>(&store).is_ok()); - assert!(ret_tuple1.typed::<(), (u32,), _>(&store).is_ok()); - assert!(ret_tuple1.typed::<(), u32, _>(&store).is_err()); + assert!(ret_tuple1.typed::<(), ((u32,),), _>(&store).is_ok()); + assert!(ret_tuple1.typed::<(), (u32,), _>(&store).is_err()); assert!(ret_string.typed::<(), (), _>(&store).is_err()); - assert!(ret_string.typed::<(), WasmStr, _>(&store).is_ok()); - assert!(ret_list_u8.typed::<(), WasmList, _>(&store).is_err()); - assert!(ret_list_u8.typed::<(), WasmList, _>(&store).is_err()); - assert!(ret_list_u8.typed::<(), WasmList, _>(&store).is_ok()); + assert!(ret_string.typed::<(), (WasmStr,), _>(&store).is_ok()); + assert!(ret_list_u8 + .typed::<(), (WasmList,), _>(&store) + .is_err()); + assert!(ret_list_u8.typed::<(), (WasmList,), _>(&store).is_err()); + assert!(ret_list_u8.typed::<(), (WasmList,), _>(&store).is_ok()); Ok(()) } @@ -262,140 +267,140 @@ fn integers() -> Result<()> { // Zero can be returned as any integer assert_eq!( instance - .get_typed_func::<(), u8, _>(&mut store, "ret-u8")? + .get_typed_func::<(), (u8,), _>(&mut store, "ret-u8")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), i8, _>(&mut store, "ret-s8")? + .get_typed_func::<(), (i8,), _>(&mut store, "ret-s8")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), u16, _>(&mut store, "ret-u16")? + .get_typed_func::<(), (u16,), _>(&mut store, "ret-u16")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), i16, _>(&mut store, "ret-s16")? + .get_typed_func::<(), (i16,), _>(&mut store, "ret-s16")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), u32, _>(&mut store, "ret-u32")? + .get_typed_func::<(), (u32,), _>(&mut store, "ret-u32")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), i32, _>(&mut store, "ret-s32")? + .get_typed_func::<(), (i32,), _>(&mut store, "ret-s32")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), u64, _>(&mut store, "ret-u64")? + .get_typed_func::<(), (u64,), _>(&mut store, "ret-u64")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), i64, _>(&mut store, "ret-s64")? + .get_typed_func::<(), (i64,), _>(&mut store, "ret-s64")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); // Returning -1 should reinterpret the bytes as defined by each type. assert_eq!( instance - .get_typed_func::<(), u8, _>(&mut store, "retm1-u8")? + .get_typed_func::<(), (u8,), _>(&mut store, "retm1-u8")? .call_and_post_return(&mut store, ())?, - 0xff + (0xff,) ); assert_eq!( instance - .get_typed_func::<(), i8, _>(&mut store, "retm1-s8")? + .get_typed_func::<(), (i8,), _>(&mut store, "retm1-s8")? .call_and_post_return(&mut store, ())?, - -1 + (-1,) ); assert_eq!( instance - .get_typed_func::<(), u16, _>(&mut store, "retm1-u16")? + .get_typed_func::<(), (u16,), _>(&mut store, "retm1-u16")? .call_and_post_return(&mut store, ())?, - 0xffff + (0xffff,) ); assert_eq!( instance - .get_typed_func::<(), i16, _>(&mut store, "retm1-s16")? + .get_typed_func::<(), (i16,), _>(&mut store, "retm1-s16")? .call_and_post_return(&mut store, ())?, - -1 + (-1,) ); assert_eq!( instance - .get_typed_func::<(), u32, _>(&mut store, "retm1-u32")? + .get_typed_func::<(), (u32,), _>(&mut store, "retm1-u32")? .call_and_post_return(&mut store, ())?, - 0xffffffff + (0xffffffff,) ); assert_eq!( instance - .get_typed_func::<(), i32, _>(&mut store, "retm1-s32")? + .get_typed_func::<(), (i32,), _>(&mut store, "retm1-s32")? .call_and_post_return(&mut store, ())?, - -1 + (-1,) ); assert_eq!( instance - .get_typed_func::<(), u64, _>(&mut store, "retm1-u64")? + .get_typed_func::<(), (u64,), _>(&mut store, "retm1-u64")? .call_and_post_return(&mut store, ())?, - 0xffffffff_ffffffff + (0xffffffff_ffffffff,) ); assert_eq!( instance - .get_typed_func::<(), i64, _>(&mut store, "retm1-s64")? + .get_typed_func::<(), (i64,), _>(&mut store, "retm1-s64")? .call_and_post_return(&mut store, ())?, - -1 + (-1,) ); // Returning 100000 should chop off bytes as necessary let ret: u32 = 100000; assert_eq!( instance - .get_typed_func::<(), u8, _>(&mut store, "retbig-u8")? + .get_typed_func::<(), (u8,), _>(&mut store, "retbig-u8")? .call_and_post_return(&mut store, ())?, - ret as u8, + (ret as u8,), ); assert_eq!( instance - .get_typed_func::<(), i8, _>(&mut store, "retbig-s8")? + .get_typed_func::<(), (i8,), _>(&mut store, "retbig-s8")? .call_and_post_return(&mut store, ())?, - ret as i8, + (ret as i8,), ); assert_eq!( instance - .get_typed_func::<(), u16, _>(&mut store, "retbig-u16")? + .get_typed_func::<(), (u16,), _>(&mut store, "retbig-u16")? .call_and_post_return(&mut store, ())?, - ret as u16, + (ret as u16,), ); assert_eq!( instance - .get_typed_func::<(), i16, _>(&mut store, "retbig-s16")? + .get_typed_func::<(), (i16,), _>(&mut store, "retbig-s16")? .call_and_post_return(&mut store, ())?, - ret as i16, + (ret as i16,), ); assert_eq!( instance - .get_typed_func::<(), u32, _>(&mut store, "retbig-u32")? + .get_typed_func::<(), (u32,), _>(&mut store, "retbig-u32")? .call_and_post_return(&mut store, ())?, - ret, + (ret,), ); assert_eq!( instance - .get_typed_func::<(), i32, _>(&mut store, "retbig-s32")? + .get_typed_func::<(), (i32,), _>(&mut store, "retbig-s32")? .call_and_post_return(&mut store, ())?, - ret as i32, + (ret as i32,), ); Ok(()) @@ -486,23 +491,24 @@ fn floats() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let f32_to_u32 = instance.get_typed_func::<(f32,), u32, _>(&mut store, "f32-to-u32")?; - let f64_to_u64 = instance.get_typed_func::<(f64,), u64, _>(&mut store, "f64-to-u64")?; - let u32_to_f32 = instance.get_typed_func::<(u32,), f32, _>(&mut store, "u32-to-f32")?; - let u64_to_f64 = instance.get_typed_func::<(u64,), f64, _>(&mut store, "u64-to-f64")?; + let f32_to_u32 = instance.get_typed_func::<(f32,), (u32,), _>(&mut store, "f32-to-u32")?; + let f64_to_u64 = instance.get_typed_func::<(f64,), (u64,), _>(&mut store, "f64-to-u64")?; + let u32_to_f32 = instance.get_typed_func::<(u32,), (f32,), _>(&mut store, "u32-to-f32")?; + let u64_to_f64 = instance.get_typed_func::<(u64,), (f64,), _>(&mut store, "u64-to-f64")?; - assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, 1.0f32.to_bits()); + assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, (1.0f32.to_bits(),)); f32_to_u32.post_return(&mut store)?; - assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, 2.0f64.to_bits()); + assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, (2.0f64.to_bits(),)); f64_to_u64.post_return(&mut store)?; - assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, 3.0); + assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, (3.0,)); u32_to_f32.post_return(&mut store)?; - assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, 4.0); + assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, (4.0,)); u64_to_f64.post_return(&mut store)?; assert_eq!( u32_to_f32 .call(&mut store, (CANON_32BIT_NAN | 1,))? + .0 .to_bits(), CANON_32BIT_NAN ); @@ -510,19 +516,20 @@ fn floats() -> Result<()> { assert_eq!( u64_to_f64 .call(&mut store, (CANON_64BIT_NAN | 1,))? + .0 .to_bits(), - CANON_64BIT_NAN + CANON_64BIT_NAN, ); u64_to_f64.post_return(&mut store)?; assert_eq!( f32_to_u32.call(&mut store, (f32::from_bits(CANON_32BIT_NAN | 1),))?, - CANON_32BIT_NAN + (CANON_32BIT_NAN,) ); f32_to_u32.post_return(&mut store)?; assert_eq!( f64_to_u64.call(&mut store, (f64::from_bits(CANON_64BIT_NAN | 1),))?, - CANON_64BIT_NAN + (CANON_64BIT_NAN,) ); f64_to_u64.post_return(&mut store)?; @@ -551,18 +558,18 @@ fn bools() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let u32_to_bool = instance.get_typed_func::<(u32,), bool, _>(&mut store, "u32-to-bool")?; - let bool_to_u32 = instance.get_typed_func::<(bool,), u32, _>(&mut store, "bool-to-u32")?; + let u32_to_bool = instance.get_typed_func::<(u32,), (bool,), _>(&mut store, "u32-to-bool")?; + let bool_to_u32 = instance.get_typed_func::<(bool,), (u32,), _>(&mut store, "bool-to-u32")?; - assert_eq!(bool_to_u32.call(&mut store, (false,))?, 0); + assert_eq!(bool_to_u32.call(&mut store, (false,))?, (0,)); bool_to_u32.post_return(&mut store)?; - assert_eq!(bool_to_u32.call(&mut store, (true,))?, 1); + assert_eq!(bool_to_u32.call(&mut store, (true,))?, (1,)); bool_to_u32.post_return(&mut store)?; - assert_eq!(u32_to_bool.call(&mut store, (0,))?, false); + assert_eq!(u32_to_bool.call(&mut store, (0,))?, (false,)); u32_to_bool.post_return(&mut store)?; - assert_eq!(u32_to_bool.call(&mut store, (1,))?, true); + assert_eq!(u32_to_bool.call(&mut store, (1,))?, (true,)); u32_to_bool.post_return(&mut store)?; - assert_eq!(u32_to_bool.call(&mut store, (2,))?, true); + assert_eq!(u32_to_bool.call(&mut store, (2,))?, (true,)); u32_to_bool.post_return(&mut store)?; Ok(()) @@ -590,13 +597,13 @@ fn chars() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let u32_to_char = instance.get_typed_func::<(u32,), char, _>(&mut store, "u32-to-char")?; - let char_to_u32 = instance.get_typed_func::<(char,), u32, _>(&mut store, "char-to-u32")?; + let u32_to_char = instance.get_typed_func::<(u32,), (char,), _>(&mut store, "u32-to-char")?; + let char_to_u32 = instance.get_typed_func::<(char,), (u32,), _>(&mut store, "char-to-u32")?; let mut roundtrip = |x: char| -> Result<()> { - assert_eq!(char_to_u32.call(&mut store, (x,))?, x as u32); + assert_eq!(char_to_u32.call(&mut store, (x,))?, (x as u32,)); char_to_u32.post_return(&mut store)?; - assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, x); + assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, (x,)); u32_to_char.post_return(&mut store)?; Ok(()) }; @@ -610,7 +617,7 @@ fn chars() -> Result<()> { let u32_to_char = |store: &mut Store<()>| { Linker::new(&engine) .instantiate(&mut *store, &component)? - .get_typed_func::<(u32,), char, _>(&mut *store, "u32-to-char") + .get_typed_func::<(u32,), (char,), _>(&mut *store, "u32-to-char") }; let err = u32_to_char(&mut store)? .call(&mut store, (0xd800,)) @@ -656,7 +663,7 @@ fn tuple_result() -> Result<()> { (type $result (tuple s8 u16 float32 float64)) (func (export "tuple") - (param s8) (param u16) (param float32) (param float64) (result $result) + (param "a" s8) (param "b" u16) (param "c" float32) (param "d" float64) (result $result) (canon lift (core func $i "foo") (memory $i "memory")) ) (func (export "invalid") (result $result) @@ -672,12 +679,12 @@ fn tuple_result() -> Result<()> { let input = (-1, 100, 3.0, 100.0); let output = instance - .get_typed_func::<(i8, u16, f32, f64), (i8, u16, f32, f64), _>(&mut store, "tuple")? + .get_typed_func::<(i8, u16, f32, f64), ((i8, u16, f32, f64),), _>(&mut store, "tuple")? .call_and_post_return(&mut store, input)?; - assert_eq!(input, output); + assert_eq!((input,), output); let invalid_func = - instance.get_typed_func::<(), (i8, u16, f32, f64), _>(&mut store, "invalid")?; + instance.get_typed_func::<(), ((i8, u16, f32, f64),), _>(&mut store, "invalid")?; let err = invalid_func.call(&mut store, ()).err().unwrap(); assert!( err.to_string().contains("pointer out of bounds of memory"), @@ -753,29 +760,29 @@ fn strings() -> Result<()> { let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let list8_to_str = - instance.get_typed_func::<(&[u8],), WasmStr, _>(&mut store, "list8-to-str")?; + instance.get_typed_func::<(&[u8],), (WasmStr,), _>(&mut store, "list8-to-str")?; let str_to_list8 = - instance.get_typed_func::<(&str,), WasmList, _>(&mut store, "str-to-list8")?; + instance.get_typed_func::<(&str,), (WasmList,), _>(&mut store, "str-to-list8")?; let list16_to_str = - instance.get_typed_func::<(&[u16],), WasmStr, _>(&mut store, "list16-to-str")?; + instance.get_typed_func::<(&[u16],), (WasmStr,), _>(&mut store, "list16-to-str")?; let str_to_list16 = - instance.get_typed_func::<(&str,), WasmList, _>(&mut store, "str-to-list16")?; + instance.get_typed_func::<(&str,), (WasmList,), _>(&mut store, "str-to-list16")?; let mut roundtrip = |x: &str| -> Result<()> { - let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?; + let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?.0; assert_eq!(ret.to_str(&store)?, x); list8_to_str.post_return(&mut store)?; let utf16 = x.encode_utf16().collect::>(); - let ret = list16_to_str.call(&mut store, (&utf16[..],))?; + let ret = list16_to_str.call(&mut store, (&utf16[..],))?.0; assert_eq!(ret.to_str(&store)?, x); list16_to_str.post_return(&mut store)?; - let ret = str_to_list8.call(&mut store, (x,))?; + let ret = str_to_list8.call(&mut store, (x,))?.0; assert_eq!(ret.iter(&store).collect::>>()?, x.as_bytes()); str_to_list8.post_return(&mut store)?; - let ret = str_to_list16.call(&mut store, (x,))?; + let ret = str_to_list16.call(&mut store, (x,))?.0; assert_eq!(ret.iter(&store).collect::>>()?, utf16,); str_to_list16.post_return(&mut store)?; @@ -788,27 +795,29 @@ fn strings() -> Result<()> { roundtrip("💝")?; roundtrip("Löwe 老虎 Léopard")?; - let ret = list8_to_str.call(&mut store, (b"\xff",))?; + let ret = list8_to_str.call(&mut store, (b"\xff",))?.0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("invalid utf-8"), "{}", err); list8_to_str.post_return(&mut store)?; - let ret = list8_to_str.call(&mut store, (b"hello there \xff invalid",))?; + let ret = list8_to_str + .call(&mut store, (b"hello there \xff invalid",))? + .0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("invalid utf-8"), "{}", err); list8_to_str.post_return(&mut store)?; - let ret = list16_to_str.call(&mut store, (&[0xd800],))?; + let ret = list16_to_str.call(&mut store, (&[0xd800],))?.0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("unpaired surrogate"), "{}", err); list16_to_str.post_return(&mut store)?; - let ret = list16_to_str.call(&mut store, (&[0xdfff],))?; + let ret = list16_to_str.call(&mut store, (&[0xdfff],))?.0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("unpaired surrogate"), "{}", err); list16_to_str.post_return(&mut store)?; - let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?; + let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?.0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("unpaired surrogate"), "{}", err); list16_to_str.post_return(&mut store)?; @@ -856,24 +865,24 @@ fn many_parameters() -> Result<()> { ) (core instance $i (instantiate $m)) - (type $result (tuple (list u8) u32)) (type $t (func - (param s8) ;; offset 0, size 1 - (param u64) ;; offset 8, size 8 - (param float32) ;; offset 16, size 4 - (param u8) ;; offset 20, size 1 - (param unit) ;; offset 21, size 0 - (param s16) ;; offset 22, size 2 - (param string) ;; offset 24, size 8 - (param (list u32)) ;; offset 32, size 8 - (param bool) ;; offset 40, size 1 - (param bool) ;; offset 41, size 1 - (param char) ;; offset 44, size 4 - (param (list bool)) ;; offset 48, size 8 - (param (list char)) ;; offset 56, size 8 - (param (list string)) ;; offset 64, size 8 + (param "p1" s8) ;; offset 0, size 1 + (param "p2" u64) ;; offset 8, size 8 + (param "p3" float32) ;; offset 16, size 4 + (param "p4" u8) ;; offset 20, size 1 + (param "p5" (tuple)) ;; offset 21, size 0 + (param "p6" s16) ;; offset 22, size 2 + (param "p7" string) ;; offset 24, size 8 + (param "p8" (list u32)) ;; offset 32, size 8 + (param "p9" bool) ;; offset 40, size 1 + (param "pa" bool) ;; offset 41, size 1 + (param "pb" char) ;; offset 44, size 4 + (param "pc" (list bool)) ;; offset 48, size 8 + (param "pd" (list char)) ;; offset 56, size 8 + (param "pe" (list string)) ;; offset 64, size 8 - (result $result) + (result "all-memory" (list u8)) + (result "pointer" u32) )) (func (export "many-param") (type $t) (canon lift @@ -1000,16 +1009,16 @@ fn some_traps() -> Result<()> { ) (type $t (func - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) + (param "s1" string) + (param "s2" string) + (param "s3" string) + (param "s4" string) + (param "s5" string) + (param "s6" string) + (param "s7" string) + (param "s8" string) + (param "s9" string) + (param "s10" string) )) (func (export "take-many-unreachable") (type $t) (canon lift (core func $i "take-many") (memory $i "memory") (realloc (func $i "realloc"))) @@ -1246,7 +1255,7 @@ fn char_bool_memory() -> Result<()> { ) (core instance $i (instantiate $m)) - (func (export "ret-tuple") (param u32) (param u32) (result (tuple bool char)) + (func (export "ret-tuple") (param "a" u32) (param "b" u32) (result "c" bool) (result "d" char) (canon lift (core func $i "ret-tuple") (memory $i "memory") (realloc (func $i "realloc"))) @@ -1328,10 +1337,10 @@ fn string_list_oob() -> Result<()> { let mut store = Store::new(&engine, ()); let ret_list_u8 = Linker::new(&engine) .instantiate(&mut store, &component)? - .get_typed_func::<(), WasmList, _>(&mut store, "ret-list-u8")?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "ret-list-u8")?; let ret_string = Linker::new(&engine) .instantiate(&mut store, &component)? - .get_typed_func::<(), WasmStr, _>(&mut store, "ret-string")?; + .get_typed_func::<(), (WasmStr,), _>(&mut store, "ret-string")?; let err = ret_list_u8.call(&mut store, ()).err().unwrap(); assert!(err.to_string().contains("out of bounds"), "{}", err); @@ -1373,8 +1382,8 @@ fn tuples() -> Result<()> { (core instance $i (instantiate $m)) (func (export "foo") - (param (tuple s32 float64)) - (param (tuple s8)) + (param "a" (tuple s32 float64)) + (param "b" (tuple s8)) (result (tuple u16)) (canon lift (core func $i "foo")) ) @@ -1385,8 +1394,8 @@ fn tuples() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let foo = instance.get_typed_func::<((i32, f64), (i8,)), (u16,), _>(&mut store, "foo")?; - assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, (3,)); + let foo = instance.get_typed_func::<((i32, f64), (i8,)), ((u16,),), _>(&mut store, "foo")?; + assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, ((3,),)); Ok(()) } @@ -1444,35 +1453,35 @@ fn option() -> Result<()> { ) (core instance $i (instantiate $m)) - (func (export "option-unit-to-u32") (param (option unit)) (result u32) + (func (export "option-unit-to-u32") (param (option (tuple))) (result u32) (canon lift (core func $i "pass0")) ) - (func (export "option-u8-to-tuple") (param (option u8)) (result (tuple u32 u32)) + (func (export "option-u8-to-tuple") (param (option u8)) (result "a" u32) (result "b" u32) (canon lift (core func $i "pass1") (memory $i "memory")) ) - (func (export "option-u32-to-tuple") (param (option u32)) (result (tuple u32 u32)) + (func (export "option-u32-to-tuple") (param (option u32)) (result "a" u32) (result "b" u32) (canon lift (core func $i "pass1") (memory $i "memory")) ) - (func (export "option-string-to-tuple") (param (option string)) (result (tuple u32 string)) + (func (export "option-string-to-tuple") (param (option string)) (result "a" u32) (result "b" string) (canon lift (core func $i "pass2") (memory $i "memory") (realloc (func $i "realloc")) ) ) - (func (export "to-option-unit") (param u32) (result (option unit)) + (func (export "to-option-unit") (param u32) (result (option (tuple))) (canon lift (core func $i "pass0")) ) - (func (export "to-option-u8") (param u32) (param u32) (result (option u8)) + (func (export "to-option-u8") (param "a" u32) (param "b" u32) (result (option u8)) (canon lift (core func $i "pass1") (memory $i "memory")) ) - (func (export "to-option-u32") (param u32) (param u32) (result (option u32)) + (func (export "to-option-u32") (param "a" u32) (param "b" u32) (result (option u32)) (canon lift (core func $i "pass1") (memory $i "memory") ) ) - (func (export "to-option-string") (param u32) (param string) (result (option string)) + (func (export "to-option-string") (param "a" u32) (param "b" string) (result (option string)) (canon lift (core func $i "pass2") (memory $i "memory") @@ -1488,10 +1497,10 @@ fn option() -> Result<()> { let linker = Linker::new(&engine); let instance = linker.instantiate(&mut store, &component)?; let option_unit_to_u32 = - instance.get_typed_func::<(Option<()>,), u32, _>(&mut store, "option-unit-to-u32")?; - assert_eq!(option_unit_to_u32.call(&mut store, (None,))?, 0); + instance.get_typed_func::<(Option<()>,), (u32,), _>(&mut store, "option-unit-to-u32")?; + assert_eq!(option_unit_to_u32.call(&mut store, (None,))?, (0,)); option_unit_to_u32.post_return(&mut store)?; - assert_eq!(option_unit_to_u32.call(&mut store, (Some(()),))?, 1); + assert_eq!(option_unit_to_u32.call(&mut store, (Some(()),))?, (1,)); option_unit_to_u32.post_return(&mut store)?; let option_u8_to_tuple = instance @@ -1534,49 +1543,49 @@ fn option() -> Result<()> { let instance = linker.instantiate(&mut store, &component)?; let to_option_unit = - instance.get_typed_func::<(u32,), Option<()>, _>(&mut store, "to-option-unit")?; - assert_eq!(to_option_unit.call(&mut store, (0,))?, None); + instance.get_typed_func::<(u32,), (Option<()>,), _>(&mut store, "to-option-unit")?; + assert_eq!(to_option_unit.call(&mut store, (0,))?, (None,)); to_option_unit.post_return(&mut store)?; - assert_eq!(to_option_unit.call(&mut store, (1,))?, Some(())); + assert_eq!(to_option_unit.call(&mut store, (1,))?, (Some(()),)); to_option_unit.post_return(&mut store)?; let err = to_option_unit.call(&mut store, (2,)).unwrap_err(); assert!(err.to_string().contains("invalid option"), "{}", err); let instance = linker.instantiate(&mut store, &component)?; let to_option_u8 = - instance.get_typed_func::<(u32, u32), Option, _>(&mut store, "to-option-u8")?; - assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, None); + instance.get_typed_func::<(u32, u32), (Option,), _>(&mut store, "to-option-u8")?; + assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, (None,)); to_option_u8.post_return(&mut store)?; - assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, Some(0)); + assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, (Some(0),)); to_option_u8.post_return(&mut store)?; - assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, Some(0xfd)); + assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, (Some(0xfd),)); to_option_u8.post_return(&mut store)?; assert!(to_option_u8.call(&mut store, (0x00_02, 0)).is_err()); let instance = linker.instantiate(&mut store, &component)?; let to_option_u32 = - instance.get_typed_func::<(u32, u32), Option, _>(&mut store, "to-option-u32")?; - assert_eq!(to_option_u32.call(&mut store, (0, 0))?, None); + instance.get_typed_func::<(u32, u32), (Option,), _>(&mut store, "to-option-u32")?; + assert_eq!(to_option_u32.call(&mut store, (0, 0))?, (None,)); to_option_u32.post_return(&mut store)?; - assert_eq!(to_option_u32.call(&mut store, (1, 0))?, Some(0)); + assert_eq!(to_option_u32.call(&mut store, (1, 0))?, (Some(0),)); to_option_u32.post_return(&mut store)?; assert_eq!( to_option_u32.call(&mut store, (1, 0x1234fead))?, - Some(0x1234fead) + (Some(0x1234fead),) ); to_option_u32.post_return(&mut store)?; assert!(to_option_u32.call(&mut store, (2, 0)).is_err()); let instance = linker.instantiate(&mut store, &component)?; let to_option_string = instance - .get_typed_func::<(u32, &str), Option, _>(&mut store, "to-option-string")?; - let ret = to_option_string.call(&mut store, (0, ""))?; + .get_typed_func::<(u32, &str), (Option,), _>(&mut store, "to-option-string")?; + let ret = to_option_string.call(&mut store, (0, ""))?.0; assert!(ret.is_none()); to_option_string.post_return(&mut store)?; - let ret = to_option_string.call(&mut store, (1, ""))?; + let ret = to_option_string.call(&mut store, (1, ""))?.0; assert_eq!(ret.unwrap().to_str(&store)?, ""); to_option_string.post_return(&mut store)?; - let ret = to_option_string.call(&mut store, (1, "cheesecake"))?; + let ret = to_option_string.call(&mut store, (1, "cheesecake"))?.0; assert_eq!(ret.unwrap().to_str(&store)?, "cheesecake"); to_option_string.post_return(&mut store)?; assert!(to_option_string.call(&mut store, (2, "")).is_err()); @@ -1637,24 +1646,24 @@ fn expected() -> Result<()> { ) (core instance $i (instantiate $m)) - (func (export "take-expected-unit") (param (expected unit unit)) (result u32) + (func (export "take-expected-unit") (param (result)) (result u32) (canon lift (core func $i "pass0")) ) - (func (export "take-expected-u8-f32") (param (expected u8 float32)) (result (tuple u32 u32)) + (func (export "take-expected-u8-f32") (param (result u8 (error float32))) (result "a" u32) (result "b" u32) (canon lift (core func $i "pass1") (memory $i "memory")) ) (type $list (list u8)) - (func (export "take-expected-string") (param (expected string $list)) (result (tuple u32 string)) + (func (export "take-expected-string") (param (result string (error $list))) (result "a" u32) (result "b" string) (canon lift (core func $i "pass2") (memory $i "memory") (realloc (func $i "realloc")) ) ) - (func (export "to-expected-unit") (param u32) (result (expected unit unit)) + (func (export "to-expected-unit") (param u32) (result (result)) (canon lift (core func $i "pass0")) ) - (func (export "to-expected-s16-f32") (param u32) (param u32) (result (expected s16 float32)) + (func (export "to-expected-s16-f32") (param "a" u32) (param "b" u32) (result (result s16 (error float32))) (canon lift (core func $i "pass1") (memory $i "memory") @@ -1669,11 +1678,11 @@ fn expected() -> Result<()> { let mut store = Store::new(&engine, ()); let linker = Linker::new(&engine); let instance = linker.instantiate(&mut store, &component)?; - let take_expected_unit = - instance.get_typed_func::<(Result<(), ()>,), u32, _>(&mut store, "take-expected-unit")?; - assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, 0); + let take_expected_unit = instance + .get_typed_func::<(Result<(), ()>,), (u32,), _>(&mut store, "take-expected-unit")?; + assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, (0,)); take_expected_unit.post_return(&mut store)?; - assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, 1); + assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, (1,)); take_expected_unit.post_return(&mut store)?; let take_expected_u8_f32 = instance @@ -1702,27 +1711,29 @@ fn expected() -> Result<()> { let instance = linker.instantiate(&mut store, &component)?; let to_expected_unit = - instance.get_typed_func::<(u32,), Result<(), ()>, _>(&mut store, "to-expected-unit")?; - assert_eq!(to_expected_unit.call(&mut store, (0,))?, Ok(())); + instance.get_typed_func::<(u32,), (Result<(), ()>,), _>(&mut store, "to-expected-unit")?; + assert_eq!(to_expected_unit.call(&mut store, (0,))?, (Ok(()),)); to_expected_unit.post_return(&mut store)?; - assert_eq!(to_expected_unit.call(&mut store, (1,))?, Err(())); + assert_eq!(to_expected_unit.call(&mut store, (1,))?, (Err(()),)); to_expected_unit.post_return(&mut store)?; let err = to_expected_unit.call(&mut store, (2,)).unwrap_err(); assert!(err.to_string().contains("invalid expected"), "{}", err); let instance = linker.instantiate(&mut store, &component)?; let to_expected_s16_f32 = instance - .get_typed_func::<(u32, u32), Result, _>(&mut store, "to-expected-s16-f32")?; - assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, Ok(0)); + .get_typed_func::<(u32, u32), (Result,), _>(&mut store, "to-expected-s16-f32")?; + assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, (Ok(0),)); to_expected_s16_f32.post_return(&mut store)?; - assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, Ok(100)); + assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, (Ok(100),)); to_expected_s16_f32.post_return(&mut store)?; assert_eq!( to_expected_s16_f32.call(&mut store, (1, 1.0f32.to_bits()))?, - Err(1.0) + (Err(1.0),) ); to_expected_s16_f32.post_return(&mut store)?; - let ret = to_expected_s16_f32.call(&mut store, (1, CANON_32BIT_NAN | 1))?; + let ret = to_expected_s16_f32 + .call(&mut store, (1, CANON_32BIT_NAN | 1))? + .0; assert_eq!(ret.unwrap_err().to_bits(), CANON_32BIT_NAN); to_expected_s16_f32.post_return(&mut store)?; assert!(to_expected_s16_f32.call(&mut store, (2, 0)).is_err()); @@ -1768,10 +1779,13 @@ fn fancy_list() -> Result<()> { (core instance $i (instantiate $m)) (type $a (option u8)) - (type $b (expected unit string)) + (type $b (result (error string))) (type $input (list (tuple $a $b))) - (type $output (tuple u32 u32 (list u8))) - (func (export "take") (param $input) (result $output) + (func (export "take") + (param $input) + (result "ptr" u32) + (result "len" u32) + (result "list" (list u8)) (canon lift (core func $i "take") (memory $i "memory") @@ -1873,9 +1887,9 @@ fn invalid_alignment() -> Result<()> { (core instance $i (instantiate $m)) (func (export "many-params") - (param string) (param string) (param string) (param string) - (param string) (param string) (param string) (param string) - (param string) (param string) (param string) (param string) + (param "s1" string) (param "s2" string) (param "s3" string) (param "s4" string) + (param "s5" string) (param "s6" string) (param "s7" string) (param "s8" string) + (param "s9" string) (param "s10" string) (param "s11" string) (param "s12" string) (canon lift (core func $i "take-i32") (memory $i "memory") @@ -1929,7 +1943,7 @@ fn invalid_alignment() -> Result<()> { ); let err = instance(&mut store)? - .get_typed_func::<(), WasmStr, _>(&mut store, "string-ret")? + .get_typed_func::<(), (WasmStr,), _>(&mut store, "string-ret")? .call(&mut store, ()) .err() .unwrap(); @@ -1940,7 +1954,7 @@ fn invalid_alignment() -> Result<()> { ); let err = instance(&mut store)? - .get_typed_func::<(), WasmList, _>(&mut store, "list-u32-ret")? + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u32-ret")? .call(&mut store, ()) .err() .unwrap(); @@ -2073,8 +2087,9 @@ fn raw_slice_of_various_types() -> Result<()> { }; let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-u8")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u8")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2083,8 +2098,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-i8")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-i8")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2094,8 +2110,9 @@ fn raw_slice_of_various_types() -> Result<()> { ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-u16")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u16")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2110,8 +2127,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-i16")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-i16")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2126,8 +2144,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-u32")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u32")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2138,8 +2157,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-i32")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-i32")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2150,8 +2170,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-u64")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u64")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2160,8 +2181,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-i64")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-i64")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2194,11 +2216,11 @@ fn lower_then_lift() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let mut linker = Linker::new(&engine); - linker.root().func_wrap("f", || Ok(2u32))?; + linker.root().func_wrap("f", || Ok((2u32,)))?; let instance = linker.instantiate(&mut store, &component)?; - let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?; - assert_eq!(f.call(&mut store, ())?, 2); + let f = instance.get_typed_func::<(), (i32,), _>(&mut store, "f")?; + assert_eq!(f.call(&mut store, ())?, (2,)); // First test strings when the import/export ABI happen to line up let component = format!( @@ -2272,11 +2294,11 @@ fn lower_then_lift() -> Result<()> { .root() .func_wrap("s2", |store: StoreContextMut<'_, ()>, x: WasmStr| { assert_eq!(x.to_str(&store)?, "hello"); - Ok(u32::MAX) + Ok((u32::MAX,)) })?; let instance = linker.instantiate(&mut store, &component)?; - let f = instance.get_typed_func::<(&str,), WasmStr, _>(&mut store, "f")?; + let f = instance.get_typed_func::<(&str,), (WasmStr,), _>(&mut store, "f")?; let err = f.call(&mut store, ("hello",)).err().unwrap(); assert!( err.to_string().contains("return pointer not aligned"), @@ -2339,7 +2361,7 @@ fn errors_that_poison_instance() -> Result<()> { assert_poisoned(f3.call(&mut store, ("x",))); let instance = linker.instantiate(&mut store, &component)?; - let f4 = instance.get_typed_func::<(), WasmStr, _>(&mut store, "f4")?; + let f4 = instance.get_typed_func::<(), (WasmStr,), _>(&mut store, "f4")?; assert!(f4.call(&mut store, ()).is_err()); assert_poisoned(f4.call(&mut store, ())); @@ -2414,7 +2436,7 @@ fn run_export_with_internal_adapter() -> Result<()> { let mut store = Store::new(&engine, ()); let linker = Linker::new(&engine); let instance = linker.instantiate(&mut store, &component)?; - let run = instance.get_typed_func::<(), u32, _>(&mut store, "run")?; - assert_eq!(run.call(&mut store, ())?, 5); + let run = instance.get_typed_func::<(), (u32,), _>(&mut store, "run")?; + assert_eq!(run.call(&mut store, ())?, (5,)); Ok(()) } diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index 5bba4fcfe0..145063dc91 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -147,11 +147,11 @@ fn simple() -> Result<()> { linker.root().func_new( &component, "", - |mut store: StoreContextMut<'_, Option>, args| { + |mut store: StoreContextMut<'_, Option>, args, _results| { if let Val::String(s) = &args[0] { assert!(store.data().is_none()); *store.data_mut() = Some(s.to_string()); - Ok(Val::Unit) + Ok(()) } else { panic!() } @@ -161,7 +161,7 @@ fn simple() -> Result<()> { instance .get_func(&mut store, "call") .unwrap() - .call(&mut store, &[])?; + .call(&mut store, &[], &mut [])?; assert_eq!(store.data().as_ref().unwrap(), "hello world"); Ok(()) @@ -244,9 +244,7 @@ fn attempt_to_leave_during_malloc() -> Result<()> { .func_wrap("thunk", || -> Result<()> { panic!("should not get here") })?; linker .root() - .func_wrap("ret-string", || -> Result { - Ok("hello".to_string()) - })?; + .func_wrap("ret-string", || -> Result<_> { Ok(("hello".to_string(),)) })?; let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); @@ -368,22 +366,22 @@ fn attempt_to_reenter_during_host() -> Result<()> { linker.root().func_new( &component, "thunk", - |mut store: StoreContextMut<'_, DynamicState>, _| { + |mut store: StoreContextMut<'_, DynamicState>, _, _| { let func = store.data_mut().func.take().unwrap(); - let trap = func.call(&mut store, &[]).unwrap_err(); + let trap = func.call(&mut store, &[], &mut []).unwrap_err(); assert!( trap.to_string() .contains("cannot reenter component instance"), "bad trap: {}", trap, ); - Ok(Val::Unit) + Ok(()) }, )?; let instance = linker.instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "run").unwrap(); store.data_mut().func = Some(func); - func.call(&mut store, &[])?; + func.call(&mut store, &[], &mut [])?; Ok(()) } @@ -532,9 +530,9 @@ fn stack_and_heap_args_and_rets() -> Result<()> { // First, test the static API let mut linker = Linker::new(&engine); - linker.root().func_wrap("f1", |x: u32| -> Result { + linker.root().func_wrap("f1", |x: u32| -> Result<(u32,)> { assert_eq!(x, 1); - Ok(2) + Ok((2,)) })?; linker.root().func_wrap( "f2", @@ -550,16 +548,16 @@ fn stack_and_heap_args_and_rets() -> Result<()> { WasmStr, WasmStr, )| - -> Result { + -> Result<(u32,)> { assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); - Ok(3) + Ok((3,)) }, )?; linker .root() - .func_wrap("f3", |arg: u32| -> Result { + .func_wrap("f3", |arg: u32| -> Result<(String,)> { assert_eq!(arg, 8); - Ok("xyz".to_string()) + Ok(("xyz".to_string(),)) })?; linker.root().func_wrap( "f4", @@ -575,9 +573,9 @@ fn stack_and_heap_args_and_rets() -> Result<()> { WasmStr, WasmStr, )| - -> Result { + -> Result<(String,)> { assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); - Ok("xyz".to_string()) + Ok(("xyz".to_string(),)) }, )?; let instance = linker.instantiate(&mut store, &component)?; @@ -588,51 +586,63 @@ fn stack_and_heap_args_and_rets() -> Result<()> { // Next, test the dynamic API let mut linker = Linker::new(&engine); - linker.root().func_new(&component, "f1", |_, args| { - if let Val::U32(x) = &args[0] { - assert_eq!(*x, 1); - Ok(Val::U32(2)) - } else { - panic!() - } - })?; - linker.root().func_new(&component, "f2", |_, args| { - if let Val::Tuple(tuple) = &args[0] { - if let Val::String(s) = &tuple.values()[0] { - assert_eq!(s.deref(), "abc"); - Ok(Val::U32(3)) + linker + .root() + .func_new(&component, "f1", |_, args, results| { + if let Val::U32(x) = &args[0] { + assert_eq!(*x, 1); + results[0] = Val::U32(2); + Ok(()) } else { panic!() } - } else { - panic!() - } - })?; - linker.root().func_new(&component, "f3", |_, args| { - if let Val::U32(x) = &args[0] { - assert_eq!(*x, 8); - Ok(Val::String("xyz".into())) - } else { - panic!(); - } - })?; - linker.root().func_new(&component, "f4", |_, args| { - if let Val::Tuple(tuple) = &args[0] { - if let Val::String(s) = &tuple.values()[0] { - assert_eq!(s.deref(), "abc"); - Ok(Val::String("xyz".into())) + })?; + linker + .root() + .func_new(&component, "f2", |_, args, results| { + if let Val::Tuple(tuple) = &args[0] { + if let Val::String(s) = &tuple.values()[0] { + assert_eq!(s.deref(), "abc"); + results[0] = Val::U32(3); + Ok(()) + } else { + panic!() + } } else { panic!() } - } else { - panic!() - } - })?; + })?; + linker + .root() + .func_new(&component, "f3", |_, args, results| { + if let Val::U32(x) = &args[0] { + assert_eq!(*x, 8); + results[0] = Val::String("xyz".into()); + Ok(()) + } else { + panic!(); + } + })?; + linker + .root() + .func_new(&component, "f4", |_, args, results| { + if let Val::Tuple(tuple) = &args[0] { + if let Val::String(s) = &tuple.values()[0] { + assert_eq!(s.deref(), "abc"); + results[0] = Val::String("xyz".into()); + Ok(()) + } else { + panic!() + } + } else { + panic!() + } + })?; let instance = linker.instantiate(&mut store, &component)?; instance .get_func(&mut store, "run") .unwrap() - .call(&mut store, &[])?; + .call(&mut store, &[], &mut [])?; Ok(()) } @@ -693,8 +703,8 @@ fn bad_import_alignment() -> Result<()> { let mut linker = Linker::new(&engine); linker .root() - .func_wrap("unaligned-retptr", || -> Result { - Ok(String::new()) + .func_wrap("unaligned-retptr", || -> Result<(String,)> { + Ok((String::new(),)) })?; linker.root().func_wrap( "unaligned-argptr", @@ -783,18 +793,20 @@ fn no_actual_wasm_code() -> Result<()> { *store.data_mut() = 0; let mut linker = Linker::new(&engine); - linker - .root() - .func_new(&component, "f", |mut store: StoreContextMut<'_, u32>, _| { + linker.root().func_new( + &component, + "f", + |mut store: StoreContextMut<'_, u32>, _, _| { *store.data_mut() += 1; - Ok(Val::Unit) - })?; + Ok(()) + }, + )?; let instance = linker.instantiate(&mut store, &component)?; let thunk = instance.get_func(&mut store, "thunk").unwrap(); assert_eq!(*store.data(), 0); - thunk.call(&mut store, &[])?; + thunk.call(&mut store, &[], &mut [])?; assert_eq!(*store.data(), 1); Ok(()) diff --git a/tests/all/component_model/macros.rs b/tests/all/component_model/macros.rs index 190d6062c7..4ca5851dd2 100644 --- a/tests/all/component_model/macros.rs +++ b/tests/all/component_model/macros.rs @@ -27,10 +27,10 @@ fn record_derive() -> Result<()> { let input = Foo { a: -42, b: 73 }; let output = instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")? + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")? .call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); // Sad path: field count mismatch (too few) @@ -41,7 +41,7 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: field count mismatch (too many) @@ -56,7 +56,7 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: field name mismatch @@ -68,7 +68,7 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: field type mismatch @@ -80,7 +80,7 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with generics this time @@ -105,10 +105,10 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let output = instance - .get_typed_func::<(Generic,), Generic, _>(&mut store, "echo")? + .get_typed_func::<(Generic,), (Generic,), _>(&mut store, "echo")? .call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); Ok(()) } @@ -130,12 +130,12 @@ fn union_derive() -> Result<()> { let component = Component::new(&engine, make_echo_component("(union s32 u32 s32)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")?; for &input in &[Foo::A(-42), Foo::B(73), Foo::C(314159265)] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Sad path: case count mismatch (too few) @@ -144,7 +144,7 @@ fn union_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case count mismatch (too many) @@ -156,11 +156,11 @@ fn union_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case type mismatch @@ -169,7 +169,7 @@ fn union_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with generics this time @@ -184,7 +184,7 @@ fn union_derive() -> Result<()> { let component = Component::new(&engine, make_echo_component("(union s32 u32 s32)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Generic,), Generic, _>( + let func = instance.get_typed_func::<(Generic,), (Generic,), _>( &mut store, "echo", )?; @@ -195,7 +195,7 @@ fn union_derive() -> Result<()> { ] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } Ok(()) @@ -220,17 +220,17 @@ fn variant_derive() -> Result<()> { let component = Component::new( &engine, make_echo_component( - r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit))"#, + r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C"))"#, 8, ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")?; for &input in &[Foo::A(-42), Foo::B(73), Foo::C] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Sad path: case count mismatch (too few) @@ -242,7 +242,7 @@ fn variant_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case count mismatch (too many) @@ -250,29 +250,26 @@ fn variant_derive() -> Result<()> { let component = Component::new( &engine, make_echo_component( - r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit) (case "D" u32))"#, + r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C") (case "D" u32))"#, 8, ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case name mismatch let component = Component::new( &engine, - make_echo_component( - r#"(variant (case "A" s32) (case "B" u32) (case "C" unit))"#, - 8, - ), + make_echo_component(r#"(variant (case "A" s32) (case "B" u32) (case "C"))"#, 8), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case type mismatch @@ -280,14 +277,14 @@ fn variant_derive() -> Result<()> { let component = Component::new( &engine, make_echo_component( - r#"(variant (case "foo-bar-baz" s32) (case "B" s32) (case "C" unit))"#, + r#"(variant (case "foo-bar-baz" s32) (case "B" s32) (case "C"))"#, 8, ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with generics this time @@ -304,18 +301,18 @@ fn variant_derive() -> Result<()> { let component = Component::new( &engine, make_echo_component( - r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit))"#, + r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C"))"#, 8, ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance - .get_typed_func::<(Generic,), Generic, _>(&mut store, "echo")?; + .get_typed_func::<(Generic,), (Generic,), _>(&mut store, "echo")?; for &input in &[Generic::::A(-42), Generic::B(73), Generic::C] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } Ok(()) @@ -342,12 +339,12 @@ fn enum_derive() -> Result<()> { make_echo_component(r#"(enum "foo-bar-baz" "B" "C")"#, 4), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")?; for &input in &[Foo::A, Foo::B, Foo::C] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Sad path: case count mismatch (too few) @@ -359,7 +356,7 @@ fn enum_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case count mismatch (too many) @@ -371,7 +368,7 @@ fn enum_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case name mismatch @@ -380,7 +377,7 @@ fn enum_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with large enums (i.e. more than 2^8 cases) @@ -404,12 +401,12 @@ fn enum_derive() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Many,), Many, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Many,), (Many,), _>(&mut store, "echo")?; for &input in &[Many::V0, Many::V1, Many::V254, Many::V255, Many::V256] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // TODO: The following case takes forever (i.e. I gave up after 30 minutes) to compile; we'll need to profile @@ -437,9 +434,9 @@ fn flags() -> Result<()> { let component = Component::new(&engine, make_echo_component(r#"(flags)"#, 0))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Flags0,), Flags0, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Flags0,), (Flags0,), _>(&mut store, "echo")?; let output = func.call_and_post_return(&mut store, (Flags0::default(),))?; - assert_eq!(output, Flags0::default()); + assert_eq!(output, (Flags0::default(),)); // Simple 8-bit flags wasmtime::component::flags! { @@ -465,7 +462,7 @@ fn flags() -> Result<()> { make_echo_component(r#"(flags "foo-bar-baz" "B" "C")"#, 4), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")?; for n in 0..8 { let mut input = Foo::default(); @@ -481,7 +478,7 @@ fn flags() -> Result<()> { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Sad path: flag count mismatch (too few) @@ -493,7 +490,7 @@ fn flags() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: flag count mismatch (too many) @@ -505,7 +502,7 @@ fn flags() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: flag name mismatch @@ -514,7 +511,7 @@ fn flags() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with large flag count (exactly 8) @@ -560,7 +557,7 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo8Exact,), Foo8Exact, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo8Exact,), (Foo8Exact,), _>(&mut store, "echo")?; for &input in &[ Foo8Exact::F0, @@ -571,7 +568,7 @@ fn flags() -> Result<()> { ] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (more than 8) @@ -609,12 +606,12 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo16,), Foo16, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo16,), (Foo16,), _>(&mut store, "echo")?; for &input in &[Foo16::F0, Foo16::F1, Foo16::F6, Foo16::F7, Foo16::F8] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (exactly 16) @@ -657,7 +654,7 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo16Exact,), Foo16Exact, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo16Exact,), (Foo16Exact,), _>(&mut store, "echo")?; for &input in &[ Foo16Exact::F0, @@ -668,7 +665,7 @@ fn flags() -> Result<()> { ] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (more than 16) @@ -696,12 +693,12 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo32,), Foo32, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo32,), (Foo32,), _>(&mut store, "echo")?; for &input in &[Foo32::F0, Foo32::F1, Foo32::F14, Foo32::F15, Foo32::F16] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (exactly 32) @@ -744,7 +741,7 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo32Exact,), Foo32Exact, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo32Exact,), (Foo32Exact,), _>(&mut store, "echo")?; for &input in &[ Foo32Exact::F0, @@ -755,7 +752,7 @@ fn flags() -> Result<()> { ] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (more than 32) @@ -783,12 +780,12 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo64,), Foo64, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo64,), (Foo64,), _>(&mut store, "echo")?; for &input in &[Foo64::F0, Foo64::F1, Foo64::F30, Foo64::F31, Foo64::F32] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (more than 64) @@ -816,12 +813,12 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo96,), Foo96, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo96,), (Foo96,), _>(&mut store, "echo")?; for &input in &[Foo96::F0, Foo96::F1, Foo96::F62, Foo96::F63, Foo96::F64] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } Ok(()) diff --git a/tests/all/component_model/nested.rs b/tests/all/component_model/nested.rs index acc04a2048..ee40c92862 100644 --- a/tests/all/component_model/nested.rs +++ b/tests/all/component_model/nested.rs @@ -162,11 +162,12 @@ fn thread_options_through_inner() -> Result<()> { let mut linker = Linker::new(&engine); linker .root() - .func_wrap("hostfn", |param: u32| Ok(param.to_string()))?; + .func_wrap("hostfn", |param: u32| Ok((param.to_string(),)))?; let instance = linker.instantiate(&mut store, &component)?; let result = instance - .get_typed_func::<(u32,), WasmStr, _>(&mut store, "run")? - .call(&mut store, (43,))?; + .get_typed_func::<(u32,), (WasmStr,), _>(&mut store, "run")? + .call(&mut store, (43,))? + .0; assert_eq!(result.to_str(&store)?, "42"); Ok(()) } diff --git a/tests/all/component_model/post_return.rs b/tests/all/component_model/post_return.rs index b0b74f15bb..b301567c6f 100644 --- a/tests/all/component_model/post_return.rs +++ b/tests/all/component_model/post_return.rs @@ -195,21 +195,21 @@ fn post_return_all_types() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, false); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let i32 = instance.get_typed_func::<(), u32, _>(&mut store, "i32")?; - let i64 = instance.get_typed_func::<(), u64, _>(&mut store, "i64")?; - let f32 = instance.get_typed_func::<(), f32, _>(&mut store, "f32")?; - let f64 = instance.get_typed_func::<(), f64, _>(&mut store, "f64")?; + let i32 = instance.get_typed_func::<(), (u32,), _>(&mut store, "i32")?; + let i64 = instance.get_typed_func::<(), (u64,), _>(&mut store, "i64")?; + let f32 = instance.get_typed_func::<(), (f32,), _>(&mut store, "f32")?; + let f64 = instance.get_typed_func::<(), (f64,), _>(&mut store, "f64")?; - assert_eq!(i32.call(&mut store, ())?, 1); + assert_eq!(i32.call(&mut store, ())?, (1,)); i32.post_return(&mut store)?; - assert_eq!(i64.call(&mut store, ())?, 2); + assert_eq!(i64.call(&mut store, ())?, (2,)); i64.post_return(&mut store)?; - assert_eq!(f32.call(&mut store, ())?, 3.); + assert_eq!(f32.call(&mut store, ())?, (3.,)); f32.post_return(&mut store)?; - assert_eq!(f64.call(&mut store, ())?, 4.); + assert_eq!(f64.call(&mut store, ())?, (4.,)); f64.post_return(&mut store)?; Ok(()) @@ -250,8 +250,8 @@ fn post_return_string() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, false); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let get = instance.get_typed_func::<(), WasmStr, _>(&mut store, "get")?; - let s = get.call(&mut store, ())?; + let get = instance.get_typed_func::<(), (WasmStr,), _>(&mut store, "get")?; + let s = get.call(&mut store, ())?.0; assert_eq!(s.to_str(&store)?, "hello world"); get.post_return(&mut store)?; diff --git a/tests/all/component_model/strings.rs b/tests/all/component_model/strings.rs index 59997090de..2d4f233a68 100644 --- a/tests/all/component_model/strings.rs +++ b/tests/all/component_model/strings.rs @@ -175,15 +175,15 @@ fn test_roundtrip(engine: &Engine, src: &str, dst: &str) -> Result<()> { .root() .func_wrap("host", |store: StoreContextMut, arg: String| { assert_eq!(*store.data(), arg); - Ok(arg) + Ok((arg,)) })?; let instance = linker.instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(String,), String, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(String,), (String,), _>(&mut store, "echo")?; for string in STRINGS { println!("testing string {string:?}"); *store.data_mut() = string.to_string(); - let ret = func.call(&mut store, (string.to_string(),))?; + let (ret,) = func.call(&mut store, (string.to_string(),))?; assert_eq!(ret, *string); func.post_return(&mut store)?; } @@ -554,7 +554,7 @@ fn test_raw_when_encoded( (func (export "f") (param i32 i32 i32) (call $f (local.get 0) (local.get 2))) ) (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) - (func (export "f") (param (list u8)) (param u32) (canon lift (core func $m "f") + (func (export "f") (param "a" (list u8)) (param "b" u32) (canon lift (core func $m "f") (memory $libc "memory") (realloc (func $libc "realloc")))) ) diff --git a/tests/misc_testsuite/component-model/adapter.wast b/tests/misc_testsuite/component-model/adapter.wast index 69d73620de..4acf0a5a70 100644 --- a/tests/misc_testsuite/component-model/adapter.wast +++ b/tests/misc_testsuite/component-model/adapter.wast @@ -121,7 +121,7 @@ (memory (export "memory") 0) ) (core instance $m (instantiate $m)) - (func $f (param (list unit)) + (func $f (param (list (record))) (canon lift (core func $m "x") (realloc (func $m "realloc")) diff --git a/tests/misc_testsuite/component-model/fused.wast b/tests/misc_testsuite/component-model/fused.wast index 77dca93edc..53981c1bdd 100644 --- a/tests/misc_testsuite/component-model/fused.wast +++ b/tests/misc_testsuite/component-model/fused.wast @@ -94,10 +94,10 @@ (component (type $roundtrip (func ;; 20 u32 params - (param u32) (param u32) (param u32) (param u32) (param u32) - (param u32) (param u32) (param u32) (param u32) (param u32) - (param u32) (param u32) (param u32) (param u32) (param u32) - (param u32) (param u32) (param u32) (param u32) (param u32) + (param "a1" u32) (param "a2" u32) (param "a3" u32) (param "a4" u32) (param "a5" u32) + (param "a6" u32) (param "a7" u32) (param "a8" u32) (param "a9" u32) (param "a10" u32) + (param "a11" u32) (param "a12" u32) (param "a13" u32) (param "a14" u32) (param "a15" u32) + (param "a16" u32) (param "a17" u32) (param "a18" u32) (param "a19" u32) (param "a20" u32) ;; 10 u32 results (result (tuple u32 u32 u32 u32 u32 u32 u32 u32 u32 u32)) @@ -717,8 +717,8 @@ ;; simple variant translation (component - (type $a (variant (case "x" unit))) - (type $b (variant (case "y" unit))) + (type $a (variant (case "x"))) + (type $b (variant (case "y"))) (component $c1 (core module $m @@ -756,7 +756,7 @@ ;; invalid variant discriminant in a parameter (assert_trap (component - (type $a (variant (case "x" unit))) + (type $a (variant (case "x"))) (component $c1 (core module $m @@ -789,7 +789,7 @@ ;; invalid variant discriminant in a result (assert_trap (component - (type $a (variant (case "x" unit))) + (type $a (variant (case "x"))) (component $c1 (core module $m @@ -889,6 +889,12 @@ (type $d (variant (case "a" float32) (case "b" float64))) (type $e (variant (case "a" float32) (case "b" s64))) + (type $func_a (func (param "x" bool) (param "a" $a))) + (type $func_b (func (param "x" bool) (param "b" $b))) + (type $func_c (func (param "x" bool) (param "c" $c))) + (type $func_d (func (param "x" bool) (param "d" $d))) + (type $func_e (func (param "x" bool) (param "e" $d))) + (component $c1 (core module $m (func (export "a") (param i32 i32 i32) @@ -943,19 +949,19 @@ ) ) (core instance $m (instantiate $m)) - (func (export "a") (param bool) (param $a) (canon lift (core func $m "a"))) - (func (export "b") (param bool) (param $b) (canon lift (core func $m "b"))) - (func (export "c") (param bool) (param $c) (canon lift (core func $m "c"))) - (func (export "d") (param bool) (param $d) (canon lift (core func $m "d"))) - (func (export "e") (param bool) (param $e) (canon lift (core func $m "e"))) + (func (export "a") (type $func_a) (canon lift (core func $m "a"))) + (func (export "b") (type $func_b) (canon lift (core func $m "b"))) + (func (export "c") (type $func_c) (canon lift (core func $m "c"))) + (func (export "d") (type $func_d) (canon lift (core func $m "d"))) + (func (export "e") (type $func_e) (canon lift (core func $m "e"))) ) (component $c2 (import "" (instance $i - (export "a" (func (param bool) (param $a))) - (export "b" (func (param bool) (param $b))) - (export "c" (func (param bool) (param $c))) - (export "d" (func (param bool) (param $d))) - (export "e" (func (param bool) (param $e))) + (export "a" (func (type $func_a))) + (export "b" (func (type $func_b))) + (export "c" (func (type $func_c))) + (export "d" (func (type $func_d))) + (export "e" (func (type $func_e))) )) (core func $a (canon lower (func $i "a"))) @@ -1008,10 +1014,10 @@ ;; different size variants (component (type $a (variant - (case "a" unit) + (case "a") (case "b" float32) (case "c" (tuple float32 u32)) - (case "d" (tuple float32 unit u64 u8)) + (case "d" (tuple float32 (record) u64 u8)) )) (component $c1 @@ -1054,11 +1060,11 @@ ) ) (core instance $m (instantiate $m)) - (func (export "a") (param u8) (param $a) (canon lift (core func $m "a"))) + (func (export "a") (param "x" u8) (param "a" $a) (canon lift (core func $m "a"))) ) (component $c2 (import "" (instance $i - (export "a" (func (param u8) (param $a))) + (export "a" (func (param "x" u8) (param "a" $a))) )) (core func $a (canon lower (func $i "a"))) @@ -1157,9 +1163,9 @@ (export "roundtrip" (func $c2 "roundtrip")) ) -(assert_return (invoke "roundtrip" (char.const "x")) (unit.const)) -(assert_return (invoke "roundtrip" (char.const "⛳")) (unit.const)) -(assert_return (invoke "roundtrip" (char.const "🍰")) (unit.const)) +(assert_return (invoke "roundtrip" (char.const "x"))) +(assert_return (invoke "roundtrip" (char.const "⛳"))) +(assert_return (invoke "roundtrip" (char.const "🍰"))) ;; invalid chars (assert_trap diff --git a/tests/misc_testsuite/component-model/simple.wast b/tests/misc_testsuite/component-model/simple.wast index df1c05b536..df4088b94e 100644 --- a/tests/misc_testsuite/component-model/simple.wast +++ b/tests/misc_testsuite/component-model/simple.wast @@ -39,4 +39,4 @@ (func (export "") (canon lift (core func $m ""))) ) -(assert_return (invoke "") (unit.const)) +(assert_return (invoke "")) diff --git a/tests/misc_testsuite/component-model/types.wast b/tests/misc_testsuite/component-model/types.wast index 91d75c4a75..eef65f7aa8 100644 --- a/tests/misc_testsuite/component-model/types.wast +++ b/tests/misc_testsuite/component-model/types.wast @@ -1,9 +1,12 @@ (component (type string) (type (func (param string))) - (type $r (record (field "x" unit) (field "y" string))) + (type $r (record (field "x" (record)) (field "y" string))) (type $u (union $r string)) - (type $e (expected $u u32)) + (type $e (result $u (error u32))) + (type (result $u)) + (type (result (error $u))) + (type (result)) (type (func (param $e) (result (option $r)))) @@ -21,17 +24,17 @@ ;; primitives in functions (type (func - (param bool) - (param u8) - (param s8) - (param u16) - (param s16) - (param u32) - (param s32) - (param u64) - (param s64) - (param char) - (param string) + (param "a" bool) + (param "b" u8) + (param "c" s8) + (param "d" u16) + (param "e" s16) + (param "f" u32) + (param "g" s32) + (param "h" u64) + (param "i" s64) + (param "j" char) + (param "k" string) )) ;; primitives in types diff --git a/tests/misc_testsuite/threads/load-store-alignment.wast b/tests/misc_testsuite/threads/load-store-alignment.wast index 5d8fff8db7..f95a8531bf 100644 --- a/tests/misc_testsuite/threads/load-store-alignment.wast +++ b/tests/misc_testsuite/threads/load-store-alignment.wast @@ -110,17 +110,17 @@ (assert_return (invoke "64.store64 o1" (i32.const 7))) ;; misaligned stores -(assert_return (invoke "32.store8" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "32.store8" (i32.const 1))) (assert_trap (invoke "32.store16" (i32.const 1)) "misaligned memory access") (assert_trap (invoke "32.store32" (i32.const 1)) "misaligned memory access") -(assert_return (invoke "64.store8" (i32.const 1)) (i64.const 0)) +(assert_return (invoke "64.store8" (i32.const 1))) (assert_trap (invoke "64.store16" (i32.const 1)) "misaligned memory access") (assert_trap (invoke "64.store32" (i32.const 1)) "misaligned memory access") (assert_trap (invoke "64.store64" (i32.const 1)) "misaligned memory access") -(assert_return (invoke "32.store8 o1" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "32.store8 o1" (i32.const 0))) (assert_trap (invoke "32.store16 o1" (i32.const 0)) "misaligned memory access") (assert_trap (invoke "32.store32 o1" (i32.const 0)) "misaligned memory access") -(assert_return (invoke "64.store8 o1" (i32.const 0)) (i64.const 0)) +(assert_return (invoke "64.store8 o1" (i32.const 0))) (assert_trap (invoke "64.store16 o1" (i32.const 0)) "misaligned memory access") (assert_trap (invoke "64.store32 o1" (i32.const 0)) "misaligned memory access") (assert_trap (invoke "64.store64 o1" (i32.const 0)) "misaligned memory access")