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,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"))
|
||||
)
|
||||
)
|
||||
)"#
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user