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