support dynamic function calls in component model (#4442)

* support dynamic function calls in component model

This addresses #4310, introducing a new `component::values::Val` type for
representing component values dynamically, as well as `component::types::Type`
for representing the corresponding interface types. It also adds a `call` method
to `component::func::Func`, which takes a slice of `Val`s as parameters and
returns a `Result<Val>` representing the result.

Note that I've moved `post_return` and `call_raw` from `TypedFunc` to `Func`
since there was nothing specific to `TypedFunc` about them, and I wanted to
reuse them.  The code in both is unchanged beyond the trivial tweaks to make
them fit in their new home.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* order variants and match cases more consistently

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* implement lift for String, Box<str>, etc.

This also removes the redundant `store` parameter from `Type::load`.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* implement code review feedback

This fixes a few issues:

- Bad offset calculation when lowering
- Missing variant padding
- Style issues regarding `types::Handle`
- Missed opportunities to reuse `Lift` and `Lower` impls

It also adds forwarding `Lift` impls for `Box<[T]>`, `Vec<T>`, etc.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* move `new_*` methods to specific `types` structs

Per review feedback, I've moved `Type::new_record` to `Record::new_val` and
added a `Type::unwrap_record` method; likewise for the other kinds of types.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* make tuple, option, and expected type comparisons recursive

These types should compare as equal across component boundaries as long as their
type parameters are equal.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* improve error diagnostic in `Type::check`

We now distinguish between more failure cases to provide an informative error
message.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* address review feedback

- Remove `WasmStr::to_str_from_memory` and `WasmList::get_from_memory`
- add `try_new` methods to various `values` types
- avoid using `ExactSizeIterator::len` where we can't trust it
- fix over-constrained bounds on forwarded `ComponentType` impls

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* rearrange code per review feedback

- Move functions from `types` to `values` module so we can make certain struct fields private
- Rename `try_new` to just `new`

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* remove special-case equality test for tuples, options, and expecteds

Instead, I've added a FIXME comment and will open an issue to do recursive
structural equality testing.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>
This commit is contained in:
Joel Dice
2022-07-25 12:38:48 -06:00
committed by GitHub
parent ee7e4f4c6b
commit 7c67e620c4
16 changed files with 2796 additions and 453 deletions

View File

@@ -1,7 +1,10 @@
use anyhow::Result;
use std::fmt::Write;
use std::iter;
use wasmtime::component::{Component, ComponentParams, Lift, Lower, TypedFunc};
use wasmtime::{AsContextMut, Config, Engine};
mod dynamic;
mod func;
mod import;
mod instance;
@@ -148,3 +151,128 @@ fn components_importing_modules() -> Result<()> {
Ok(())
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum Type {
S8,
U8,
S16,
U16,
I32,
I64,
F32,
F64,
}
impl Type {
fn store(&self) -> &'static str {
match self {
Self::S8 | Self::U8 => "store8",
Self::S16 | Self::U16 => "store16",
Self::I32 | Self::F32 | Self::I64 | Self::F64 => "store",
}
}
fn primitive(&self) -> &'static str {
match self {
Self::S8 | Self::U8 | Self::S16 | Self::U16 | Self::I32 => "i32",
Self::I64 => "i64",
Self::F32 => "f32",
Self::F64 => "f64",
}
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
struct Param(Type, Option<usize>);
fn make_echo_component(type_definition: &str, type_size: u32) -> String {
let mut offset = 0;
make_echo_component_with_params(
type_definition,
&iter::repeat(Type::I32)
.map(|ty| {
let param = Param(ty, Some(offset));
offset += 4;
param
})
.take(usize::try_from(type_size).unwrap() / 4)
.collect::<Vec<_>>(),
)
}
fn make_echo_component_with_params(type_definition: &str, params: &[Param]) -> String {
let func = if params.len() == 1 || params.len() > 16 {
let primitive = if params.len() == 1 {
params[0].0.primitive()
} else {
"i32"
};
format!(
r#"
(func (export "echo") (param {primitive}) (result {primitive})
local.get 0
)"#,
)
} else {
let mut param_string = String::new();
let mut store = String::new();
let mut size = 8;
for (index, Param(ty, offset)) in params.iter().enumerate() {
let primitive = ty.primitive();
write!(&mut param_string, " {primitive}").unwrap();
if let Some(offset) = offset {
write!(
&mut store,
"({primitive}.{} offset={offset} (local.get $base) (local.get {index}))",
ty.store(),
)
.unwrap();
size = size.max(offset + 8);
}
}
format!(
r#"
(func (export "echo") (param{param_string}) (result i32)
(local $base i32)
(local.set $base
(call $realloc
(i32.const 0)
(i32.const 0)
(i32.const 4)
(i32.const {size})))
{store}
local.get $base
)"#
)
};
format!(
r#"
(component
(core module $m
{func}
(memory (export "memory") 1)
{REALLOC_AND_FREE}
)
(core instance $i (instantiate $m))
(type $Foo {type_definition})
(func (export "echo") (param $Foo) (result $Foo)
(canon lift
(core func $i "echo")
(memory $i "memory")
(realloc (func $i "realloc"))
)
)
)"#
)
}

View File

@@ -0,0 +1,511 @@
use super::{make_echo_component, make_echo_component_with_params, Param, Type};
use anyhow::Result;
use wasmtime::component::{self, Component, Func, Linker, Val};
use wasmtime::{AsContextMut, Store};
trait FuncExt {
fn call_and_post_return(&self, store: impl AsContextMut, args: &[Val]) -> Result<Val>;
}
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)?;
self.post_return(&mut store)?;
Ok(result)
}
}
#[test]
fn primitives() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
for (input, ty, param) in [
(Val::Bool(true), "bool", Param(Type::U8, Some(0))),
(Val::S8(-42), "s8", Param(Type::S8, Some(0))),
(Val::U8(42), "u8", Param(Type::U8, Some(0))),
(Val::S16(-4242), "s16", Param(Type::S16, Some(0))),
(Val::U16(4242), "u16", Param(Type::U16, Some(0))),
(Val::S32(-314159265), "s32", Param(Type::I32, Some(0))),
(Val::U32(314159265), "u32", Param(Type::I32, Some(0))),
(Val::S64(-31415926535897), "s64", Param(Type::I64, Some(0))),
(Val::U64(31415926535897), "u64", Param(Type::I64, Some(0))),
(
Val::Float32(3.14159265_f32.to_bits()),
"float32",
Param(Type::F32, Some(0)),
),
(
Val::Float64(3.14159265_f64.to_bits()),
"float64",
Param(Type::F64, Some(0)),
),
(Val::Char('🦀'), "char", Param(Type::I32, Some(0))),
] {
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()])?;
assert_eq!(input, output);
}
// Sad path: type mismatch
let component = Component::new(
&engine,
make_echo_component_with_params("float64", &[Param(Type::F64, Some(0))]),
)?;
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)])
.unwrap_err();
assert!(err.to_string().contains("type mismatch"), "{err}");
// Sad path: arity mismatch (too many)
let err = func
.call_and_post_return(
&mut store,
&[
Val::Float64(3.14159265_f64.to_bits()),
Val::Float64(3.14159265_f64.to_bits()),
],
)
.unwrap_err();
assert!(
err.to_string().contains("expected 1 argument(s), got 2"),
"{err}"
);
// Sad path: arity mismatch (too few)
let err = func.call_and_post_return(&mut store, &[]).unwrap_err();
assert!(
err.to_string().contains("expected 1 argument(s), got 0"),
"{err}"
);
Ok(())
}
#[test]
fn strings() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(&engine, make_echo_component("string", 8))?;
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);
Ok(())
}
#[test]
fn lists() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(&engine, make_echo_component("(list u32)", 8))?;
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 input = ty.unwrap_list().new_val(Box::new([
Val::U32(32343),
Val::U32(79023439),
Val::U32(2084037802),
]))?;
let output = func.call_and_post_return(&mut store, &[input.clone()])?;
assert_eq!(input, output);
// Sad path: type mismatch
let err = ty
.unwrap_list()
.new_val(Box::new([
Val::U32(32343),
Val::U32(79023439),
Val::Float32(3.14159265_f32.to_bits()),
]))
.unwrap_err();
assert!(err.to_string().contains("type mismatch"), "{err}");
Ok(())
}
#[test]
fn records() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"(record (field "A" u32) (field "B" float64) (field "C" (record (field "D" bool) (field "E" u32))))"#,
&[
Param(Type::I32, Some(0)),
Param(Type::F64, Some(8)),
Param(Type::U8, Some(16)),
Param(Type::I32, Some(20)),
],
),
)?;
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 inner_type = &ty.unwrap_record().fields().nth(2).unwrap().ty;
let input = ty.unwrap_record().new_val([
("A", Val::U32(32343)),
("B", Val::Float64(3.14159265_f64.to_bits())),
(
"C",
inner_type
.unwrap_record()
.new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
),
])?;
let output = func.call_and_post_return(&mut store, &[input.clone()])?;
assert_eq!(input, output);
// Sad path: type mismatch
let err = ty
.unwrap_record()
.new_val([
("A", Val::S32(32343)),
("B", Val::Float64(3.14159265_f64.to_bits())),
(
"C",
inner_type
.unwrap_record()
.new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
),
])
.unwrap_err();
assert!(err.to_string().contains("type mismatch"), "{err}");
// Sad path: too many fields
let err = ty
.unwrap_record()
.new_val([
("A", Val::U32(32343)),
("B", Val::Float64(3.14159265_f64.to_bits())),
(
"C",
inner_type
.unwrap_record()
.new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
),
("F", Val::Unit),
])
.unwrap_err();
assert!(
err.to_string().contains("expected 3 value(s); got 4"),
"{err}"
);
// Sad path: too few fields
let err = ty
.unwrap_record()
.new_val([
("A", Val::U32(32343)),
("B", Val::Float64(3.14159265_f64.to_bits())),
])
.unwrap_err();
assert!(
err.to_string().contains("expected 3 value(s); got 2"),
"{err}"
);
Ok(())
}
#[test]
fn variants() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(
&engine,
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, None),
],
),
)?;
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 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()])?;
assert_eq!(input, output);
// Do it again, this time using case "C"
let component = Component::new(
&engine,
dbg!(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 input = ty.unwrap_variant().new_val(
"C",
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()])?;
assert_eq!(input, output);
// Sad path: type mismatch
let err = ty
.unwrap_variant()
.new_val("B", Val::U64(314159265))
.unwrap_err();
assert!(err.to_string().contains("type mismatch"), "{err}");
// Sad path: unknown case
let err = ty
.unwrap_variant()
.new_val("D", Val::U64(314159265))
.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
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"
(record
(field "A" (variant
(case "A" u32)
(case "B" float64)
(case "C" (record (field "D" bool) (field "E" u32)))))
(field "B" u32))"#,
&[
Param(Type::U8, Some(0)),
Param(Type::I64, Some(8)),
Param(Type::I32, None),
Param(Type::I32, Some(16)),
],
),
)?;
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 a_type = &ty.unwrap_record().fields().nth(0).unwrap().ty;
let input = ty.unwrap_record().new_val([
(
"A",
a_type.unwrap_variant().new_val("A", Val::U32(314159265))?,
),
("B", Val::U32(628318530)),
])?;
let output = func.call_and_post_return(&mut store, &[input.clone()])?;
assert_eq!(input, output);
Ok(())
}
#[test]
fn flags() -> Result<()> {
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"(flags "A" "B" "C" "D" "E")"#,
&[Param(Type::U8, Some(0))],
),
)?;
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 input = ty.unwrap_flags().new_val(&["B", "D"])?;
let output = func.call_and_post_return(&mut store, &[input.clone()])?;
assert_eq!(input, output);
// Sad path: unknown flags
let err = ty.unwrap_flags().new_val(&["B", "D", "F"]).unwrap_err();
assert!(err.to_string().contains("unknown flag"), "{err}");
Ok(())
}
#[test]
fn everything() -> Result<()> {
// This serves to test both nested types and storing parameters on the heap (i.e. exceeding `MAX_STACK_PARAMS`)
let engine = super::engine();
let mut store = Store::new(&engine, ());
let component = Component::new(
&engine,
make_echo_component_with_params(
r#"
(record
(field "A" u32)
(field "B" (enum "1" "2"))
(field "C" (record (field "D" bool) (field "E" u32)))
(field "F" (list (flags "G" "H" "I")))
(field "J" (variant
(case "K" u32)
(case "L" float64)
(case "M" (record (field "N" bool) (field "O" u32)))))
(field "P" s8)
(field "Q" s16)
(field "R" s32)
(field "S" s64)
(field "T" float32)
(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))
)"#,
&[
Param(Type::I32, Some(0)),
Param(Type::U8, Some(4)),
Param(Type::U8, Some(5)),
Param(Type::I32, Some(8)),
Param(Type::I32, Some(12)),
Param(Type::I32, Some(16)),
Param(Type::U8, Some(20)),
Param(Type::I64, Some(28)),
Param(Type::I32, Some(32)),
Param(Type::S8, Some(36)),
Param(Type::S16, Some(38)),
Param(Type::I32, Some(40)),
Param(Type::I64, Some(48)),
Param(Type::F32, Some(56)),
Param(Type::F64, Some(64)),
Param(Type::I32, Some(72)),
Param(Type::I32, Some(76)),
Param(Type::I32, Some(80)),
Param(Type::I32, Some(84)),
Param(Type::I32, Some(88)),
Param(Type::I64, Some(96)),
Param(Type::U8, Some(104)),
Param(Type::I32, Some(108)),
Param(Type::U8, Some(112)),
Param(Type::I32, Some(116)),
Param(Type::I32, Some(120)),
],
),
)?;
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 types = ty
.unwrap_record()
.fields()
.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],
);
let f_element_type = &f_type.unwrap_list().ty();
let input = ty.unwrap_record().new_val([
("A", Val::U32(32343)),
("B", b_type.unwrap_enum().new_val("2")?),
(
"C",
c_type
.unwrap_record()
.new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
),
(
"F",
f_type.unwrap_list().new_val(Box::new([f_element_type
.unwrap_flags()
.new_val(&["G", "I"])?]))?,
),
(
"J",
j_type
.unwrap_variant()
.new_val("L", Val::Float64(3.14159265_f64.to_bits()))?,
),
("P", Val::S8(42)),
("Q", Val::S16(4242)),
("R", Val::S32(42424242)),
("S", Val::S64(424242424242424242)),
("T", Val::Float32(3.14159265_f32.to_bits())),
("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
.unwrap_tuple()
.new_val(Box::new([Val::U32(42), Val::U32(24)]))?,
),
(
"Z",
z_type
.unwrap_union()
.new_val(1, Val::Float64(3.14159265_f64.to_bits()))?,
),
(
"AA",
aa_type.unwrap_option().new_val(Some(Val::U32(314159265)))?,
),
(
"BB",
bb_type
.unwrap_expected()
.new_val(Ok(Val::String(Box::from("no problem"))))?,
),
])?;
let output = func.call_and_post_return(&mut store, &[input.clone()])?;
assert_eq!(input, output);
Ok(())
}

View File

@@ -1,75 +1,9 @@
use super::TypedFuncExt;
use super::{make_echo_component, TypedFuncExt};
use anyhow::Result;
use component_macro_test::{add_variants, flags_test};
use std::fmt::Write;
use wasmtime::component::{Component, ComponentType, Lift, Linker, Lower};
use wasmtime::Store;
fn make_echo_component(type_definition: &str, type_size: u32) -> String {
if type_size <= 4 {
format!(
r#"
(component
(core module $m
(func (export "echo") (param i32) (result i32)
local.get 0
)
(memory (export "memory") 1)
)
(core instance $i (instantiate $m))
{}
(func (export "echo") (param $Foo) (result $Foo)
(canon lift (core func $i "echo") (memory $i "memory"))
)
)"#,
type_definition
)
} else {
let mut params = String::new();
let mut store = String::new();
for index in 0..(type_size / 4) {
params.push_str(" i32");
write!(
&mut store,
"(i32.store offset={} (local.get $base) (local.get {}))",
index * 4,
index,
)
.unwrap();
}
format!(
r#"
(component
(core module $m
(func (export "echo") (param{}) (result i32)
(local $base i32)
(local.set $base (i32.const 0))
{}
local.get $base
)
(memory (export "memory") 1)
)
(core instance $i (instantiate $m))
{}
(func (export "echo") (param $Foo) (result $Foo)
(canon lift (core func $i "echo") (memory $i "memory"))
)
)"#,
params, store, type_definition
)
}
}
#[test]
fn record_derive() -> Result<()> {
#[derive(ComponentType, Lift, Lower, PartialEq, Eq, Debug, Copy, Clone)]
@@ -87,10 +21,7 @@ fn record_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (record (field "foo-bar-baz" s32) (field "b" u32)))"#,
8,
),
make_echo_component(r#"(record (field "foo-bar-baz" s32) (field "b" u32))"#, 8),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -105,7 +36,7 @@ fn record_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (record (field "foo-bar-baz" s32)))"#, 4),
make_echo_component(r#"(record (field "foo-bar-baz" s32))"#, 4),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -118,7 +49,7 @@ fn record_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (record (field "foo-bar-baz" s32) (field "b" u32) (field "c" u32)))"#,
r#"(record (field "foo-bar-baz" s32) (field "b" u32) (field "c" u32))"#,
12,
),
)?;
@@ -132,7 +63,7 @@ fn record_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (record (field "a" s32) (field "b" u32)))"#, 8),
make_echo_component(r#"(record (field "a" s32) (field "b" u32))"#, 8),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -144,10 +75,7 @@ fn record_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (record (field "foo-bar-baz" s32) (field "b" s32)))"#,
8,
),
make_echo_component(r#"(record (field "foo-bar-baz" s32) (field "b" s32))"#, 8),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -172,10 +100,7 @@ fn record_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (record (field "foo-bar-baz" s32) (field "b" u32)))"#,
8,
),
make_echo_component(r#"(record (field "foo-bar-baz" s32) (field "b" u32))"#, 8),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -203,10 +128,7 @@ fn union_derive() -> Result<()> {
// Happy path: component type matches case count and types
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (union s32 u32 s32))"#, 8),
)?;
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")?;
@@ -218,10 +140,7 @@ fn union_derive() -> Result<()> {
// Sad path: case count mismatch (too few)
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (union s32 u32))"#, 8),
)?;
let component = Component::new(&engine, make_echo_component("(union s32 u32)", 8))?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
assert!(instance
@@ -232,7 +151,7 @@ fn union_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (union s32 u32 s32 s32))"#, 8),
make_echo_component(r#"(union s32 u32 s32 s32)"#, 8),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -246,10 +165,7 @@ fn union_derive() -> Result<()> {
// Sad path: case type mismatch
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (union s32 s32 s32))"#, 8),
)?;
let component = Component::new(&engine, make_echo_component("(union s32 s32 s32)", 8))?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
assert!(instance
@@ -266,10 +182,7 @@ fn union_derive() -> Result<()> {
C(C),
}
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (union s32 u32 s32))"#, 8),
)?;
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>, _>(
&mut store, "echo",
@@ -307,7 +220,7 @@ fn variant_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit)))"#,
r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit))"#,
8,
),
)?;
@@ -324,10 +237,7 @@ fn variant_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (variant (case "foo-bar-baz" s32) (case "B" u32)))"#,
8,
),
make_echo_component(r#"(variant (case "foo-bar-baz" s32) (case "B" u32))"#, 8),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -340,7 +250,7 @@ fn variant_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (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" unit) (case "D" u32))"#,
8,
),
)?;
@@ -355,7 +265,7 @@ fn variant_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (variant (case "A" s32) (case "B" u32) (case "C" unit)))"#,
r#"(variant (case "A" s32) (case "B" u32) (case "C" unit))"#,
8,
),
)?;
@@ -370,7 +280,7 @@ fn variant_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (variant (case "foo-bar-baz" s32) (case "B" s32) (case "C" unit)))"#,
r#"(variant (case "foo-bar-baz" s32) (case "B" s32) (case "C" unit))"#,
8,
),
)?;
@@ -394,7 +304,7 @@ fn variant_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(
r#"(type $Foo (variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit)))"#,
r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit))"#,
8,
),
)?;
@@ -429,7 +339,7 @@ fn enum_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (enum "foo-bar-baz" "B" "C"))"#, 4),
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")?;
@@ -444,7 +354,7 @@ fn enum_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (enum "foo-bar-baz" "B"))"#, 4),
make_echo_component(r#"(enum "foo-bar-baz" "B")"#, 4),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -456,7 +366,7 @@ fn enum_derive() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (enum "foo-bar-baz" "B" "C" "D"))"#, 4),
make_echo_component(r#"(enum "foo-bar-baz" "B" "C" "D")"#, 4),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -466,10 +376,7 @@ fn enum_derive() -> Result<()> {
// Sad path: case name mismatch
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (enum "A" "B" "C"))"#, 4),
)?;
let component = Component::new(&engine, make_echo_component(r#"(enum "A" "B" "C")"#, 4))?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
assert!(instance
@@ -487,7 +394,7 @@ fn enum_derive() -> Result<()> {
&engine,
make_echo_component(
&format!(
r#"(type $Foo (enum {}))"#,
"(enum {})",
(0..257)
.map(|index| format!(r#""V{}""#, index))
.collect::<Vec<_>>()
@@ -542,7 +449,7 @@ fn flags() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (flags "foo-bar-baz" "B" "C"))"#, 4),
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")?;
@@ -568,7 +475,7 @@ fn flags() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (flags "foo-bar-baz" "B"))"#, 4),
make_echo_component(r#"(flags "foo-bar-baz" "B")"#, 4),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -580,7 +487,7 @@ fn flags() -> Result<()> {
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (flags "foo-bar-baz" "B" "C" "D"))"#, 4),
make_echo_component(r#"(flags "foo-bar-baz" "B" "C" "D")"#, 4),
)?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -590,10 +497,7 @@ fn flags() -> Result<()> {
// Sad path: flag name mismatch
let component = Component::new(
&engine,
make_echo_component(r#"(type $Foo (flags "A" "B" "C"))"#, 4),
)?;
let component = Component::new(&engine, make_echo_component(r#"(flags "A" "B" "C")"#, 4))?;
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
assert!(instance
@@ -633,7 +537,7 @@ fn flags() -> Result<()> {
&engine,
make_echo_component(
&format!(
r#"(type $Foo (flags {}))"#,
r#"(flags {})"#,
(0..8)
.map(|index| format!(r#""F{}""#, index))
.collect::<Vec<_>>()
@@ -682,7 +586,7 @@ fn flags() -> Result<()> {
&engine,
make_echo_component(
&format!(
r#"(type $Foo (flags {}))"#,
"(flags {})",
(0..9)
.map(|index| format!(r#""F{}""#, index))
.collect::<Vec<_>>()
@@ -730,7 +634,7 @@ fn flags() -> Result<()> {
&engine,
make_echo_component(
&format!(
r#"(type $Foo (flags {}))"#,
r#"(flags {})"#,
(0..16)
.map(|index| format!(r#""F{}""#, index))
.collect::<Vec<_>>()
@@ -769,7 +673,7 @@ fn flags() -> Result<()> {
&engine,
make_echo_component(
&format!(
r#"(type $Foo (flags {}))"#,
"(flags {})",
(0..17)
.map(|index| format!(r#""F{}""#, index))
.collect::<Vec<_>>()
@@ -817,7 +721,7 @@ fn flags() -> Result<()> {
&engine,
make_echo_component(
&format!(
r#"(type $Foo (flags {}))"#,
r#"(flags {})"#,
(0..32)
.map(|index| format!(r#""F{}""#, index))
.collect::<Vec<_>>()
@@ -856,7 +760,7 @@ fn flags() -> Result<()> {
&engine,
make_echo_component(
&format!(
r#"(type $Foo (flags {}))"#,
"(flags {})",
(0..33)
.map(|index| format!(r#""F{}""#, index))
.collect::<Vec<_>>()
@@ -889,7 +793,7 @@ fn flags() -> Result<()> {
&engine,
make_echo_component(
&format!(
r#"(type $Foo (flags {}))"#,
"(flags {})",
(0..65)
.map(|index| format!(r#""F{}""#, index))
.collect::<Vec<_>>()