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:
@@ -17,6 +17,7 @@ proc-macro = true
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0", features = ["extra-traits"] }
|
||||
wasmtime-component-util = { path = "../component-util", version = "=0.40.0" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::fmt;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{braced, parse_macro_input, parse_quote, Data, DeriveInput, Error, Result, Token};
|
||||
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum VariantStyle {
|
||||
@@ -147,64 +148,6 @@ fn add_trait_bounds(generics: &syn::Generics, bound: syn::TypeParamBound) -> syn
|
||||
generics
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum DiscriminantSize {
|
||||
Size1,
|
||||
Size2,
|
||||
Size4,
|
||||
}
|
||||
|
||||
impl DiscriminantSize {
|
||||
fn quote(self, discriminant: usize) -> TokenStream {
|
||||
match self {
|
||||
Self::Size1 => {
|
||||
let discriminant = u8::try_from(discriminant).unwrap();
|
||||
quote!(#discriminant)
|
||||
}
|
||||
Self::Size2 => {
|
||||
let discriminant = u16::try_from(discriminant).unwrap();
|
||||
quote!(#discriminant)
|
||||
}
|
||||
Self::Size4 => {
|
||||
let discriminant = u32::try_from(discriminant).unwrap();
|
||||
quote!(#discriminant)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DiscriminantSize> for u32 {
|
||||
fn from(size: DiscriminantSize) -> u32 {
|
||||
match size {
|
||||
DiscriminantSize::Size1 => 1,
|
||||
DiscriminantSize::Size2 => 2,
|
||||
DiscriminantSize::Size4 => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DiscriminantSize> for usize {
|
||||
fn from(size: DiscriminantSize) -> usize {
|
||||
match size {
|
||||
DiscriminantSize::Size1 => 1,
|
||||
DiscriminantSize::Size2 => 2,
|
||||
DiscriminantSize::Size4 => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn discriminant_size(case_count: usize) -> Option<DiscriminantSize> {
|
||||
if case_count <= 0xFF {
|
||||
Some(DiscriminantSize::Size1)
|
||||
} else if case_count <= 0xFFFF {
|
||||
Some(DiscriminantSize::Size2)
|
||||
} else if case_count <= 0xFFFF_FFFF {
|
||||
Some(DiscriminantSize::Size4)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantCase<'a> {
|
||||
attrs: &'a [syn::Attribute],
|
||||
ident: &'a syn::Ident,
|
||||
@@ -288,7 +231,7 @@ fn expand_variant(
|
||||
));
|
||||
}
|
||||
|
||||
let discriminant_size = discriminant_size(body.variants.len()).ok_or_else(|| {
|
||||
let discriminant_size = DiscriminantSize::from_count(body.variants.len()).ok_or_else(|| {
|
||||
Error::new(
|
||||
input.ident.span(),
|
||||
"`enum`s with more than 2^32 variants are not supported",
|
||||
@@ -417,7 +360,7 @@ fn expand_record_for_component_type(
|
||||
const SIZE32: usize = {
|
||||
let mut size = 0;
|
||||
#sizes
|
||||
size
|
||||
#internal::align_to(size, Self::ALIGN32)
|
||||
};
|
||||
|
||||
const ALIGN32: u32 = {
|
||||
@@ -439,6 +382,23 @@ fn expand_record_for_component_type(
|
||||
Ok(quote!(const _: () = { #expanded };))
|
||||
}
|
||||
|
||||
fn quote(size: DiscriminantSize, discriminant: usize) -> TokenStream {
|
||||
match size {
|
||||
DiscriminantSize::Size1 => {
|
||||
let discriminant = u8::try_from(discriminant).unwrap();
|
||||
quote!(#discriminant)
|
||||
}
|
||||
DiscriminantSize::Size2 => {
|
||||
let discriminant = u16::try_from(discriminant).unwrap();
|
||||
quote!(#discriminant)
|
||||
}
|
||||
DiscriminantSize::Size4 => {
|
||||
let discriminant = u32::try_from(discriminant).unwrap();
|
||||
quote!(#discriminant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Lift, attributes(component))]
|
||||
pub fn lift(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
expand(&LiftExpander, &parse_macro_input!(input as DeriveInput))
|
||||
@@ -523,7 +483,7 @@ impl Expander for LiftExpander {
|
||||
for (index, VariantCase { ident, ty, .. }) in cases.iter().enumerate() {
|
||||
let index_u32 = u32::try_from(index).unwrap();
|
||||
|
||||
let index_quoted = discriminant_size.quote(index);
|
||||
let index_quoted = quote(discriminant_size, index);
|
||||
|
||||
if let Some(ty) = ty {
|
||||
lifts.extend(
|
||||
@@ -666,7 +626,7 @@ impl Expander for LowerExpander {
|
||||
for (index, VariantCase { ident, ty, .. }) in cases.iter().enumerate() {
|
||||
let index_u32 = u32::try_from(index).unwrap();
|
||||
|
||||
let index_quoted = discriminant_size.quote(index);
|
||||
let index_quoted = quote(discriminant_size, index);
|
||||
|
||||
let discriminant_size = usize::from(discriminant_size);
|
||||
|
||||
@@ -989,19 +949,6 @@ impl Parse for Flags {
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
fn ceiling_divide(n: usize, d: usize) -> usize {
|
||||
(n + d - 1) / d
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn flags(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
expand_flags(&parse_macro_input!(input as Flags))
|
||||
@@ -1010,13 +957,7 @@ pub fn flags(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
}
|
||||
|
||||
fn expand_flags(flags: &Flags) -> Result<TokenStream> {
|
||||
let size = if flags.flags.len() <= 8 {
|
||||
FlagsSize::Size1
|
||||
} else if flags.flags.len() <= 16 {
|
||||
FlagsSize::Size2
|
||||
} else {
|
||||
FlagsSize::Size4Plus(ceiling_divide(flags.flags.len(), 32))
|
||||
};
|
||||
let size = FlagsSize::from_count(flags.flags.len());
|
||||
|
||||
let ty;
|
||||
let eq;
|
||||
|
||||
Reference in New Issue
Block a user