1ca7081a2SAlex Crichton //! This module generates test cases for the Wasmtime component model function APIs,
2ca7081a2SAlex Crichton //! e.g. `wasmtime::component::func::Func` and `TypedFunc`.
3ca7081a2SAlex Crichton //!
4ca7081a2SAlex Crichton //! Each case includes a list of arbitrary interface types to use as parameters, plus another one to use as a
5ca7081a2SAlex Crichton //! result, and a component which exports a function and imports a function.  The exported function forwards its
6ca7081a2SAlex Crichton //! parameters to the imported one and forwards the result back to the caller.  This serves to exercise Wasmtime's
7ca7081a2SAlex Crichton //! lifting and lowering code and verify the values remain intact during both processes.
8ca7081a2SAlex Crichton 
9ca7081a2SAlex Crichton use arbitrary::{Arbitrary, Unstructured};
100b9ff9bfSAlex Crichton use indexmap::IndexSet;
11ca7081a2SAlex Crichton use proc_macro2::{Ident, TokenStream};
1290ac295eSAlex Crichton use quote::{ToTokens, format_ident, quote};
13ca7081a2SAlex Crichton use std::borrow::Cow;
14ca7081a2SAlex Crichton use std::fmt::{self, Debug, Write};
150b9ff9bfSAlex Crichton use std::hash::{Hash, Hasher};
16ca7081a2SAlex Crichton use std::iter;
17ca7081a2SAlex Crichton use std::ops::Deref;
18ca7081a2SAlex Crichton use wasmtime_component_util::{DiscriminantSize, FlagsSize, REALLOC_AND_FREE};
19ca7081a2SAlex Crichton 
20ca7081a2SAlex Crichton const MAX_FLAT_PARAMS: usize = 16;
210b9ff9bfSAlex Crichton const MAX_FLAT_ASYNC_PARAMS: usize = 4;
22ca7081a2SAlex Crichton const MAX_FLAT_RESULTS: usize = 1;
23ca7081a2SAlex Crichton 
24ca7081a2SAlex Crichton /// The name of the imported host function which the generated component will call
25ca7081a2SAlex Crichton pub const IMPORT_FUNCTION: &str = "echo-import";
26ca7081a2SAlex Crichton 
27ca7081a2SAlex Crichton /// The name of the exported guest function which the host should call
28ca7081a2SAlex Crichton pub const EXPORT_FUNCTION: &str = "echo-export";
29ca7081a2SAlex Crichton 
30ca7081a2SAlex Crichton /// Wasmtime allows up to 100 type depth so limit this to just under that.
31ca7081a2SAlex Crichton pub const MAX_TYPE_DEPTH: u32 = 99;
32ca7081a2SAlex Crichton 
330b9ff9bfSAlex Crichton macro_rules! uwriteln {
340b9ff9bfSAlex Crichton     ($($arg:tt)*) => {
350b9ff9bfSAlex Crichton         writeln!($($arg)*).unwrap()
360b9ff9bfSAlex Crichton     };
370b9ff9bfSAlex Crichton }
380b9ff9bfSAlex Crichton 
390b9ff9bfSAlex Crichton macro_rules! uwrite {
400b9ff9bfSAlex Crichton     ($($arg:tt)*) => {
410b9ff9bfSAlex Crichton         write!($($arg)*).unwrap()
420b9ff9bfSAlex Crichton     };
430b9ff9bfSAlex Crichton }
440b9ff9bfSAlex Crichton 
450b9ff9bfSAlex Crichton #[derive(Debug, Copy, Clone, PartialEq, Eq)]
46ca7081a2SAlex Crichton enum CoreType {
47ca7081a2SAlex Crichton     I32,
48ca7081a2SAlex Crichton     I64,
49ca7081a2SAlex Crichton     F32,
50ca7081a2SAlex Crichton     F64,
51ca7081a2SAlex Crichton }
52ca7081a2SAlex Crichton 
53ca7081a2SAlex Crichton impl CoreType {
54ca7081a2SAlex Crichton     /// This is the `join` operation specified in [the canonical
55ca7081a2SAlex Crichton     /// ABI](https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#flattening) for
56ca7081a2SAlex Crichton     /// variant types.
join(self, other: Self) -> Self57ca7081a2SAlex Crichton     fn join(self, other: Self) -> Self {
58ca7081a2SAlex Crichton         match (self, other) {
59ca7081a2SAlex Crichton             _ if self == other => self,
60ca7081a2SAlex Crichton             (Self::I32, Self::F32) | (Self::F32, Self::I32) => Self::I32,
61ca7081a2SAlex Crichton             _ => Self::I64,
62ca7081a2SAlex Crichton         }
63ca7081a2SAlex Crichton     }
64ca7081a2SAlex Crichton }
65ca7081a2SAlex Crichton 
66ca7081a2SAlex Crichton impl fmt::Display for CoreType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result67ca7081a2SAlex Crichton     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68ca7081a2SAlex Crichton         match self {
69ca7081a2SAlex Crichton             Self::I32 => f.write_str("i32"),
70ca7081a2SAlex Crichton             Self::I64 => f.write_str("i64"),
71ca7081a2SAlex Crichton             Self::F32 => f.write_str("f32"),
72ca7081a2SAlex Crichton             Self::F64 => f.write_str("f64"),
73ca7081a2SAlex Crichton         }
74ca7081a2SAlex Crichton     }
75ca7081a2SAlex Crichton }
76ca7081a2SAlex Crichton 
77ca7081a2SAlex Crichton /// Wraps a `Box<[T]>` and provides an `Arbitrary` implementation that always generates slices of length less than
78ca7081a2SAlex Crichton /// or equal to the longest tuple for which Wasmtime generates a `ComponentType` impl
79ca7081a2SAlex Crichton #[derive(Debug, Clone)]
80ca7081a2SAlex Crichton pub struct VecInRange<T, const L: u32, const H: u32>(Vec<T>);
81ca7081a2SAlex Crichton 
82ca7081a2SAlex Crichton impl<T, const L: u32, const H: u32> VecInRange<T, L, H> {
new<'a>( input: &mut Unstructured<'a>, fuel: &mut u32, generate: impl Fn(&mut Unstructured<'a>, &mut u32) -> arbitrary::Result<T>, ) -> arbitrary::Result<Self>83ca7081a2SAlex Crichton     fn new<'a>(
84ca7081a2SAlex Crichton         input: &mut Unstructured<'a>,
85ca7081a2SAlex Crichton         fuel: &mut u32,
86ca7081a2SAlex Crichton         generate: impl Fn(&mut Unstructured<'a>, &mut u32) -> arbitrary::Result<T>,
87ca7081a2SAlex Crichton     ) -> arbitrary::Result<Self> {
88ca7081a2SAlex Crichton         let mut ret = Vec::new();
89ca7081a2SAlex Crichton         input.arbitrary_loop(Some(L), Some(H), |input| {
90ca7081a2SAlex Crichton             if *fuel > 0 {
91ca7081a2SAlex Crichton                 *fuel = *fuel - 1;
92ca7081a2SAlex Crichton                 ret.push(generate(input, fuel)?);
93ca7081a2SAlex Crichton                 Ok(std::ops::ControlFlow::Continue(()))
94ca7081a2SAlex Crichton             } else {
95ca7081a2SAlex Crichton                 Ok(std::ops::ControlFlow::Break(()))
96ca7081a2SAlex Crichton             }
97ca7081a2SAlex Crichton         })?;
98ca7081a2SAlex Crichton         Ok(Self(ret))
99ca7081a2SAlex Crichton     }
100ca7081a2SAlex Crichton }
101ca7081a2SAlex Crichton 
102ca7081a2SAlex Crichton impl<T, const L: u32, const H: u32> Deref for VecInRange<T, L, H> {
103ca7081a2SAlex Crichton     type Target = [T];
104ca7081a2SAlex Crichton 
deref(&self) -> &[T]105ca7081a2SAlex Crichton     fn deref(&self) -> &[T] {
106ca7081a2SAlex Crichton         self.0.deref()
107ca7081a2SAlex Crichton     }
108ca7081a2SAlex Crichton }
109ca7081a2SAlex Crichton 
110ca7081a2SAlex Crichton /// Represents a component model interface type
111ca7081a2SAlex Crichton #[expect(missing_docs, reason = "self-describing")]
112ca7081a2SAlex Crichton #[derive(Debug, Clone)]
113ca7081a2SAlex Crichton pub enum Type {
114ca7081a2SAlex Crichton     Bool,
115ca7081a2SAlex Crichton     S8,
116ca7081a2SAlex Crichton     U8,
117ca7081a2SAlex Crichton     S16,
118ca7081a2SAlex Crichton     U16,
119ca7081a2SAlex Crichton     S32,
120ca7081a2SAlex Crichton     U32,
121ca7081a2SAlex Crichton     S64,
122ca7081a2SAlex Crichton     U64,
123ca7081a2SAlex Crichton     Float32,
124ca7081a2SAlex Crichton     Float64,
125ca7081a2SAlex Crichton     Char,
126ca7081a2SAlex Crichton     String,
127ca7081a2SAlex Crichton     List(Box<Type>),
1281b59b579SYordis Prieto     Map(Box<Type>, Box<Type>),
129ca7081a2SAlex Crichton 
130ca7081a2SAlex Crichton     // Give records the ability to generate a generous amount of fields but
131ca7081a2SAlex Crichton     // don't let the fuzzer go too wild since `wasmparser`'s validator currently
132ca7081a2SAlex Crichton     // has hard limits in the 1000-ish range on the number of fields a record
133ca7081a2SAlex Crichton     // may contain.
134ca7081a2SAlex Crichton     Record(VecInRange<Type, 1, 200>),
135ca7081a2SAlex Crichton 
136ca7081a2SAlex Crichton     // Tuples can only have up to 16 type parameters in wasmtime right now for
137ca7081a2SAlex Crichton     // the static API, but the standard library only supports `Debug` up to 11
138ca7081a2SAlex Crichton     // elements, so compromise at an even 10.
139ca7081a2SAlex Crichton     Tuple(VecInRange<Type, 1, 10>),
140ca7081a2SAlex Crichton 
141ca7081a2SAlex Crichton     // Like records, allow a good number of variants, but variants require at
142ca7081a2SAlex Crichton     // least one case.
143ca7081a2SAlex Crichton     Variant(VecInRange<Option<Type>, 1, 200>),
144ca7081a2SAlex Crichton     Enum(u32),
145ca7081a2SAlex Crichton 
146ca7081a2SAlex Crichton     Option(Box<Type>),
147ca7081a2SAlex Crichton     Result {
148ca7081a2SAlex Crichton         ok: Option<Box<Type>>,
149ca7081a2SAlex Crichton         err: Option<Box<Type>>,
150ca7081a2SAlex Crichton     },
151ca7081a2SAlex Crichton 
152ca7081a2SAlex Crichton     Flags(u32),
153ca7081a2SAlex Crichton }
154ca7081a2SAlex Crichton 
155ca7081a2SAlex Crichton impl Type {
generate( u: &mut Unstructured<'_>, depth: u32, fuel: &mut u32, ) -> arbitrary::Result<Type>156ca7081a2SAlex Crichton     pub fn generate(
157ca7081a2SAlex Crichton         u: &mut Unstructured<'_>,
158ca7081a2SAlex Crichton         depth: u32,
159ca7081a2SAlex Crichton         fuel: &mut u32,
160ca7081a2SAlex Crichton     ) -> arbitrary::Result<Type> {
161ca7081a2SAlex Crichton         *fuel = fuel.saturating_sub(1);
1621b59b579SYordis Prieto         let max = if depth == 0 || *fuel == 0 { 12 } else { 21 };
163ca7081a2SAlex Crichton         Ok(match u.int_in_range(0..=max)? {
164ca7081a2SAlex Crichton             0 => Type::Bool,
165ca7081a2SAlex Crichton             1 => Type::S8,
166ca7081a2SAlex Crichton             2 => Type::U8,
167ca7081a2SAlex Crichton             3 => Type::S16,
168ca7081a2SAlex Crichton             4 => Type::U16,
169ca7081a2SAlex Crichton             5 => Type::S32,
170ca7081a2SAlex Crichton             6 => Type::U32,
171ca7081a2SAlex Crichton             7 => Type::S64,
172ca7081a2SAlex Crichton             8 => Type::U64,
173ca7081a2SAlex Crichton             9 => Type::Float32,
174ca7081a2SAlex Crichton             10 => Type::Float64,
175ca7081a2SAlex Crichton             11 => Type::Char,
176ca7081a2SAlex Crichton             12 => Type::String,
177ca7081a2SAlex Crichton             // ^-- if you add something here update the `depth == 0` case above
178ca7081a2SAlex Crichton             13 => Type::List(Box::new(Type::generate(u, depth - 1, fuel)?)),
179ca7081a2SAlex Crichton             14 => Type::Record(Type::generate_list(u, depth - 1, fuel)?),
180ca7081a2SAlex Crichton             15 => Type::Tuple(Type::generate_list(u, depth - 1, fuel)?),
181ca7081a2SAlex Crichton             16 => Type::Variant(VecInRange::new(u, fuel, |u, fuel| {
182ca7081a2SAlex Crichton                 Type::generate_opt(u, depth - 1, fuel)
183ca7081a2SAlex Crichton             })?),
184ca7081a2SAlex Crichton             17 => {
185ca7081a2SAlex Crichton                 let amt = u.int_in_range(1..=(*fuel).max(1).min(257))?;
186ca7081a2SAlex Crichton                 *fuel -= amt;
187ca7081a2SAlex Crichton                 Type::Enum(amt)
188ca7081a2SAlex Crichton             }
189ca7081a2SAlex Crichton             18 => Type::Option(Box::new(Type::generate(u, depth - 1, fuel)?)),
190ca7081a2SAlex Crichton             19 => Type::Result {
191ca7081a2SAlex Crichton                 ok: Type::generate_opt(u, depth - 1, fuel)?.map(Box::new),
192ca7081a2SAlex Crichton                 err: Type::generate_opt(u, depth - 1, fuel)?.map(Box::new),
193ca7081a2SAlex Crichton             },
194ca7081a2SAlex Crichton             20 => {
195ca7081a2SAlex Crichton                 let amt = u.int_in_range(1..=(*fuel).min(32))?;
196ca7081a2SAlex Crichton                 *fuel -= amt;
197ca7081a2SAlex Crichton                 Type::Flags(amt)
198ca7081a2SAlex Crichton             }
1991b59b579SYordis Prieto             21 => Type::Map(
2001b59b579SYordis Prieto                 Box::new(Type::generate_hashable_key(u, fuel)?),
2011b59b579SYordis Prieto                 Box::new(Type::generate(u, depth - 1, fuel)?),
2021b59b579SYordis Prieto             ),
203ca7081a2SAlex Crichton             // ^-- if you add something here update the `depth != 0` case above
204ca7081a2SAlex Crichton             _ => unreachable!(),
205ca7081a2SAlex Crichton         })
206ca7081a2SAlex Crichton     }
207ca7081a2SAlex Crichton 
2081b59b579SYordis Prieto     /// Generate a type that can be used as a HashMap key (implements Hash + Eq).
2091b59b579SYordis Prieto     /// This excludes floats and complex types that might contain floats.
generate_hashable_key(u: &mut Unstructured<'_>, fuel: &mut u32) -> arbitrary::Result<Type>2101b59b579SYordis Prieto     fn generate_hashable_key(u: &mut Unstructured<'_>, fuel: &mut u32) -> arbitrary::Result<Type> {
2111b59b579SYordis Prieto         *fuel = fuel.saturating_sub(1);
2121b59b579SYordis Prieto         // Only generate types that implement Hash and Eq:
2131b59b579SYordis Prieto         // - No Float32/Float64 (NaN comparison issues)
2141b59b579SYordis Prieto         // - No complex types (Record, Tuple, Variant, etc.) as they might contain floats
2151b59b579SYordis Prieto         // - String is allowed as it implements Hash + Eq
2161b59b579SYordis Prieto         Ok(match u.int_in_range(0..=11)? {
2171b59b579SYordis Prieto             0 => Type::Bool,
2181b59b579SYordis Prieto             1 => Type::S8,
2191b59b579SYordis Prieto             2 => Type::U8,
2201b59b579SYordis Prieto             3 => Type::S16,
2211b59b579SYordis Prieto             4 => Type::U16,
2221b59b579SYordis Prieto             5 => Type::S32,
2231b59b579SYordis Prieto             6 => Type::U32,
2241b59b579SYordis Prieto             7 => Type::S64,
2251b59b579SYordis Prieto             8 => Type::U64,
2261b59b579SYordis Prieto             9 => Type::Char,
2271b59b579SYordis Prieto             10 => Type::String,
2281b59b579SYordis Prieto             11 => {
2291b59b579SYordis Prieto                 let amt = u.int_in_range(1..=(*fuel).max(1).min(257))?;
2301b59b579SYordis Prieto                 *fuel = fuel.saturating_sub(amt);
2311b59b579SYordis Prieto                 Type::Enum(amt)
2321b59b579SYordis Prieto             }
2331b59b579SYordis Prieto             _ => unreachable!(),
2341b59b579SYordis Prieto         })
2351b59b579SYordis Prieto     }
2361b59b579SYordis Prieto 
generate_opt( u: &mut Unstructured<'_>, depth: u32, fuel: &mut u32, ) -> arbitrary::Result<Option<Type>>237ca7081a2SAlex Crichton     fn generate_opt(
238ca7081a2SAlex Crichton         u: &mut Unstructured<'_>,
239ca7081a2SAlex Crichton         depth: u32,
240ca7081a2SAlex Crichton         fuel: &mut u32,
241ca7081a2SAlex Crichton     ) -> arbitrary::Result<Option<Type>> {
242ca7081a2SAlex Crichton         Ok(if u.arbitrary()? {
243ca7081a2SAlex Crichton             Some(Type::generate(u, depth, fuel)?)
244ca7081a2SAlex Crichton         } else {
245ca7081a2SAlex Crichton             None
246ca7081a2SAlex Crichton         })
247ca7081a2SAlex Crichton     }
248ca7081a2SAlex Crichton 
generate_list<const L: u32, const H: u32>( u: &mut Unstructured<'_>, depth: u32, fuel: &mut u32, ) -> arbitrary::Result<VecInRange<Type, L, H>>249ca7081a2SAlex Crichton     fn generate_list<const L: u32, const H: u32>(
250ca7081a2SAlex Crichton         u: &mut Unstructured<'_>,
251ca7081a2SAlex Crichton         depth: u32,
252ca7081a2SAlex Crichton         fuel: &mut u32,
253ca7081a2SAlex Crichton     ) -> arbitrary::Result<VecInRange<Type, L, H>> {
254ca7081a2SAlex Crichton         VecInRange::new(u, fuel, |u, fuel| Type::generate(u, depth, fuel))
255ca7081a2SAlex Crichton     }
2560b9ff9bfSAlex Crichton 
2570b9ff9bfSAlex Crichton     /// Generates text format wasm into `s` to store a value of this type, in
2580b9ff9bfSAlex Crichton     /// its flat representation stored in the `locals` provided, to the local
2590b9ff9bfSAlex Crichton     /// named `ptr` at the `offset` provided.
2600b9ff9bfSAlex Crichton     ///
2610b9ff9bfSAlex Crichton     /// This will register helper functions necessary in `helpers`. The
2620b9ff9bfSAlex Crichton     /// `locals` iterator will be advanced for all locals consumed by this
2630b9ff9bfSAlex Crichton     /// store operation.
store_flat<'a>( &'a self, s: &mut String, ptr: &str, offset: u32, locals: &mut dyn Iterator<Item = FlatSource>, helpers: &mut IndexSet<Helper<'a>>, )2640b9ff9bfSAlex Crichton     fn store_flat<'a>(
2650b9ff9bfSAlex Crichton         &'a self,
2660b9ff9bfSAlex Crichton         s: &mut String,
2670b9ff9bfSAlex Crichton         ptr: &str,
2680b9ff9bfSAlex Crichton         offset: u32,
2690b9ff9bfSAlex Crichton         locals: &mut dyn Iterator<Item = FlatSource>,
2700b9ff9bfSAlex Crichton         helpers: &mut IndexSet<Helper<'a>>,
2710b9ff9bfSAlex Crichton     ) {
2720b9ff9bfSAlex Crichton         enum Kind {
2730b9ff9bfSAlex Crichton             Primitive(&'static str),
2740b9ff9bfSAlex Crichton             PointerPair,
2750b9ff9bfSAlex Crichton             Helper,
2760b9ff9bfSAlex Crichton         }
2770b9ff9bfSAlex Crichton         let kind = match self {
2780b9ff9bfSAlex Crichton             Type::Bool | Type::S8 | Type::U8 => Kind::Primitive("i32.store8"),
2790b9ff9bfSAlex Crichton             Type::S16 | Type::U16 => Kind::Primitive("i32.store16"),
2800b9ff9bfSAlex Crichton             Type::S32 | Type::U32 | Type::Char => Kind::Primitive("i32.store"),
2810b9ff9bfSAlex Crichton             Type::S64 | Type::U64 => Kind::Primitive("i64.store"),
2820b9ff9bfSAlex Crichton             Type::Float32 => Kind::Primitive("f32.store"),
2830b9ff9bfSAlex Crichton             Type::Float64 => Kind::Primitive("f64.store"),
2841b59b579SYordis Prieto             Type::String | Type::List(_) | Type::Map(_, _) => Kind::PointerPair,
2850b9ff9bfSAlex Crichton             Type::Enum(n) if *n <= (1 << 8) => Kind::Primitive("i32.store8"),
2860b9ff9bfSAlex Crichton             Type::Enum(n) if *n <= (1 << 16) => Kind::Primitive("i32.store16"),
2870b9ff9bfSAlex Crichton             Type::Enum(_) => Kind::Primitive("i32.store"),
2880b9ff9bfSAlex Crichton             Type::Flags(n) if *n <= 8 => Kind::Primitive("i32.store8"),
2890b9ff9bfSAlex Crichton             Type::Flags(n) if *n <= 16 => Kind::Primitive("i32.store16"),
2900b9ff9bfSAlex Crichton             Type::Flags(n) if *n <= 32 => Kind::Primitive("i32.store"),
2910b9ff9bfSAlex Crichton             Type::Flags(_) => unreachable!(),
2920b9ff9bfSAlex Crichton             Type::Record(_)
2930b9ff9bfSAlex Crichton             | Type::Tuple(_)
2940b9ff9bfSAlex Crichton             | Type::Variant(_)
2950b9ff9bfSAlex Crichton             | Type::Option(_)
2960b9ff9bfSAlex Crichton             | Type::Result { .. } => Kind::Helper,
2970b9ff9bfSAlex Crichton         };
2980b9ff9bfSAlex Crichton 
2990b9ff9bfSAlex Crichton         match kind {
3000b9ff9bfSAlex Crichton             Kind::Primitive(op) => uwriteln!(
3010b9ff9bfSAlex Crichton                 s,
3020b9ff9bfSAlex Crichton                 "({op} offset={offset} (local.get {ptr}) {})",
3030b9ff9bfSAlex Crichton                 locals.next().unwrap()
3040b9ff9bfSAlex Crichton             ),
3050b9ff9bfSAlex Crichton             Kind::PointerPair => {
3060b9ff9bfSAlex Crichton                 let abi_ptr = locals.next().unwrap();
3070b9ff9bfSAlex Crichton                 let abi_len = locals.next().unwrap();
3080b9ff9bfSAlex Crichton                 uwriteln!(s, "(i32.store offset={offset} (local.get {ptr}) {abi_ptr})",);
3090b9ff9bfSAlex Crichton                 let offset = offset + 4;
3100b9ff9bfSAlex Crichton                 uwriteln!(s, "(i32.store offset={offset} (local.get {ptr}) {abi_len})",);
3110b9ff9bfSAlex Crichton             }
3120b9ff9bfSAlex Crichton             Kind::Helper => {
3130b9ff9bfSAlex Crichton                 let (index, _) = helpers.insert_full(Helper(self));
3140b9ff9bfSAlex Crichton                 uwriteln!(s, "(i32.add (local.get {ptr}) (i32.const {offset}))");
3150b9ff9bfSAlex Crichton                 for _ in 0..self.lowered().len() {
3160b9ff9bfSAlex Crichton                     let i = locals.next().unwrap();
3170b9ff9bfSAlex Crichton                     uwriteln!(s, "{i}");
3180b9ff9bfSAlex Crichton                 }
3190b9ff9bfSAlex Crichton                 uwriteln!(s, "call $store_helper_{index}");
3200b9ff9bfSAlex Crichton             }
3210b9ff9bfSAlex Crichton         }
3220b9ff9bfSAlex Crichton     }
3230b9ff9bfSAlex Crichton 
3240b9ff9bfSAlex Crichton     /// Generates a text-format wasm function which takes a pointer and this
3250b9ff9bfSAlex Crichton     /// type's flat representation as arguments and then stores this value in
3260b9ff9bfSAlex Crichton     /// the first argument.
3270b9ff9bfSAlex Crichton     ///
3280b9ff9bfSAlex Crichton     /// This is used to store records/variants to cut down on the size of final
3290b9ff9bfSAlex Crichton     /// functions and make codegen here a bit easier.
store_flat_helper<'a>( &'a self, s: &mut String, i: usize, helpers: &mut IndexSet<Helper<'a>>, )3300b9ff9bfSAlex Crichton     fn store_flat_helper<'a>(
3310b9ff9bfSAlex Crichton         &'a self,
3320b9ff9bfSAlex Crichton         s: &mut String,
3330b9ff9bfSAlex Crichton         i: usize,
3340b9ff9bfSAlex Crichton         helpers: &mut IndexSet<Helper<'a>>,
3350b9ff9bfSAlex Crichton     ) {
3360b9ff9bfSAlex Crichton         uwrite!(s, "(func $store_helper_{i} (param i32)");
3370b9ff9bfSAlex Crichton         let lowered = self.lowered();
3380b9ff9bfSAlex Crichton         for ty in &lowered {
3390b9ff9bfSAlex Crichton             uwrite!(s, " (param {ty})");
3400b9ff9bfSAlex Crichton         }
3410b9ff9bfSAlex Crichton         s.push_str("\n");
3420b9ff9bfSAlex Crichton         let locals = (0..lowered.len() as u32).map(|i| i + 1).collect::<Vec<_>>();
3430b9ff9bfSAlex Crichton         let record = |s: &mut String, helpers: &mut IndexSet<Helper<'a>>, types: &'a [Type]| {
3440b9ff9bfSAlex Crichton             let mut locals = locals.iter().cloned().map(FlatSource::Local);
3450b9ff9bfSAlex Crichton             for (offset, ty) in record_field_offsets(types) {
3460b9ff9bfSAlex Crichton                 ty.store_flat(s, "0", offset, &mut locals, helpers);
3470b9ff9bfSAlex Crichton             }
3480b9ff9bfSAlex Crichton             assert!(locals.next().is_none());
3490b9ff9bfSAlex Crichton         };
3500b9ff9bfSAlex Crichton         let variant = |s: &mut String,
3510b9ff9bfSAlex Crichton                        helpers: &mut IndexSet<Helper<'a>>,
3520b9ff9bfSAlex Crichton                        types: &[Option<&'a Type>]| {
3530b9ff9bfSAlex Crichton             let (size, offset) = variant_memory_info(types.iter().cloned());
3540b9ff9bfSAlex Crichton             // One extra block for out-of-bounds discriminants.
3550b9ff9bfSAlex Crichton             for _ in 0..types.len() + 1 {
3560b9ff9bfSAlex Crichton                 s.push_str("block\n");
3570b9ff9bfSAlex Crichton             }
3580b9ff9bfSAlex Crichton 
3590b9ff9bfSAlex Crichton             // Store the discriminant in memory, then branch on it to figure
3600b9ff9bfSAlex Crichton             // out which case we're in.
3610b9ff9bfSAlex Crichton             let store = match size {
3620b9ff9bfSAlex Crichton                 DiscriminantSize::Size1 => "i32.store8",
3630b9ff9bfSAlex Crichton                 DiscriminantSize::Size2 => "i32.store16",
3640b9ff9bfSAlex Crichton                 DiscriminantSize::Size4 => "i32.store",
3650b9ff9bfSAlex Crichton             };
3660b9ff9bfSAlex Crichton             uwriteln!(s, "({store} (local.get 0) (local.get 1))");
3670b9ff9bfSAlex Crichton             s.push_str("local.get 1\n");
3680b9ff9bfSAlex Crichton             s.push_str("br_table");
3690b9ff9bfSAlex Crichton             for i in 0..types.len() + 1 {
3700b9ff9bfSAlex Crichton                 uwrite!(s, " {i}");
3710b9ff9bfSAlex Crichton             }
3720b9ff9bfSAlex Crichton             s.push_str("\nend\n");
3730b9ff9bfSAlex Crichton 
3740b9ff9bfSAlex Crichton             // Store each payload individually while converting locals from
3750b9ff9bfSAlex Crichton             // their source types to the precise type necessary for this
3760b9ff9bfSAlex Crichton             // variant.
3770b9ff9bfSAlex Crichton             for ty in types {
3780b9ff9bfSAlex Crichton                 if let Some(ty) = ty {
3790b9ff9bfSAlex Crichton                     let ty_lowered = ty.lowered();
3800b9ff9bfSAlex Crichton                     let mut locals = locals[1..].iter().zip(&lowered[1..]).zip(&ty_lowered).map(
3810b9ff9bfSAlex Crichton                         |((i, from), to)| FlatSource::LocalConvert {
3820b9ff9bfSAlex Crichton                             local: *i,
3830b9ff9bfSAlex Crichton                             from: *from,
3840b9ff9bfSAlex Crichton                             to: *to,
3850b9ff9bfSAlex Crichton                         },
3860b9ff9bfSAlex Crichton                     );
3870b9ff9bfSAlex Crichton                     ty.store_flat(s, "0", offset, &mut locals, helpers);
3880b9ff9bfSAlex Crichton                 }
3890b9ff9bfSAlex Crichton                 s.push_str("return\n");
3900b9ff9bfSAlex Crichton                 s.push_str("end\n");
3910b9ff9bfSAlex Crichton             }
3920b9ff9bfSAlex Crichton 
3930b9ff9bfSAlex Crichton             // Catch-all result which is for out-of-bounds discriminants.
3940b9ff9bfSAlex Crichton             s.push_str("unreachable\n");
3950b9ff9bfSAlex Crichton         };
3960b9ff9bfSAlex Crichton         match self {
3970b9ff9bfSAlex Crichton             Type::Bool
3980b9ff9bfSAlex Crichton             | Type::S8
3990b9ff9bfSAlex Crichton             | Type::U8
4000b9ff9bfSAlex Crichton             | Type::S16
4010b9ff9bfSAlex Crichton             | Type::U16
4020b9ff9bfSAlex Crichton             | Type::S32
4030b9ff9bfSAlex Crichton             | Type::U32
4040b9ff9bfSAlex Crichton             | Type::Char
4050b9ff9bfSAlex Crichton             | Type::S64
4060b9ff9bfSAlex Crichton             | Type::U64
4070b9ff9bfSAlex Crichton             | Type::Float32
4080b9ff9bfSAlex Crichton             | Type::Float64
4090b9ff9bfSAlex Crichton             | Type::String
4100b9ff9bfSAlex Crichton             | Type::List(_)
4111b59b579SYordis Prieto             | Type::Map(_, _)
4120b9ff9bfSAlex Crichton             | Type::Flags(_)
4130b9ff9bfSAlex Crichton             | Type::Enum(_) => unreachable!(),
4140b9ff9bfSAlex Crichton 
4150b9ff9bfSAlex Crichton             Type::Record(r) => record(s, helpers, r),
4160b9ff9bfSAlex Crichton             Type::Tuple(t) => record(s, helpers, t),
4170b9ff9bfSAlex Crichton             Type::Variant(v) => variant(
4180b9ff9bfSAlex Crichton                 s,
4190b9ff9bfSAlex Crichton                 helpers,
4200b9ff9bfSAlex Crichton                 &v.iter().map(|t| t.as_ref()).collect::<Vec<_>>(),
4210b9ff9bfSAlex Crichton             ),
4220b9ff9bfSAlex Crichton             Type::Option(o) => variant(s, helpers, &[None, Some(&**o)]),
4230b9ff9bfSAlex Crichton             Type::Result { ok, err } => variant(s, helpers, &[ok.as_deref(), err.as_deref()]),
4240b9ff9bfSAlex Crichton         };
4250b9ff9bfSAlex Crichton         s.push_str(")\n");
4260b9ff9bfSAlex Crichton     }
4270b9ff9bfSAlex Crichton 
4280b9ff9bfSAlex Crichton     /// Same as `store_flat`, except loads the flat values from `ptr+offset`.
4290b9ff9bfSAlex Crichton     ///
4300b9ff9bfSAlex Crichton     /// Results are placed directly on the wasm stack.
load_flat<'a>( &'a self, s: &mut String, ptr: &str, offset: u32, helpers: &mut IndexSet<Helper<'a>>, )4310b9ff9bfSAlex Crichton     fn load_flat<'a>(
4320b9ff9bfSAlex Crichton         &'a self,
4330b9ff9bfSAlex Crichton         s: &mut String,
4340b9ff9bfSAlex Crichton         ptr: &str,
4350b9ff9bfSAlex Crichton         offset: u32,
4360b9ff9bfSAlex Crichton         helpers: &mut IndexSet<Helper<'a>>,
4370b9ff9bfSAlex Crichton     ) {
4380b9ff9bfSAlex Crichton         enum Kind {
4390b9ff9bfSAlex Crichton             Primitive(&'static str),
4400b9ff9bfSAlex Crichton             PointerPair,
4410b9ff9bfSAlex Crichton             Helper,
4420b9ff9bfSAlex Crichton         }
4430b9ff9bfSAlex Crichton         let kind = match self {
4440b9ff9bfSAlex Crichton             Type::Bool | Type::U8 => Kind::Primitive("i32.load8_u"),
4450b9ff9bfSAlex Crichton             Type::S8 => Kind::Primitive("i32.load8_s"),
4460b9ff9bfSAlex Crichton             Type::U16 => Kind::Primitive("i32.load16_u"),
4470b9ff9bfSAlex Crichton             Type::S16 => Kind::Primitive("i32.load16_s"),
4480b9ff9bfSAlex Crichton             Type::U32 | Type::S32 | Type::Char => Kind::Primitive("i32.load"),
4490b9ff9bfSAlex Crichton             Type::U64 | Type::S64 => Kind::Primitive("i64.load"),
4500b9ff9bfSAlex Crichton             Type::Float32 => Kind::Primitive("f32.load"),
4510b9ff9bfSAlex Crichton             Type::Float64 => Kind::Primitive("f64.load"),
4521b59b579SYordis Prieto             Type::String | Type::List(_) | Type::Map(_, _) => Kind::PointerPair,
4530b9ff9bfSAlex Crichton             Type::Enum(n) if *n <= (1 << 8) => Kind::Primitive("i32.load8_u"),
4540b9ff9bfSAlex Crichton             Type::Enum(n) if *n <= (1 << 16) => Kind::Primitive("i32.load16_u"),
4550b9ff9bfSAlex Crichton             Type::Enum(_) => Kind::Primitive("i32.load"),
4560b9ff9bfSAlex Crichton             Type::Flags(n) if *n <= 8 => Kind::Primitive("i32.load8_u"),
4570b9ff9bfSAlex Crichton             Type::Flags(n) if *n <= 16 => Kind::Primitive("i32.load16_u"),
4580b9ff9bfSAlex Crichton             Type::Flags(n) if *n <= 32 => Kind::Primitive("i32.load"),
4590b9ff9bfSAlex Crichton             Type::Flags(_) => unreachable!(),
4600b9ff9bfSAlex Crichton 
4610b9ff9bfSAlex Crichton             Type::Record(_)
4620b9ff9bfSAlex Crichton             | Type::Tuple(_)
4630b9ff9bfSAlex Crichton             | Type::Variant(_)
4640b9ff9bfSAlex Crichton             | Type::Option(_)
4650b9ff9bfSAlex Crichton             | Type::Result { .. } => Kind::Helper,
4660b9ff9bfSAlex Crichton         };
4670b9ff9bfSAlex Crichton         match kind {
4680b9ff9bfSAlex Crichton             Kind::Primitive(op) => uwriteln!(s, "({op} offset={offset} (local.get {ptr}))"),
4690b9ff9bfSAlex Crichton             Kind::PointerPair => {
4700b9ff9bfSAlex Crichton                 uwriteln!(s, "(i32.load offset={offset} (local.get {ptr}))",);
4710b9ff9bfSAlex Crichton                 let offset = offset + 4;
4720b9ff9bfSAlex Crichton                 uwriteln!(s, "(i32.load offset={offset} (local.get {ptr}))",);
4730b9ff9bfSAlex Crichton             }
4740b9ff9bfSAlex Crichton             Kind::Helper => {
4750b9ff9bfSAlex Crichton                 let (index, _) = helpers.insert_full(Helper(self));
4760b9ff9bfSAlex Crichton                 uwriteln!(s, "(i32.add (local.get {ptr}) (i32.const {offset}))");
4770b9ff9bfSAlex Crichton                 uwriteln!(s, "call $load_helper_{index}");
4780b9ff9bfSAlex Crichton             }
4790b9ff9bfSAlex Crichton         }
4800b9ff9bfSAlex Crichton     }
4810b9ff9bfSAlex Crichton 
4820b9ff9bfSAlex Crichton     /// Same as `store_flat_helper` but for loading the flat representation.
load_flat_helper<'a>( &'a self, s: &mut String, i: usize, helpers: &mut IndexSet<Helper<'a>>, )4830b9ff9bfSAlex Crichton     fn load_flat_helper<'a>(
4840b9ff9bfSAlex Crichton         &'a self,
4850b9ff9bfSAlex Crichton         s: &mut String,
4860b9ff9bfSAlex Crichton         i: usize,
4870b9ff9bfSAlex Crichton         helpers: &mut IndexSet<Helper<'a>>,
4880b9ff9bfSAlex Crichton     ) {
4890b9ff9bfSAlex Crichton         uwrite!(s, "(func $load_helper_{i} (param i32)");
4900b9ff9bfSAlex Crichton         let lowered = self.lowered();
4910b9ff9bfSAlex Crichton         for ty in &lowered {
4920b9ff9bfSAlex Crichton             uwrite!(s, " (result {ty})");
4930b9ff9bfSAlex Crichton         }
4940b9ff9bfSAlex Crichton         s.push_str("\n");
4950b9ff9bfSAlex Crichton         let record = |s: &mut String, helpers: &mut IndexSet<Helper<'a>>, types: &'a [Type]| {
4960b9ff9bfSAlex Crichton             for (offset, ty) in record_field_offsets(types) {
4970b9ff9bfSAlex Crichton                 ty.load_flat(s, "0", offset, helpers);
4980b9ff9bfSAlex Crichton             }
4990b9ff9bfSAlex Crichton         };
5000b9ff9bfSAlex Crichton         let variant = |s: &mut String,
5010b9ff9bfSAlex Crichton                        helpers: &mut IndexSet<Helper<'a>>,
5020b9ff9bfSAlex Crichton                        types: &[Option<&'a Type>]| {
5030b9ff9bfSAlex Crichton             let (size, offset) = variant_memory_info(types.iter().cloned());
5040b9ff9bfSAlex Crichton 
5050b9ff9bfSAlex Crichton             // Destination locals where the flat representation will be stored.
5060b9ff9bfSAlex Crichton             // These are automatically zero which handles unused fields too.
5070b9ff9bfSAlex Crichton             for (i, ty) in lowered.iter().enumerate() {
5080b9ff9bfSAlex Crichton                 uwriteln!(s, " (local $r{i} {ty})");
5090b9ff9bfSAlex Crichton             }
5100b9ff9bfSAlex Crichton 
5110b9ff9bfSAlex Crichton             // Return block each case jumps to after setting all locals.
5120b9ff9bfSAlex Crichton             s.push_str("block $r\n");
5130b9ff9bfSAlex Crichton 
5140b9ff9bfSAlex Crichton             // One extra block for "out of bounds discriminant".
5150b9ff9bfSAlex Crichton             for _ in 0..types.len() + 1 {
5160b9ff9bfSAlex Crichton                 s.push_str("block\n");
5170b9ff9bfSAlex Crichton             }
5180b9ff9bfSAlex Crichton 
5190b9ff9bfSAlex Crichton             // Load the discriminant and branch on it, storing it in
5200b9ff9bfSAlex Crichton             // `$r0` as well which is the first flat local representation.
5210b9ff9bfSAlex Crichton             let load = match size {
5220b9ff9bfSAlex Crichton                 DiscriminantSize::Size1 => "i32.load8_u",
5230b9ff9bfSAlex Crichton                 DiscriminantSize::Size2 => "i32.load16",
5240b9ff9bfSAlex Crichton                 DiscriminantSize::Size4 => "i32.load",
5250b9ff9bfSAlex Crichton             };
5260b9ff9bfSAlex Crichton             uwriteln!(s, "({load} (local.get 0))");
5270b9ff9bfSAlex Crichton             s.push_str("local.tee $r0\n");
5280b9ff9bfSAlex Crichton             s.push_str("br_table");
5290b9ff9bfSAlex Crichton             for i in 0..types.len() + 1 {
5300b9ff9bfSAlex Crichton                 uwrite!(s, " {i}");
5310b9ff9bfSAlex Crichton             }
5320b9ff9bfSAlex Crichton             s.push_str("\nend\n");
5330b9ff9bfSAlex Crichton 
5340b9ff9bfSAlex Crichton             // For each payload, which is in its own block, load payloads from
5350b9ff9bfSAlex Crichton             // memory as necessary and convert them into the final locals.
5360b9ff9bfSAlex Crichton             for ty in types {
5370b9ff9bfSAlex Crichton                 if let Some(ty) = ty {
5380b9ff9bfSAlex Crichton                     let ty_lowered = ty.lowered();
5390b9ff9bfSAlex Crichton                     ty.load_flat(s, "0", offset, helpers);
5400b9ff9bfSAlex Crichton                     for (i, (from, to)) in ty_lowered.iter().zip(&lowered[1..]).enumerate().rev() {
5410b9ff9bfSAlex Crichton                         let i = i + 1;
5420b9ff9bfSAlex Crichton                         match (from, to) {
5430b9ff9bfSAlex Crichton                             (CoreType::F32, CoreType::I32) => {
5440b9ff9bfSAlex Crichton                                 s.push_str("i32.reinterpret_f32\n");
5450b9ff9bfSAlex Crichton                             }
5460b9ff9bfSAlex Crichton                             (CoreType::I32, CoreType::I64) => {
5470b9ff9bfSAlex Crichton                                 s.push_str("i64.extend_i32_u\n");
5480b9ff9bfSAlex Crichton                             }
5490b9ff9bfSAlex Crichton                             (CoreType::F32, CoreType::I64) => {
5500b9ff9bfSAlex Crichton                                 s.push_str("i32.reinterpret_f32\n");
5510b9ff9bfSAlex Crichton                                 s.push_str("i64.extend_i32_u\n");
5520b9ff9bfSAlex Crichton                             }
5530b9ff9bfSAlex Crichton                             (CoreType::F64, CoreType::I64) => {
5540b9ff9bfSAlex Crichton                                 s.push_str("i64.reinterpret_f64\n");
5550b9ff9bfSAlex Crichton                             }
5560b9ff9bfSAlex Crichton                             (a, b) if a == b => {}
5570b9ff9bfSAlex Crichton                             _ => unimplemented!("convert {from:?} to {to:?}"),
5580b9ff9bfSAlex Crichton                         }
5590b9ff9bfSAlex Crichton                         uwriteln!(s, "local.set $r{i}");
5600b9ff9bfSAlex Crichton                     }
5610b9ff9bfSAlex Crichton                 }
5620b9ff9bfSAlex Crichton                 s.push_str("br $r\n");
5630b9ff9bfSAlex Crichton                 s.push_str("end\n");
5640b9ff9bfSAlex Crichton             }
5650b9ff9bfSAlex Crichton 
5660b9ff9bfSAlex Crichton             // The catch-all block for out-of-bounds discriminants.
5670b9ff9bfSAlex Crichton             s.push_str("unreachable\n");
5680b9ff9bfSAlex Crichton             s.push_str("end\n");
5690b9ff9bfSAlex Crichton             for i in 0..lowered.len() {
5700b9ff9bfSAlex Crichton                 uwriteln!(s, " local.get $r{i}");
5710b9ff9bfSAlex Crichton             }
5720b9ff9bfSAlex Crichton         };
5730b9ff9bfSAlex Crichton 
5740b9ff9bfSAlex Crichton         match self {
5750b9ff9bfSAlex Crichton             Type::Bool
5760b9ff9bfSAlex Crichton             | Type::S8
5770b9ff9bfSAlex Crichton             | Type::U8
5780b9ff9bfSAlex Crichton             | Type::S16
5790b9ff9bfSAlex Crichton             | Type::U16
5800b9ff9bfSAlex Crichton             | Type::S32
5810b9ff9bfSAlex Crichton             | Type::U32
5820b9ff9bfSAlex Crichton             | Type::Char
5830b9ff9bfSAlex Crichton             | Type::S64
5840b9ff9bfSAlex Crichton             | Type::U64
5850b9ff9bfSAlex Crichton             | Type::Float32
5860b9ff9bfSAlex Crichton             | Type::Float64
5870b9ff9bfSAlex Crichton             | Type::String
5880b9ff9bfSAlex Crichton             | Type::List(_)
5891b59b579SYordis Prieto             | Type::Map(_, _)
5900b9ff9bfSAlex Crichton             | Type::Flags(_)
5910b9ff9bfSAlex Crichton             | Type::Enum(_) => unreachable!(),
5920b9ff9bfSAlex Crichton 
5930b9ff9bfSAlex Crichton             Type::Record(r) => record(s, helpers, r),
5940b9ff9bfSAlex Crichton             Type::Tuple(t) => record(s, helpers, t),
5950b9ff9bfSAlex Crichton             Type::Variant(v) => variant(
5960b9ff9bfSAlex Crichton                 s,
5970b9ff9bfSAlex Crichton                 helpers,
5980b9ff9bfSAlex Crichton                 &v.iter().map(|t| t.as_ref()).collect::<Vec<_>>(),
5990b9ff9bfSAlex Crichton             ),
6000b9ff9bfSAlex Crichton             Type::Option(o) => variant(s, helpers, &[None, Some(&**o)]),
6010b9ff9bfSAlex Crichton             Type::Result { ok, err } => variant(s, helpers, &[ok.as_deref(), err.as_deref()]),
6020b9ff9bfSAlex Crichton         };
6030b9ff9bfSAlex Crichton         s.push_str(")\n");
6040b9ff9bfSAlex Crichton     }
6050b9ff9bfSAlex Crichton }
6060b9ff9bfSAlex Crichton 
6070b9ff9bfSAlex Crichton #[derive(Clone)]
6080b9ff9bfSAlex Crichton enum FlatSource {
6090b9ff9bfSAlex Crichton     Local(u32),
6100b9ff9bfSAlex Crichton     LocalConvert {
6110b9ff9bfSAlex Crichton         local: u32,
6120b9ff9bfSAlex Crichton         from: CoreType,
6130b9ff9bfSAlex Crichton         to: CoreType,
6140b9ff9bfSAlex Crichton     },
6150b9ff9bfSAlex Crichton }
6160b9ff9bfSAlex Crichton 
6170b9ff9bfSAlex Crichton impl fmt::Display for FlatSource {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result6180b9ff9bfSAlex Crichton     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6190b9ff9bfSAlex Crichton         match self {
6200b9ff9bfSAlex Crichton             FlatSource::Local(i) => write!(f, "(local.get {i})"),
6210b9ff9bfSAlex Crichton             FlatSource::LocalConvert { local, from, to } => {
6220b9ff9bfSAlex Crichton                 match (from, to) {
6230b9ff9bfSAlex Crichton                     (a, b) if a == b => write!(f, "(local.get {local})"),
6240b9ff9bfSAlex Crichton                     (CoreType::I32, CoreType::F32) => {
6250b9ff9bfSAlex Crichton                         write!(f, "(f32.reinterpret_i32 (local.get {local}))")
6260b9ff9bfSAlex Crichton                     }
6270b9ff9bfSAlex Crichton                     (CoreType::I64, CoreType::I32) => {
6280b9ff9bfSAlex Crichton                         write!(f, "(i32.wrap_i64 (local.get {local}))")
6290b9ff9bfSAlex Crichton                     }
6300b9ff9bfSAlex Crichton                     (CoreType::I64, CoreType::F64) => {
6310b9ff9bfSAlex Crichton                         write!(f, "(f64.reinterpret_i64 (local.get {local}))")
6320b9ff9bfSAlex Crichton                     }
6330b9ff9bfSAlex Crichton                     (CoreType::I64, CoreType::F32) => {
6340b9ff9bfSAlex Crichton                         write!(
6350b9ff9bfSAlex Crichton                             f,
6360b9ff9bfSAlex Crichton                             "(f32.reinterpret_i32 (i32.wrap_i64 (local.get {local})))"
6370b9ff9bfSAlex Crichton                         )
6380b9ff9bfSAlex Crichton                     }
6390b9ff9bfSAlex Crichton                     _ => unimplemented!("convert {from:?} to {to:?}"),
6400b9ff9bfSAlex Crichton                 }
6410b9ff9bfSAlex Crichton                 // ..
6420b9ff9bfSAlex Crichton             }
6430b9ff9bfSAlex Crichton         }
6440b9ff9bfSAlex Crichton     }
645ca7081a2SAlex Crichton }
646ca7081a2SAlex Crichton 
lower_record<'a>(types: impl Iterator<Item = &'a Type>, vec: &mut Vec<CoreType>)647ca7081a2SAlex Crichton fn lower_record<'a>(types: impl Iterator<Item = &'a Type>, vec: &mut Vec<CoreType>) {
648ca7081a2SAlex Crichton     for ty in types {
649ca7081a2SAlex Crichton         ty.lower(vec);
650ca7081a2SAlex Crichton     }
651ca7081a2SAlex Crichton }
652ca7081a2SAlex Crichton 
lower_variant<'a>(types: impl Iterator<Item = Option<&'a Type>>, vec: &mut Vec<CoreType>)653ca7081a2SAlex Crichton fn lower_variant<'a>(types: impl Iterator<Item = Option<&'a Type>>, vec: &mut Vec<CoreType>) {
654ca7081a2SAlex Crichton     vec.push(CoreType::I32);
655ca7081a2SAlex Crichton     let offset = vec.len();
656ca7081a2SAlex Crichton     for ty in types {
657ca7081a2SAlex Crichton         let ty = match ty {
658ca7081a2SAlex Crichton             Some(ty) => ty,
659ca7081a2SAlex Crichton             None => continue,
660ca7081a2SAlex Crichton         };
661ca7081a2SAlex Crichton         for (index, ty) in ty.lowered().iter().enumerate() {
662ca7081a2SAlex Crichton             let index = offset + index;
663ca7081a2SAlex Crichton             if index < vec.len() {
664ca7081a2SAlex Crichton                 vec[index] = vec[index].join(*ty);
665ca7081a2SAlex Crichton             } else {
666ca7081a2SAlex Crichton                 vec.push(*ty)
667ca7081a2SAlex Crichton             }
668ca7081a2SAlex Crichton         }
669ca7081a2SAlex Crichton     }
670ca7081a2SAlex Crichton }
671ca7081a2SAlex Crichton 
u32_count_from_flag_count(count: usize) -> usize672ca7081a2SAlex Crichton fn u32_count_from_flag_count(count: usize) -> usize {
673ca7081a2SAlex Crichton     match FlagsSize::from_count(count) {
674ca7081a2SAlex Crichton         FlagsSize::Size0 => 0,
675ca7081a2SAlex Crichton         FlagsSize::Size1 | FlagsSize::Size2 => 1,
676ca7081a2SAlex Crichton         FlagsSize::Size4Plus(n) => n.into(),
677ca7081a2SAlex Crichton     }
678ca7081a2SAlex Crichton }
679ca7081a2SAlex Crichton 
680ca7081a2SAlex Crichton struct SizeAndAlignment {
681ca7081a2SAlex Crichton     size: usize,
682ca7081a2SAlex Crichton     alignment: u32,
683ca7081a2SAlex Crichton }
684ca7081a2SAlex Crichton 
685ca7081a2SAlex Crichton impl Type {
lowered(&self) -> Vec<CoreType>686ca7081a2SAlex Crichton     fn lowered(&self) -> Vec<CoreType> {
687ca7081a2SAlex Crichton         let mut vec = Vec::new();
688ca7081a2SAlex Crichton         self.lower(&mut vec);
689ca7081a2SAlex Crichton         vec
690ca7081a2SAlex Crichton     }
691ca7081a2SAlex Crichton 
lower(&self, vec: &mut Vec<CoreType>)692ca7081a2SAlex Crichton     fn lower(&self, vec: &mut Vec<CoreType>) {
693ca7081a2SAlex Crichton         match self {
694ca7081a2SAlex Crichton             Type::Bool
695ca7081a2SAlex Crichton             | Type::U8
696ca7081a2SAlex Crichton             | Type::S8
697ca7081a2SAlex Crichton             | Type::S16
698ca7081a2SAlex Crichton             | Type::U16
699ca7081a2SAlex Crichton             | Type::S32
700ca7081a2SAlex Crichton             | Type::U32
701ca7081a2SAlex Crichton             | Type::Char
702ca7081a2SAlex Crichton             | Type::Enum(_) => vec.push(CoreType::I32),
703ca7081a2SAlex Crichton             Type::S64 | Type::U64 => vec.push(CoreType::I64),
704ca7081a2SAlex Crichton             Type::Float32 => vec.push(CoreType::F32),
705ca7081a2SAlex Crichton             Type::Float64 => vec.push(CoreType::F64),
7061b59b579SYordis Prieto             Type::String | Type::List(_) | Type::Map(_, _) => {
707ca7081a2SAlex Crichton                 vec.push(CoreType::I32);
708ca7081a2SAlex Crichton                 vec.push(CoreType::I32);
709ca7081a2SAlex Crichton             }
710ca7081a2SAlex Crichton             Type::Record(types) => lower_record(types.iter(), vec),
711ca7081a2SAlex Crichton             Type::Tuple(types) => lower_record(types.0.iter(), vec),
712ca7081a2SAlex Crichton             Type::Variant(types) => lower_variant(types.0.iter().map(|t| t.as_ref()), vec),
713ca7081a2SAlex Crichton             Type::Option(ty) => lower_variant([None, Some(&**ty)].into_iter(), vec),
714ca7081a2SAlex Crichton             Type::Result { ok, err } => {
715ca7081a2SAlex Crichton                 lower_variant([ok.as_deref(), err.as_deref()].into_iter(), vec)
716ca7081a2SAlex Crichton             }
717ca7081a2SAlex Crichton             Type::Flags(count) => vec.extend(
718ca7081a2SAlex Crichton                 iter::repeat(CoreType::I32).take(u32_count_from_flag_count(*count as usize)),
719ca7081a2SAlex Crichton             ),
720ca7081a2SAlex Crichton         }
721ca7081a2SAlex Crichton     }
722ca7081a2SAlex Crichton 
size_and_alignment(&self) -> SizeAndAlignment723ca7081a2SAlex Crichton     fn size_and_alignment(&self) -> SizeAndAlignment {
724ca7081a2SAlex Crichton         match self {
725ca7081a2SAlex Crichton             Type::Bool | Type::S8 | Type::U8 => SizeAndAlignment {
726ca7081a2SAlex Crichton                 size: 1,
727ca7081a2SAlex Crichton                 alignment: 1,
728ca7081a2SAlex Crichton             },
729ca7081a2SAlex Crichton 
730ca7081a2SAlex Crichton             Type::S16 | Type::U16 => SizeAndAlignment {
731ca7081a2SAlex Crichton                 size: 2,
732ca7081a2SAlex Crichton                 alignment: 2,
733ca7081a2SAlex Crichton             },
734ca7081a2SAlex Crichton 
735ca7081a2SAlex Crichton             Type::S32 | Type::U32 | Type::Char | Type::Float32 => SizeAndAlignment {
736ca7081a2SAlex Crichton                 size: 4,
737ca7081a2SAlex Crichton                 alignment: 4,
738ca7081a2SAlex Crichton             },
739ca7081a2SAlex Crichton 
740ca7081a2SAlex Crichton             Type::S64 | Type::U64 | Type::Float64 => SizeAndAlignment {
741ca7081a2SAlex Crichton                 size: 8,
742ca7081a2SAlex Crichton                 alignment: 8,
743ca7081a2SAlex Crichton             },
744ca7081a2SAlex Crichton 
7451b59b579SYordis Prieto             Type::String | Type::List(_) | Type::Map(_, _) => SizeAndAlignment {
746ca7081a2SAlex Crichton                 size: 8,
747ca7081a2SAlex Crichton                 alignment: 4,
748ca7081a2SAlex Crichton             },
749ca7081a2SAlex Crichton 
750ca7081a2SAlex Crichton             Type::Record(types) => record_size_and_alignment(types.iter()),
751ca7081a2SAlex Crichton 
752ca7081a2SAlex Crichton             Type::Tuple(types) => record_size_and_alignment(types.0.iter()),
753ca7081a2SAlex Crichton 
754ca7081a2SAlex Crichton             Type::Variant(types) => variant_size_and_alignment(types.0.iter().map(|t| t.as_ref())),
755ca7081a2SAlex Crichton 
756ca7081a2SAlex Crichton             Type::Enum(count) => variant_size_and_alignment((0..*count).map(|_| None)),
757ca7081a2SAlex Crichton 
758ca7081a2SAlex Crichton             Type::Option(ty) => variant_size_and_alignment([None, Some(&**ty)].into_iter()),
759ca7081a2SAlex Crichton 
760ca7081a2SAlex Crichton             Type::Result { ok, err } => {
761ca7081a2SAlex Crichton                 variant_size_and_alignment([ok.as_deref(), err.as_deref()].into_iter())
762ca7081a2SAlex Crichton             }
763ca7081a2SAlex Crichton 
764ca7081a2SAlex Crichton             Type::Flags(count) => match FlagsSize::from_count(*count as usize) {
765ca7081a2SAlex Crichton                 FlagsSize::Size0 => SizeAndAlignment {
766ca7081a2SAlex Crichton                     size: 0,
767ca7081a2SAlex Crichton                     alignment: 1,
768ca7081a2SAlex Crichton                 },
769ca7081a2SAlex Crichton                 FlagsSize::Size1 => SizeAndAlignment {
770ca7081a2SAlex Crichton                     size: 1,
771ca7081a2SAlex Crichton                     alignment: 1,
772ca7081a2SAlex Crichton                 },
773ca7081a2SAlex Crichton                 FlagsSize::Size2 => SizeAndAlignment {
774ca7081a2SAlex Crichton                     size: 2,
775ca7081a2SAlex Crichton                     alignment: 2,
776ca7081a2SAlex Crichton                 },
777ca7081a2SAlex Crichton                 FlagsSize::Size4Plus(n) => SizeAndAlignment {
778ca7081a2SAlex Crichton                     size: usize::from(n) * 4,
779ca7081a2SAlex Crichton                     alignment: 4,
780ca7081a2SAlex Crichton                 },
781ca7081a2SAlex Crichton             },
782ca7081a2SAlex Crichton         }
783ca7081a2SAlex Crichton     }
784ca7081a2SAlex Crichton }
785ca7081a2SAlex Crichton 
align_to(a: usize, align: u32) -> usize786ca7081a2SAlex Crichton fn align_to(a: usize, align: u32) -> usize {
787ca7081a2SAlex Crichton     let align = align as usize;
788ca7081a2SAlex Crichton     (a + (align - 1)) & !(align - 1)
789ca7081a2SAlex Crichton }
790ca7081a2SAlex Crichton 
record_field_offsets<'a>( types: impl IntoIterator<Item = &'a Type>, ) -> impl Iterator<Item = (u32, &'a Type)>7910b9ff9bfSAlex Crichton fn record_field_offsets<'a>(
7920b9ff9bfSAlex Crichton     types: impl IntoIterator<Item = &'a Type>,
7930b9ff9bfSAlex Crichton ) -> impl Iterator<Item = (u32, &'a Type)> {
7940b9ff9bfSAlex Crichton     let mut offset = 0;
7950b9ff9bfSAlex Crichton     types.into_iter().map(move |ty| {
7960b9ff9bfSAlex Crichton         let SizeAndAlignment { size, alignment } = ty.size_and_alignment();
7970b9ff9bfSAlex Crichton         let ret = align_to(offset, alignment);
7980b9ff9bfSAlex Crichton         offset = ret + size;
7990b9ff9bfSAlex Crichton         (ret as u32, ty)
8000b9ff9bfSAlex Crichton     })
8010b9ff9bfSAlex Crichton }
8020b9ff9bfSAlex Crichton 
record_size_and_alignment<'a>(types: impl IntoIterator<Item = &'a Type>) -> SizeAndAlignment8030b9ff9bfSAlex Crichton fn record_size_and_alignment<'a>(types: impl IntoIterator<Item = &'a Type>) -> SizeAndAlignment {
804ca7081a2SAlex Crichton     let mut offset = 0;
805ca7081a2SAlex Crichton     let mut align = 1;
806ca7081a2SAlex Crichton     for ty in types {
807ca7081a2SAlex Crichton         let SizeAndAlignment { size, alignment } = ty.size_and_alignment();
808ca7081a2SAlex Crichton         offset = align_to(offset, alignment) + size;
809ca7081a2SAlex Crichton         align = align.max(alignment);
810ca7081a2SAlex Crichton     }
811ca7081a2SAlex Crichton 
812ca7081a2SAlex Crichton     SizeAndAlignment {
813ca7081a2SAlex Crichton         size: align_to(offset, align),
814ca7081a2SAlex Crichton         alignment: align,
815ca7081a2SAlex Crichton     }
816ca7081a2SAlex Crichton }
817ca7081a2SAlex Crichton 
variant_size_and_alignment<'a>( types: impl ExactSizeIterator<Item = Option<&'a Type>>, ) -> SizeAndAlignment818ca7081a2SAlex Crichton fn variant_size_and_alignment<'a>(
819ca7081a2SAlex Crichton     types: impl ExactSizeIterator<Item = Option<&'a Type>>,
820ca7081a2SAlex Crichton ) -> SizeAndAlignment {
821ca7081a2SAlex Crichton     let discriminant_size = DiscriminantSize::from_count(types.len()).unwrap();
822ca7081a2SAlex Crichton     let mut alignment = u32::from(discriminant_size);
823ca7081a2SAlex Crichton     let mut size = 0;
824ca7081a2SAlex Crichton     for ty in types {
825ca7081a2SAlex Crichton         if let Some(ty) = ty {
826ca7081a2SAlex Crichton             let size_and_alignment = ty.size_and_alignment();
827ca7081a2SAlex Crichton             alignment = alignment.max(size_and_alignment.alignment);
828ca7081a2SAlex Crichton             size = size.max(size_and_alignment.size);
829ca7081a2SAlex Crichton         }
830ca7081a2SAlex Crichton     }
831ca7081a2SAlex Crichton 
832ca7081a2SAlex Crichton     SizeAndAlignment {
833ca7081a2SAlex Crichton         size: align_to(
834ca7081a2SAlex Crichton             align_to(usize::from(discriminant_size), alignment) + size,
835ca7081a2SAlex Crichton             alignment,
836ca7081a2SAlex Crichton         ),
837ca7081a2SAlex Crichton         alignment,
838ca7081a2SAlex Crichton     }
839ca7081a2SAlex Crichton }
840ca7081a2SAlex Crichton 
variant_memory_info<'a>( types: impl ExactSizeIterator<Item = Option<&'a Type>>, ) -> (DiscriminantSize, u32)8410b9ff9bfSAlex Crichton fn variant_memory_info<'a>(
8420b9ff9bfSAlex Crichton     types: impl ExactSizeIterator<Item = Option<&'a Type>>,
8430b9ff9bfSAlex Crichton ) -> (DiscriminantSize, u32) {
8440b9ff9bfSAlex Crichton     let discriminant_size = DiscriminantSize::from_count(types.len()).unwrap();
8450b9ff9bfSAlex Crichton     let mut alignment = u32::from(discriminant_size);
8460b9ff9bfSAlex Crichton     for ty in types {
8470b9ff9bfSAlex Crichton         if let Some(ty) = ty {
8480b9ff9bfSAlex Crichton             let size_and_alignment = ty.size_and_alignment();
8490b9ff9bfSAlex Crichton             alignment = alignment.max(size_and_alignment.alignment);
8500b9ff9bfSAlex Crichton         }
8510b9ff9bfSAlex Crichton     }
8520b9ff9bfSAlex Crichton 
8530b9ff9bfSAlex Crichton     (
8540b9ff9bfSAlex Crichton         discriminant_size,
8550b9ff9bfSAlex Crichton         align_to(usize::from(discriminant_size), alignment) as u32,
8560b9ff9bfSAlex Crichton     )
8570b9ff9bfSAlex Crichton }
8580b9ff9bfSAlex Crichton 
8590b9ff9bfSAlex Crichton /// Generates the internals of a core wasm module which imports a single
8600b9ff9bfSAlex Crichton /// component function `IMPORT_FUNCTION` and exports a single component
8610b9ff9bfSAlex Crichton /// function `EXPORT_FUNCTION`.
8620b9ff9bfSAlex Crichton ///
8630b9ff9bfSAlex Crichton /// The component function takes `params` as arguments and optionally returns
8640b9ff9bfSAlex Crichton /// `result`. The `lift_abi` and `lower_abi` fields indicate the ABI in-use for
8650b9ff9bfSAlex Crichton /// this operation.
make_import_and_export( params: &[&Type], result: Option<&Type>, lift_abi: LiftAbi, lower_abi: LowerAbi, ) -> String8660b9ff9bfSAlex Crichton fn make_import_and_export(
8670b9ff9bfSAlex Crichton     params: &[&Type],
8680b9ff9bfSAlex Crichton     result: Option<&Type>,
8690b9ff9bfSAlex Crichton     lift_abi: LiftAbi,
8700b9ff9bfSAlex Crichton     lower_abi: LowerAbi,
8710b9ff9bfSAlex Crichton ) -> String {
872ca7081a2SAlex Crichton     let params_lowered = params
873ca7081a2SAlex Crichton         .iter()
874ca7081a2SAlex Crichton         .flat_map(|ty| ty.lowered())
875ca7081a2SAlex Crichton         .collect::<Box<[_]>>();
876ca7081a2SAlex Crichton     let result_lowered = result.map(|t| t.lowered()).unwrap_or(Vec::new());
877ca7081a2SAlex Crichton 
8780b9ff9bfSAlex Crichton     let mut wat = String::new();
879ca7081a2SAlex Crichton 
8800b9ff9bfSAlex Crichton     enum Location {
8810b9ff9bfSAlex Crichton         Flat,
8820b9ff9bfSAlex Crichton         Indirect(u32),
883ca7081a2SAlex Crichton     }
884ca7081a2SAlex Crichton 
8850b9ff9bfSAlex Crichton     // Generate the core wasm type corresponding to the imported function being
8860b9ff9bfSAlex Crichton     // lowered with `lower_abi`.
8870b9ff9bfSAlex Crichton     wat.push_str(&format!("(type $import (func"));
8880b9ff9bfSAlex Crichton     let max_import_params = match lower_abi {
8890b9ff9bfSAlex Crichton         LowerAbi::Sync => MAX_FLAT_PARAMS,
8900b9ff9bfSAlex Crichton         LowerAbi::Async => MAX_FLAT_ASYNC_PARAMS,
891ca7081a2SAlex Crichton     };
8920b9ff9bfSAlex Crichton     let (import_params_loc, nparams) = push_params(&mut wat, &params_lowered, max_import_params);
8930b9ff9bfSAlex Crichton     let import_results_loc = match lower_abi {
8940b9ff9bfSAlex Crichton         LowerAbi::Sync => {
8950b9ff9bfSAlex Crichton             push_result_or_retptr(&mut wat, &result_lowered, nparams, MAX_FLAT_RESULTS)
8960b9ff9bfSAlex Crichton         }
8970b9ff9bfSAlex Crichton         LowerAbi::Async => {
8980b9ff9bfSAlex Crichton             let loc = if result.is_none() {
8990b9ff9bfSAlex Crichton                 Location::Flat
9000b9ff9bfSAlex Crichton             } else {
9010b9ff9bfSAlex Crichton                 wat.push_str(" (param i32)"); // result pointer
9020b9ff9bfSAlex Crichton                 Location::Indirect(nparams)
9030b9ff9bfSAlex Crichton             };
9040b9ff9bfSAlex Crichton             wat.push_str(" (result i32)"); // status code
9050b9ff9bfSAlex Crichton             loc
9060b9ff9bfSAlex Crichton         }
9070b9ff9bfSAlex Crichton     };
9080b9ff9bfSAlex Crichton     wat.push_str("))\n");
909ca7081a2SAlex Crichton 
9100b9ff9bfSAlex Crichton     // Generate the import function.
9110b9ff9bfSAlex Crichton     wat.push_str(&format!(
9120b9ff9bfSAlex Crichton         r#"(import "host" "{IMPORT_FUNCTION}" (func $host (type $import)))"#
9130b9ff9bfSAlex Crichton     ));
9140b9ff9bfSAlex Crichton 
9150b9ff9bfSAlex Crichton     // Do the same as above for the exported function's type which is lifted
9160b9ff9bfSAlex Crichton     // with `lift_abi`.
9170b9ff9bfSAlex Crichton     //
9180b9ff9bfSAlex Crichton     // Note that `export_results_loc` being `None` means that `task.return` is
9190b9ff9bfSAlex Crichton     // used to communicate results.
9200b9ff9bfSAlex Crichton     wat.push_str(&format!("(type $export (func"));
9210b9ff9bfSAlex Crichton     let (export_params_loc, _nparams) = push_params(&mut wat, &params_lowered, MAX_FLAT_PARAMS);
9220b9ff9bfSAlex Crichton     let export_results_loc = match lift_abi {
9230b9ff9bfSAlex Crichton         LiftAbi::Sync => Some(push_group(&mut wat, "result", &result_lowered, MAX_FLAT_RESULTS).0),
9240b9ff9bfSAlex Crichton         LiftAbi::AsyncCallback => {
9250b9ff9bfSAlex Crichton             wat.push_str(" (result i32)"); // status code
9260b9ff9bfSAlex Crichton             None
9270b9ff9bfSAlex Crichton         }
9280b9ff9bfSAlex Crichton         LiftAbi::AsyncStackful => None,
9290b9ff9bfSAlex Crichton     };
9300b9ff9bfSAlex Crichton     wat.push_str("))\n");
9310b9ff9bfSAlex Crichton 
9320b9ff9bfSAlex Crichton     // If the export is async, generate `task.return` as an import as well
933*ab78bd82SHo Kim     // which is necessary to communicate the results.
9340b9ff9bfSAlex Crichton     if export_results_loc.is_none() {
9350b9ff9bfSAlex Crichton         wat.push_str(&format!("(type $task.return (func"));
9360b9ff9bfSAlex Crichton         push_params(&mut wat, &result_lowered, MAX_FLAT_PARAMS);
9370b9ff9bfSAlex Crichton         wat.push_str("))\n");
9380b9ff9bfSAlex Crichton         wat.push_str(&format!(
9390b9ff9bfSAlex Crichton             r#"(import "" "task.return" (func $task.return (type $task.return)))"#
9400b9ff9bfSAlex Crichton         ));
941ca7081a2SAlex Crichton     }
942ca7081a2SAlex Crichton 
9430b9ff9bfSAlex Crichton     wat.push_str(&format!(
944ca7081a2SAlex Crichton         r#"
9450b9ff9bfSAlex Crichton (func (export "{EXPORT_FUNCTION}") (type $export)
9460b9ff9bfSAlex Crichton     (local $retptr i32)
9470b9ff9bfSAlex Crichton     (local $argptr i32)
9480b9ff9bfSAlex Crichton         "#
9490b9ff9bfSAlex Crichton     ));
9500b9ff9bfSAlex Crichton     let mut store_helpers = IndexSet::new();
9510b9ff9bfSAlex Crichton     let mut load_helpers = IndexSet::new();
952ca7081a2SAlex Crichton 
9530b9ff9bfSAlex Crichton     match (export_params_loc, import_params_loc) {
9540b9ff9bfSAlex Crichton         // flat => flat is just moving locals around
9550b9ff9bfSAlex Crichton         (Location::Flat, Location::Flat) => {
9560b9ff9bfSAlex Crichton             for (index, _) in params_lowered.iter().enumerate() {
9570b9ff9bfSAlex Crichton                 uwrite!(wat, "local.get {index}\n");
9580b9ff9bfSAlex Crichton             }
9590b9ff9bfSAlex Crichton         }
960ca7081a2SAlex Crichton 
9610b9ff9bfSAlex Crichton         // indirect => indirect is just moving locals around
9620b9ff9bfSAlex Crichton         (Location::Indirect(i), Location::Indirect(j)) => {
9630b9ff9bfSAlex Crichton             assert_eq!(j, 0);
9640b9ff9bfSAlex Crichton             uwrite!(wat, "local.get {i}\n");
9650b9ff9bfSAlex Crichton         }
966ca7081a2SAlex Crichton 
9670b9ff9bfSAlex Crichton         // flat => indirect means that all parameters are stored in memory as
9680b9ff9bfSAlex Crichton         // if it was a record of all the parameters.
9690b9ff9bfSAlex Crichton         (Location::Flat, Location::Indirect(_)) => {
9700b9ff9bfSAlex Crichton             let SizeAndAlignment { size, alignment } =
9710b9ff9bfSAlex Crichton                 record_size_and_alignment(params.iter().cloned());
9720b9ff9bfSAlex Crichton             wat.push_str(&format!(
973ca7081a2SAlex Crichton                 r#"
9740b9ff9bfSAlex Crichton                     (local.set $argptr
975ca7081a2SAlex Crichton                         (call $realloc
976ca7081a2SAlex Crichton                             (i32.const 0)
977ca7081a2SAlex Crichton                             (i32.const 0)
978ca7081a2SAlex Crichton                             (i32.const {alignment})
979ca7081a2SAlex Crichton                             (i32.const {size})))
9800b9ff9bfSAlex Crichton                     local.get $argptr
9810b9ff9bfSAlex Crichton                 "#
9820b9ff9bfSAlex Crichton             ));
9830b9ff9bfSAlex Crichton             let mut locals = (0..params_lowered.len() as u32).map(FlatSource::Local);
9840b9ff9bfSAlex Crichton             for (offset, ty) in record_field_offsets(params.iter().cloned()) {
9850b9ff9bfSAlex Crichton                 ty.store_flat(&mut wat, "$argptr", offset, &mut locals, &mut store_helpers);
9860b9ff9bfSAlex Crichton             }
9870b9ff9bfSAlex Crichton             assert!(locals.next().is_none());
9880b9ff9bfSAlex Crichton         }
989ca7081a2SAlex Crichton 
9900b9ff9bfSAlex Crichton         (Location::Indirect(_), Location::Flat) => unreachable!(),
9910b9ff9bfSAlex Crichton     }
992ca7081a2SAlex Crichton 
9930b9ff9bfSAlex Crichton     // Pass a return-pointer if necessary.
9940b9ff9bfSAlex Crichton     match import_results_loc {
9950b9ff9bfSAlex Crichton         Location::Flat => {}
9960b9ff9bfSAlex Crichton         Location::Indirect(_) => {
9970b9ff9bfSAlex Crichton             let SizeAndAlignment { size, alignment } = result.unwrap().size_and_alignment();
9980b9ff9bfSAlex Crichton 
9990b9ff9bfSAlex Crichton             wat.push_str(&format!(
10000b9ff9bfSAlex Crichton                 r#"
10010b9ff9bfSAlex Crichton                     (local.set $retptr
10020b9ff9bfSAlex Crichton                         (call $realloc
10030b9ff9bfSAlex Crichton                             (i32.const 0)
10040b9ff9bfSAlex Crichton                             (i32.const 0)
10050b9ff9bfSAlex Crichton                             (i32.const {alignment})
10060b9ff9bfSAlex Crichton                             (i32.const {size})))
10070b9ff9bfSAlex Crichton                     local.get $retptr
10080b9ff9bfSAlex Crichton                 "#
10090b9ff9bfSAlex Crichton             ));
1010ca7081a2SAlex Crichton         }
1011ca7081a2SAlex Crichton     }
1012ca7081a2SAlex Crichton 
10130b9ff9bfSAlex Crichton     wat.push_str("call $host\n");
10140b9ff9bfSAlex Crichton 
10150b9ff9bfSAlex Crichton     // Assert the lowered call is ready if an async code was returned.
10160b9ff9bfSAlex Crichton     //
10170b9ff9bfSAlex Crichton     // TODO: handle when the import isn't ready yet
10180b9ff9bfSAlex Crichton     if let LowerAbi::Async = lower_abi {
10190b9ff9bfSAlex Crichton         wat.push_str("i32.const 2\n");
10200b9ff9bfSAlex Crichton         wat.push_str("i32.ne\n");
10210b9ff9bfSAlex Crichton         wat.push_str("if unreachable end\n");
10220b9ff9bfSAlex Crichton     }
10230b9ff9bfSAlex Crichton 
10240b9ff9bfSAlex Crichton     // TODO: conditionally inject a yield here
10250b9ff9bfSAlex Crichton 
10260b9ff9bfSAlex Crichton     match (import_results_loc, export_results_loc) {
10270b9ff9bfSAlex Crichton         // flat => flat results involves nothing, the results are already on
10280b9ff9bfSAlex Crichton         // the stack.
10290b9ff9bfSAlex Crichton         (Location::Flat, Some(Location::Flat)) => {}
10300b9ff9bfSAlex Crichton 
10310b9ff9bfSAlex Crichton         // indirect => indirect results requires returning the `$retptr` the
10320b9ff9bfSAlex Crichton         // host call filled in.
10330b9ff9bfSAlex Crichton         (Location::Indirect(_), Some(Location::Indirect(_))) => {
10340b9ff9bfSAlex Crichton             wat.push_str("local.get $retptr\n");
10350b9ff9bfSAlex Crichton         }
10360b9ff9bfSAlex Crichton 
10370b9ff9bfSAlex Crichton         // indirect => flat requires loading the result from the return pointer
10380b9ff9bfSAlex Crichton         (Location::Indirect(_), Some(Location::Flat)) => {
10390b9ff9bfSAlex Crichton             result
10400b9ff9bfSAlex Crichton                 .unwrap()
10410b9ff9bfSAlex Crichton                 .load_flat(&mut wat, "$retptr", 0, &mut load_helpers);
10420b9ff9bfSAlex Crichton         }
10430b9ff9bfSAlex Crichton 
10440b9ff9bfSAlex Crichton         // flat => task.return is easy, the results are already there so just
10450b9ff9bfSAlex Crichton         // call the function.
10460b9ff9bfSAlex Crichton         (Location::Flat, None) => {
10470b9ff9bfSAlex Crichton             wat.push_str("call $task.return\n");
10480b9ff9bfSAlex Crichton         }
10490b9ff9bfSAlex Crichton 
10500b9ff9bfSAlex Crichton         // indirect => task.return needs to forward `$retptr` if the results
10510b9ff9bfSAlex Crichton         // are indirect, or otherwise it must be loaded from memory to a flat
10520b9ff9bfSAlex Crichton         // representation.
10530b9ff9bfSAlex Crichton         (Location::Indirect(_), None) => {
10540b9ff9bfSAlex Crichton             if result_lowered.len() <= MAX_FLAT_PARAMS {
10550b9ff9bfSAlex Crichton                 result
10560b9ff9bfSAlex Crichton                     .unwrap()
10570b9ff9bfSAlex Crichton                     .load_flat(&mut wat, "$retptr", 0, &mut load_helpers);
10580b9ff9bfSAlex Crichton             } else {
10590b9ff9bfSAlex Crichton                 wat.push_str("local.get $retptr\n");
10600b9ff9bfSAlex Crichton             }
10610b9ff9bfSAlex Crichton             wat.push_str("call $task.return\n");
10620b9ff9bfSAlex Crichton         }
10630b9ff9bfSAlex Crichton 
10640b9ff9bfSAlex Crichton         (Location::Flat, Some(Location::Indirect(_))) => unreachable!(),
10650b9ff9bfSAlex Crichton     }
10660b9ff9bfSAlex Crichton 
10670b9ff9bfSAlex Crichton     if let LiftAbi::AsyncCallback = lift_abi {
10680b9ff9bfSAlex Crichton         wat.push_str("i32.const 0\n"); // completed status code
10690b9ff9bfSAlex Crichton     }
10700b9ff9bfSAlex Crichton 
10710b9ff9bfSAlex Crichton     wat.push_str(")\n");
10720b9ff9bfSAlex Crichton 
10730b9ff9bfSAlex Crichton     // Generate a `callback` function for the callback ABI.
10740b9ff9bfSAlex Crichton     //
10750b9ff9bfSAlex Crichton     // TODO: fill this in
10760b9ff9bfSAlex Crichton     if let LiftAbi::AsyncCallback = lift_abi {
10770b9ff9bfSAlex Crichton         wat.push_str(
10780b9ff9bfSAlex Crichton             r#"
10790b9ff9bfSAlex Crichton (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
10800b9ff9bfSAlex Crichton             "#,
10810b9ff9bfSAlex Crichton         );
10820b9ff9bfSAlex Crichton     }
10830b9ff9bfSAlex Crichton 
10840b9ff9bfSAlex Crichton     // Fill out all store/load helpers that were needed during generation
10850b9ff9bfSAlex Crichton     // above. This is a fix-point-loop since each helper may end up requiring
10860b9ff9bfSAlex Crichton     // more helpers.
10870b9ff9bfSAlex Crichton     let mut i = 0;
10880b9ff9bfSAlex Crichton     while i < store_helpers.len() {
10890b9ff9bfSAlex Crichton         let ty = store_helpers[i].0;
10900b9ff9bfSAlex Crichton         ty.store_flat_helper(&mut wat, i, &mut store_helpers);
10910b9ff9bfSAlex Crichton         i += 1;
10920b9ff9bfSAlex Crichton     }
10930b9ff9bfSAlex Crichton     i = 0;
10940b9ff9bfSAlex Crichton     while i < load_helpers.len() {
10950b9ff9bfSAlex Crichton         let ty = load_helpers[i].0;
10960b9ff9bfSAlex Crichton         ty.load_flat_helper(&mut wat, i, &mut load_helpers);
10970b9ff9bfSAlex Crichton         i += 1;
10980b9ff9bfSAlex Crichton     }
10990b9ff9bfSAlex Crichton 
11000b9ff9bfSAlex Crichton     return wat;
11010b9ff9bfSAlex Crichton 
11020b9ff9bfSAlex Crichton     fn push_params(wat: &mut String, params: &[CoreType], max_flat: usize) -> (Location, u32) {
11030b9ff9bfSAlex Crichton         push_group(wat, "param", params, max_flat)
11040b9ff9bfSAlex Crichton     }
11050b9ff9bfSAlex Crichton 
11060b9ff9bfSAlex Crichton     fn push_group(
11070b9ff9bfSAlex Crichton         wat: &mut String,
11080b9ff9bfSAlex Crichton         name: &str,
11090b9ff9bfSAlex Crichton         params: &[CoreType],
11100b9ff9bfSAlex Crichton         max_flat: usize,
11110b9ff9bfSAlex Crichton     ) -> (Location, u32) {
11120b9ff9bfSAlex Crichton         let mut nparams = 0;
11130b9ff9bfSAlex Crichton         let loc = if params.is_empty() {
11140b9ff9bfSAlex Crichton             // nothing to emit...
11150b9ff9bfSAlex Crichton             Location::Flat
11160b9ff9bfSAlex Crichton         } else if params.len() <= max_flat {
11170b9ff9bfSAlex Crichton             wat.push_str(&format!(" ({name}"));
11180b9ff9bfSAlex Crichton             for ty in params {
11190b9ff9bfSAlex Crichton                 wat.push_str(&format!(" {ty}"));
11200b9ff9bfSAlex Crichton                 nparams += 1;
11210b9ff9bfSAlex Crichton             }
11220b9ff9bfSAlex Crichton             wat.push_str(")");
11230b9ff9bfSAlex Crichton             Location::Flat
11240b9ff9bfSAlex Crichton         } else {
11250b9ff9bfSAlex Crichton             wat.push_str(&format!(" ({name} i32)"));
11260b9ff9bfSAlex Crichton             nparams += 1;
11270b9ff9bfSAlex Crichton             Location::Indirect(0)
11280b9ff9bfSAlex Crichton         };
11290b9ff9bfSAlex Crichton         (loc, nparams)
11300b9ff9bfSAlex Crichton     }
11310b9ff9bfSAlex Crichton 
11320b9ff9bfSAlex Crichton     fn push_result_or_retptr(
11330b9ff9bfSAlex Crichton         wat: &mut String,
11340b9ff9bfSAlex Crichton         results: &[CoreType],
11350b9ff9bfSAlex Crichton         nparams: u32,
11360b9ff9bfSAlex Crichton         max_flat: usize,
11370b9ff9bfSAlex Crichton     ) -> Location {
11380b9ff9bfSAlex Crichton         if results.is_empty() {
11390b9ff9bfSAlex Crichton             // nothing to emit...
11400b9ff9bfSAlex Crichton             Location::Flat
11410b9ff9bfSAlex Crichton         } else if results.len() <= max_flat {
11420b9ff9bfSAlex Crichton             wat.push_str(" (result");
11430b9ff9bfSAlex Crichton             for ty in results {
11440b9ff9bfSAlex Crichton                 wat.push_str(&format!(" {ty}"));
11450b9ff9bfSAlex Crichton             }
11460b9ff9bfSAlex Crichton             wat.push_str(")");
11470b9ff9bfSAlex Crichton             Location::Flat
11480b9ff9bfSAlex Crichton         } else {
11490b9ff9bfSAlex Crichton             wat.push_str(" (param i32)");
11500b9ff9bfSAlex Crichton             Location::Indirect(nparams)
11510b9ff9bfSAlex Crichton         }
11520b9ff9bfSAlex Crichton     }
11530b9ff9bfSAlex Crichton }
11540b9ff9bfSAlex Crichton 
11550b9ff9bfSAlex Crichton struct Helper<'a>(&'a Type);
11560b9ff9bfSAlex Crichton 
11570b9ff9bfSAlex Crichton impl Hash for Helper<'_> {
hash<H: Hasher>(&self, h: &mut H)11580b9ff9bfSAlex Crichton     fn hash<H: Hasher>(&self, h: &mut H) {
11590b9ff9bfSAlex Crichton         std::ptr::hash(self.0, h);
11600b9ff9bfSAlex Crichton     }
11610b9ff9bfSAlex Crichton }
11620b9ff9bfSAlex Crichton 
11630b9ff9bfSAlex Crichton impl PartialEq for Helper<'_> {
eq(&self, other: &Self) -> bool11640b9ff9bfSAlex Crichton     fn eq(&self, other: &Self) -> bool {
11650b9ff9bfSAlex Crichton         std::ptr::eq(self.0, other.0)
11660b9ff9bfSAlex Crichton     }
11670b9ff9bfSAlex Crichton }
11680b9ff9bfSAlex Crichton 
11690b9ff9bfSAlex Crichton impl Eq for Helper<'_> {}
11700b9ff9bfSAlex Crichton 
make_rust_name(name_counter: &mut u32) -> Ident1171ca7081a2SAlex Crichton fn make_rust_name(name_counter: &mut u32) -> Ident {
1172ca7081a2SAlex Crichton     let name = format_ident!("Foo{name_counter}");
1173ca7081a2SAlex Crichton     *name_counter += 1;
1174ca7081a2SAlex Crichton     name
1175ca7081a2SAlex Crichton }
1176ca7081a2SAlex Crichton 
1177ca7081a2SAlex Crichton /// Generate a [`TokenStream`] containing the rust type name for a type.
1178ca7081a2SAlex Crichton ///
1179ca7081a2SAlex Crichton /// The `name_counter` parameter is used to generate names for each recursively visited type.  The `declarations`
1180ca7081a2SAlex Crichton /// parameter is used to accumulate declarations for each recursively visited type.
rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStream) -> TokenStream1181ca7081a2SAlex Crichton pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStream) -> TokenStream {
1182ca7081a2SAlex Crichton     match ty {
1183ca7081a2SAlex Crichton         Type::Bool => quote!(bool),
1184ca7081a2SAlex Crichton         Type::S8 => quote!(i8),
1185ca7081a2SAlex Crichton         Type::U8 => quote!(u8),
1186ca7081a2SAlex Crichton         Type::S16 => quote!(i16),
1187ca7081a2SAlex Crichton         Type::U16 => quote!(u16),
1188ca7081a2SAlex Crichton         Type::S32 => quote!(i32),
1189ca7081a2SAlex Crichton         Type::U32 => quote!(u32),
1190ca7081a2SAlex Crichton         Type::S64 => quote!(i64),
1191ca7081a2SAlex Crichton         Type::U64 => quote!(u64),
1192ca7081a2SAlex Crichton         Type::Float32 => quote!(Float32),
1193ca7081a2SAlex Crichton         Type::Float64 => quote!(Float64),
1194ca7081a2SAlex Crichton         Type::Char => quote!(char),
1195ca7081a2SAlex Crichton         Type::String => quote!(Box<str>),
1196ca7081a2SAlex Crichton         Type::List(ty) => {
1197ca7081a2SAlex Crichton             let ty = rust_type(ty, name_counter, declarations);
1198ca7081a2SAlex Crichton             quote!(Vec<#ty>)
1199ca7081a2SAlex Crichton         }
12001b59b579SYordis Prieto         Type::Map(key_ty, value_ty) => {
12011b59b579SYordis Prieto             let key_ty = rust_type(key_ty, name_counter, declarations);
12021b59b579SYordis Prieto             let value_ty = rust_type(value_ty, name_counter, declarations);
12031b59b579SYordis Prieto             quote!(std::collections::HashMap<#key_ty, #value_ty>)
12041b59b579SYordis Prieto         }
1205ca7081a2SAlex Crichton         Type::Record(types) => {
1206ca7081a2SAlex Crichton             let fields = types
1207ca7081a2SAlex Crichton                 .iter()
1208ca7081a2SAlex Crichton                 .enumerate()
1209ca7081a2SAlex Crichton                 .map(|(index, ty)| {
1210ca7081a2SAlex Crichton                     let name = format_ident!("f{index}");
1211ca7081a2SAlex Crichton                     let ty = rust_type(ty, name_counter, declarations);
1212ca7081a2SAlex Crichton                     quote!(#name: #ty,)
1213ca7081a2SAlex Crichton                 })
1214ca7081a2SAlex Crichton                 .collect::<TokenStream>();
1215ca7081a2SAlex Crichton 
1216ca7081a2SAlex Crichton             let name = make_rust_name(name_counter);
1217ca7081a2SAlex Crichton 
1218ca7081a2SAlex Crichton             declarations.extend(quote! {
1219ca7081a2SAlex Crichton                 #[derive(ComponentType, Lift, Lower, PartialEq, Debug, Clone, Arbitrary)]
1220ca7081a2SAlex Crichton                 #[component(record)]
1221ca7081a2SAlex Crichton                 struct #name {
1222ca7081a2SAlex Crichton                     #fields
1223ca7081a2SAlex Crichton                 }
1224ca7081a2SAlex Crichton             });
1225ca7081a2SAlex Crichton 
1226ca7081a2SAlex Crichton             quote!(#name)
1227ca7081a2SAlex Crichton         }
1228ca7081a2SAlex Crichton         Type::Tuple(types) => {
1229ca7081a2SAlex Crichton             let fields = types
1230ca7081a2SAlex Crichton                 .0
1231ca7081a2SAlex Crichton                 .iter()
1232ca7081a2SAlex Crichton                 .map(|ty| {
1233ca7081a2SAlex Crichton                     let ty = rust_type(ty, name_counter, declarations);
1234ca7081a2SAlex Crichton                     quote!(#ty,)
1235ca7081a2SAlex Crichton                 })
1236ca7081a2SAlex Crichton                 .collect::<TokenStream>();
1237ca7081a2SAlex Crichton 
1238ca7081a2SAlex Crichton             quote!((#fields))
1239ca7081a2SAlex Crichton         }
1240ca7081a2SAlex Crichton         Type::Variant(types) => {
1241ca7081a2SAlex Crichton             let cases = types
1242ca7081a2SAlex Crichton                 .0
1243ca7081a2SAlex Crichton                 .iter()
1244ca7081a2SAlex Crichton                 .enumerate()
1245ca7081a2SAlex Crichton                 .map(|(index, ty)| {
1246ca7081a2SAlex Crichton                     let name = format_ident!("C{index}");
1247ca7081a2SAlex Crichton                     let ty = match ty {
1248ca7081a2SAlex Crichton                         Some(ty) => {
1249ca7081a2SAlex Crichton                             let ty = rust_type(ty, name_counter, declarations);
1250ca7081a2SAlex Crichton                             quote!((#ty))
1251ca7081a2SAlex Crichton                         }
1252ca7081a2SAlex Crichton                         None => quote!(),
1253ca7081a2SAlex Crichton                     };
1254ca7081a2SAlex Crichton                     quote!(#name #ty,)
1255ca7081a2SAlex Crichton                 })
1256ca7081a2SAlex Crichton                 .collect::<TokenStream>();
1257ca7081a2SAlex Crichton 
1258ca7081a2SAlex Crichton             let name = make_rust_name(name_counter);
1259ca7081a2SAlex Crichton             declarations.extend(quote! {
1260ca7081a2SAlex Crichton                 #[derive(ComponentType, Lift, Lower, PartialEq, Debug, Clone, Arbitrary)]
1261ca7081a2SAlex Crichton                 #[component(variant)]
1262ca7081a2SAlex Crichton                 enum #name {
1263ca7081a2SAlex Crichton                     #cases
1264ca7081a2SAlex Crichton                 }
1265ca7081a2SAlex Crichton             });
1266ca7081a2SAlex Crichton 
1267ca7081a2SAlex Crichton             quote!(#name)
1268ca7081a2SAlex Crichton         }
1269ca7081a2SAlex Crichton         Type::Enum(count) => {
1270ca7081a2SAlex Crichton             let cases = (0..*count)
1271ca7081a2SAlex Crichton                 .map(|index| {
1272ca7081a2SAlex Crichton                     let name = format_ident!("E{index}");
1273ca7081a2SAlex Crichton                     quote!(#name,)
1274ca7081a2SAlex Crichton                 })
1275ca7081a2SAlex Crichton                 .collect::<TokenStream>();
1276ca7081a2SAlex Crichton 
1277ca7081a2SAlex Crichton             let name = make_rust_name(name_counter);
12787d857e63SAlex Crichton             let repr = match DiscriminantSize::from_count(*count as usize).unwrap() {
12797d857e63SAlex Crichton                 DiscriminantSize::Size1 => quote!(u8),
12807d857e63SAlex Crichton                 DiscriminantSize::Size2 => quote!(u16),
12817d857e63SAlex Crichton                 DiscriminantSize::Size4 => quote!(u32),
1282ca7081a2SAlex Crichton             };
1283ca7081a2SAlex Crichton 
1284ca7081a2SAlex Crichton             declarations.extend(quote! {
12851b59b579SYordis Prieto                 #[derive(ComponentType, Lift, Lower, PartialEq, Eq, Hash, Debug, Copy, Clone, Arbitrary)]
1286ca7081a2SAlex Crichton                 #[component(enum)]
1287ca7081a2SAlex Crichton                 #[repr(#repr)]
1288ca7081a2SAlex Crichton                 enum #name {
1289ca7081a2SAlex Crichton                     #cases
1290ca7081a2SAlex Crichton                 }
1291ca7081a2SAlex Crichton             });
1292ca7081a2SAlex Crichton 
1293ca7081a2SAlex Crichton             quote!(#name)
1294ca7081a2SAlex Crichton         }
1295ca7081a2SAlex Crichton         Type::Option(ty) => {
1296ca7081a2SAlex Crichton             let ty = rust_type(ty, name_counter, declarations);
1297ca7081a2SAlex Crichton             quote!(Option<#ty>)
1298ca7081a2SAlex Crichton         }
1299ca7081a2SAlex Crichton         Type::Result { ok, err } => {
1300ca7081a2SAlex Crichton             let ok = match ok {
1301ca7081a2SAlex Crichton                 Some(ok) => rust_type(ok, name_counter, declarations),
1302ca7081a2SAlex Crichton                 None => quote!(()),
1303ca7081a2SAlex Crichton             };
1304ca7081a2SAlex Crichton             let err = match err {
1305ca7081a2SAlex Crichton                 Some(err) => rust_type(err, name_counter, declarations),
1306ca7081a2SAlex Crichton                 None => quote!(()),
1307ca7081a2SAlex Crichton             };
1308ca7081a2SAlex Crichton             quote!(Result<#ok, #err>)
1309ca7081a2SAlex Crichton         }
1310ca7081a2SAlex Crichton         Type::Flags(count) => {
1311ca7081a2SAlex Crichton             let type_name = make_rust_name(name_counter);
1312ca7081a2SAlex Crichton 
1313ca7081a2SAlex Crichton             let mut flags = TokenStream::new();
1314ca7081a2SAlex Crichton             let mut names = TokenStream::new();
1315ca7081a2SAlex Crichton 
1316ca7081a2SAlex Crichton             for index in 0..*count {
1317ca7081a2SAlex Crichton                 let name = format_ident!("F{index}");
1318ca7081a2SAlex Crichton                 flags.extend(quote!(const #name;));
1319ca7081a2SAlex Crichton                 names.extend(quote!(#type_name::#name,))
1320ca7081a2SAlex Crichton             }
1321ca7081a2SAlex Crichton 
1322ca7081a2SAlex Crichton             declarations.extend(quote! {
1323ca7081a2SAlex Crichton                 wasmtime::component::flags! {
1324ca7081a2SAlex Crichton                     #type_name {
1325ca7081a2SAlex Crichton                         #flags
1326ca7081a2SAlex Crichton                     }
1327ca7081a2SAlex Crichton                 }
1328ca7081a2SAlex Crichton 
1329ca7081a2SAlex Crichton                 impl<'a> arbitrary::Arbitrary<'a> for #type_name {
1330ca7081a2SAlex Crichton                     fn arbitrary(input: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1331ca7081a2SAlex Crichton                         let mut flags = #type_name::default();
1332ca7081a2SAlex Crichton                         for flag in [#names] {
1333ca7081a2SAlex Crichton                             if input.arbitrary()? {
1334ca7081a2SAlex Crichton                                 flags |= flag;
1335ca7081a2SAlex Crichton                             }
1336ca7081a2SAlex Crichton                         }
1337ca7081a2SAlex Crichton                         Ok(flags)
1338ca7081a2SAlex Crichton                     }
1339ca7081a2SAlex Crichton                 }
1340ca7081a2SAlex Crichton             });
1341ca7081a2SAlex Crichton 
1342ca7081a2SAlex Crichton             quote!(#type_name)
1343ca7081a2SAlex Crichton         }
1344ca7081a2SAlex Crichton     }
1345ca7081a2SAlex Crichton }
1346ca7081a2SAlex Crichton 
1347ca7081a2SAlex Crichton #[derive(Default)]
1348ca7081a2SAlex Crichton struct TypesBuilder<'a> {
1349ca7081a2SAlex Crichton     next: u32,
1350ca7081a2SAlex Crichton     worklist: Vec<(u32, &'a Type)>,
1351ca7081a2SAlex Crichton }
1352ca7081a2SAlex Crichton 
1353ca7081a2SAlex Crichton impl<'a> TypesBuilder<'a> {
write_ref(&mut self, ty: &'a Type, dst: &mut String)1354ca7081a2SAlex Crichton     fn write_ref(&mut self, ty: &'a Type, dst: &mut String) {
1355ca7081a2SAlex Crichton         match ty {
1356ca7081a2SAlex Crichton             // Primitive types can be referenced directly
1357ca7081a2SAlex Crichton             Type::Bool => dst.push_str("bool"),
1358ca7081a2SAlex Crichton             Type::S8 => dst.push_str("s8"),
1359ca7081a2SAlex Crichton             Type::U8 => dst.push_str("u8"),
1360ca7081a2SAlex Crichton             Type::S16 => dst.push_str("s16"),
1361ca7081a2SAlex Crichton             Type::U16 => dst.push_str("u16"),
1362ca7081a2SAlex Crichton             Type::S32 => dst.push_str("s32"),
1363ca7081a2SAlex Crichton             Type::U32 => dst.push_str("u32"),
1364ca7081a2SAlex Crichton             Type::S64 => dst.push_str("s64"),
1365ca7081a2SAlex Crichton             Type::U64 => dst.push_str("u64"),
1366ca7081a2SAlex Crichton             Type::Float32 => dst.push_str("float32"),
1367ca7081a2SAlex Crichton             Type::Float64 => dst.push_str("float64"),
1368ca7081a2SAlex Crichton             Type::Char => dst.push_str("char"),
1369ca7081a2SAlex Crichton             Type::String => dst.push_str("string"),
1370ca7081a2SAlex Crichton 
1371ca7081a2SAlex Crichton             // Otherwise emit a reference to the type and remember to generate
1372ca7081a2SAlex Crichton             // the corresponding type alias later.
1373ca7081a2SAlex Crichton             Type::List(_)
13741b59b579SYordis Prieto             | Type::Map(_, _)
1375ca7081a2SAlex Crichton             | Type::Record(_)
1376ca7081a2SAlex Crichton             | Type::Tuple(_)
1377ca7081a2SAlex Crichton             | Type::Variant(_)
1378ca7081a2SAlex Crichton             | Type::Enum(_)
1379ca7081a2SAlex Crichton             | Type::Option(_)
1380ca7081a2SAlex Crichton             | Type::Result { .. }
1381ca7081a2SAlex Crichton             | Type::Flags(_) => {
1382ca7081a2SAlex Crichton                 let idx = self.next;
1383ca7081a2SAlex Crichton                 self.next += 1;
13840b9ff9bfSAlex Crichton                 uwrite!(dst, "$t{idx}");
1385ca7081a2SAlex Crichton                 self.worklist.push((idx, ty));
1386ca7081a2SAlex Crichton             }
1387ca7081a2SAlex Crichton         }
1388ca7081a2SAlex Crichton     }
1389ca7081a2SAlex Crichton 
write_decl(&mut self, idx: u32, ty: &'a Type) -> String1390ca7081a2SAlex Crichton     fn write_decl(&mut self, idx: u32, ty: &'a Type) -> String {
1391ca7081a2SAlex Crichton         let mut decl = format!("(type $t{idx}' ");
1392ca7081a2SAlex Crichton         match ty {
1393ca7081a2SAlex Crichton             Type::Bool
1394ca7081a2SAlex Crichton             | Type::S8
1395ca7081a2SAlex Crichton             | Type::U8
1396ca7081a2SAlex Crichton             | Type::S16
1397ca7081a2SAlex Crichton             | Type::U16
1398ca7081a2SAlex Crichton             | Type::S32
1399ca7081a2SAlex Crichton             | Type::U32
1400ca7081a2SAlex Crichton             | Type::S64
1401ca7081a2SAlex Crichton             | Type::U64
1402ca7081a2SAlex Crichton             | Type::Float32
1403ca7081a2SAlex Crichton             | Type::Float64
1404ca7081a2SAlex Crichton             | Type::Char
1405ca7081a2SAlex Crichton             | Type::String => unreachable!(),
1406ca7081a2SAlex Crichton 
1407ca7081a2SAlex Crichton             Type::List(ty) => {
1408ca7081a2SAlex Crichton                 decl.push_str("(list ");
1409ca7081a2SAlex Crichton                 self.write_ref(ty, &mut decl);
1410ca7081a2SAlex Crichton                 decl.push_str(")");
1411ca7081a2SAlex Crichton             }
14121b59b579SYordis Prieto             Type::Map(key_ty, value_ty) => {
14131b59b579SYordis Prieto                 decl.push_str("(map ");
14141b59b579SYordis Prieto                 self.write_ref(key_ty, &mut decl);
14151b59b579SYordis Prieto                 decl.push_str(" ");
14161b59b579SYordis Prieto                 self.write_ref(value_ty, &mut decl);
14171b59b579SYordis Prieto                 decl.push_str(")");
14181b59b579SYordis Prieto             }
1419ca7081a2SAlex Crichton             Type::Record(types) => {
1420ca7081a2SAlex Crichton                 decl.push_str("(record");
1421ca7081a2SAlex Crichton                 for (index, ty) in types.iter().enumerate() {
14220b9ff9bfSAlex Crichton                     uwrite!(decl, r#" (field "f{index}" "#);
1423ca7081a2SAlex Crichton                     self.write_ref(ty, &mut decl);
1424ca7081a2SAlex Crichton                     decl.push_str(")");
1425ca7081a2SAlex Crichton                 }
1426ca7081a2SAlex Crichton                 decl.push_str(")");
1427ca7081a2SAlex Crichton             }
1428ca7081a2SAlex Crichton             Type::Tuple(types) => {
1429ca7081a2SAlex Crichton                 decl.push_str("(tuple");
1430ca7081a2SAlex Crichton                 for ty in types.iter() {
1431ca7081a2SAlex Crichton                     decl.push_str(" ");
1432ca7081a2SAlex Crichton                     self.write_ref(ty, &mut decl);
1433ca7081a2SAlex Crichton                 }
1434ca7081a2SAlex Crichton                 decl.push_str(")");
1435ca7081a2SAlex Crichton             }
1436ca7081a2SAlex Crichton             Type::Variant(types) => {
1437ca7081a2SAlex Crichton                 decl.push_str("(variant");
1438ca7081a2SAlex Crichton                 for (index, ty) in types.iter().enumerate() {
14390b9ff9bfSAlex Crichton                     uwrite!(decl, r#" (case "C{index}""#);
1440ca7081a2SAlex Crichton                     if let Some(ty) = ty {
1441ca7081a2SAlex Crichton                         decl.push_str(" ");
1442ca7081a2SAlex Crichton                         self.write_ref(ty, &mut decl);
1443ca7081a2SAlex Crichton                     }
1444ca7081a2SAlex Crichton                     decl.push_str(")");
1445ca7081a2SAlex Crichton                 }
1446ca7081a2SAlex Crichton                 decl.push_str(")");
1447ca7081a2SAlex Crichton             }
1448ca7081a2SAlex Crichton             Type::Enum(count) => {
1449ca7081a2SAlex Crichton                 decl.push_str("(enum");
1450ca7081a2SAlex Crichton                 for index in 0..*count {
14510b9ff9bfSAlex Crichton                     uwrite!(decl, r#" "E{index}""#);
1452ca7081a2SAlex Crichton                 }
1453ca7081a2SAlex Crichton                 decl.push_str(")");
1454ca7081a2SAlex Crichton             }
1455ca7081a2SAlex Crichton             Type::Option(ty) => {
1456ca7081a2SAlex Crichton                 decl.push_str("(option ");
1457ca7081a2SAlex Crichton                 self.write_ref(ty, &mut decl);
1458ca7081a2SAlex Crichton                 decl.push_str(")");
1459ca7081a2SAlex Crichton             }
1460ca7081a2SAlex Crichton             Type::Result { ok, err } => {
1461ca7081a2SAlex Crichton                 decl.push_str("(result");
1462ca7081a2SAlex Crichton                 if let Some(ok) = ok {
1463ca7081a2SAlex Crichton                     decl.push_str(" ");
1464ca7081a2SAlex Crichton                     self.write_ref(ok, &mut decl);
1465ca7081a2SAlex Crichton                 }
1466ca7081a2SAlex Crichton                 if let Some(err) = err {
1467ca7081a2SAlex Crichton                     decl.push_str(" (error ");
1468ca7081a2SAlex Crichton                     self.write_ref(err, &mut decl);
1469ca7081a2SAlex Crichton                     decl.push_str(")");
1470ca7081a2SAlex Crichton                 }
1471ca7081a2SAlex Crichton                 decl.push_str(")");
1472ca7081a2SAlex Crichton             }
1473ca7081a2SAlex Crichton             Type::Flags(count) => {
1474ca7081a2SAlex Crichton                 decl.push_str("(flags");
1475ca7081a2SAlex Crichton                 for index in 0..*count {
14760b9ff9bfSAlex Crichton                     uwrite!(decl, r#" "F{index}""#);
1477ca7081a2SAlex Crichton                 }
1478ca7081a2SAlex Crichton                 decl.push_str(")");
1479ca7081a2SAlex Crichton             }
1480ca7081a2SAlex Crichton         }
1481ca7081a2SAlex Crichton         decl.push_str(")\n");
14820b9ff9bfSAlex Crichton         uwriteln!(decl, "(import \"t{idx}\" (type $t{idx} (eq $t{idx}')))");
1483ca7081a2SAlex Crichton         decl
1484ca7081a2SAlex Crichton     }
1485ca7081a2SAlex Crichton }
1486ca7081a2SAlex Crichton 
1487ca7081a2SAlex Crichton /// Represents custom fragments of a WAT file which may be used to create a component for exercising [`TestCase`]s
1488ca7081a2SAlex Crichton #[derive(Debug)]
1489ca7081a2SAlex Crichton pub struct Declarations {
1490ca7081a2SAlex Crichton     /// Type declarations (if any) referenced by `params` and/or `result`
1491ca7081a2SAlex Crichton     pub types: Cow<'static, str>,
1492ca7081a2SAlex Crichton     /// Types to thread through when instantiating sub-components.
1493ca7081a2SAlex Crichton     pub type_instantiation_args: Cow<'static, str>,
1494ca7081a2SAlex Crichton     /// Parameter declarations used for the imported and exported functions
1495ca7081a2SAlex Crichton     pub params: Cow<'static, str>,
1496ca7081a2SAlex Crichton     /// Result declaration used for the imported and exported functions
1497ca7081a2SAlex Crichton     pub results: Cow<'static, str>,
14980b9ff9bfSAlex Crichton     /// Implementation of the "caller" component, which invokes the `callee`
14990b9ff9bfSAlex Crichton     /// composed component.
15000b9ff9bfSAlex Crichton     pub caller_module: Cow<'static, str>,
15010b9ff9bfSAlex Crichton     /// Implementation of the "callee" component, which invokes the host.
15020b9ff9bfSAlex Crichton     pub callee_module: Cow<'static, str>,
15030b9ff9bfSAlex Crichton     /// Options used for caller/calle ABI/etc.
15040b9ff9bfSAlex Crichton     pub options: TestCaseOptions,
1505ca7081a2SAlex Crichton }
1506ca7081a2SAlex Crichton 
1507ca7081a2SAlex Crichton impl Declarations {
1508ca7081a2SAlex Crichton     /// Generate a complete WAT file based on the specified fragments.
make_component(&self) -> Box<str>1509ca7081a2SAlex Crichton     pub fn make_component(&self) -> Box<str> {
1510ca7081a2SAlex Crichton         let Self {
1511ca7081a2SAlex Crichton             types,
1512ca7081a2SAlex Crichton             type_instantiation_args,
1513ca7081a2SAlex Crichton             params,
1514ca7081a2SAlex Crichton             results,
15150b9ff9bfSAlex Crichton             caller_module,
15160b9ff9bfSAlex Crichton             callee_module,
15170b9ff9bfSAlex Crichton             options,
1518ca7081a2SAlex Crichton         } = self;
15190b9ff9bfSAlex Crichton         let mk_component = |name: &str,
15200b9ff9bfSAlex Crichton                             module: &str,
15210b9ff9bfSAlex Crichton                             import_async: bool,
15220b9ff9bfSAlex Crichton                             export_async: bool,
15230b9ff9bfSAlex Crichton                             encoding: StringEncoding,
15240b9ff9bfSAlex Crichton                             lift_abi: LiftAbi,
15250b9ff9bfSAlex Crichton                             lower_abi: LowerAbi| {
15260b9ff9bfSAlex Crichton             let import_async = if import_async { "async" } else { "" };
15270b9ff9bfSAlex Crichton             let export_async = if export_async { "async" } else { "" };
15280b9ff9bfSAlex Crichton             let lower_async_option = match lower_abi {
15290b9ff9bfSAlex Crichton                 LowerAbi::Sync => "",
15300b9ff9bfSAlex Crichton                 LowerAbi::Async => "async",
15310b9ff9bfSAlex Crichton             };
15320b9ff9bfSAlex Crichton             let lift_async_option = match lift_abi {
15330b9ff9bfSAlex Crichton                 LiftAbi::Sync => "",
15340b9ff9bfSAlex Crichton                 LiftAbi::AsyncStackful => "async",
15350b9ff9bfSAlex Crichton                 LiftAbi::AsyncCallback => "async (callback (func $i \"callback\"))",
15360b9ff9bfSAlex Crichton             };
15370b9ff9bfSAlex Crichton 
15380b9ff9bfSAlex Crichton             let mut intrinsic_defs = String::new();
15390b9ff9bfSAlex Crichton             let mut intrinsic_imports = String::new();
15400b9ff9bfSAlex Crichton 
15410b9ff9bfSAlex Crichton             match lift_abi {
15420b9ff9bfSAlex Crichton                 LiftAbi::Sync => {}
15430b9ff9bfSAlex Crichton                 LiftAbi::AsyncCallback | LiftAbi::AsyncStackful => {
15440b9ff9bfSAlex Crichton                     intrinsic_defs.push_str(&format!(
15450b9ff9bfSAlex Crichton                         r#"
15460b9ff9bfSAlex Crichton (core func $task.return (canon task.return {results}
15470b9ff9bfSAlex Crichton     (memory $libc "memory") string-encoding={encoding}))
15480b9ff9bfSAlex Crichton                         "#,
15490b9ff9bfSAlex Crichton                     ));
15500b9ff9bfSAlex Crichton                     intrinsic_imports.push_str(
15510b9ff9bfSAlex Crichton                         r#"
15520b9ff9bfSAlex Crichton (with "" (instance (export "task.return" (func $task.return))))
15530b9ff9bfSAlex Crichton                         "#,
15540b9ff9bfSAlex Crichton                     );
15550b9ff9bfSAlex Crichton                 }
15560b9ff9bfSAlex Crichton             }
15570b9ff9bfSAlex Crichton 
1558ca7081a2SAlex Crichton             format!(
1559ca7081a2SAlex Crichton                 r#"
1560ca7081a2SAlex Crichton (component ${name}
1561ca7081a2SAlex Crichton     {types}
15620b9ff9bfSAlex Crichton     (type $import_sig (func {import_async} {params} {results}))
15630b9ff9bfSAlex Crichton     (type $export_sig (func {export_async} {params} {results}))
15640b9ff9bfSAlex Crichton     (import "{IMPORT_FUNCTION}" (func $f (type $import_sig)))
1565ca7081a2SAlex Crichton 
1566ca7081a2SAlex Crichton     (core instance $libc (instantiate $libc))
1567ca7081a2SAlex Crichton 
1568ca7081a2SAlex Crichton     (core func $f_lower (canon lower
1569ca7081a2SAlex Crichton         (func $f)
1570ca7081a2SAlex Crichton         (memory $libc "memory")
1571ca7081a2SAlex Crichton         (realloc (func $libc "realloc"))
1572ca7081a2SAlex Crichton         string-encoding={encoding}
15730b9ff9bfSAlex Crichton         {lower_async_option}
1574ca7081a2SAlex Crichton     ))
1575ca7081a2SAlex Crichton 
15760b9ff9bfSAlex Crichton     {intrinsic_defs}
15770b9ff9bfSAlex Crichton 
15780b9ff9bfSAlex Crichton     (core module $m
15790b9ff9bfSAlex Crichton         (memory (import "libc" "memory") 1)
15800b9ff9bfSAlex Crichton         (func $realloc (import "libc" "realloc") (param i32 i32 i32 i32) (result i32))
15810b9ff9bfSAlex Crichton 
15820b9ff9bfSAlex Crichton         {module}
15830b9ff9bfSAlex Crichton     )
15840b9ff9bfSAlex Crichton 
1585ca7081a2SAlex Crichton     (core instance $i (instantiate $m
1586ca7081a2SAlex Crichton         (with "libc" (instance $libc))
1587ca7081a2SAlex Crichton         (with "host" (instance (export "{IMPORT_FUNCTION}" (func $f_lower))))
15880b9ff9bfSAlex Crichton         {intrinsic_imports}
1589ca7081a2SAlex Crichton     ))
1590ca7081a2SAlex Crichton 
15910b9ff9bfSAlex Crichton     (func (export "{EXPORT_FUNCTION}") (type $export_sig)
1592ca7081a2SAlex Crichton         (canon lift
1593ca7081a2SAlex Crichton             (core func $i "{EXPORT_FUNCTION}")
1594ca7081a2SAlex Crichton             (memory $libc "memory")
1595ca7081a2SAlex Crichton             (realloc (func $libc "realloc"))
1596ca7081a2SAlex Crichton             string-encoding={encoding}
15970b9ff9bfSAlex Crichton             {lift_async_option}
1598ca7081a2SAlex Crichton         )
1599ca7081a2SAlex Crichton     )
1600ca7081a2SAlex Crichton )
1601ca7081a2SAlex Crichton             "#
1602ca7081a2SAlex Crichton             )
1603ca7081a2SAlex Crichton         };
1604ca7081a2SAlex Crichton 
16050b9ff9bfSAlex Crichton         let c1 = mk_component(
16060b9ff9bfSAlex Crichton             "callee",
16070b9ff9bfSAlex Crichton             &callee_module,
16080b9ff9bfSAlex Crichton             options.host_async,
16090b9ff9bfSAlex Crichton             options.guest_callee_async,
16100b9ff9bfSAlex Crichton             options.callee_encoding,
16110b9ff9bfSAlex Crichton             options.callee_lift_abi,
16120b9ff9bfSAlex Crichton             options.callee_lower_abi,
16130b9ff9bfSAlex Crichton         );
16140b9ff9bfSAlex Crichton         let c2 = mk_component(
16150b9ff9bfSAlex Crichton             "caller",
16160b9ff9bfSAlex Crichton             &caller_module,
16170b9ff9bfSAlex Crichton             options.guest_callee_async,
16180b9ff9bfSAlex Crichton             options.guest_caller_async,
16190b9ff9bfSAlex Crichton             options.caller_encoding,
16200b9ff9bfSAlex Crichton             options.caller_lift_abi,
16210b9ff9bfSAlex Crichton             options.caller_lower_abi,
16220b9ff9bfSAlex Crichton         );
16230b9ff9bfSAlex Crichton         let host_async = if options.host_async { "async" } else { "" };
1624ca7081a2SAlex Crichton 
1625ca7081a2SAlex Crichton         format!(
1626ca7081a2SAlex Crichton             r#"
1627ca7081a2SAlex Crichton             (component
1628ca7081a2SAlex Crichton                 (core module $libc
1629ca7081a2SAlex Crichton                     (memory (export "memory") 1)
1630ca7081a2SAlex Crichton                     {REALLOC_AND_FREE}
1631ca7081a2SAlex Crichton                 )
1632ca7081a2SAlex Crichton 
1633ca7081a2SAlex Crichton 
1634ca7081a2SAlex Crichton                 {types}
1635ca7081a2SAlex Crichton 
16360b9ff9bfSAlex Crichton                 (type $host_sig (func {host_async} {params} {results}))
16370b9ff9bfSAlex Crichton                 (import "{IMPORT_FUNCTION}" (func $f (type $host_sig)))
1638ca7081a2SAlex Crichton 
1639ca7081a2SAlex Crichton                 {c1}
1640ca7081a2SAlex Crichton                 {c2}
16410b9ff9bfSAlex Crichton                 (instance $c1 (instantiate $callee
1642ca7081a2SAlex Crichton                     {type_instantiation_args}
1643ca7081a2SAlex Crichton                     (with "{IMPORT_FUNCTION}" (func $f))
1644ca7081a2SAlex Crichton                 ))
16450b9ff9bfSAlex Crichton                 (instance $c2 (instantiate $caller
1646ca7081a2SAlex Crichton                     {type_instantiation_args}
1647ca7081a2SAlex Crichton                     (with "{IMPORT_FUNCTION}" (func $c1 "{EXPORT_FUNCTION}"))
1648ca7081a2SAlex Crichton                 ))
1649ca7081a2SAlex Crichton                 (export "{EXPORT_FUNCTION}" (func $c2 "{EXPORT_FUNCTION}"))
1650ca7081a2SAlex Crichton             )"#,
1651ca7081a2SAlex Crichton         )
1652ca7081a2SAlex Crichton         .into()
1653ca7081a2SAlex Crichton     }
1654ca7081a2SAlex Crichton }
1655ca7081a2SAlex Crichton 
1656ca7081a2SAlex Crichton /// Represents a test case for calling a component function
1657ca7081a2SAlex Crichton #[derive(Debug)]
1658ca7081a2SAlex Crichton pub struct TestCase<'a> {
1659ca7081a2SAlex Crichton     /// The types of parameters to pass to the function
1660ca7081a2SAlex Crichton     pub params: Vec<&'a Type>,
1661ca7081a2SAlex Crichton     /// The result types of the function
1662ca7081a2SAlex Crichton     pub result: Option<&'a Type>,
16630b9ff9bfSAlex Crichton     /// ABI options to use for this test case.
16640b9ff9bfSAlex Crichton     pub options: TestCaseOptions,
1665ca7081a2SAlex Crichton }
1666ca7081a2SAlex Crichton 
16670b9ff9bfSAlex Crichton /// Collection of options which configure how the caller/callee/etc ABIs are
16680b9ff9bfSAlex Crichton /// all configured.
16690b9ff9bfSAlex Crichton #[derive(Debug, Arbitrary, Copy, Clone)]
16700b9ff9bfSAlex Crichton pub struct TestCaseOptions {
16710b9ff9bfSAlex Crichton     /// Whether or not the guest caller component (the entrypoint) is using an
16720b9ff9bfSAlex Crichton     /// `async` function type.
16730b9ff9bfSAlex Crichton     pub guest_caller_async: bool,
16740b9ff9bfSAlex Crichton     /// Whether or not the guest callee component (what the entrypoint calls)
16750b9ff9bfSAlex Crichton     /// is using an `async` function type.
16760b9ff9bfSAlex Crichton     pub guest_callee_async: bool,
16770b9ff9bfSAlex Crichton     /// Whether or not the host is using an async function type (what the
16780b9ff9bfSAlex Crichton     /// guest callee calls).
16790b9ff9bfSAlex Crichton     pub host_async: bool,
16800b9ff9bfSAlex Crichton     /// The string encoding of the caller component.
16810b9ff9bfSAlex Crichton     pub caller_encoding: StringEncoding,
16820b9ff9bfSAlex Crichton     /// The string encoding of the callee component.
16830b9ff9bfSAlex Crichton     pub callee_encoding: StringEncoding,
16840b9ff9bfSAlex Crichton     /// The ABI that the caller component is using to lift its export (the main
16850b9ff9bfSAlex Crichton     /// entrypoint).
16860b9ff9bfSAlex Crichton     pub caller_lift_abi: LiftAbi,
16870b9ff9bfSAlex Crichton     /// The ABI that the callee component is using to lift its export (called
16880b9ff9bfSAlex Crichton     /// by the caller).
16890b9ff9bfSAlex Crichton     pub callee_lift_abi: LiftAbi,
16900b9ff9bfSAlex Crichton     /// The ABI that the caller component is using to lower its import (the
16910b9ff9bfSAlex Crichton     /// callee's export).
16920b9ff9bfSAlex Crichton     pub caller_lower_abi: LowerAbi,
16930b9ff9bfSAlex Crichton     /// The ABI that the callee component is using to lower its import (the
16940b9ff9bfSAlex Crichton     /// host function).
16950b9ff9bfSAlex Crichton     pub callee_lower_abi: LowerAbi,
16960b9ff9bfSAlex Crichton }
16970b9ff9bfSAlex Crichton 
16980b9ff9bfSAlex Crichton #[derive(Debug, Arbitrary, Copy, Clone)]
16990b9ff9bfSAlex Crichton pub enum LiftAbi {
17000b9ff9bfSAlex Crichton     Sync,
17010b9ff9bfSAlex Crichton     AsyncStackful,
17020b9ff9bfSAlex Crichton     AsyncCallback,
17030b9ff9bfSAlex Crichton }
17040b9ff9bfSAlex Crichton 
17050b9ff9bfSAlex Crichton #[derive(Debug, Arbitrary, Copy, Clone)]
17060b9ff9bfSAlex Crichton pub enum LowerAbi {
17070b9ff9bfSAlex Crichton     Sync,
17080b9ff9bfSAlex Crichton     Async,
17090b9ff9bfSAlex Crichton }
17100b9ff9bfSAlex Crichton 
17110b9ff9bfSAlex Crichton impl<'a> TestCase<'a> {
generate(types: &'a [Type], u: &mut Unstructured<'_>) -> arbitrary::Result<Self>17120b9ff9bfSAlex Crichton     pub fn generate(types: &'a [Type], u: &mut Unstructured<'_>) -> arbitrary::Result<Self> {
17130b9ff9bfSAlex Crichton         let max_params = if types.len() > 0 { 5 } else { 0 };
17140b9ff9bfSAlex Crichton         let params = (0..u.int_in_range(0..=max_params)?)
17150b9ff9bfSAlex Crichton             .map(|_| u.choose(&types))
17160b9ff9bfSAlex Crichton             .collect::<arbitrary::Result<Vec<_>>>()?;
17170b9ff9bfSAlex Crichton         let result = if types.len() > 0 && u.arbitrary()? {
17180b9ff9bfSAlex Crichton             Some(u.choose(&types)?)
17190b9ff9bfSAlex Crichton         } else {
17200b9ff9bfSAlex Crichton             None
17210b9ff9bfSAlex Crichton         };
17220b9ff9bfSAlex Crichton 
17238992b99bSJoel Dice         let mut options = u.arbitrary::<TestCaseOptions>()?;
17248992b99bSJoel Dice 
17258992b99bSJoel Dice         // Sync tasks cannot call async functions via a sync lower, nor can they
17268992b99bSJoel Dice         // block in other ways (e.g. by calling `waitable-set.wait`, returning
17278992b99bSJoel Dice         // `CALLBACK_CODE_WAIT`, etc.) prior to returning.  Therefore,
17288992b99bSJoel Dice         // async-ness cascades to the callers:
17298992b99bSJoel Dice         if options.host_async {
17308992b99bSJoel Dice             options.guest_callee_async = true;
17318992b99bSJoel Dice         }
17328992b99bSJoel Dice         if options.guest_callee_async {
17338992b99bSJoel Dice             options.guest_caller_async = true;
17348992b99bSJoel Dice         }
17358992b99bSJoel Dice 
17360b9ff9bfSAlex Crichton         Ok(Self {
17370b9ff9bfSAlex Crichton             params,
17380b9ff9bfSAlex Crichton             result,
17398992b99bSJoel Dice             options,
17400b9ff9bfSAlex Crichton         })
17410b9ff9bfSAlex Crichton     }
17420b9ff9bfSAlex Crichton 
1743ca7081a2SAlex Crichton     /// Generate a `Declarations` for this `TestCase` which may be used to build a component to execute the case.
declarations(&self) -> Declarations1744ca7081a2SAlex Crichton     pub fn declarations(&self) -> Declarations {
1745ca7081a2SAlex Crichton         let mut builder = TypesBuilder::default();
1746ca7081a2SAlex Crichton 
1747ca7081a2SAlex Crichton         let mut params = String::new();
1748ca7081a2SAlex Crichton         for (i, ty) in self.params.iter().enumerate() {
1749ca7081a2SAlex Crichton             params.push_str(&format!(" (param \"p{i}\" "));
1750ca7081a2SAlex Crichton             builder.write_ref(ty, &mut params);
1751ca7081a2SAlex Crichton             params.push_str(")");
1752ca7081a2SAlex Crichton         }
1753ca7081a2SAlex Crichton 
1754ca7081a2SAlex Crichton         let mut results = String::new();
1755ca7081a2SAlex Crichton         if let Some(ty) = self.result {
1756ca7081a2SAlex Crichton             results.push_str(&format!(" (result "));
1757ca7081a2SAlex Crichton             builder.write_ref(ty, &mut results);
1758ca7081a2SAlex Crichton             results.push_str(")");
1759ca7081a2SAlex Crichton         }
1760ca7081a2SAlex Crichton 
17610b9ff9bfSAlex Crichton         let caller_module = make_import_and_export(
17620b9ff9bfSAlex Crichton             &self.params,
17630b9ff9bfSAlex Crichton             self.result,
17640b9ff9bfSAlex Crichton             self.options.caller_lift_abi,
17650b9ff9bfSAlex Crichton             self.options.caller_lower_abi,
17660b9ff9bfSAlex Crichton         );
17670b9ff9bfSAlex Crichton         let callee_module = make_import_and_export(
17680b9ff9bfSAlex Crichton             &self.params,
17690b9ff9bfSAlex Crichton             self.result,
17700b9ff9bfSAlex Crichton             self.options.callee_lift_abi,
17710b9ff9bfSAlex Crichton             self.options.callee_lower_abi,
17720b9ff9bfSAlex Crichton         );
1773ca7081a2SAlex Crichton 
1774ca7081a2SAlex Crichton         let mut type_decls = Vec::new();
1775ca7081a2SAlex Crichton         let mut type_instantiation_args = String::new();
1776ca7081a2SAlex Crichton         while let Some((idx, ty)) = builder.worklist.pop() {
1777ca7081a2SAlex Crichton             type_decls.push(builder.write_decl(idx, ty));
17780b9ff9bfSAlex Crichton             uwriteln!(type_instantiation_args, "(with \"t{idx}\" (type $t{idx}))");
1779ca7081a2SAlex Crichton         }
1780ca7081a2SAlex Crichton 
1781ca7081a2SAlex Crichton         // Note that types are printed here in reverse order since they were
1782ca7081a2SAlex Crichton         // pushed onto `type_decls` as they were referenced meaning the last one
1783ca7081a2SAlex Crichton         // is the "base" one.
1784ca7081a2SAlex Crichton         let mut types = String::new();
1785ca7081a2SAlex Crichton         for decl in type_decls.into_iter().rev() {
1786ca7081a2SAlex Crichton             types.push_str(&decl);
1787ca7081a2SAlex Crichton             types.push_str("\n");
1788ca7081a2SAlex Crichton         }
1789ca7081a2SAlex Crichton 
1790ca7081a2SAlex Crichton         Declarations {
1791ca7081a2SAlex Crichton             types: types.into(),
1792ca7081a2SAlex Crichton             type_instantiation_args: type_instantiation_args.into(),
1793ca7081a2SAlex Crichton             params: params.into(),
1794ca7081a2SAlex Crichton             results: results.into(),
17950b9ff9bfSAlex Crichton             caller_module: caller_module.into(),
17960b9ff9bfSAlex Crichton             callee_module: callee_module.into(),
17970b9ff9bfSAlex Crichton             options: self.options,
1798ca7081a2SAlex Crichton         }
1799ca7081a2SAlex Crichton     }
1800ca7081a2SAlex Crichton }
1801ca7081a2SAlex Crichton 
1802ca7081a2SAlex Crichton #[derive(Copy, Clone, Debug, Arbitrary)]
1803ca7081a2SAlex Crichton pub enum StringEncoding {
1804ca7081a2SAlex Crichton     Utf8,
1805ca7081a2SAlex Crichton     Utf16,
1806ca7081a2SAlex Crichton     Latin1OrUtf16,
1807ca7081a2SAlex Crichton }
1808ca7081a2SAlex Crichton 
1809ca7081a2SAlex Crichton impl fmt::Display for StringEncoding {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1810ca7081a2SAlex Crichton     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1811ca7081a2SAlex Crichton         match self {
1812ca7081a2SAlex Crichton             StringEncoding::Utf8 => fmt::Display::fmt(&"utf8", f),
1813ca7081a2SAlex Crichton             StringEncoding::Utf16 => fmt::Display::fmt(&"utf16", f),
1814ca7081a2SAlex Crichton             StringEncoding::Latin1OrUtf16 => fmt::Display::fmt(&"latin1+utf16", f),
1815ca7081a2SAlex Crichton         }
1816ca7081a2SAlex Crichton     }
1817ca7081a2SAlex Crichton }
1818ca7081a2SAlex Crichton 
18190b9ff9bfSAlex Crichton impl ToTokens for TestCaseOptions {
to_tokens(&self, tokens: &mut TokenStream)18200b9ff9bfSAlex Crichton     fn to_tokens(&self, tokens: &mut TokenStream) {
18210b9ff9bfSAlex Crichton         let TestCaseOptions {
18220b9ff9bfSAlex Crichton             guest_caller_async,
18230b9ff9bfSAlex Crichton             guest_callee_async,
18240b9ff9bfSAlex Crichton             host_async,
18250b9ff9bfSAlex Crichton             caller_encoding,
18260b9ff9bfSAlex Crichton             callee_encoding,
18270b9ff9bfSAlex Crichton             caller_lift_abi,
18280b9ff9bfSAlex Crichton             callee_lift_abi,
18290b9ff9bfSAlex Crichton             caller_lower_abi,
18300b9ff9bfSAlex Crichton             callee_lower_abi,
18310b9ff9bfSAlex Crichton         } = self;
18320b9ff9bfSAlex Crichton         tokens.extend(quote!(wasmtime_test_util::component_fuzz::TestCaseOptions {
18330b9ff9bfSAlex Crichton             guest_caller_async: #guest_caller_async,
18340b9ff9bfSAlex Crichton             guest_callee_async: #guest_callee_async,
18350b9ff9bfSAlex Crichton             host_async: #host_async,
18360b9ff9bfSAlex Crichton             caller_encoding: #caller_encoding,
18370b9ff9bfSAlex Crichton             callee_encoding: #callee_encoding,
18380b9ff9bfSAlex Crichton             caller_lift_abi: #caller_lift_abi,
18390b9ff9bfSAlex Crichton             callee_lift_abi: #callee_lift_abi,
18400b9ff9bfSAlex Crichton             caller_lower_abi: #caller_lower_abi,
18410b9ff9bfSAlex Crichton             callee_lower_abi: #callee_lower_abi,
18420b9ff9bfSAlex Crichton         }));
18430b9ff9bfSAlex Crichton     }
18440b9ff9bfSAlex Crichton }
18450b9ff9bfSAlex Crichton 
18460b9ff9bfSAlex Crichton impl ToTokens for LowerAbi {
to_tokens(&self, tokens: &mut TokenStream)18470b9ff9bfSAlex Crichton     fn to_tokens(&self, tokens: &mut TokenStream) {
18480b9ff9bfSAlex Crichton         let me = match self {
18490b9ff9bfSAlex Crichton             LowerAbi::Sync => quote!(Sync),
18500b9ff9bfSAlex Crichton             LowerAbi::Async => quote!(Async),
18510b9ff9bfSAlex Crichton         };
18520b9ff9bfSAlex Crichton         tokens.extend(quote!(wasmtime_test_util::component_fuzz::LowerAbi::#me));
18530b9ff9bfSAlex Crichton     }
18540b9ff9bfSAlex Crichton }
18550b9ff9bfSAlex Crichton 
18560b9ff9bfSAlex Crichton impl ToTokens for LiftAbi {
to_tokens(&self, tokens: &mut TokenStream)18570b9ff9bfSAlex Crichton     fn to_tokens(&self, tokens: &mut TokenStream) {
18580b9ff9bfSAlex Crichton         let me = match self {
18590b9ff9bfSAlex Crichton             LiftAbi::Sync => quote!(Sync),
18600b9ff9bfSAlex Crichton             LiftAbi::AsyncCallback => quote!(AsyncCallback),
18610b9ff9bfSAlex Crichton             LiftAbi::AsyncStackful => quote!(AsyncStackful),
18620b9ff9bfSAlex Crichton         };
18630b9ff9bfSAlex Crichton         tokens.extend(quote!(wasmtime_test_util::component_fuzz::LiftAbi::#me));
18640b9ff9bfSAlex Crichton     }
18650b9ff9bfSAlex Crichton }
18660b9ff9bfSAlex Crichton 
1867ca7081a2SAlex Crichton impl ToTokens for StringEncoding {
to_tokens(&self, tokens: &mut TokenStream)1868ca7081a2SAlex Crichton     fn to_tokens(&self, tokens: &mut TokenStream) {
1869ca7081a2SAlex Crichton         let me = match self {
1870ca7081a2SAlex Crichton             StringEncoding::Utf8 => quote!(Utf8),
1871ca7081a2SAlex Crichton             StringEncoding::Utf16 => quote!(Utf16),
1872ca7081a2SAlex Crichton             StringEncoding::Latin1OrUtf16 => quote!(Latin1OrUtf16),
1873ca7081a2SAlex Crichton         };
1874ca7081a2SAlex Crichton         tokens.extend(quote!(wasmtime_test_util::component_fuzz::StringEncoding::#me));
1875ca7081a2SAlex Crichton     }
1876ca7081a2SAlex Crichton }
18770b9ff9bfSAlex Crichton 
18780b9ff9bfSAlex Crichton #[cfg(test)]
18790b9ff9bfSAlex Crichton mod tests {
18800b9ff9bfSAlex Crichton     use super::*;
18810b9ff9bfSAlex Crichton 
18820b9ff9bfSAlex Crichton     #[test]
arbtest()18830b9ff9bfSAlex Crichton     fn arbtest() {
18840b9ff9bfSAlex Crichton         arbtest::arbtest(|u| {
18850b9ff9bfSAlex Crichton             let mut fuel = 100;
18860b9ff9bfSAlex Crichton             let types = (0..5)
18870b9ff9bfSAlex Crichton                 .map(|_| Type::generate(u, 3, &mut fuel))
18880b9ff9bfSAlex Crichton                 .collect::<arbitrary::Result<Vec<_>>>()?;
18890b9ff9bfSAlex Crichton             let case = TestCase::generate(&types, u)?;
18900b9ff9bfSAlex Crichton             let decls = case.declarations();
18910b9ff9bfSAlex Crichton             let component = decls.make_component();
18920b9ff9bfSAlex Crichton             let wasm = wat::parse_str(&component).unwrap_or_else(|e| {
18930b9ff9bfSAlex Crichton                 panic!("failed to parse generated component as wat: {e}\n\n{component}");
18940b9ff9bfSAlex Crichton             });
18950b9ff9bfSAlex Crichton             wasmparser::Validator::new_with_features(wasmparser::WasmFeatures::all())
18960b9ff9bfSAlex Crichton                 .validate_all(&wasm)
18970b9ff9bfSAlex Crichton                 .unwrap_or_else(|e| {
18980b9ff9bfSAlex Crichton                     let mut wat = String::new();
18990b9ff9bfSAlex Crichton                     let mut dst = wasmprinter::PrintFmtWrite(&mut wat);
19000b9ff9bfSAlex Crichton                     let to_print = if wasmprinter::Config::new()
19010b9ff9bfSAlex Crichton                         .print_offsets(true)
19020b9ff9bfSAlex Crichton                         .print_operand_stack(true)
19030b9ff9bfSAlex Crichton                         .print(&wasm, &mut dst)
19040b9ff9bfSAlex Crichton                         .is_ok()
19050b9ff9bfSAlex Crichton                     {
19060b9ff9bfSAlex Crichton                         &wat[..]
19070b9ff9bfSAlex Crichton                     } else {
19080b9ff9bfSAlex Crichton                         &component[..]
19090b9ff9bfSAlex Crichton                     };
19100b9ff9bfSAlex Crichton                     panic!("generated component is not valid wasm: {e}\n\n{to_print}");
19110b9ff9bfSAlex Crichton                 });
19120b9ff9bfSAlex Crichton             Ok(())
19130b9ff9bfSAlex Crichton         })
19140b9ff9bfSAlex Crichton         .budget_ms(1_000)
19150b9ff9bfSAlex Crichton         // .seed(0x3c9050d4000000e9)
19160b9ff9bfSAlex Crichton         ;
19170b9ff9bfSAlex Crichton     }
19180b9ff9bfSAlex Crichton }
1919