1 use super::{make_echo_component, make_echo_component_with_params, Param, Type};
2 use anyhow::Result;
3 use component_test_util::FuncExt;
4 use wasmtime::component::{self, Component, Linker, Val};
5 use wasmtime::Store;
6 
7 #[test]
8 fn primitives() -> Result<()> {
9     let engine = super::engine();
10     let mut store = Store::new(&engine, ());
11 
12     for (input, ty, param) in [
13         (Val::Bool(true), "bool", Param(Type::U8, Some(0))),
14         (Val::S8(-42), "s8", Param(Type::S8, Some(0))),
15         (Val::U8(42), "u8", Param(Type::U8, Some(0))),
16         (Val::S16(-4242), "s16", Param(Type::S16, Some(0))),
17         (Val::U16(4242), "u16", Param(Type::U16, Some(0))),
18         (Val::S32(-314159265), "s32", Param(Type::I32, Some(0))),
19         (Val::U32(314159265), "u32", Param(Type::I32, Some(0))),
20         (Val::S64(-31415926535897), "s64", Param(Type::I64, Some(0))),
21         (Val::U64(31415926535897), "u64", Param(Type::I64, Some(0))),
22         (
23             Val::Float32(3.14159265_f32.to_bits()),
24             "float32",
25             Param(Type::F32, Some(0)),
26         ),
27         (
28             Val::Float64(3.14159265_f64.to_bits()),
29             "float64",
30             Param(Type::F64, Some(0)),
31         ),
32         (Val::Char('��'), "char", Param(Type::I32, Some(0))),
33     ] {
34         let component = Component::new(&engine, make_echo_component_with_params(ty, &[param]))?;
35         let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
36         let func = instance.get_func(&mut store, "echo").unwrap();
37         let output = func.call_and_post_return(&mut store, &[input.clone()])?;
38 
39         assert_eq!(input, output);
40     }
41 
42     // Sad path: type mismatch
43 
44     let component = Component::new(
45         &engine,
46         make_echo_component_with_params("float64", &[Param(Type::F64, Some(0))]),
47     )?;
48     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
49     let func = instance.get_func(&mut store, "echo").unwrap();
50     let err = func
51         .call_and_post_return(&mut store, &[Val::U64(42)])
52         .unwrap_err();
53 
54     assert!(err.to_string().contains("type mismatch"), "{err}");
55 
56     // Sad path: arity mismatch (too many)
57 
58     let err = func
59         .call_and_post_return(
60             &mut store,
61             &[
62                 Val::Float64(3.14159265_f64.to_bits()),
63                 Val::Float64(3.14159265_f64.to_bits()),
64             ],
65         )
66         .unwrap_err();
67 
68     assert!(
69         err.to_string().contains("expected 1 argument(s), got 2"),
70         "{err}"
71     );
72 
73     // Sad path: arity mismatch (too few)
74 
75     let err = func.call_and_post_return(&mut store, &[]).unwrap_err();
76 
77     assert!(
78         err.to_string().contains("expected 1 argument(s), got 0"),
79         "{err}"
80     );
81 
82     Ok(())
83 }
84 
85 #[test]
86 fn strings() -> Result<()> {
87     let engine = super::engine();
88     let mut store = Store::new(&engine, ());
89 
90     let component = Component::new(&engine, make_echo_component("string", 8))?;
91     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
92     let func = instance.get_func(&mut store, "echo").unwrap();
93     let input = Val::String(Box::from("hello, component!"));
94     let output = func.call_and_post_return(&mut store, &[input.clone()])?;
95 
96     assert_eq!(input, output);
97 
98     Ok(())
99 }
100 
101 #[test]
102 fn lists() -> Result<()> {
103     let engine = super::engine();
104     let mut store = Store::new(&engine, ());
105 
106     let component = Component::new(&engine, make_echo_component("(list u32)", 8))?;
107     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
108     let func = instance.get_func(&mut store, "echo").unwrap();
109     let ty = &func.params(&store)[0];
110     let input = ty.unwrap_list().new_val(Box::new([
111         Val::U32(32343),
112         Val::U32(79023439),
113         Val::U32(2084037802),
114     ]))?;
115     let output = func.call_and_post_return(&mut store, &[input.clone()])?;
116 
117     assert_eq!(input, output);
118 
119     // Sad path: type mismatch
120 
121     let err = ty
122         .unwrap_list()
123         .new_val(Box::new([
124             Val::U32(32343),
125             Val::U32(79023439),
126             Val::Float32(3.14159265_f32.to_bits()),
127         ]))
128         .unwrap_err();
129 
130     assert!(err.to_string().contains("type mismatch"), "{err}");
131 
132     Ok(())
133 }
134 
135 #[test]
136 fn records() -> Result<()> {
137     let engine = super::engine();
138     let mut store = Store::new(&engine, ());
139 
140     let component = Component::new(
141         &engine,
142         make_echo_component_with_params(
143             r#"(record (field "A" u32) (field "B" float64) (field "C" (record (field "D" bool) (field "E" u32))))"#,
144             &[
145                 Param(Type::I32, Some(0)),
146                 Param(Type::F64, Some(8)),
147                 Param(Type::U8, Some(16)),
148                 Param(Type::I32, Some(20)),
149             ],
150         ),
151     )?;
152     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
153     let func = instance.get_func(&mut store, "echo").unwrap();
154     let ty = &func.params(&store)[0];
155     let inner_type = &ty.unwrap_record().fields().nth(2).unwrap().ty;
156     let input = ty.unwrap_record().new_val([
157         ("A", Val::U32(32343)),
158         ("B", Val::Float64(3.14159265_f64.to_bits())),
159         (
160             "C",
161             inner_type
162                 .unwrap_record()
163                 .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
164         ),
165     ])?;
166     let output = func.call_and_post_return(&mut store, &[input.clone()])?;
167 
168     assert_eq!(input, output);
169 
170     // Sad path: type mismatch
171 
172     let err = ty
173         .unwrap_record()
174         .new_val([
175             ("A", Val::S32(32343)),
176             ("B", Val::Float64(3.14159265_f64.to_bits())),
177             (
178                 "C",
179                 inner_type
180                     .unwrap_record()
181                     .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
182             ),
183         ])
184         .unwrap_err();
185 
186     assert!(err.to_string().contains("type mismatch"), "{err}");
187 
188     // Sad path: too many fields
189 
190     let err = ty
191         .unwrap_record()
192         .new_val([
193             ("A", Val::U32(32343)),
194             ("B", Val::Float64(3.14159265_f64.to_bits())),
195             (
196                 "C",
197                 inner_type
198                     .unwrap_record()
199                     .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
200             ),
201             ("F", Val::Unit),
202         ])
203         .unwrap_err();
204 
205     assert!(
206         err.to_string().contains("expected 3 value(s); got 4"),
207         "{err}"
208     );
209 
210     // Sad path: too few fields
211 
212     let err = ty
213         .unwrap_record()
214         .new_val([
215             ("A", Val::U32(32343)),
216             ("B", Val::Float64(3.14159265_f64.to_bits())),
217         ])
218         .unwrap_err();
219 
220     assert!(
221         err.to_string().contains("expected 3 value(s); got 2"),
222         "{err}"
223     );
224 
225     Ok(())
226 }
227 
228 #[test]
229 fn variants() -> Result<()> {
230     let engine = super::engine();
231     let mut store = Store::new(&engine, ());
232 
233     let component = Component::new(
234         &engine,
235         make_echo_component_with_params(
236             r#"(variant (case "A" u32) (case "B" float64) (case "C" (record (field "D" bool) (field "E" u32))))"#,
237             &[
238                 Param(Type::U8, Some(0)),
239                 Param(Type::I64, Some(8)),
240                 Param(Type::I32, None),
241             ],
242         ),
243     )?;
244     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
245     let func = instance.get_func(&mut store, "echo").unwrap();
246     let ty = &func.params(&store)[0];
247     let input = ty
248         .unwrap_variant()
249         .new_val("B", Val::Float64(3.14159265_f64.to_bits()))?;
250     let output = func.call_and_post_return(&mut store, &[input.clone()])?;
251 
252     assert_eq!(input, output);
253 
254     // Do it again, this time using case "C"
255 
256     let component = Component::new(
257         &engine,
258         dbg!(make_echo_component_with_params(
259             r#"(variant (case "A" u32) (case "B" float64) (case "C" (record (field "D" bool) (field "E" u32))))"#,
260             &[
261                 Param(Type::U8, Some(0)),
262                 Param(Type::I64, Some(8)),
263                 Param(Type::I32, Some(12)),
264             ],
265         )),
266     )?;
267     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
268     let func = instance.get_func(&mut store, "echo").unwrap();
269     let ty = &func.params(&store)[0];
270     let c_type = &ty.unwrap_variant().cases().nth(2).unwrap().ty;
271     let input = ty.unwrap_variant().new_val(
272         "C",
273         c_type
274             .unwrap_record()
275             .new_val([("D", Val::Bool(true)), ("E", Val::U32(314159265))])?,
276     )?;
277     let output = func.call_and_post_return(&mut store, &[input.clone()])?;
278 
279     assert_eq!(input, output);
280 
281     // Sad path: type mismatch
282 
283     let err = ty
284         .unwrap_variant()
285         .new_val("B", Val::U64(314159265))
286         .unwrap_err();
287 
288     assert!(err.to_string().contains("type mismatch"), "{err}");
289 
290     // Sad path: unknown case
291 
292     let err = ty
293         .unwrap_variant()
294         .new_val("D", Val::U64(314159265))
295         .unwrap_err();
296 
297     assert!(err.to_string().contains("unknown variant case"), "{err}");
298 
299     // Make sure we lift variants which have cases of different sizes with the correct alignment
300 
301     let component = Component::new(
302         &engine,
303         make_echo_component_with_params(
304             r#"
305             (record
306                 (field "A" (variant
307                                (case "A" u32)
308                                (case "B" float64)
309                                (case "C" (record (field "D" bool) (field "E" u32)))))
310                 (field "B" u32))"#,
311             &[
312                 Param(Type::U8, Some(0)),
313                 Param(Type::I64, Some(8)),
314                 Param(Type::I32, None),
315                 Param(Type::I32, Some(16)),
316             ],
317         ),
318     )?;
319     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
320     let func = instance.get_func(&mut store, "echo").unwrap();
321     let ty = &func.params(&store)[0];
322     let a_type = &ty.unwrap_record().fields().nth(0).unwrap().ty;
323     let input = ty.unwrap_record().new_val([
324         (
325             "A",
326             a_type.unwrap_variant().new_val("A", Val::U32(314159265))?,
327         ),
328         ("B", Val::U32(628318530)),
329     ])?;
330     let output = func.call_and_post_return(&mut store, &[input.clone()])?;
331 
332     assert_eq!(input, output);
333 
334     Ok(())
335 }
336 
337 #[test]
338 fn flags() -> Result<()> {
339     let engine = super::engine();
340     let mut store = Store::new(&engine, ());
341 
342     let component = Component::new(
343         &engine,
344         make_echo_component_with_params(
345             r#"(flags "A" "B" "C" "D" "E")"#,
346             &[Param(Type::U8, Some(0))],
347         ),
348     )?;
349     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
350     let func = instance.get_func(&mut store, "echo").unwrap();
351     let ty = &func.params(&store)[0];
352     let input = ty.unwrap_flags().new_val(&["B", "D"])?;
353     let output = func.call_and_post_return(&mut store, &[input.clone()])?;
354 
355     assert_eq!(input, output);
356 
357     // Sad path: unknown flags
358 
359     let err = ty.unwrap_flags().new_val(&["B", "D", "F"]).unwrap_err();
360 
361     assert!(err.to_string().contains("unknown flag"), "{err}");
362 
363     Ok(())
364 }
365 
366 #[test]
367 fn everything() -> Result<()> {
368     // This serves to test both nested types and storing parameters on the heap (i.e. exceeding `MAX_STACK_PARAMS`)
369 
370     let engine = super::engine();
371     let mut store = Store::new(&engine, ());
372 
373     let component = Component::new(
374         &engine,
375         make_echo_component_with_params(
376             r#"
377             (record
378                 (field "A" u32)
379                 (field "B" (enum "1" "2"))
380                 (field "C" (record (field "D" bool) (field "E" u32)))
381                 (field "F" (list (flags "G" "H" "I")))
382                 (field "J" (variant
383                                (case "K" u32)
384                                (case "L" float64)
385                                (case "M" (record (field "N" bool) (field "O" u32)))))
386                 (field "P" s8)
387                 (field "Q" s16)
388                 (field "R" s32)
389                 (field "S" s64)
390                 (field "T" float32)
391                 (field "U" float64)
392                 (field "V" string)
393                 (field "W" char)
394                 (field "X" unit)
395                 (field "Y" (tuple u32 u32))
396                 (field "Z" (union u32 float64))
397                 (field "AA" (option u32))
398                 (field "BB" (expected string string))
399             )"#,
400             &[
401                 Param(Type::I32, Some(0)),
402                 Param(Type::U8, Some(4)),
403                 Param(Type::U8, Some(5)),
404                 Param(Type::I32, Some(8)),
405                 Param(Type::I32, Some(12)),
406                 Param(Type::I32, Some(16)),
407                 Param(Type::U8, Some(20)),
408                 Param(Type::I64, Some(28)),
409                 Param(Type::I32, Some(32)),
410                 Param(Type::S8, Some(36)),
411                 Param(Type::S16, Some(38)),
412                 Param(Type::I32, Some(40)),
413                 Param(Type::I64, Some(48)),
414                 Param(Type::F32, Some(56)),
415                 Param(Type::F64, Some(64)),
416                 Param(Type::I32, Some(72)),
417                 Param(Type::I32, Some(76)),
418                 Param(Type::I32, Some(80)),
419                 Param(Type::I32, Some(84)),
420                 Param(Type::I32, Some(88)),
421                 Param(Type::I64, Some(96)),
422                 Param(Type::U8, Some(104)),
423                 Param(Type::I32, Some(108)),
424                 Param(Type::U8, Some(112)),
425                 Param(Type::I32, Some(116)),
426                 Param(Type::I32, Some(120)),
427             ],
428         ),
429     )?;
430     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
431     let func = instance.get_func(&mut store, "echo").unwrap();
432     let ty = &func.params(&store)[0];
433     let types = ty
434         .unwrap_record()
435         .fields()
436         .map(|field| field.ty)
437         .collect::<Box<[component::Type]>>();
438     let (b_type, c_type, f_type, j_type, y_type, z_type, aa_type, bb_type) = (
439         &types[1], &types[2], &types[3], &types[4], &types[14], &types[15], &types[16], &types[17],
440     );
441     let f_element_type = &f_type.unwrap_list().ty();
442     let input = ty.unwrap_record().new_val([
443         ("A", Val::U32(32343)),
444         ("B", b_type.unwrap_enum().new_val("2")?),
445         (
446             "C",
447             c_type
448                 .unwrap_record()
449                 .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
450         ),
451         (
452             "F",
453             f_type.unwrap_list().new_val(Box::new([f_element_type
454                 .unwrap_flags()
455                 .new_val(&["G", "I"])?]))?,
456         ),
457         (
458             "J",
459             j_type
460                 .unwrap_variant()
461                 .new_val("L", Val::Float64(3.14159265_f64.to_bits()))?,
462         ),
463         ("P", Val::S8(42)),
464         ("Q", Val::S16(4242)),
465         ("R", Val::S32(42424242)),
466         ("S", Val::S64(424242424242424242)),
467         ("T", Val::Float32(3.14159265_f32.to_bits())),
468         ("U", Val::Float64(3.14159265_f64.to_bits())),
469         ("V", Val::String(Box::from("wow, nice types"))),
470         ("W", Val::Char('��')),
471         ("X", Val::Unit),
472         (
473             "Y",
474             y_type
475                 .unwrap_tuple()
476                 .new_val(Box::new([Val::U32(42), Val::U32(24)]))?,
477         ),
478         (
479             "Z",
480             z_type
481                 .unwrap_union()
482                 .new_val(1, Val::Float64(3.14159265_f64.to_bits()))?,
483         ),
484         (
485             "AA",
486             aa_type.unwrap_option().new_val(Some(Val::U32(314159265)))?,
487         ),
488         (
489             "BB",
490             bb_type
491                 .unwrap_expected()
492                 .new_val(Ok(Val::String(Box::from("no problem"))))?,
493         ),
494     ])?;
495     let output = func.call_and_post_return(&mut store, &[input.clone()])?;
496 
497     assert_eq!(input, output);
498 
499     Ok(())
500 }
501