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:
@@ -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<_>>()
|
||||
|
||||
Reference in New Issue
Block a user