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
This commit is contained in:
Alex Crichton
2022-08-17 11:17:34 -05:00
committed by GitHub
parent 3629bbbd55
commit 57dca934ad
46 changed files with 1425 additions and 1133 deletions

34
Cargo.lock generated
View File

@@ -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]]

View File

@@ -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" }

View File

@@ -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 }

View File

@@ -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 } => {

View File

@@ -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];
}
};

View File

@@ -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'] }

View File

@@ -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"

View File

@@ -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 }

View File

@@ -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();

View File

@@ -94,9 +94,9 @@ indices! {
/// Index pointing to an option type in the component model (aka a
/// `Option<T, E>`)
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<T, E>`)
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<TypeFlagsIndex, TypeFlags>,
unions: PrimaryMap<TypeUnionIndex, TypeUnion>,
options: PrimaryMap<TypeOptionIndex, TypeOption>,
expecteds: PrimaryMap<TypeExpectedIndex, TypeExpected>,
results: PrimaryMap<TypeResultIndex, TypeResult>,
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<TypeFlagsIndex> for ComponentTypes { TypeFlags => flags }
impl Index<TypeUnionIndex> for ComponentTypes { TypeUnion => unions }
impl Index<TypeOptionIndex> for ComponentTypes { TypeOption => options }
impl Index<TypeExpectedIndex> for ComponentTypes { TypeExpected => expecteds }
impl Index<TypeResultIndex> for ComponentTypes { TypeResult => results }
}
// Additionally forward anything that can index `ModuleTypes` to `ModuleTypes`
@@ -315,7 +313,7 @@ pub struct ComponentTypesBuilder {
flags: HashMap<TypeFlags, TypeFlagsIndex>,
unions: HashMap<TypeUnion, TypeUnionIndex>,
options: HashMap<TypeOption, TypeOptionIndex>,
expecteds: HashMap<TypeExpected, TypeExpectedIndex>,
results: HashMap<TypeResult, TypeResultIndex>,
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::<Box<[_]>>();
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::<Box<[_]>>();
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::<Box<[_]>>();
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<wasmparser::ComponentValType>,
err: &Option<wasmparser::ComponentValType>,
) -> 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<FlatTypes<'_>> {
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<String>, InterfaceType)]>,
/// The return value of this function.
pub result: InterfaceType,
/// The return values of this function.
pub results: Box<[(Option<String>, 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<Item = &'a CanonicalAbiInfo>,
I: IntoIterator<Item = Option<&'a CanonicalAbiInfo>>,
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>]) -> 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<Item = &'a CanonicalAbiInfo>,
I: IntoIterator<Item = Option<&'a CanonicalAbiInfo>>,
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<CanonicalAbiInfo>]) -> 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<InterfaceType>,
}
/// 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<T, E>`
pub ok: InterfaceType,
pub ok: Option<InterfaceType>,
/// The `E` in `Result<T, E>`
pub err: InterfaceType,
pub err: Option<InterfaceType>,
/// 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<TypeFlagsIndex, FlatTypesStorage>,
unions: PrimaryMap<TypeUnionIndex, FlatTypesStorage>,
options: PrimaryMap<TypeOptionIndex, FlatTypesStorage>,
expecteds: PrimaryMap<TypeExpectedIndex, FlatTypesStorage>,
results: PrimaryMap<TypeResultIndex, FlatTypesStorage>,
}
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<Item = Option<FlatTypes<'a>>>,
I: IntoIterator<Item = Option<Option<FlatTypes<'a>>>>,
{
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))]);
}
}

View File

@@ -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;

View File

@@ -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::<Vec<_>>();
let dst_tys = &self.types[adapter.lower.ty].results;
let dst_tys = dst_tys.iter().map(|(_, ty)| *ty).collect::<Vec<_>>();
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<I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
where
I: IntoIterator<Item = InterfaceType>,
I: IntoIterator<Item = Option<&'a InterfaceType>>,
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 {

View File

@@ -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 } => {

View File

@@ -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

View File

@@ -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::<Vec<_>>();
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::<Vec<_>>();
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();

View File

@@ -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<Val>)>, args: &[Val]| -> Result<Val> {
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<Val>, Option<Vec<Val>>)>,
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::<arbitrary::Result<Box<[_]>>>()?;
.collect::<arbitrary::Result<Vec<_>>>()?;
let results = result_tys
.iter()
.map(|ty| component_types::arbitrary_val(ty, input))
.collect::<arbitrary::Result<Vec<_>>>()?;
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, &params, &mut actual)
.unwrap();
log::trace!("received results {actual:?}");
assert_eq!(actual, results);
}
Ok(())

View File

@@ -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<const L: usize, const H: usize>(usize);
impl<const L: usize, const H: usize> UsizeInRange<L, H> {
@@ -74,7 +74,7 @@ impl<'a, const L: usize, const H: usize> Arbitrary<'a> for UsizeInRange<L, H> {
/// 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<T, const L: u32, const H: u32>(Vec<T>);
impl<'a, T: Arbitrary<'a>, const L: u32, const H: u32> Arbitrary<'a> for VecInRange<T, L, H> {
@@ -98,9 +98,8 @@ impl<T, const L: u32, const H: u32> Deref for VecInRange<T, L, H> {
/// 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<Type, 1, 200>),
Variant(VecInRange<Option<Type>, 1, 200>),
Enum(UsizeInRange<1, 257>),
Union(VecInRange<Type, 1, 200>),
Option(Box<Type>),
Expected { ok: Box<Type>, err: Box<Type> },
Result {
ok: Option<Box<Type>>,
err: Option<Box<Type>>,
},
// 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<Item = &'a Type>, vec: &mut Vec<CoreTyp
}
}
fn lower_variant<'a>(types: impl Iterator<Item = &'a Type>, vec: &mut Vec<CoreType>) {
fn lower_variant<'a>(types: impl Iterator<Item = Option<&'a Type>>, vec: &mut Vec<CoreType>) {
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<CoreType>) {
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<Item = &'a Type>) -> SizeA
}
fn variant_size_and_alignment<'a>(
types: impl ExactSizeIterator<Item = &'a Type>,
types: impl ExactSizeIterator<Item = Option<&'a Type>>,
) -> 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::<Box<[_]>>();
let result_lowered = result.lowered();
let results_lowered = results
.iter()
.flat_map(|ty| ty.lowered())
.collect::<Box<[_]>>();
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::<TokenStream>();
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::<TokenStream>();
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::<TokenStream>();
@@ -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<Type, 0, MAX_ARITY>,
/// The result types of the the function
pub results: VecInRange<Type, 0, MAX_ARITY>,
/// 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<Self> {
Ok(Self {
params: input
.arbitrary_iter()?
.take(MAX_ARITY)
.collect::<arbitrary::Result<Box<[_]>>>()?,
result: input.arbitrary()?,
encoding1: input.arbitrary()?,
encoding2: input.arbitrary()?,
})
}
}
#[derive(Copy, Clone, Debug, Arbitrary)]
pub enum StringEncoding {
Utf8,

View File

@@ -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<P, R> {
@@ -13,8 +13,8 @@ pub trait TypedFuncExt<P, R> {
impl<P, R> TypedFuncExt<P, R> for TypedFunc<P, R>
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<R> {
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<Val>;
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<Val> {
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(())
}
}

View File

@@ -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 }

View File

@@ -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"

View File

@@ -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<Params, Return, S>(&self, store: S) -> Result<TypedFunc<Params, Return>>
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<TypedFunc<Params, Return>>
where
Params: ComponentParams + Lower,
Return: Lift,
Params: ComponentNamedList + Lower,
Return: ComponentNamedList + Lift,
{
self.typecheck::<Params, Return>(store)?;
unsafe { Ok(TypedFunc::new_unchecked(*self)) }
@@ -240,15 +240,16 @@ impl Func {
fn typecheck<Params, Return>(&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<Val> {
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<Vec<_>>>()?;
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, &param_abi, &params, 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<ValRaw>; 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, &param_abi, &param_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<Val> {
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(())
}
}

View File

@@ -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<F, P, R>(func: F, entrypoint: VMLoweringCallee) -> Arc<HostFunc>
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<Val> + Send + Sync + 'static,
>(
pub(crate) fn new_dynamic<T, F>(
func: F,
index: TypeFuncIndex,
types: &Arc<ComponentTypes>,
) -> Arc<HostFunc> {
) -> Arc<HostFunc>
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<P, R>(ty: TypeFuncIndex, types: &Arc<ComponentTypes>) -> 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<T, F, $($args,)* R> IntoComponentFunc<T, ($($args,)*), R> for F
where
F: Fn($($args),*) -> Result<R> + 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<T, F, $($args,)* R> IntoComponentFunc<T, (StoreContextMut<'_, T>, $($args,)*), R> for F
where
F: Fn(StoreContextMut<'_, T>, $($args),*) -> Result<R> + 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<T, F>(
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<T, F>(
closure: F,
) -> Result<()>
where
F: FnOnce(StoreContextMut<'_, T>, &[Val]) -> Result<Val>,
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<ValRaw>]>(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<F> {
@@ -471,10 +478,7 @@ struct DynamicContext<F> {
types: Types,
}
extern "C" fn dynamic_entrypoint<
T,
F: Fn(StoreContextMut<'_, T>, &[Val]) -> Result<Val> + Send + Sync + 'static,
>(
extern "C" fn dynamic_entrypoint<T, F>(
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<F>;
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),
)
})
}

View File

@@ -60,7 +60,7 @@ impl<Params, Return> Clone for TypedFunc<Params, Return> {
impl<Params, Return> TypedFunc<Params, Return>
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<String>, 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<CanonicalAbiInfo>];
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<fn(&InterfaceType, &ComponentTypes) -> 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<<u32 as ComponentType>::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<T> ComponentVariant for Option<T>
where
T: ComponentType,
{
const CASES: &'static [CanonicalAbiInfo] = &[<() as ComponentType>::ABI, T::ABI];
const CASES: &'static [Option<CanonicalAbiInfo>] = &[None, Some(T::ABI)];
}
unsafe impl<T> Lower for Option<T>
@@ -1667,17 +1671,25 @@ where
{
type Lower = ResultLower<T::Lower, E::Lower>;
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<CanonicalAbiInfo>] = &[Some(T::ABI), Some(E::ABI)];
}
unsafe impl<T, E> Lower for Result<T, E>
@@ -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<String>, InterfaceType)],
fn typecheck_named_list(
names: &[(Option<String>, 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",

View File

@@ -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<TypedFunc<Params, Results>>
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<Params, Results>(&mut self, name: &str) -> Result<TypedFunc<Params, Results>>
where
Params: ComponentParams + Lower,
Results: Lift,
Params: ComponentNamedList + Lower,
Results: ComponentNamedList + Lift,
{
let func = self
.func(name)

View File

@@ -238,7 +238,7 @@ impl<T> 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<Val> + Send + Sync + 'static,
F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static,
>(
&mut self,
component: &Component,

View File

@@ -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};

View File

@@ -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<Type>,
}
/// A `variant` interface type
@@ -121,7 +121,7 @@ pub struct Variant(Handle<TypeVariantIndex>);
impl Variant {
/// Instantiate this type with the specified case `name` and `value`.
pub fn new_val(&self, name: &str, value: Val) -> Result<Val> {
pub fn new_val(&self, name: &str, value: Option<Val>) -> Result<Val> {
Ok(Val::Variant(values::Variant::new(self, name, value)?))
}
@@ -129,7 +129,7 @@ impl Variant {
pub fn cases(&self) -> impl ExactSizeIterator<Item = Case> {
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<TypeOptionIndex>);
pub struct OptionType(Handle<TypeOptionIndex>);
impl Option {
impl OptionType {
/// Instantiate this type with the specified `value`.
pub fn new_val(&self, value: std::option::Option<Val>) -> Result<Val> {
Ok(Val::Option(values::Option::new(self, value)?))
pub fn new_val(&self, value: Option<Val>) -> Result<Val> {
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<TypeExpectedIndex>);
pub struct ResultType(Handle<TypeResultIndex>);
impl Expected {
impl ResultType {
/// Instantiate this type with the specified `value`.
pub fn new_val(&self, value: Result<Val, Val>) -> Result<Val> {
Ok(Val::Expected(values::Expected::new(self, value)?))
pub fn new_val(&self, value: Result<Option<Val>, Option<Val>>) -> Result<Val> {
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<Type> {
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<Type> {
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<ComponentTypes>) -> 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(),
}
}

View File

@@ -180,12 +180,12 @@ impl fmt::Debug for Tuple {
pub struct Variant {
ty: types::Variant,
discriminant: u32,
value: Box<Val>,
value: Option<Box<Val>>,
}
impl Variant {
/// Instantiate the specified type with the specified case `name` and `value`.
pub fn new(ty: &types::Variant, name: &str, value: Val) -> Result<Self> {
pub fn new(ty: &types::Variant, name: &str, value: Option<Val>) -> Result<Self> {
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<Val>,
value: Option<Box<Val>>,
}
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<Val>,
value: Option<Box<Val>>,
}
impl Option {
impl OptionVal {
/// Instantiate the specified type with the specified `value`.
pub fn new(ty: &types::Option, value: std::option::Option<Val>) -> Result<Self> {
pub fn new(ty: &types::OptionType, value: Option<Val>) -> Result<Self> {
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<Val>,
value: Option<Box<Val>>,
}
impl Expected {
impl ResultVal {
/// Instantiate the specified type with the specified `value`.
pub fn new(ty: &types::Expected, value: Result<Val, Val>) -> Result<Self> {
pub fn new(ty: &types::ResultType, value: Result<Option<Val>, Option<Val>>) -> Result<Self> {
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>, 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<str>),
/// 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<Val> {
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<Val> {
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<ValRaw>>,
) -> 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<T>(
&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<Item = Type>,
mut types: impl ExactSizeIterator<Item = Option<Type>>,
mem: &Memory,
bytes: &[u8],
) -> Result<(u32, Val)> {
) -> Result<(u32, Option<Box<Val>>)> {
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<Item = Type>,
mut types: impl ExactSizeIterator<Item = Option<Type>>,
store: &StoreOpaque,
options: &Options,
src: &mut std::slice::Iter<'_, ValRaw>,
) -> Result<(u32, Val)> {
) -> Result<(u32, Option<Box<Val>>)> {
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);
}

View File

@@ -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]

View File

@@ -9,7 +9,6 @@ pub use wasmtime::component::*;
pub fn val(v: &WastVal<'_>, ty: &Type) -> Result<Val> {
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<Val> {
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<Val> {
}
_ => 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<Val> {
})
}
fn payload_val(name: &str, v: Option<&WastVal<'_>>, ty: Option<&Type>) -> Result<Option<Val>> {
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<T>(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}`")

View File

@@ -48,11 +48,11 @@ pub fn link_spectest<T>(linker: &mut Linker<T>, store: &mut Store<T>) -> Result<
#[cfg(feature = "component-model")]
pub fn link_component_spectest<T>(linker: &mut component::Linker<T>) -> 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,

View File

@@ -47,7 +47,7 @@ impl<T> Outcome<T> {
enum Results {
Core(Vec<Val>),
#[cfg(feature = "component-model")]
Component(component::Val),
Component(Vec<component::Val>),
}
enum InstanceKind {
@@ -148,7 +148,7 @@ impl<T> WastContext<T> {
#[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<T> WastContext<T> {
})
.collect::<Result<Vec<_>>>()?;
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<T> WastContext<T> {
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<T> WastContext<T> {
}
}
#[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(())

View File

@@ -75,7 +75,7 @@ mod component {
let Declarations {
types,
params,
result,
results,
import_and_export,
encoding1,
encoding2,
@@ -92,16 +92,22 @@ mod component {
})
.collect::<TokenStream>();
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::<TokenStream>();
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,

View File

@@ -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 <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.16.0"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasm-mutate]]
who = "Alex Crichton <alex@alexcrichton.com>"
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 <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.2.7"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasm-smith]]
who = "Alex Crichton <alex@alexcrichton.com>"
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 <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.11.4"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasmparser]]
who = "Alex Crichton <alex@alexcrichton.com>"
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 <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.89.0"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasmparser]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.89.1"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasmprinter]]
who = "Alex Crichton <alex@alexcrichton.com>"
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 <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.2.39"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wast]]
who = "Alex Crichton <alex@alexcrichton.com>"
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 <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "46.0.0"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wat]]
who = "Alex Crichton <alex@alexcrichton.com>"
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 <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "1.0.48"
notes = "The Bytecode Alliance is the author of this crate."

View File

@@ -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::<Box<[component::Type]>>();
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(())
}

View File

@@ -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<u16>, _>(&store).is_err());
assert!(ret_list_u8.typed::<(), WasmList<i8>, _>(&store).is_err());
assert!(ret_list_u8.typed::<(), WasmList<u8>, _>(&store).is_ok());
assert!(ret_string.typed::<(), (WasmStr,), _>(&store).is_ok());
assert!(ret_list_u8
.typed::<(), (WasmList<u16>,), _>(&store)
.is_err());
assert!(ret_list_u8.typed::<(), (WasmList<i8>,), _>(&store).is_err());
assert!(ret_list_u8.typed::<(), (WasmList<u8>,), _>(&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<u8>, _>(&mut store, "str-to-list8")?;
instance.get_typed_func::<(&str,), (WasmList<u8>,), _>(&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<u16>, _>(&mut store, "str-to-list16")?;
instance.get_typed_func::<(&str,), (WasmList<u16>,), _>(&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::<Vec<_>>();
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::<Result<Vec<_>>>()?, 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::<Result<Vec<_>>>()?, 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<u8>, _>(&mut store, "ret-list-u8")?;
.get_typed_func::<(), (WasmList<u8>,), _>(&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<u8>, _>(&mut store, "to-option-u8")?;
assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, None);
instance.get_typed_func::<(u32, u32), (Option<u8>,), _>(&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<u32>, _>(&mut store, "to-option-u32")?;
assert_eq!(to_option_u32.call(&mut store, (0, 0))?, None);
instance.get_typed_func::<(u32, u32), (Option<u32>,), _>(&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<WasmStr>, _>(&mut store, "to-option-string")?;
let ret = to_option_string.call(&mut store, (0, ""))?;
.get_typed_func::<(u32, &str), (Option<WasmStr>,), _>(&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<i16, f32>, _>(&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<i16, f32>,), _>(&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<u32>, _>(&mut store, "list-u32-ret")?
.get_typed_func::<(), (WasmList<u32>,), _>(&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<u8>, _>(&mut store, "list-u8")?
.call_and_post_return(&mut store, ())?;
.get_typed_func::<(), (WasmList<u8>,), _>(&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<i8>, _>(&mut store, "list-i8")?
.call_and_post_return(&mut store, ())?;
.get_typed_func::<(), (WasmList<i8>,), _>(&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<u16>, _>(&mut store, "list-u16")?
.call_and_post_return(&mut store, ())?;
.get_typed_func::<(), (WasmList<u16>,), _>(&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<i16>, _>(&mut store, "list-i16")?
.call_and_post_return(&mut store, ())?;
.get_typed_func::<(), (WasmList<i16>,), _>(&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<u32>, _>(&mut store, "list-u32")?
.call_and_post_return(&mut store, ())?;
.get_typed_func::<(), (WasmList<u32>,), _>(&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<i32>, _>(&mut store, "list-i32")?
.call_and_post_return(&mut store, ())?;
.get_typed_func::<(), (WasmList<i32>,), _>(&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<u64>, _>(&mut store, "list-u64")?
.call_and_post_return(&mut store, ())?;
.get_typed_func::<(), (WasmList<u64>,), _>(&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<i64>, _>(&mut store, "list-i64")?
.call_and_post_return(&mut store, ())?;
.get_typed_func::<(), (WasmList<i64>,), _>(&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(())
}

View File

@@ -147,11 +147,11 @@ fn simple() -> Result<()> {
linker.root().func_new(
&component,
"",
|mut store: StoreContextMut<'_, Option<String>>, args| {
|mut store: StoreContextMut<'_, Option<String>>, 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<String> {
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<u32> {
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<u32> {
-> Result<(u32,)> {
assert_eq!(arg.0.to_str(&cx).unwrap(), "abc");
Ok(3)
Ok((3,))
},
)?;
linker
.root()
.func_wrap("f3", |arg: u32| -> Result<String> {
.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<String> {
-> 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<String> {
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(())

View File

@@ -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<i32, u32>,), Generic<i32, u32>, _>(&mut store, "echo")?
.get_typed_func::<(Generic<i32, u32>,), (Generic<i32, u32>,), _>(&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<i32, u32, i32>,), Generic<i32, u32, i32>, _>(
let func = instance.get_typed_func::<(Generic<i32, u32, i32>,), (Generic<i32, u32, i32>,), _>(
&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<i32, u32>,), Generic<i32, u32>, _>(&mut store, "echo")?;
.get_typed_func::<(Generic<i32, u32>,), (Generic<i32, u32>,), _>(&mut store, "echo")?;
for &input in &[Generic::<i32, u32>::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(())

View File

@@ -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(())
}

View File

@@ -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)?;

View File

@@ -175,15 +175,15 @@ fn test_roundtrip(engine: &Engine, src: &str, dst: &str) -> Result<()> {
.root()
.func_wrap("host", |store: StoreContextMut<String>, 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"))))
)

View File

@@ -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"))

View File

@@ -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

View File

@@ -39,4 +39,4 @@
(func (export "") (canon lift (core func $m "")))
)
(assert_return (invoke "") (unit.const))
(assert_return (invoke ""))

View File

@@ -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

View File

@@ -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")