Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/component_model/import.rs
1692 views
1
#![cfg(not(miri))]
2
3
use super::REALLOC_AND_FREE;
4
use anyhow::Result;
5
use std::ops::Deref;
6
use wasmtime::component::*;
7
use wasmtime::{Config, Engine, Store, StoreContextMut, Trap, WasmBacktrace};
8
9
#[test]
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]
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]
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]
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
let mut store = Store::new(&engine, ());
352
353
// Assert that during a host import if we return values to wasm that a trap
354
// happens if we try to leave the instance.
355
let trap = linker
356
.instantiate(&mut store, &component)?
357
.get_typed_func::<(), ()>(&mut store, "run")?
358
.call(&mut store, ())
359
.unwrap_err();
360
assert!(
361
format!("{trap:?}").contains("cannot leave component instance"),
362
"bad trap: {trap:?}",
363
);
364
365
let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();
366
assert_eq!(trace.len(), 4);
367
368
// This was our entry point...
369
assert_eq!(trace[3].module().name(), Some("m"));
370
assert_eq!(trace[3].func_name(), Some("run"));
371
372
// ... which called an imported function which ends up being originally
373
// defined by the shim instance. The shim instance then does an indirect
374
// call through a table which goes to the `canon.lower`'d host function
375
assert_eq!(trace[2].module().name(), Some("host_shim"));
376
assert_eq!(trace[2].func_name(), Some("shim_ret_string"));
377
378
// ... and the lowered host function will call realloc to allocate space for
379
// the result
380
assert_eq!(trace[1].module().name(), Some("m"));
381
assert_eq!(trace[1].func_name(), Some("realloc"));
382
383
// ... but realloc calls the shim instance and tries to exit the
384
// component, triggering a dynamic trap
385
assert_eq!(trace[0].module().name(), Some("host_shim"));
386
assert_eq!(trace[0].func_name(), Some("shim_thunk"));
387
388
// In addition to the above trap also ensure that when we enter a wasm
389
// component if we try to leave while lowering then that's also a dynamic
390
// trap.
391
let trap = linker
392
.instantiate(&mut store, &component)?
393
.get_typed_func::<(&str,), ()>(&mut store, "take-string")?
394
.call(&mut store, ("x",))
395
.unwrap_err();
396
assert!(
397
format!("{trap:?}").contains("cannot leave component instance"),
398
"bad trap: {trap:?}",
399
);
400
Ok(())
401
}
402
403
#[test]
404
fn attempt_to_reenter_during_host() -> Result<()> {
405
let component = r#"
406
(component
407
(import "thunk" (func $thunk))
408
(core func $thunk_lower (canon lower (func $thunk)))
409
410
(core module $m
411
(import "host" "thunk" (func $thunk))
412
413
(func $run (export "run")
414
call $thunk)
415
)
416
(core instance $m (instantiate $m
417
(with "host" (instance (export "thunk" (func $thunk_lower))))
418
))
419
420
(func (export "run")
421
(canon lift (core func $m "run"))
422
)
423
)
424
"#;
425
426
let engine = super::engine();
427
let component = Component::new(&engine, component)?;
428
429
// First, test the static API
430
431
struct StaticState {
432
func: Option<TypedFunc<(), ()>>,
433
}
434
435
let mut store = Store::new(&engine, StaticState { func: None });
436
let mut linker = Linker::new(&engine);
437
linker.root().func_wrap(
438
"thunk",
439
|mut store: StoreContextMut<'_, StaticState>, _: ()| -> Result<()> {
440
let func = store.data_mut().func.take().unwrap();
441
let trap = func.call(&mut store, ()).unwrap_err();
442
assert_eq!(
443
trap.downcast_ref(),
444
Some(&Trap::CannotEnterComponent),
445
"bad trap: {trap:?}",
446
);
447
Ok(())
448
},
449
)?;
450
let instance = linker.instantiate(&mut store, &component)?;
451
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
452
store.data_mut().func = Some(func);
453
func.call(&mut store, ())?;
454
455
// Next, test the dynamic API
456
457
struct DynamicState {
458
func: Option<Func>,
459
}
460
461
let mut store = Store::new(&engine, DynamicState { func: None });
462
let mut linker = Linker::new(&engine);
463
linker.root().func_new(
464
"thunk",
465
|mut store: StoreContextMut<'_, DynamicState>, _, _| {
466
let func = store.data_mut().func.take().unwrap();
467
let trap = func.call(&mut store, &[], &mut []).unwrap_err();
468
assert_eq!(
469
trap.downcast_ref(),
470
Some(&Trap::CannotEnterComponent),
471
"bad trap: {trap:?}",
472
);
473
Ok(())
474
},
475
)?;
476
let instance = linker.instantiate(&mut store, &component)?;
477
let func = instance.get_func(&mut store, "run").unwrap();
478
store.data_mut().func = Some(func);
479
func.call(&mut store, &[], &mut [])?;
480
481
Ok(())
482
}
483
484
#[tokio::test]
485
async fn stack_and_heap_args_and_rets() -> Result<()> {
486
test_stack_and_heap_args_and_rets(false).await
487
}
488
489
#[tokio::test]
490
async fn stack_and_heap_args_and_rets_concurrent() -> Result<()> {
491
test_stack_and_heap_args_and_rets(true).await
492
}
493
494
async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> {
495
let (body, async_lower_opts, async_lift_opts) = if concurrent {
496
(
497
r#"
498
(import "host" "f1" (func $f1 (param i32 i32) (result i32)))
499
(import "host" "f2" (func $f2 (param i32 i32) (result i32)))
500
(import "host" "f3" (func $f3 (param i32 i32) (result i32)))
501
(import "host" "f4" (func $f4 (param i32 i32) (result i32)))
502
503
(func $run (export "run") (result i32)
504
(local $params i32)
505
(local $results i32)
506
507
block
508
(local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4)))
509
(call $f1 (i32.const 1) (local.get $results))
510
drop
511
(i32.load offset=0 (local.get $results))
512
i32.const 2
513
i32.eq
514
br_if 0
515
unreachable
516
end
517
518
block
519
(local.set $params (call $allocate_empty_strings))
520
(local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4)))
521
(call $f2 (local.get $params) (local.get $results))
522
drop
523
(i32.load offset=0 (local.get $results))
524
i32.const 3
525
i32.eq
526
br_if 0
527
unreachable
528
end
529
530
block
531
(local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
532
(call $f3 (i32.const 8) (local.get $results))
533
drop
534
(call $validate_string_ret (local.get $results))
535
end
536
537
block
538
(local.set $params (call $allocate_empty_strings))
539
(local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
540
(call $f4 (local.get $params) (local.get $results))
541
drop
542
(call $validate_string_ret (local.get $results))
543
end
544
545
(call $task-return)
546
547
i32.const 0
548
)
549
"#,
550
"async",
551
r#"async (callback (func $m "callback"))"#,
552
)
553
} else {
554
(
555
r#"
556
(import "host" "f1" (func $f1 (param i32) (result i32)))
557
(import "host" "f2" (func $f2 (param i32) (result i32)))
558
(import "host" "f3" (func $f3 (param i32 i32)))
559
(import "host" "f4" (func $f4 (param i32 i32)))
560
561
(func $run (export "run")
562
block
563
i32.const 1
564
call $f1
565
i32.const 2
566
i32.eq
567
br_if 0
568
unreachable
569
end
570
571
block
572
call $allocate_empty_strings
573
call $f2
574
i32.const 3
575
i32.eq
576
br_if 0
577
unreachable
578
end
579
580
block
581
i32.const 8
582
i32.const 16000
583
call $f3
584
(call $validate_string_ret (i32.const 16000))
585
end
586
587
block
588
call $allocate_empty_strings
589
i32.const 20000
590
call $f4
591
(call $validate_string_ret (i32.const 20000))
592
end
593
)
594
"#,
595
"",
596
"",
597
)
598
};
599
600
let component = format!(
601
r#"
602
(component
603
(type $many_params (tuple
604
string string string string
605
string string string string
606
string))
607
(import "f1" (func $f1 (param "a" u32) (result u32)))
608
(import "f2" (func $f2 (param "a" $many_params) (result u32)))
609
(import "f3" (func $f3 (param "a" u32) (result string)))
610
(import "f4" (func $f4 (param "a" $many_params) (result string)))
611
612
(core module $libc
613
{REALLOC_AND_FREE}
614
(memory (export "memory") 1)
615
)
616
(core instance $libc (instantiate (module $libc)))
617
618
(core func $f1_lower (canon lower (func $f1)
619
(memory $libc "memory")
620
(realloc (func $libc "realloc"))
621
{async_lower_opts}
622
))
623
(core func $f2_lower (canon lower (func $f2)
624
(memory $libc "memory")
625
(realloc (func $libc "realloc"))
626
{async_lower_opts}
627
))
628
(core func $f3_lower (canon lower (func $f3)
629
(memory $libc "memory")
630
(realloc (func $libc "realloc"))
631
{async_lower_opts}
632
))
633
(core func $f4_lower (canon lower (func $f4)
634
(memory $libc "memory")
635
(realloc (func $libc "realloc"))
636
{async_lower_opts}
637
))
638
639
(core module $m
640
(import "libc" "memory" (memory 1))
641
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
642
(import "host" "task.return" (func $task-return))
643
{body}
644
645
(func (export "callback") (param i32 i32 i32) (result i32) unreachable)
646
647
(func $allocate_empty_strings (result i32)
648
(local $ret i32)
649
(local $offset i32)
650
(local $cnt i32)
651
(local.set $ret (i32.const 8000))
652
(local.set $cnt (i32.const 9))
653
654
loop
655
(call $setup_str (i32.add (local.get $ret) (local.get $offset)))
656
(local.set $offset (i32.add (local.get $offset) (i32.const 8)))
657
658
(local.tee $cnt (i32.add (local.get $cnt) (i32.const -1)))
659
br_if 0
660
end
661
662
local.get $ret
663
)
664
(func $setup_str (param $addr i32)
665
(i32.store offset=0 (local.get $addr) (i32.const 1000))
666
(i32.store offset=4 (local.get $addr) (i32.const 3))
667
)
668
669
(func $validate_string_ret (param $addr i32)
670
(local $base i32)
671
(local $len i32)
672
(local.set $base (i32.load (local.get $addr)))
673
(local.set $len (i32.load offset=4 (local.get $addr)))
674
675
block
676
local.get $len
677
i32.const 3
678
i32.eq
679
br_if 0
680
unreachable
681
end
682
683
(i32.load8_u offset=0 (local.get $base))
684
i32.const 120 ;; 'x'
685
i32.ne
686
if unreachable end
687
688
(i32.load8_u offset=1 (local.get $base))
689
i32.const 121 ;; 'y'
690
i32.ne
691
if unreachable end
692
693
(i32.load8_u offset=2 (local.get $base))
694
i32.const 122 ;; 'z'
695
i32.ne
696
if unreachable end
697
)
698
699
(data (i32.const 1000) "abc")
700
)
701
(core func $task-return (canon task.return))
702
(core instance $m (instantiate $m
703
(with "libc" (instance $libc))
704
(with "host" (instance
705
(export "f1" (func $f1_lower))
706
(export "f2" (func $f2_lower))
707
(export "f3" (func $f3_lower))
708
(export "f4" (func $f4_lower))
709
(export "task.return" (func $task-return))
710
))
711
))
712
713
(func (export "run")
714
(canon lift (core func $m "run") {async_lift_opts})
715
)
716
)
717
"#
718
);
719
720
let mut config = Config::new();
721
config.wasm_component_model_async(true);
722
config.async_support(true);
723
let engine = &Engine::new(&config)?;
724
let component = Component::new(&engine, component)?;
725
let mut store = Store::new(&engine, ());
726
727
// First, test the static API
728
729
let mut linker = Linker::new(&engine);
730
if concurrent {
731
linker
732
.root()
733
.func_wrap_concurrent("f1", |_, (x,): (u32,)| {
734
assert_eq!(x, 1);
735
Box::pin(async { Ok((2u32,)) })
736
})?;
737
linker.root().func_wrap_concurrent(
738
"f2",
739
|accessor,
740
(arg,): ((
741
WasmStr,
742
WasmStr,
743
WasmStr,
744
WasmStr,
745
WasmStr,
746
WasmStr,
747
WasmStr,
748
WasmStr,
749
WasmStr,
750
),)| {
751
accessor.with(|v| assert_eq!(arg.0.to_str(&v).unwrap(), "abc"));
752
Box::pin(async { Ok((3u32,)) })
753
},
754
)?;
755
linker
756
.root()
757
.func_wrap_concurrent("f3", |_, (arg,): (u32,)| {
758
assert_eq!(arg, 8);
759
Box::pin(async { Ok(("xyz".to_string(),)) })
760
})?;
761
linker.root().func_wrap_concurrent(
762
"f4",
763
|accessor,
764
(arg,): ((
765
WasmStr,
766
WasmStr,
767
WasmStr,
768
WasmStr,
769
WasmStr,
770
WasmStr,
771
WasmStr,
772
WasmStr,
773
WasmStr,
774
),)| {
775
accessor.with(|v| assert_eq!(arg.0.to_str(&v).unwrap(), "abc"));
776
Box::pin(async { Ok(("xyz".to_string(),)) })
777
},
778
)?;
779
} else {
780
linker
781
.root()
782
.func_wrap("f1", |_, (x,): (u32,)| -> Result<(u32,)> {
783
assert_eq!(x, 1);
784
Ok((2,))
785
})?;
786
linker.root().func_wrap(
787
"f2",
788
|cx: StoreContextMut<'_, ()>,
789
(arg,): ((
790
WasmStr,
791
WasmStr,
792
WasmStr,
793
WasmStr,
794
WasmStr,
795
WasmStr,
796
WasmStr,
797
WasmStr,
798
WasmStr,
799
),)|
800
-> Result<(u32,)> {
801
assert_eq!(arg.0.to_str(&cx).unwrap(), "abc");
802
Ok((3,))
803
},
804
)?;
805
linker
806
.root()
807
.func_wrap("f3", |_, (arg,): (u32,)| -> Result<(String,)> {
808
assert_eq!(arg, 8);
809
Ok(("xyz".to_string(),))
810
})?;
811
linker.root().func_wrap(
812
"f4",
813
|cx: StoreContextMut<'_, ()>,
814
(arg,): ((
815
WasmStr,
816
WasmStr,
817
WasmStr,
818
WasmStr,
819
WasmStr,
820
WasmStr,
821
WasmStr,
822
WasmStr,
823
WasmStr,
824
),)|
825
-> Result<(String,)> {
826
assert_eq!(arg.0.to_str(&cx).unwrap(), "abc");
827
Ok(("xyz".to_string(),))
828
},
829
)?;
830
}
831
832
let instance = linker.instantiate_async(&mut store, &component).await?;
833
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
834
835
if concurrent {
836
instance
837
.run_concurrent(&mut store, async move |accessor| {
838
run.call_concurrent(accessor, ()).await
839
})
840
.await??;
841
} else {
842
run.call_async(&mut store, ()).await?;
843
}
844
845
// Next, test the dynamic API
846
847
let mut linker = Linker::new(&engine);
848
if concurrent {
849
linker
850
.root()
851
.func_new_concurrent("f1", |_, args, results| {
852
if let Val::U32(x) = &args[0] {
853
assert_eq!(*x, 1);
854
Box::pin(async {
855
results[0] = Val::U32(2);
856
Ok(())
857
})
858
} else {
859
panic!()
860
}
861
})?;
862
linker
863
.root()
864
.func_new_concurrent("f2", |_, args, results| {
865
if let Val::Tuple(tuple) = &args[0] {
866
if let Val::String(s) = &tuple[0] {
867
assert_eq!(s.deref(), "abc");
868
Box::pin(async {
869
results[0] = Val::U32(3);
870
Ok(())
871
})
872
} else {
873
panic!()
874
}
875
} else {
876
panic!()
877
}
878
})?;
879
linker
880
.root()
881
.func_new_concurrent("f3", |_, args, results| {
882
if let Val::U32(x) = &args[0] {
883
assert_eq!(*x, 8);
884
Box::pin(async {
885
results[0] = Val::String("xyz".into());
886
Ok(())
887
})
888
} else {
889
panic!();
890
}
891
})?;
892
linker
893
.root()
894
.func_new_concurrent("f4", |_, args, results| {
895
if let Val::Tuple(tuple) = &args[0] {
896
if let Val::String(s) = &tuple[0] {
897
assert_eq!(s.deref(), "abc");
898
Box::pin(async {
899
results[0] = Val::String("xyz".into());
900
Ok(())
901
})
902
} else {
903
panic!()
904
}
905
} else {
906
panic!()
907
}
908
})?;
909
} else {
910
linker.root().func_new("f1", |_, args, results| {
911
if let Val::U32(x) = &args[0] {
912
assert_eq!(*x, 1);
913
results[0] = Val::U32(2);
914
Ok(())
915
} else {
916
panic!()
917
}
918
})?;
919
linker.root().func_new("f2", |_, args, results| {
920
if let Val::Tuple(tuple) = &args[0] {
921
if let Val::String(s) = &tuple[0] {
922
assert_eq!(s.deref(), "abc");
923
results[0] = Val::U32(3);
924
Ok(())
925
} else {
926
panic!()
927
}
928
} else {
929
panic!()
930
}
931
})?;
932
linker.root().func_new("f3", |_, args, results| {
933
if let Val::U32(x) = &args[0] {
934
assert_eq!(*x, 8);
935
results[0] = Val::String("xyz".into());
936
Ok(())
937
} else {
938
panic!();
939
}
940
})?;
941
linker.root().func_new("f4", |_, args, results| {
942
if let Val::Tuple(tuple) = &args[0] {
943
if let Val::String(s) = &tuple[0] {
944
assert_eq!(s.deref(), "abc");
945
results[0] = Val::String("xyz".into());
946
Ok(())
947
} else {
948
panic!()
949
}
950
} else {
951
panic!()
952
}
953
})?;
954
}
955
956
let instance = linker.instantiate_async(&mut store, &component).await?;
957
let run = instance.get_func(&mut store, "run").unwrap();
958
959
if concurrent {
960
instance
961
.run_concurrent(&mut store, async |store| {
962
run.call_concurrent(store, &[], &mut []).await
963
})
964
.await??;
965
} else {
966
run.call_async(&mut store, &[], &mut []).await?;
967
}
968
969
Ok(())
970
}
971
972
#[test]
973
fn bad_import_alignment() -> Result<()> {
974
let component = format!(
975
r#"
976
(component
977
(import "unaligned-retptr" (func $unaligned_retptr (result string)))
978
(type $many_arg (tuple
979
string string string string
980
string string string string
981
string
982
))
983
(import "unaligned-argptr" (func $unaligned_argptr (param "a" $many_arg)))
984
(core module $libc_panic
985
(memory (export "memory") 1)
986
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
987
unreachable)
988
)
989
(core instance $libc_panic (instantiate $libc_panic))
990
991
(core func $unaligned_retptr_lower
992
(canon lower (func $unaligned_retptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc")))
993
)
994
(core func $unaligned_argptr_lower
995
(canon lower (func $unaligned_argptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc")))
996
)
997
998
(core module $m
999
(import "host" "unaligned-retptr" (func $unaligned_retptr (param i32)))
1000
(import "host" "unaligned-argptr" (func $unaligned_argptr (param i32)))
1001
1002
(func (export "unaligned-retptr")
1003
(call $unaligned_retptr (i32.const 1)))
1004
(func (export "unaligned-argptr")
1005
(call $unaligned_argptr (i32.const 1)))
1006
)
1007
(core instance $m (instantiate $m
1008
(with "host" (instance
1009
(export "unaligned-retptr" (func $unaligned_retptr_lower))
1010
(export "unaligned-argptr" (func $unaligned_argptr_lower))
1011
))
1012
))
1013
1014
(func (export "unaligned-retptr2")
1015
(canon lift (core func $m "unaligned-retptr"))
1016
)
1017
(func (export "unaligned-argptr2")
1018
(canon lift (core func $m "unaligned-argptr"))
1019
)
1020
)
1021
"#
1022
);
1023
1024
let engine = super::engine();
1025
let mut linker = Linker::new(&engine);
1026
linker
1027
.root()
1028
.func_wrap("unaligned-retptr", |_, _: ()| -> Result<(String,)> {
1029
Ok((String::new(),))
1030
})?;
1031
linker.root().func_wrap(
1032
"unaligned-argptr",
1033
|_,
1034
_: ((
1035
WasmStr,
1036
WasmStr,
1037
WasmStr,
1038
WasmStr,
1039
WasmStr,
1040
WasmStr,
1041
WasmStr,
1042
WasmStr,
1043
WasmStr,
1044
),)|
1045
-> Result<()> { unreachable!() },
1046
)?;
1047
let component = Component::new(&engine, component)?;
1048
let mut store = Store::new(&engine, ());
1049
1050
let trap = linker
1051
.instantiate(&mut store, &component)?
1052
.get_typed_func::<(), ()>(&mut store, "unaligned-retptr2")?
1053
.call(&mut store, ())
1054
.unwrap_err();
1055
assert!(
1056
format!("{trap:?}").contains("pointer not aligned"),
1057
"{}",
1058
trap
1059
);
1060
let trap = linker
1061
.instantiate(&mut store, &component)?
1062
.get_typed_func::<(), ()>(&mut store, "unaligned-argptr2")?
1063
.call(&mut store, ())
1064
.unwrap_err();
1065
assert!(
1066
format!("{trap:?}").contains("pointer not aligned"),
1067
"{}",
1068
trap
1069
);
1070
1071
Ok(())
1072
}
1073
1074
#[test]
1075
fn no_actual_wasm_code() -> Result<()> {
1076
let component = r#"
1077
(component
1078
(import "f" (func $f))
1079
1080
(core func $f_lower
1081
(canon lower (func $f))
1082
)
1083
(core module $m
1084
(import "" "" (func $f))
1085
(export "f" (func $f))
1086
)
1087
(core instance $i (instantiate $m
1088
(with "" (instance
1089
(export "" (func $f_lower))
1090
))
1091
))
1092
(func (export "thunk")
1093
(canon lift
1094
(core func $i "f")
1095
)
1096
)
1097
)
1098
"#;
1099
1100
let engine = super::engine();
1101
let component = Component::new(&engine, component)?;
1102
let mut store = Store::new(&engine, 0);
1103
1104
// First, test the static API
1105
1106
let mut linker = Linker::new(&engine);
1107
linker.root().func_wrap(
1108
"f",
1109
|mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> {
1110
*store.data_mut() += 1;
1111
Ok(())
1112
},
1113
)?;
1114
1115
let instance = linker.instantiate(&mut store, &component)?;
1116
let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?;
1117
1118
assert_eq!(*store.data(), 0);
1119
thunk.call(&mut store, ())?;
1120
assert_eq!(*store.data(), 1);
1121
1122
// Next, test the dynamic API
1123
1124
*store.data_mut() = 0;
1125
let mut linker = Linker::new(&engine);
1126
linker
1127
.root()
1128
.func_new("f", |mut store: StoreContextMut<'_, u32>, _, _| {
1129
*store.data_mut() += 1;
1130
Ok(())
1131
})?;
1132
1133
let instance = linker.instantiate(&mut store, &component)?;
1134
let thunk = instance.get_func(&mut store, "thunk").unwrap();
1135
1136
assert_eq!(*store.data(), 0);
1137
thunk.call(&mut store, &[], &mut [])?;
1138
assert_eq!(*store.data(), 1);
1139
1140
Ok(())
1141
}
1142
1143
#[test]
1144
fn use_types_across_component_boundaries() -> Result<()> {
1145
// Create a component that exports a function that returns a record
1146
let engine = super::engine();
1147
let component = Component::new(
1148
&engine,
1149
r#"(component
1150
(type (;0;) (record (field "a" u8) (field "b" string)))
1151
(import "my-record" (type $my-record (eq 0)))
1152
(core module $m
1153
(memory $memory 17)
1154
(export "memory" (memory $memory))
1155
(func (export "my-func") (result i32)
1156
i32.const 4
1157
return))
1158
(core instance $instance (instantiate $m))
1159
(type $func-type (func (result $my-record)))
1160
(alias core export $instance "my-func" (core func $my-func))
1161
(alias core export $instance "memory" (core memory $memory))
1162
(func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8))
1163
(export $export "my-func" (func $my-func))
1164
)"#,
1165
)?;
1166
let mut store = Store::new(&engine, 0);
1167
let linker = Linker::new(&engine);
1168
let instance = linker.instantiate(&mut store, &component)?;
1169
let my_func = instance.get_func(&mut store, "my-func").unwrap();
1170
let mut results = vec![Val::Bool(false)];
1171
my_func.call(&mut store, &[], &mut results)?;
1172
1173
// Create another component that exports a function that takes that record as an argument
1174
let component = Component::new(
1175
&engine,
1176
format!(
1177
r#"(component
1178
(type (;0;) (record (field "a" u8) (field "b" string)))
1179
(import "my-record" (type $my-record (eq 0)))
1180
(core module $m
1181
(memory $memory 17)
1182
(export "memory" (memory $memory))
1183
{REALLOC_AND_FREE}
1184
(func (export "my-func") (param i32 i32 i32)))
1185
(core instance $instance (instantiate $m))
1186
(type $func-type (func (param "my-record" $my-record)))
1187
(alias core export $instance "my-func" (core func $my-func))
1188
(alias core export $instance "memory" (core memory $memory))
1189
(func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8 (realloc (func $instance "realloc"))))
1190
(export $export "my-func" (func $my-func))
1191
)"#
1192
),
1193
)?;
1194
let mut store = Store::new(&engine, 0);
1195
let linker = Linker::new(&engine);
1196
let instance = linker.instantiate(&mut store, &component)?;
1197
let my_func = instance.get_func(&mut store, "my-func").unwrap();
1198
// Call the exported function with the return values of the call to the previous component's exported function
1199
my_func.call(&mut store, &results, &mut [])?;
1200
1201
Ok(())
1202
}
1203
1204