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

@@ -0,0 +1,11 @@
[package]
name = "wasmtime-component-util"
version = "0.40.0"
authors = ["The Wasmtime Project Developers"]
description = "Utility types and functions to support the component model in Wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/bytecodealliance/wasmtime"
documentation = "https://docs.rs/wasmtime-component-util/"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
edition = "2021"

View File

@@ -0,0 +1,75 @@
/// Represents the possible sizes in bytes of the discriminant of a variant type in the component model
#[derive(Debug, Copy, Clone)]
pub enum DiscriminantSize {
/// 8-bit discriminant
Size1,
/// 16-bit discriminant
Size2,
/// 32-bit discriminant
Size4,
}
impl DiscriminantSize {
/// Calculate the size of discriminant needed to represent a variant with the specified number of cases.
pub fn from_count(count: usize) -> Option<Self> {
if count <= 0xFF {
Some(Self::Size1)
} else if count <= 0xFFFF {
Some(Self::Size2)
} else if count <= 0xFFFF_FFFF {
Some(Self::Size4)
} else {
None
}
}
}
impl From<DiscriminantSize> for u32 {
/// Size of the discriminant as a `u32`
fn from(size: DiscriminantSize) -> u32 {
match size {
DiscriminantSize::Size1 => 1,
DiscriminantSize::Size2 => 2,
DiscriminantSize::Size4 => 4,
}
}
}
impl From<DiscriminantSize> for usize {
/// Size of the discriminant as a `usize`
fn from(size: DiscriminantSize) -> usize {
match size {
DiscriminantSize::Size1 => 1,
DiscriminantSize::Size2 => 2,
DiscriminantSize::Size4 => 4,
}
}
}
/// Represents the number of bytes required to store a flags value in the component model
pub enum FlagsSize {
/// Flags can fit in a u8
Size1,
/// Flags can fit in a u16
Size2,
/// Flags can fit in a specified number of u32 fields
Size4Plus(usize),
}
impl FlagsSize {
/// Calculate the size needed to represent a value with the specified number of flags.
pub fn from_count(count: usize) -> FlagsSize {
if count <= 8 {
FlagsSize::Size1
} else if count <= 16 {
FlagsSize::Size2
} else {
FlagsSize::Size4Plus(ceiling_divide(count, 32))
}
}
}
/// Divide `n` by `d`, rounding up in the case of a non-zero remainder.
fn ceiling_divide(n: usize, d: usize) -> usize {
(n + d - 1) / d
}