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