xref: /wasmtime-44.0.1/tests/all/host_funcs.rs (revision cc8d04f4)
1 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
2 use wasmtime::bail;
3 use wasmtime::*;
4 
5 #[test]
wrap_func() -> Result<()>6 fn wrap_func() -> Result<()> {
7     let engine = Engine::default();
8     let mut linker = Linker::<()>::new(&engine);
9     linker.allow_shadowing(true);
10 
11     linker.func_wrap("", "", || {})?;
12     linker.func_wrap("m", "f", |_: i32| {})?;
13     linker.func_wrap("m", "f2", |_: i32, _: i64| {})?;
14     linker.func_wrap("m2", "f", |_: f32, _: f64| {})?;
15     linker.func_wrap("m2", "f2", || -> i32 { 0 })?;
16     linker.func_wrap("", "", || -> i64 { 0 })?;
17     linker.func_wrap("m", "f", || -> f32 { 0.0 })?;
18     linker.func_wrap("m2", "f", || -> f64 { 0.0 })?;
19     linker.func_wrap("m3", "", || -> Option<Rooted<ExternRef>> { None })?;
20     linker.func_wrap("m3", "f", || -> Option<Func> { None })?;
21 
22     linker.func_wrap("", "f1", || -> Result<()> {
23         loop {}
24     })?;
25     linker.func_wrap("", "f2", || -> Result<i32> {
26         loop {}
27     })?;
28     linker.func_wrap("", "f3", || -> Result<i64> {
29         loop {}
30     })?;
31     linker.func_wrap("", "f4", || -> Result<f32> {
32         loop {}
33     })?;
34     linker.func_wrap("", "f5", || -> Result<f64> {
35         loop {}
36     })?;
37     linker.func_wrap("", "f6", || -> Result<Option<Rooted<ExternRef>>> {
38         loop {}
39     })?;
40     linker.func_wrap("", "f7", || -> Result<Option<Func>> {
41         loop {}
42     })?;
43     Ok(())
44 }
45 
46 #[test]
drop_func() -> Result<()>47 fn drop_func() -> Result<()> {
48     static HITS: AtomicUsize = AtomicUsize::new(0);
49     struct A;
50 
51     impl Drop for A {
52         fn drop(&mut self) {
53             HITS.fetch_add(1, SeqCst);
54         }
55     }
56 
57     let engine = Engine::default();
58     let mut linker = Linker::<()>::new(&engine);
59     linker.allow_shadowing(true);
60 
61     let a = A;
62     linker.func_wrap("", "", move || {
63         let _ = &a;
64     })?;
65 
66     assert_eq!(HITS.load(SeqCst), 0);
67 
68     // Define the function again to ensure redefined functions are dropped
69 
70     let a = A;
71     linker.func_wrap("", "", move || {
72         let _ = &a;
73     })?;
74 
75     assert_eq!(HITS.load(SeqCst), 1);
76 
77     drop(linker);
78 
79     assert_eq!(HITS.load(SeqCst), 2);
80 
81     Ok(())
82 }
83 
84 #[test]
85 #[cfg_attr(miri, ignore)]
drop_delayed() -> Result<()>86 fn drop_delayed() -> Result<()> {
87     static HITS: AtomicUsize = AtomicUsize::new(0);
88 
89     struct A;
90 
91     impl Drop for A {
92         fn drop(&mut self) {
93             HITS.fetch_add(1, SeqCst);
94         }
95     }
96 
97     let engine = Engine::default();
98     let mut linker = Linker::<()>::new(&engine);
99 
100     let a = A;
101     linker.func_wrap("", "", move || {
102         let _ = &a;
103     })?;
104 
105     assert_eq!(HITS.load(SeqCst), 0);
106 
107     let module = Module::new(&engine, &wat::parse_str(r#"(import "" "" (func))"#)?)?;
108 
109     let mut store = Store::new(&engine, ());
110     let func = linker.get(&mut store, "", "").unwrap();
111     Instance::new(&mut store, &module, &[func])?;
112 
113     drop(store);
114 
115     assert_eq!(HITS.load(SeqCst), 0);
116 
117     let mut store = Store::new(&engine, ());
118     let func = linker.get(&mut store, "", "").unwrap();
119     Instance::new(&mut store, &module, &[func])?;
120 
121     drop(store);
122 
123     assert_eq!(HITS.load(SeqCst), 0);
124 
125     drop(linker);
126 
127     assert_eq!(HITS.load(SeqCst), 1);
128 
129     Ok(())
130 }
131 
132 #[test]
signatures_match() -> Result<()>133 fn signatures_match() -> Result<()> {
134     let engine = Engine::default();
135     let mut linker = Linker::<()>::new(&engine);
136 
137     linker.func_wrap("", "f1", || {})?;
138     linker.func_wrap("", "f2", || -> i32 {
139         loop {}
140     })?;
141     linker.func_wrap("", "f3", || -> i64 {
142         loop {}
143     })?;
144     linker.func_wrap("", "f4", || -> f32 {
145         loop {}
146     })?;
147     linker.func_wrap("", "f5", || -> f64 {
148         loop {}
149     })?;
150     linker.func_wrap(
151         "",
152         "f6",
153         |_: f32,
154          _: f64,
155          _: i32,
156          _: i64,
157          _: i32,
158          _: Option<Rooted<ExternRef>>,
159          _: Option<Func>|
160          -> f64 { loop {} },
161     )?;
162 
163     let mut store = Store::new(&engine, ());
164 
165     let f = linker
166         .get(&mut store, "", "f1")
167         .unwrap()
168         .into_func()
169         .unwrap();
170     assert_eq!(f.ty(&store).params().len(), 0);
171     assert_eq!(f.ty(&store).results().len(), 0);
172 
173     let f = linker
174         .get(&mut store, "", "f2")
175         .unwrap()
176         .into_func()
177         .unwrap();
178     assert_eq!(f.ty(&store).params().len(), 0);
179     assert_eq!(f.ty(&store).results().len(), 1);
180     assert!(f.ty(&store).results().nth(0).unwrap().is_i32());
181 
182     let f = linker
183         .get(&mut store, "", "f3")
184         .unwrap()
185         .into_func()
186         .unwrap();
187     assert_eq!(f.ty(&store).params().len(), 0);
188     assert_eq!(f.ty(&store).results().len(), 1);
189     assert!(f.ty(&store).results().nth(0).unwrap().is_i64());
190 
191     let f = linker
192         .get(&mut store, "", "f4")
193         .unwrap()
194         .into_func()
195         .unwrap();
196     assert_eq!(f.ty(&store).params().len(), 0);
197     assert_eq!(f.ty(&store).results().len(), 1);
198     assert!(f.ty(&store).results().nth(0).unwrap().is_f32());
199 
200     let f = linker
201         .get(&mut store, "", "f5")
202         .unwrap()
203         .into_func()
204         .unwrap();
205     assert_eq!(f.ty(&store).params().len(), 0);
206     assert_eq!(f.ty(&store).results().len(), 1);
207     assert!(f.ty(&store).results().nth(0).unwrap().is_f64());
208 
209     let f = linker
210         .get(&mut store, "", "f6")
211         .unwrap()
212         .into_func()
213         .unwrap();
214 
215     assert_eq!(f.ty(&store).params().len(), 7);
216     assert!(f.ty(&store).params().nth(0).unwrap().is_f32());
217     assert!(f.ty(&store).params().nth(1).unwrap().is_f64());
218     assert!(f.ty(&store).params().nth(2).unwrap().is_i32());
219     assert!(f.ty(&store).params().nth(3).unwrap().is_i64());
220     assert!(f.ty(&store).params().nth(4).unwrap().is_i32());
221     assert!(f.ty(&store).params().nth(5).unwrap().is_externref());
222     assert!(f.ty(&store).params().nth(6).unwrap().is_funcref());
223 
224     assert_eq!(f.ty(&store).results().len(), 1);
225     assert!(f.ty(&store).results().nth(0).unwrap().is_f64());
226 
227     Ok(())
228 }
229 
230 #[test]
231 #[cfg_attr(miri, ignore)]
import_works() -> Result<()>232 fn import_works() -> Result<()> {
233     static HITS: AtomicUsize = AtomicUsize::new(0);
234 
235     let wasm = wat::parse_str(
236         r#"
237             (import "" "f1" (func))
238             (import "" "f2" (func (param i32) (result i32)))
239             (import "" "f3" (func (param i32) (param i64)))
240             (import "" "f4" (func (param i32 i64 i32 f32 f64 externref funcref)))
241 
242             (func (export "run") (param externref funcref)
243                 call 0
244                 i32.const 0
245                 call 1
246                 i32.const 1
247                 i32.add
248                 i64.const 3
249                 call 2
250 
251                 i32.const 100
252                 i64.const 200
253                 i32.const 300
254                 f32.const 400
255                 f64.const 500
256                 local.get 0
257                 local.get 1
258                 call 3
259             )
260         "#,
261     )?;
262 
263     let engine = Engine::default();
264     let mut linker = Linker::<()>::new(&engine);
265 
266     linker.func_wrap("", "f1", || {
267         assert_eq!(HITS.fetch_add(1, SeqCst), 0);
268     })?;
269 
270     linker.func_wrap("", "f2", |x: i32| -> i32 {
271         assert_eq!(x, 0);
272         assert_eq!(HITS.fetch_add(1, SeqCst), 1);
273         1
274     })?;
275 
276     linker.func_wrap("", "f3", |x: i32, y: i64| {
277         assert_eq!(x, 2);
278         assert_eq!(y, 3);
279         assert_eq!(HITS.fetch_add(1, SeqCst), 2);
280     })?;
281 
282     linker.func_wrap(
283         "",
284         "f4",
285         |mut caller: Caller<'_, _>,
286          a: i32,
287          b: i64,
288          c: i32,
289          d: f32,
290          e: f64,
291          f: Option<Rooted<ExternRef>>,
292          g: Option<Func>| {
293             assert_eq!(a, 100);
294             assert_eq!(b, 200);
295             assert_eq!(c, 300);
296             assert_eq!(d, 400.0);
297             assert_eq!(e, 500.0);
298             assert_eq!(
299                 f.as_ref()
300                     .unwrap()
301                     .data(&caller)
302                     .unwrap()
303                     .unwrap()
304                     .downcast_ref::<String>()
305                     .unwrap(),
306                 "hello"
307             );
308             let mut results = [Val::I32(0)];
309             g.as_ref()
310                 .unwrap()
311                 .call(&mut caller, &[], &mut results)
312                 .unwrap();
313             assert_eq!(results[0].unwrap_i32(), 42);
314             assert_eq!(HITS.fetch_add(1, SeqCst), 3);
315         },
316     )?;
317 
318     let module = Module::new(&engine, &wasm)?;
319 
320     let mut store = Store::new(&engine, ());
321     let instance = linker.instantiate(&mut store, &module)?;
322     let run = instance.get_func(&mut store, "run").unwrap();
323     let funcref = Val::FuncRef(Some(Func::wrap(&mut store, || -> i32 { 42 })));
324     let externref = Val::ExternRef(Some(ExternRef::new(&mut store, "hello".to_string())?));
325     run.call(&mut store, &[externref, funcref], &mut [])?;
326 
327     assert_eq!(HITS.load(SeqCst), 4);
328 
329     Ok(())
330 }
331 
332 #[test]
333 #[cfg_attr(miri, ignore)]
call_import_many_args() -> Result<()>334 fn call_import_many_args() -> Result<()> {
335     let wasm = wat::parse_str(
336         r#"
337             (import "" "host" (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)))
338             (func (export "run")
339                 i32.const 1
340                 i32.const 2
341                 i32.const 3
342                 i32.const 4
343                 i32.const 5
344                 i32.const 6
345                 i32.const 7
346                 i32.const 8
347                 i32.const 9
348                 i32.const 10
349                 call 0
350             )
351         "#,
352     )?;
353 
354     let engine = Engine::default();
355     let mut linker = Linker::<()>::new(&engine);
356 
357     linker.func_wrap(
358         "",
359         "host",
360         |x1: i32,
361          x2: i32,
362          x3: i32,
363          x4: i32,
364          x5: i32,
365          x6: i32,
366          x7: i32,
367          x8: i32,
368          x9: i32,
369          x10: i32| {
370             assert_eq!(x1, 1);
371             assert_eq!(x2, 2);
372             assert_eq!(x3, 3);
373             assert_eq!(x4, 4);
374             assert_eq!(x5, 5);
375             assert_eq!(x6, 6);
376             assert_eq!(x7, 7);
377             assert_eq!(x8, 8);
378             assert_eq!(x9, 9);
379             assert_eq!(x10, 10);
380         },
381     )?;
382 
383     let module = Module::new(&engine, &wasm)?;
384     let mut store = Store::new(&engine, ());
385     let instance = linker.instantiate(&mut store, &module)?;
386     let run = instance.get_func(&mut store, "run").unwrap();
387     run.call(&mut store, &[], &mut [])?;
388 
389     Ok(())
390 }
391 
392 #[test]
393 #[cfg_attr(miri, ignore)]
call_wasm_many_args() -> Result<()>394 fn call_wasm_many_args() -> Result<()> {
395     let wasm = wat::parse_str(
396         r#"
397             (func (export "run") (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
398                 i32.const 1
399                 local.get 0
400                 i32.ne
401                 if
402                     unreachable
403                 end
404 
405                 i32.const 10
406                 local.get 9
407                 i32.ne
408                 if
409                     unreachable
410                 end
411             )
412 
413             (func (export "test")
414                 i32.const 1
415                 i32.const 2
416                 i32.const 3
417                 i32.const 4
418                 i32.const 5
419                 i32.const 6
420                 i32.const 7
421                 i32.const 8
422                 i32.const 9
423                 i32.const 10
424                 call 0
425             )
426         "#,
427     )?;
428 
429     let engine = Engine::default();
430     let module = Module::new(&engine, &wasm)?;
431 
432     let mut store = Store::new(&engine, ());
433     let instance = Instance::new(&mut store, &module, &[])?;
434 
435     let run = instance.get_func(&mut store, "run").unwrap();
436     run.call(
437         &mut store,
438         &[
439             1.into(),
440             2.into(),
441             3.into(),
442             4.into(),
443             5.into(),
444             6.into(),
445             7.into(),
446             8.into(),
447             9.into(),
448             10.into(),
449         ],
450         &mut [],
451     )?;
452 
453     let typed_run = instance
454         .get_typed_func::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), ()>(
455             &mut store, "run",
456         )?;
457     typed_run.call(&mut store, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))?;
458 
459     let test = instance.get_func(&mut store, "test").unwrap();
460     test.call(&mut store, &[], &mut [])?;
461 
462     Ok(())
463 }
464 
465 #[test]
466 #[cfg_attr(miri, ignore)]
trap_smoke() -> Result<()>467 fn trap_smoke() -> Result<()> {
468     let engine = Engine::default();
469     let mut linker = Linker::<()>::new(&engine);
470     linker.func_wrap("", "", || -> Result<()> { bail!("test") })?;
471 
472     let mut store = Store::new(&engine, ());
473 
474     let f = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
475 
476     let err = f.call(&mut store, &[], &mut []).unwrap_err();
477 
478     assert!(err.to_string().contains("test"));
479 
480     Ok(())
481 }
482 
483 #[test]
484 #[cfg_attr(miri, ignore)]
trap_import() -> Result<()>485 fn trap_import() -> Result<()> {
486     let wasm = wat::parse_str(
487         r#"
488             (import "" "" (func))
489             (start 0)
490         "#,
491     )?;
492 
493     let engine = Engine::default();
494     let mut linker = Linker::new(&engine);
495     linker.func_wrap("", "", || -> Result<()> { bail!("foo") })?;
496 
497     let module = Module::new(&engine, &wasm)?;
498     let mut store = Store::new(&engine, ());
499 
500     let trap = linker.instantiate(&mut store, &module).unwrap_err();
501 
502     assert!(trap.to_string().contains("foo"));
503 
504     Ok(())
505 }
506 
507 #[test]
508 #[cfg_attr(miri, ignore)]
new_from_signature() -> Result<()>509 fn new_from_signature() -> Result<()> {
510     let engine = Engine::default();
511     let mut linker = Linker::new(&engine);
512 
513     let ty = FuncType::new(&engine, None, None);
514     linker.func_new("", "f1", ty, |_, _, _| panic!())?;
515 
516     let ty = FuncType::new(&engine, Some(ValType::I32), Some(ValType::F64));
517     linker.func_new("", "f2", ty, |_, _, _| panic!())?;
518 
519     let mut store = Store::new(&engine, ());
520 
521     let f = linker
522         .get(&mut store, "", "f1")
523         .unwrap()
524         .into_func()
525         .unwrap();
526     assert!(f.typed::<(), ()>(&store).is_ok());
527     assert!(f.typed::<(), i32>(&store).is_err());
528     assert!(f.typed::<i32, ()>(&store).is_err());
529 
530     let f = linker
531         .get(&mut store, "", "f2")
532         .unwrap()
533         .into_func()
534         .unwrap();
535     assert!(f.typed::<(), ()>(&store).is_err());
536     assert!(f.typed::<(), i32>(&store).is_err());
537     assert!(f.typed::<i32, ()>(&store).is_err());
538     assert!(f.typed::<i32, f64>(&store).is_ok());
539 
540     Ok(())
541 }
542 
543 #[test]
call_wrapped_func() -> Result<()>544 fn call_wrapped_func() -> Result<()> {
545     let engine = Engine::default();
546     let mut linker = Linker::new(&engine);
547     let mut results = [Val::I32(0)];
548 
549     linker.func_wrap("", "f1", |a: i32, b: i64, c: f32, d: f64| {
550         assert_eq!(a, 1);
551         assert_eq!(b, 2);
552         assert_eq!(c, 3.0);
553         assert_eq!(d, 4.0);
554     })?;
555 
556     linker.func_wrap("", "f2", || 1i32)?;
557 
558     linker.func_wrap("", "f3", || 2i64)?;
559 
560     linker.func_wrap("", "f4", || 3.0f32)?;
561 
562     linker.func_wrap("", "f5", || 4.0f64)?;
563 
564     let mut store = Store::new(&engine, ());
565 
566     let f = linker
567         .get(&mut store, "", "f1")
568         .unwrap()
569         .into_func()
570         .unwrap();
571     f.call(
572         &mut store,
573         &[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()],
574         &mut [],
575     )?;
576     f.typed::<(i32, i64, f32, f64), ()>(&store)?
577         .call(&mut store, (1, 2, 3.0, 4.0))?;
578 
579     let f = linker
580         .get(&mut store, "", "f2")
581         .unwrap()
582         .into_func()
583         .unwrap();
584     f.call(&mut store, &[], &mut results)?;
585     assert_eq!(results[0].unwrap_i32(), 1);
586     assert_eq!(f.typed::<(), i32>(&store)?.call(&mut store, ())?, 1);
587 
588     let f = linker
589         .get(&mut store, "", "f3")
590         .unwrap()
591         .into_func()
592         .unwrap();
593     f.call(&mut store, &[], &mut results)?;
594     assert_eq!(results[0].unwrap_i64(), 2);
595     assert_eq!(f.typed::<(), i64>(&store)?.call(&mut store, ())?, 2);
596 
597     let f = linker
598         .get(&mut store, "", "f4")
599         .unwrap()
600         .into_func()
601         .unwrap();
602     f.call(&mut store, &[], &mut results)?;
603     assert_eq!(results[0].unwrap_f32(), 3.0);
604     assert_eq!(f.typed::<(), f32>(&store)?.call(&mut store, ())?, 3.0);
605 
606     let f = linker
607         .get(&mut store, "", "f5")
608         .unwrap()
609         .into_func()
610         .unwrap();
611     f.call(&mut store, &[], &mut results)?;
612     assert_eq!(results[0].unwrap_f64(), 4.0);
613     assert_eq!(f.typed::<(), f64>(&store)?.call(&mut store, ())?, 4.0);
614 
615     Ok(())
616 }
617 
618 #[test]
619 #[cfg_attr(miri, ignore)]
func_return_nothing() -> Result<()>620 fn func_return_nothing() -> Result<()> {
621     let engine = Engine::default();
622     let mut linker = Linker::new(&engine);
623     let ty = FuncType::new(&engine, None, Some(ValType::I32));
624     linker.func_new("", "", ty, |_, _, _| Ok(()))?;
625 
626     let mut store = Store::new(&engine, ());
627     let f = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
628     let err = f.call(&mut store, &[], &mut [Val::I32(0)]).unwrap_err();
629     assert!(
630         err.to_string()
631             .contains("function attempted to return an incompatible value")
632     );
633     Ok(())
634 }
635 
636 #[test]
637 #[cfg_attr(miri, ignore)]
call_via_funcref() -> Result<()>638 fn call_via_funcref() -> Result<()> {
639     static HITS: AtomicUsize = AtomicUsize::new(0);
640 
641     struct A;
642 
643     impl Drop for A {
644         fn drop(&mut self) {
645             HITS.fetch_add(1, SeqCst);
646         }
647     }
648 
649     let wasm = wat::parse_str(
650         r#"
651             (table $t 1 funcref)
652             (type $add (func (param i32 i32) (result i32)))
653             (func (export "call") (param funcref) (result i32 funcref)
654                 (table.set $t (i32.const 0) (local.get 0))
655                 (call_indirect (type $add) (i32.const 3) (i32.const 4) (i32.const 0))
656                 (local.get 0)
657             )
658         "#,
659     )?;
660 
661     let engine = Engine::default();
662     let mut linker = Linker::new(&engine);
663     let a = A;
664     linker.func_wrap("", "", move |x: i32, y: i32| {
665         let _ = &a;
666         x + y
667     })?;
668 
669     let module = Module::new(&engine, &wasm)?;
670     let mut store = Store::new(&engine, ());
671     let instance = Instance::new(&mut store, &module, &[])?;
672 
673     let f = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
674     let mut results = [Val::I32(0), Val::I32(0)];
675     instance
676         .get_func(&mut store, "call")
677         .unwrap()
678         .call(&mut store, &[f.into()], &mut results)?;
679     assert_eq!(results[0].unwrap_i32(), 7);
680 
681     {
682         let f = results[1].unwrap_funcref().unwrap();
683         let mut results = [Val::I32(0)];
684         f.call(&mut store, &[1.into(), 2.into()], &mut results)?;
685         assert_eq!(results[0].unwrap_i32(), 3);
686     }
687 
688     assert_eq!(HITS.load(SeqCst), 0);
689 
690     drop(store);
691 
692     assert_eq!(HITS.load(SeqCst), 0);
693 
694     drop(linker);
695 
696     assert_eq!(HITS.load(SeqCst), 1);
697 
698     Ok(())
699 }
700 
701 #[test]
store_with_context() -> Result<()>702 fn store_with_context() -> Result<()> {
703     struct Ctx {
704         called: bool,
705     }
706 
707     let engine = Engine::default();
708     let mut linker = Linker::new(&engine);
709 
710     linker.func_wrap("", "", |mut caller: Caller<'_, Ctx>| {
711         caller.data_mut().called = true;
712     })?;
713 
714     let mut store = Store::new(&engine, Ctx { called: false });
715 
716     let f = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
717     f.call(&mut store, &[], &mut [])?;
718 
719     assert!(store.data().called);
720 
721     Ok(())
722 }
723 
724 #[test]
725 #[cfg_attr(miri, ignore)]
wasi_imports() -> Result<()>726 fn wasi_imports() -> Result<()> {
727     let engine = Engine::default();
728     let mut linker = Linker::new(&engine);
729     wasmtime_wasi::p1::add_to_linker_sync(&mut linker, |t| t)?;
730 
731     let wasm = wat::parse_str(
732         r#"
733         (import "wasi_snapshot_preview1" "proc_exit" (func $__wasi_proc_exit (param i32)))
734         (memory (export "memory") 0)
735         (func (export "_start")
736             (call $__wasi_proc_exit (i32.const 123))
737         )
738         "#,
739     )?;
740 
741     let module = Module::new(&engine, wasm)?;
742     let mut store = Store::new(&engine, wasmtime_wasi::WasiCtxBuilder::new().build_p1());
743     let instance = linker.instantiate(&mut store, &module)?;
744 
745     let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
746     let exit = start
747         .call(&mut store, ())
748         .unwrap_err()
749         .downcast::<wasmtime_wasi::I32Exit>()?;
750     assert_eq!(exit.0, 123);
751 
752     Ok(())
753 }
754