Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/component_model/import.rs
3067 views
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]
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
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]
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]
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]
498
async fn stack_and_heap_args_and_rets_concurrent() -> Result<()> {
499
test_stack_and_heap_args_and_rets(true).await
500
}
501
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?.0)
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]
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]
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]
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