1 #![cfg(not(miri))]
2 
3 use super::REALLOC_AND_FREE;
4 use std::ops::Deref;
5 use wasmtime::Result;
6 use wasmtime::component::*;
7 use wasmtime::{Config, Engine, Store, StoreContextMut, Trap, WasmBacktrace};
8 
9 #[test]
can_compile() -> Result<()>10 fn can_compile() -> Result<()> {
11     let engine = super::engine();
12     let libc = r#"
13         (core module $libc
14             (memory (export "memory") 1)
15             (func (export "realloc") (param i32 i32 i32 i32) (result i32)
16                 unreachable)
17         )
18         (core instance $libc (instantiate $libc))
19     "#;
20     Component::new(
21         &engine,
22         r#"(component
23             (import "a" (func $f))
24             (core func (canon lower (func $f)))
25         )"#,
26     )?;
27     Component::new(
28         &engine,
29         format!(
30             r#"(component
31                 (import "a" (func $f (param "a" string)))
32                 {libc}
33                 (core func (canon lower (func $f) (memory $libc "memory") (realloc (func $libc "realloc"))))
34             )"#
35         ),
36     )?;
37     Component::new(
38         &engine,
39         format!(
40             r#"(component
41                 (import "f1" (func $f1 (param "a" string) (result string)))
42                 {libc}
43                 (core func (canon lower (func $f1) (memory $libc "memory") (realloc (func $libc "realloc"))))
44 
45                 (import "f2" (func $f2 (param "a" u32) (result (list u8))))
46                 (core instance $libc2 (instantiate $libc))
47                 (core func (canon lower (func $f2) (memory $libc2 "memory") (realloc (func $libc2 "realloc"))))
48 
49                 (core func (canon lower (func $f1) (memory $libc2 "memory") (realloc (func $libc2 "realloc"))))
50                 (core func (canon lower (func $f2) (memory $libc "memory") (realloc (func $libc "realloc"))))
51             )"#
52         ),
53     )?;
54     Component::new(
55         &engine,
56         format!(
57             r#"(component
58                 (import "log" (func $log (param "a" string)))
59                 {libc}
60                 (core func $log_lower (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc"))))
61 
62                 (core module $logger
63                     (import "host" "log" (func $log (param i32 i32)))
64                     (import "libc" "memory" (memory 1))
65 
66                     (func (export "call")
67                         i32.const 0
68                         i32.const 0
69                         call $log)
70                 )
71                 (core instance $logger (instantiate $logger
72                     (with "host" (instance (export "log" (func $log_lower))))
73                     (with "libc" (instance $libc))
74                 ))
75 
76                 (func (export "call")
77                     (canon lift (core func $logger "call"))
78                 )
79             )"#
80         ),
81     )?;
82     Ok(())
83 }
84 
85 #[test]
simple() -> Result<()>86 fn simple() -> Result<()> {
87     let component = r#"
88         (component
89             (import "a" (func $log (param "a" string)))
90 
91             (core module $libc
92                 (memory (export "memory") 1)
93 
94                 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
95                     unreachable)
96             )
97             (core instance $libc (instantiate $libc))
98             (core func $log_lower
99                 (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc")))
100             )
101             (core module $m
102                 (import "libc" "memory" (memory 1))
103                 (import "host" "log" (func $log (param i32 i32)))
104 
105                 (func (export "call")
106                     i32.const 5
107                     i32.const 11
108                     call $log)
109 
110                 (data (i32.const 5) "hello world")
111             )
112             (core instance $i (instantiate $m
113                 (with "libc" (instance $libc))
114                 (with "host" (instance (export "log" (func $log_lower))))
115             ))
116             (func (export "call")
117                 (canon lift (core func $i "call"))
118             )
119         )
120     "#;
121 
122     let engine = super::engine();
123     let component = Component::new(&engine, component)?;
124     let mut store = Store::new(&engine, None);
125     assert!(store.data().is_none());
126 
127     // First, test the static API
128 
129     let mut linker = Linker::new(&engine);
130     linker.root().func_wrap(
131         "a",
132         |mut store: StoreContextMut<'_, Option<String>>, (arg,): (WasmStr,)| -> Result<_> {
133             let s = arg.to_str(&store)?.to_string();
134             assert!(store.data().is_none());
135             *store.data_mut() = Some(s);
136             Ok(())
137         },
138     )?;
139     let instance = linker.instantiate(&mut store, &component)?;
140     instance
141         .get_typed_func::<(), ()>(&mut store, "call")?
142         .call(&mut store, ())?;
143     assert_eq!(store.data().as_ref().unwrap(), "hello world");
144 
145     // Next, test the dynamic API
146 
147     *store.data_mut() = None;
148     let mut linker = Linker::new(&engine);
149     linker.root().func_new(
150         "a",
151         |mut store: StoreContextMut<'_, Option<String>>, _, args, _results| {
152             if let Val::String(s) = &args[0] {
153                 assert!(store.data().is_none());
154                 *store.data_mut() = Some(s.to_string());
155                 Ok(())
156             } else {
157                 panic!()
158             }
159         },
160     )?;
161     let instance = linker.instantiate(&mut store, &component)?;
162     instance
163         .get_func(&mut store, "call")
164         .unwrap()
165         .call(&mut store, &[], &mut [])?;
166     assert_eq!(store.data().as_ref().unwrap(), "hello world");
167 
168     Ok(())
169 }
170 
171 #[test]
functions_in_instances() -> Result<()>172 fn functions_in_instances() -> Result<()> {
173     let component = r#"
174         (component
175             (type $import-type (instance
176                 (export "a" (func (param "a" string)))
177             ))
178             (import (interface "test:test/foo") (instance $import (type $import-type)))
179             (alias export $import "a" (func $log))
180 
181             (core module $libc
182                 (memory (export "memory") 1)
183 
184                 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
185                     unreachable)
186             )
187             (core instance $libc (instantiate $libc))
188             (core func $log_lower
189                 (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc")))
190             )
191             (core module $m
192                 (import "libc" "memory" (memory 1))
193                 (import "host" "log" (func $log (param i32 i32)))
194 
195                 (func (export "call")
196                     i32.const 5
197                     i32.const 11
198                     call $log)
199 
200                 (data (i32.const 5) "hello world")
201             )
202             (core instance $i (instantiate $m
203                 (with "libc" (instance $libc))
204                 (with "host" (instance (export "log" (func $log_lower))))
205             ))
206             (func $call
207                 (canon lift (core func $i "call"))
208             )
209             (component $c
210                 (import "import-call" (func $f))
211                 (export "call" (func $f))
212             )
213             (instance $export (instantiate $c
214                 (with "import-call" (func $call))
215             ))
216             (export (interface "test:test/foo") (instance $export))
217         )
218     "#;
219 
220     let engine = super::engine();
221     let component = Component::new(&engine, component)?;
222     let instance_index = component.get_export_index(None, "test:test/foo").unwrap();
223     let func_index = component
224         .get_export_index(Some(&instance_index), "call")
225         .unwrap();
226     let mut store = Store::new(&engine, None);
227     assert!(store.data().is_none());
228 
229     // First, test the static API
230 
231     let mut linker = Linker::new(&engine);
232     linker.instance("test:test/foo")?.func_wrap(
233         "a",
234         |mut store: StoreContextMut<'_, Option<String>>, (arg,): (WasmStr,)| -> Result<_> {
235             let s = arg.to_str(&store)?.to_string();
236             assert!(store.data().is_none());
237             *store.data_mut() = Some(s);
238             Ok(())
239         },
240     )?;
241     let instance = linker.instantiate(&mut store, &component)?;
242     let func = instance.get_typed_func::<(), ()>(&mut store, &func_index)?;
243     func.call(&mut store, ())?;
244     assert_eq!(store.data().as_ref().unwrap(), "hello world");
245 
246     // Next, test the dynamic API
247 
248     *store.data_mut() = None;
249     let mut linker = Linker::new(&engine);
250     linker.instance("test:test/foo")?.func_new(
251         "a",
252         |mut store: StoreContextMut<'_, Option<String>>, _, args, _results| {
253             if let Val::String(s) = &args[0] {
254                 assert!(store.data().is_none());
255                 *store.data_mut() = Some(s.to_string());
256                 Ok(())
257             } else {
258                 panic!()
259             }
260         },
261     )?;
262     let instance = linker.instantiate(&mut store, &component)?;
263     let func = instance.get_func(&mut store, func_index).unwrap();
264     func.call(&mut store, &[], &mut [])?;
265     assert_eq!(store.data().as_ref().unwrap(), "hello world");
266 
267     Ok(())
268 }
269 
270 #[test]
attempt_to_leave_during_malloc() -> Result<()>271 fn attempt_to_leave_during_malloc() -> Result<()> {
272     let component = r#"
273 (component
274   (import "thunk" (func $thunk))
275   (import "ret-string" (func $ret_string (result string)))
276 
277   (core module $host_shim
278     (table (export "table") 2 funcref)
279     (func $shim_thunk (export "thunk")
280       i32.const 0
281       call_indirect)
282     (func $shim_ret_string (export "ret-string") (param i32)
283       local.get 0
284       i32.const 1
285       call_indirect (param i32))
286   )
287   (core instance $host_shim (instantiate $host_shim))
288 
289   (core module $m
290     (import "host" "thunk" (func $thunk))
291     (import "host" "ret-string" (func $ret_string (param i32)))
292 
293     (memory (export "memory") 1)
294 
295     (func $realloc (export "realloc") (param i32 i32 i32 i32) (result i32)
296       call $thunk
297       unreachable)
298 
299     (func $run (export "run")
300       i32.const 8
301       call $ret_string)
302 
303     (func (export "take-string") (param i32 i32)
304         unreachable)
305   )
306   (core instance $m (instantiate $m (with "host" (instance $host_shim))))
307 
308   (core module $host_shim_filler_inner
309     (import "shim" "table" (table 2 funcref))
310     (import "host" "thunk" (func $thunk))
311     (import "host" "ret-string" (func $ret_string (param i32)))
312     (elem (i32.const 0) $thunk $ret_string)
313   )
314 
315   (core func $thunk_lower
316     (canon lower (func $thunk) (memory $m "memory") (realloc (func $m "realloc")))
317   )
318 
319   (core func $ret_string_lower
320     (canon lower (func $ret_string) (memory $m "memory") (realloc (func $m "realloc")))
321   )
322 
323   (core instance (instantiate $host_shim_filler_inner
324     (with "shim" (instance $host_shim))
325     (with "host" (instance
326       (export "thunk" (func $thunk_lower))
327       (export "ret-string" (func $ret_string_lower))
328     ))
329   ))
330 
331   (func (export "run")
332     (canon lift (core func $m "run"))
333   )
334   (func (export "take-string") (param "a" string)
335     (canon lift (core func $m "take-string") (memory $m "memory") (realloc (func $m "realloc")))
336   )
337 )
338     "#;
339 
340     let engine = super::engine();
341     let mut linker = Linker::new(&engine);
342     linker.root().func_wrap("thunk", |_, _: ()| -> Result<()> {
343         panic!("should not get here")
344     })?;
345     linker
346         .root()
347         .func_wrap("ret-string", |_, _: ()| -> Result<_> {
348             Ok(("hello".to_string(),))
349         })?;
350     let component = Component::new(&engine, component)?;
351 
352     {
353         let mut store = Store::new(&engine, ());
354 
355         // Assert that during a host import if we return values to wasm that a trap
356         // happens if we try to leave the instance.
357         let trap = linker
358             .instantiate(&mut store, &component)?
359             .get_typed_func::<(), ()>(&mut store, "run")?
360             .call(&mut store, ())
361             .unwrap_err();
362         assert!(
363             format!("{trap:?}").contains("cannot leave component instance"),
364             "bad trap: {trap:?}",
365         );
366 
367         let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();
368         assert_eq!(trace.len(), 4);
369 
370         // This was our entry point...
371         assert_eq!(trace[3].module().name(), Some("m"));
372         assert_eq!(trace[3].func_name(), Some("run"));
373 
374         // ... which called an imported function which ends up being originally
375         // defined by the shim instance. The shim instance then does an indirect
376         // call through a table which goes to the `canon.lower`'d host function
377         assert_eq!(trace[2].module().name(), Some("host_shim"));
378         assert_eq!(trace[2].func_name(), Some("shim_ret_string"));
379 
380         // ... and the lowered host function will call realloc to allocate space for
381         // the result
382         assert_eq!(trace[1].module().name(), Some("m"));
383         assert_eq!(trace[1].func_name(), Some("realloc"));
384 
385         // ... but realloc calls the shim instance and tries to exit the
386         // component, triggering a dynamic trap
387         assert_eq!(trace[0].module().name(), Some("host_shim"));
388         assert_eq!(trace[0].func_name(), Some("shim_thunk"));
389     }
390 
391     {
392         let mut store = Store::new(&engine, ());
393 
394         // In addition to the above trap also ensure that when we enter a wasm
395         // component if we try to leave while lowering then that's also a dynamic
396         // trap.
397         let trap = linker
398             .instantiate(&mut store, &component)?
399             .get_typed_func::<(&str,), ()>(&mut store, "take-string")?
400             .call(&mut store, ("x",))
401             .unwrap_err();
402         assert!(
403             format!("{trap:?}").contains("cannot leave component instance"),
404             "bad trap: {trap:?}",
405         );
406     }
407 
408     Ok(())
409 }
410 
411 #[test]
attempt_to_reenter_during_host() -> Result<()>412 fn attempt_to_reenter_during_host() -> Result<()> {
413     let component = r#"
414 (component
415   (import "thunk" (func $thunk))
416   (core func $thunk_lower (canon lower (func $thunk)))
417 
418   (core module $m
419     (import "host" "thunk" (func $thunk))
420 
421     (func $run (export "run")
422       call $thunk)
423   )
424   (core instance $m (instantiate $m
425     (with "host" (instance (export "thunk" (func $thunk_lower))))
426   ))
427 
428   (func (export "run")
429     (canon lift (core func $m "run"))
430   )
431 )
432     "#;
433 
434     let engine = super::engine();
435     let component = Component::new(&engine, component)?;
436 
437     // First, test the static API
438 
439     struct StaticState {
440         func: Option<TypedFunc<(), ()>>,
441     }
442 
443     let mut store = Store::new(&engine, StaticState { func: None });
444     let mut linker = Linker::new(&engine);
445     linker.root().func_wrap(
446         "thunk",
447         |mut store: StoreContextMut<'_, StaticState>, _: ()| -> Result<()> {
448             let func = store.data_mut().func.take().unwrap();
449             let trap = func.call(&mut store, ()).unwrap_err();
450             assert_eq!(
451                 trap.downcast_ref(),
452                 Some(&Trap::CannotEnterComponent),
453                 "bad trap: {trap:?}",
454             );
455             Ok(())
456         },
457     )?;
458     let instance = linker.instantiate(&mut store, &component)?;
459     let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
460     store.data_mut().func = Some(func);
461     func.call(&mut store, ())?;
462 
463     // Next, test the dynamic API
464 
465     struct DynamicState {
466         func: Option<Func>,
467     }
468 
469     let mut store = Store::new(&engine, DynamicState { func: None });
470     let mut linker = Linker::new(&engine);
471     linker.root().func_new(
472         "thunk",
473         |mut store: StoreContextMut<'_, DynamicState>, _, _, _| {
474             let func = store.data_mut().func.take().unwrap();
475             let trap = func.call(&mut store, &[], &mut []).unwrap_err();
476             assert_eq!(
477                 trap.downcast_ref(),
478                 Some(&Trap::CannotEnterComponent),
479                 "bad trap: {trap:?}",
480             );
481             Ok(())
482         },
483     )?;
484     let instance = linker.instantiate(&mut store, &component)?;
485     let func = instance.get_func(&mut store, "run").unwrap();
486     store.data_mut().func = Some(func);
487     func.call(&mut store, &[], &mut [])?;
488 
489     Ok(())
490 }
491 
492 #[tokio::test]
stack_and_heap_args_and_rets() -> Result<()>493 async fn stack_and_heap_args_and_rets() -> Result<()> {
494     test_stack_and_heap_args_and_rets(false).await
495 }
496 
497 #[tokio::test]
stack_and_heap_args_and_rets_concurrent() -> Result<()>498 async fn stack_and_heap_args_and_rets_concurrent() -> Result<()> {
499     test_stack_and_heap_args_and_rets(true).await
500 }
501 
test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()>502 async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> {
503     let (body, async_lower_opts, async_lift_opts, async_type) = if concurrent {
504         (
505             r#"
506     (import "host" "f1" (func $f1 (param i32 i32) (result i32)))
507     (import "host" "f2" (func $f2 (param i32 i32) (result i32)))
508     (import "host" "f3" (func $f3 (param i32 i32) (result i32)))
509     (import "host" "f4" (func $f4 (param i32 i32) (result i32)))
510 
511     (func $run (export "run") (result i32)
512       (local $params i32)
513       (local $results i32)
514 
515       block
516         (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4)))
517         (call $f1 (i32.const 1) (local.get $results))
518         drop
519         (i32.load offset=0 (local.get $results))
520         i32.const 2
521         i32.eq
522         br_if 0
523         unreachable
524       end
525 
526       block
527         (local.set $params (call $allocate_empty_strings))
528         (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4)))
529         (call $f2 (local.get $params) (local.get $results))
530         drop
531         (i32.load offset=0 (local.get $results))
532         i32.const 3
533         i32.eq
534         br_if 0
535         unreachable
536       end
537 
538       block
539         (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
540         (call $f3 (i32.const 8) (local.get $results))
541         drop
542         (call $validate_string_ret (local.get $results))
543       end
544 
545       block
546         (local.set $params (call $allocate_empty_strings))
547         (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
548         (call $f4 (local.get $params) (local.get $results))
549         drop
550         (call $validate_string_ret (local.get $results))
551       end
552 
553       (call $task-return)
554 
555       i32.const 0
556     )
557             "#,
558             "async",
559             r#"async (callback (func $m "callback"))"#,
560             "async",
561         )
562     } else {
563         (
564             r#"
565     (import "host" "f1" (func $f1 (param i32) (result i32)))
566     (import "host" "f2" (func $f2 (param i32) (result i32)))
567     (import "host" "f3" (func $f3 (param i32 i32)))
568     (import "host" "f4" (func $f4 (param i32 i32)))
569 
570     (func $run (export "run")
571       block
572         i32.const 1
573         call $f1
574         i32.const 2
575         i32.eq
576         br_if 0
577         unreachable
578       end
579 
580       block
581         call $allocate_empty_strings
582         call $f2
583         i32.const 3
584         i32.eq
585         br_if 0
586         unreachable
587       end
588 
589       block
590         i32.const 8
591         i32.const 16000
592         call $f3
593         (call $validate_string_ret (i32.const 16000))
594       end
595 
596       block
597         call $allocate_empty_strings
598         i32.const 20000
599         call $f4
600         (call $validate_string_ret (i32.const 20000))
601       end
602     )
603             "#,
604             "",
605             "",
606             "",
607         )
608     };
609 
610     let component = format!(
611         r#"
612 (component
613   (type $many_params (tuple
614                       string string string string
615                       string string string string
616                       string))
617   (import "f1" (func $f1 {async_type} (param "a" u32) (result u32)))
618   (import "f2" (func $f2 {async_type} (param "a" $many_params) (result u32)))
619   (import "f3" (func $f3 {async_type} (param "a" u32) (result string)))
620   (import "f4" (func $f4 {async_type} (param "a" $many_params) (result string)))
621 
622   (core module $libc
623     {REALLOC_AND_FREE}
624     (memory (export "memory") 1)
625   )
626   (core instance $libc (instantiate (module $libc)))
627 
628   (core func $f1_lower (canon lower (func $f1)
629       (memory $libc "memory")
630       (realloc (func $libc "realloc"))
631       {async_lower_opts}
632   ))
633   (core func $f2_lower (canon lower (func $f2)
634       (memory $libc "memory")
635       (realloc (func $libc "realloc"))
636       {async_lower_opts}
637   ))
638   (core func $f3_lower (canon lower (func $f3)
639       (memory $libc "memory")
640       (realloc (func $libc "realloc"))
641       {async_lower_opts}
642   ))
643   (core func $f4_lower (canon lower (func $f4)
644       (memory $libc "memory")
645       (realloc (func $libc "realloc"))
646       {async_lower_opts}
647   ))
648 
649   (core module $m
650     (import "libc" "memory" (memory 1))
651     (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
652     (import "host" "task.return" (func $task-return))
653     {body}
654 
655     (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
656 
657     (func $allocate_empty_strings (result i32)
658       (local $ret i32)
659       (local $offset i32)
660       (local $cnt i32)
661       (local.set $ret (i32.const 8000))
662       (local.set $cnt (i32.const 9))
663 
664       loop
665         (call $setup_str (i32.add (local.get $ret) (local.get $offset)))
666         (local.set $offset (i32.add (local.get $offset) (i32.const 8)))
667 
668         (local.tee $cnt (i32.add (local.get $cnt) (i32.const -1)))
669         br_if 0
670       end
671 
672       local.get $ret
673     )
674     (func $setup_str (param $addr i32)
675       (i32.store offset=0 (local.get $addr) (i32.const 1000))
676       (i32.store offset=4 (local.get $addr) (i32.const 3))
677     )
678 
679     (func $validate_string_ret (param $addr i32)
680       (local $base i32)
681       (local $len i32)
682       (local.set $base (i32.load (local.get $addr)))
683       (local.set $len (i32.load offset=4 (local.get $addr)))
684 
685       block
686         local.get $len
687         i32.const 3
688         i32.eq
689         br_if 0
690         unreachable
691       end
692 
693       (i32.load8_u offset=0 (local.get $base))
694       i32.const 120 ;; 'x'
695       i32.ne
696       if unreachable end
697 
698       (i32.load8_u offset=1 (local.get $base))
699       i32.const 121 ;; 'y'
700       i32.ne
701       if unreachable end
702 
703       (i32.load8_u offset=2 (local.get $base))
704       i32.const 122 ;; 'z'
705       i32.ne
706       if unreachable end
707     )
708 
709     (data (i32.const 1000) "abc")
710   )
711   (core func $task-return (canon task.return))
712   (core instance $m (instantiate $m
713     (with "libc" (instance $libc))
714     (with "host" (instance
715       (export "f1" (func $f1_lower))
716       (export "f2" (func $f2_lower))
717       (export "f3" (func $f3_lower))
718       (export "f4" (func $f4_lower))
719       (export "task.return" (func $task-return))
720     ))
721   ))
722 
723   (func (export "run") {async_type}
724     (canon lift (core func $m "run") {async_lift_opts})
725   )
726 )
727         "#
728     );
729 
730     let mut config = Config::new();
731     config.wasm_component_model_async(true);
732     let engine = &Engine::new(&config)?;
733     let component = Component::new(&engine, component)?;
734     let mut store = Store::new(&engine, ());
735 
736     // First, test the static API
737 
738     let mut linker = Linker::new(&engine);
739     if concurrent {
740         linker
741             .root()
742             .func_wrap_concurrent("f1", |_, (x,): (u32,)| {
743                 assert_eq!(x, 1);
744                 Box::pin(async { Ok((2u32,)) })
745             })?;
746         linker.root().func_wrap_concurrent(
747             "f2",
748             |accessor,
749              (arg,): ((
750                 WasmStr,
751                 WasmStr,
752                 WasmStr,
753                 WasmStr,
754                 WasmStr,
755                 WasmStr,
756                 WasmStr,
757                 WasmStr,
758                 WasmStr,
759             ),)| {
760                 accessor.with(|v| assert_eq!(arg.0.to_str(&v).unwrap(), "abc"));
761                 Box::pin(async { Ok((3u32,)) })
762             },
763         )?;
764         linker
765             .root()
766             .func_wrap_concurrent("f3", |_, (arg,): (u32,)| {
767                 assert_eq!(arg, 8);
768                 Box::pin(async { Ok(("xyz".to_string(),)) })
769             })?;
770         linker.root().func_wrap_concurrent(
771             "f4",
772             |accessor,
773              (arg,): ((
774                 WasmStr,
775                 WasmStr,
776                 WasmStr,
777                 WasmStr,
778                 WasmStr,
779                 WasmStr,
780                 WasmStr,
781                 WasmStr,
782                 WasmStr,
783             ),)| {
784                 accessor.with(|v| assert_eq!(arg.0.to_str(&v).unwrap(), "abc"));
785                 Box::pin(async { Ok(("xyz".to_string(),)) })
786             },
787         )?;
788     } else {
789         linker
790             .root()
791             .func_wrap("f1", |_, (x,): (u32,)| -> Result<(u32,)> {
792                 assert_eq!(x, 1);
793                 Ok((2,))
794             })?;
795         linker.root().func_wrap(
796             "f2",
797             |cx: StoreContextMut<'_, ()>,
798              (arg,): ((
799                 WasmStr,
800                 WasmStr,
801                 WasmStr,
802                 WasmStr,
803                 WasmStr,
804                 WasmStr,
805                 WasmStr,
806                 WasmStr,
807                 WasmStr,
808             ),)|
809              -> Result<(u32,)> {
810                 assert_eq!(arg.0.to_str(&cx).unwrap(), "abc");
811                 Ok((3,))
812             },
813         )?;
814         linker
815             .root()
816             .func_wrap("f3", |_, (arg,): (u32,)| -> Result<(String,)> {
817                 assert_eq!(arg, 8);
818                 Ok(("xyz".to_string(),))
819             })?;
820         linker.root().func_wrap(
821             "f4",
822             |cx: StoreContextMut<'_, ()>,
823              (arg,): ((
824                 WasmStr,
825                 WasmStr,
826                 WasmStr,
827                 WasmStr,
828                 WasmStr,
829                 WasmStr,
830                 WasmStr,
831                 WasmStr,
832                 WasmStr,
833             ),)|
834              -> Result<(String,)> {
835                 assert_eq!(arg.0.to_str(&cx).unwrap(), "abc");
836                 Ok(("xyz".to_string(),))
837             },
838         )?;
839     }
840 
841     let instance = linker.instantiate_async(&mut store, &component).await?;
842     let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
843 
844     if concurrent {
845         store
846             .run_concurrent(async move |accessor| {
847                 wasmtime::error::Ok(run.call_concurrent(accessor, ()).await?)
848             })
849             .await??;
850     } else {
851         run.call_async(&mut store, ()).await?;
852     }
853 
854     // Next, test the dynamic API
855 
856     let mut linker = Linker::new(&engine);
857     if concurrent {
858         linker
859             .root()
860             .func_new_concurrent("f1", |_, _, args, results| {
861                 if let Val::U32(x) = &args[0] {
862                     assert_eq!(*x, 1);
863                     Box::pin(async {
864                         results[0] = Val::U32(2);
865                         Ok(())
866                     })
867                 } else {
868                     panic!()
869                 }
870             })?;
871         linker
872             .root()
873             .func_new_concurrent("f2", |_, _, args, results| {
874                 if let Val::Tuple(tuple) = &args[0] {
875                     if let Val::String(s) = &tuple[0] {
876                         assert_eq!(s.deref(), "abc");
877                         Box::pin(async {
878                             results[0] = Val::U32(3);
879                             Ok(())
880                         })
881                     } else {
882                         panic!()
883                     }
884                 } else {
885                     panic!()
886                 }
887             })?;
888         linker
889             .root()
890             .func_new_concurrent("f3", |_, _, args, results| {
891                 if let Val::U32(x) = &args[0] {
892                     assert_eq!(*x, 8);
893                     Box::pin(async {
894                         results[0] = Val::String("xyz".into());
895                         Ok(())
896                     })
897                 } else {
898                     panic!();
899                 }
900             })?;
901         linker
902             .root()
903             .func_new_concurrent("f4", |_, _, args, results| {
904                 if let Val::Tuple(tuple) = &args[0] {
905                     if let Val::String(s) = &tuple[0] {
906                         assert_eq!(s.deref(), "abc");
907                         Box::pin(async {
908                             results[0] = Val::String("xyz".into());
909                             Ok(())
910                         })
911                     } else {
912                         panic!()
913                     }
914                 } else {
915                     panic!()
916                 }
917             })?;
918     } else {
919         linker.root().func_new("f1", |_, _, args, results| {
920             if let Val::U32(x) = &args[0] {
921                 assert_eq!(*x, 1);
922                 results[0] = Val::U32(2);
923                 Ok(())
924             } else {
925                 panic!()
926             }
927         })?;
928         linker.root().func_new("f2", |_, _, args, results| {
929             if let Val::Tuple(tuple) = &args[0] {
930                 if let Val::String(s) = &tuple[0] {
931                     assert_eq!(s.deref(), "abc");
932                     results[0] = Val::U32(3);
933                     Ok(())
934                 } else {
935                     panic!()
936                 }
937             } else {
938                 panic!()
939             }
940         })?;
941         linker.root().func_new("f3", |_, _, args, results| {
942             if let Val::U32(x) = &args[0] {
943                 assert_eq!(*x, 8);
944                 results[0] = Val::String("xyz".into());
945                 Ok(())
946             } else {
947                 panic!();
948             }
949         })?;
950         linker.root().func_new("f4", |_, _, args, results| {
951             if let Val::Tuple(tuple) = &args[0] {
952                 if let Val::String(s) = &tuple[0] {
953                     assert_eq!(s.deref(), "abc");
954                     results[0] = Val::String("xyz".into());
955                     Ok(())
956                 } else {
957                     panic!()
958                 }
959             } else {
960                 panic!()
961             }
962         })?;
963     }
964 
965     let instance = linker.instantiate_async(&mut store, &component).await?;
966     let run = instance.get_func(&mut store, "run").unwrap();
967 
968     if concurrent {
969         store
970             .run_concurrent(async |store| {
971                 run.call_concurrent(store, &[], &mut []).await?;
972                 wasmtime::error::Ok(())
973             })
974             .await??;
975     } else {
976         run.call_async(&mut store, &[], &mut []).await?;
977     }
978 
979     Ok(())
980 }
981 
982 #[test]
bad_import_alignment() -> Result<()>983 fn bad_import_alignment() -> Result<()> {
984     let component = format!(
985         r#"
986 (component
987   (import "unaligned-retptr" (func $unaligned_retptr (result string)))
988   (type $many_arg (tuple
989     string string string string
990     string string string string
991     string
992   ))
993   (import "unaligned-argptr" (func $unaligned_argptr (param "a" $many_arg)))
994   (core module $libc_panic
995     (memory (export "memory") 1)
996     (func (export "realloc") (param i32 i32 i32 i32) (result i32)
997       unreachable)
998   )
999   (core instance $libc_panic (instantiate $libc_panic))
1000 
1001   (core func $unaligned_retptr_lower
1002     (canon lower (func $unaligned_retptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc")))
1003   )
1004   (core func $unaligned_argptr_lower
1005     (canon lower (func $unaligned_argptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc")))
1006   )
1007 
1008   (core module $m
1009     (import "host" "unaligned-retptr" (func $unaligned_retptr (param i32)))
1010     (import "host" "unaligned-argptr" (func $unaligned_argptr (param i32)))
1011 
1012     (func (export "unaligned-retptr")
1013      (call $unaligned_retptr (i32.const 1)))
1014     (func (export "unaligned-argptr")
1015      (call $unaligned_argptr (i32.const 1)))
1016   )
1017   (core instance $m (instantiate $m
1018     (with "host" (instance
1019       (export "unaligned-retptr" (func $unaligned_retptr_lower))
1020       (export "unaligned-argptr" (func $unaligned_argptr_lower))
1021     ))
1022   ))
1023 
1024   (func (export "unaligned-retptr2")
1025     (canon lift (core func $m "unaligned-retptr"))
1026   )
1027   (func (export "unaligned-argptr2")
1028     (canon lift (core func $m "unaligned-argptr"))
1029   )
1030 )
1031         "#
1032     );
1033 
1034     let engine = super::engine();
1035     let mut linker = Linker::new(&engine);
1036     linker
1037         .root()
1038         .func_wrap("unaligned-retptr", |_, _: ()| -> Result<(String,)> {
1039             Ok((String::new(),))
1040         })?;
1041     linker.root().func_wrap(
1042         "unaligned-argptr",
1043         |_,
1044          _: ((
1045             WasmStr,
1046             WasmStr,
1047             WasmStr,
1048             WasmStr,
1049             WasmStr,
1050             WasmStr,
1051             WasmStr,
1052             WasmStr,
1053             WasmStr,
1054         ),)|
1055          -> Result<()> { unreachable!() },
1056     )?;
1057     let component = Component::new(&engine, component)?;
1058 
1059     {
1060         let mut store = Store::new(&engine, ());
1061         let trap = linker
1062             .instantiate(&mut store, &component)?
1063             .get_typed_func::<(), ()>(&mut store, "unaligned-retptr2")?
1064             .call(&mut store, ())
1065             .unwrap_err();
1066         assert!(
1067             format!("{trap:?}").contains("pointer not aligned"),
1068             "{}",
1069             trap
1070         );
1071     }
1072 
1073     {
1074         let mut store = Store::new(&engine, ());
1075         let trap = linker
1076             .instantiate(&mut store, &component)?
1077             .get_typed_func::<(), ()>(&mut store, "unaligned-argptr2")?
1078             .call(&mut store, ())
1079             .unwrap_err();
1080         assert!(
1081             format!("{trap:?}").contains("pointer not aligned"),
1082             "{}",
1083             trap
1084         );
1085     }
1086 
1087     Ok(())
1088 }
1089 
1090 #[test]
no_actual_wasm_code() -> Result<()>1091 fn no_actual_wasm_code() -> Result<()> {
1092     let component = r#"
1093         (component
1094             (import "f" (func $f))
1095 
1096             (core func $f_lower
1097                 (canon lower (func $f))
1098             )
1099             (core module $m
1100                 (import "" "" (func $f))
1101                 (export "f" (func $f))
1102             )
1103             (core instance $i (instantiate $m
1104                 (with "" (instance
1105                     (export "" (func $f_lower))
1106                 ))
1107             ))
1108             (func (export "thunk")
1109                 (canon lift
1110                     (core func $i "f")
1111                 )
1112             )
1113         )
1114     "#;
1115 
1116     let engine = super::engine();
1117     let component = Component::new(&engine, component)?;
1118     let mut store = Store::new(&engine, 0);
1119 
1120     // First, test the static API
1121 
1122     let mut linker = Linker::new(&engine);
1123     linker.root().func_wrap(
1124         "f",
1125         |mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> {
1126             *store.data_mut() += 1;
1127             Ok(())
1128         },
1129     )?;
1130 
1131     let instance = linker.instantiate(&mut store, &component)?;
1132     let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?;
1133 
1134     assert_eq!(*store.data(), 0);
1135     thunk.call(&mut store, ())?;
1136     assert_eq!(*store.data(), 1);
1137 
1138     // Next, test the dynamic API
1139 
1140     *store.data_mut() = 0;
1141     let mut linker = Linker::new(&engine);
1142     linker
1143         .root()
1144         .func_new("f", |mut store: StoreContextMut<'_, u32>, _, _, _| {
1145             *store.data_mut() += 1;
1146             Ok(())
1147         })?;
1148 
1149     let instance = linker.instantiate(&mut store, &component)?;
1150     let thunk = instance.get_func(&mut store, "thunk").unwrap();
1151 
1152     assert_eq!(*store.data(), 0);
1153     thunk.call(&mut store, &[], &mut [])?;
1154     assert_eq!(*store.data(), 1);
1155 
1156     Ok(())
1157 }
1158 
1159 #[test]
use_types_across_component_boundaries() -> Result<()>1160 fn use_types_across_component_boundaries() -> Result<()> {
1161     // Create a component that exports a function that returns a record
1162     let engine = super::engine();
1163     let component = Component::new(
1164         &engine,
1165         r#"(component
1166             (type (;0;) (record (field "a" u8) (field "b" string)))
1167             (import "my-record" (type $my-record (eq 0)))
1168             (core module $m
1169                 (memory $memory 17)
1170                 (export "memory" (memory $memory))
1171                 (func (export "my-func") (result i32)
1172                     i32.const 4
1173                     return))
1174             (core instance $instance (instantiate $m))
1175             (type $func-type (func (result $my-record)))
1176             (alias core export $instance "my-func" (core func $my-func))
1177             (alias core export $instance "memory" (core memory $memory))
1178             (func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8))
1179             (export $export "my-func" (func $my-func))
1180         )"#,
1181     )?;
1182     let mut store = Store::new(&engine, 0);
1183     let linker = Linker::new(&engine);
1184     let instance = linker.instantiate(&mut store, &component)?;
1185     let my_func = instance.get_func(&mut store, "my-func").unwrap();
1186     let mut results = vec![Val::Bool(false)];
1187     my_func.call(&mut store, &[], &mut results)?;
1188 
1189     // Create another component that exports a function that takes that record as an argument
1190     let component = Component::new(
1191         &engine,
1192         format!(
1193             r#"(component
1194             (type (;0;) (record (field "a" u8) (field "b" string)))
1195             (import "my-record" (type $my-record (eq 0)))
1196             (core module $m
1197                 (memory $memory 17)
1198                 (export "memory" (memory $memory))
1199                 {REALLOC_AND_FREE}
1200                 (func (export "my-func") (param i32 i32 i32)))
1201             (core instance $instance (instantiate $m))
1202             (type $func-type (func (param "my-record" $my-record)))
1203             (alias core export $instance "my-func" (core func $my-func))
1204             (alias core export $instance "memory" (core memory $memory))
1205             (func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8 (realloc (func $instance "realloc"))))
1206             (export $export "my-func" (func $my-func))
1207         )"#
1208         ),
1209     )?;
1210     let mut store = Store::new(&engine, 0);
1211     let linker = Linker::new(&engine);
1212     let instance = linker.instantiate(&mut store, &component)?;
1213     let my_func = instance.get_func(&mut store, "my-func").unwrap();
1214     // Call the exported function with the return values of the call to the previous component's exported function
1215     my_func.call(&mut store, &results, &mut [])?;
1216 
1217     Ok(())
1218 }
1219 
1220 #[test]
hostcall_fuel_limits_val() -> Result<()>1221 fn hostcall_fuel_limits_val() -> Result<()> {
1222     let engine = super::engine();
1223     let component = Component::new(
1224         &engine,
1225         r#"(component
1226             (import "hi" (func $hi (param "x" (list u8))))
1227             (core module $libc
1228                 (memory (export "memory") 10)
1229             )
1230             (core module $m
1231                 (import "libc" "memory" (memory 1))
1232                 (import "" "hi" (func $hi (param i32 i32)))
1233 
1234                 (func (export "run")
1235                     i32.const 0
1236                     memory.size
1237                     i32.const 65536
1238                     i32.mul
1239                     call $hi)
1240             )
1241             (core instance $libc (instantiate $libc))
1242             (core func $hi (canon lower (func $hi) (memory $libc "memory")))
1243             (core instance $i (instantiate $m
1244                 (with "libc" (instance $libc))
1245                 (with "" (instance (export "hi" (func $hi))))
1246             ))
1247             (func (export "run") (canon lift (core func $i "run")))
1248         )"#,
1249     )?;
1250     let mut store = Store::new(&engine, 0);
1251     let mut linker = Linker::new(&engine);
1252     linker.root().func_new("hi", |_, _, _, _| Ok(()))?;
1253     let instance = linker.instantiate(&mut store, &component)?;
1254     let run = instance.get_func(&mut store, "run").unwrap();
1255     run.call(&mut store, &[], &mut [])?;
1256 
1257     store.set_hostcall_fuel(100);
1258     assert!(run.call(&mut store, &[], &mut []).is_err());
1259 
1260     Ok(())
1261 }
1262