Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/gc.rs
3050 views
1
use super::ref_types_module;
2
use std::sync::Arc;
3
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
4
use wasmtime::*;
5
6
struct SetFlagOnDrop(Arc<AtomicBool>);
7
8
impl Drop for SetFlagOnDrop {
9
fn drop(&mut self) {
10
self.0.store(true, SeqCst);
11
}
12
}
13
14
#[test]
15
#[cfg_attr(miri, ignore)]
16
fn smoke_test_gc_no_epochs() -> Result<()> {
17
smoke_test_gc_impl(false)
18
}
19
20
#[test]
21
#[cfg_attr(miri, ignore)]
22
fn smoke_test_gc_yes_epochs() -> Result<()> {
23
smoke_test_gc_impl(true)
24
}
25
26
fn smoke_test_gc_impl(use_epochs: bool) -> Result<()> {
27
let (mut store, module) = ref_types_module(
28
use_epochs,
29
r#"
30
(module
31
(import "" "" (func $do_gc))
32
(func $recursive (export "func") (param i32 externref) (result externref)
33
local.get 0
34
i32.eqz
35
if (result externref)
36
call $do_gc
37
local.get 1
38
else
39
local.get 0
40
i32.const 1
41
i32.sub
42
local.get 1
43
call $recursive
44
end
45
)
46
)
47
"#,
48
)?;
49
50
let do_gc = Func::wrap(&mut store, |mut caller: Caller<'_, _>| {
51
// Do a GC with `externref`s on the stack in Wasm frames.
52
caller.gc(None)
53
});
54
let instance = Instance::new(&mut store, &module, &[do_gc.into()])?;
55
let func = instance.get_func(&mut store, "func").unwrap();
56
57
let inner_dropped = Arc::new(AtomicBool::new(false));
58
59
{
60
let mut scope = RootScope::new(&mut store);
61
62
let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
63
{
64
let args = [Val::I32(5), Val::ExternRef(Some(r))];
65
func.call(&mut scope, &args, &mut [Val::I32(0)])?;
66
}
67
68
// Doing a GC should see that there aren't any `externref`s on the stack in
69
// Wasm frames anymore.
70
scope.as_context_mut().gc(None)?;
71
72
// But the scope should still be rooting `r`.
73
assert!(!inner_dropped.load(SeqCst));
74
}
75
76
// Exiting the scope and unrooting `r` should have dropped the inner
77
// `SetFlagOnDrop` value.
78
assert!(inner_dropped.load(SeqCst));
79
80
Ok(())
81
}
82
83
struct CountDrops(Arc<AtomicUsize>);
84
85
impl Drop for CountDrops {
86
fn drop(&mut self) {
87
self.0.fetch_add(1, SeqCst);
88
}
89
}
90
91
#[test]
92
#[cfg_attr(miri, ignore)]
93
fn wasm_dropping_refs() -> Result<()> {
94
let (mut store, module) = ref_types_module(
95
false,
96
r#"
97
(module
98
(func (export "drop_ref") (param externref)
99
nop
100
)
101
)
102
"#,
103
)?;
104
105
let instance = Instance::new(&mut store, &module, &[])?;
106
let drop_ref = instance.get_func(&mut store, "drop_ref").unwrap();
107
108
let num_refs_dropped = Arc::new(AtomicUsize::new(0));
109
110
for _ in 0..4096 {
111
let mut scope = RootScope::new(&mut store);
112
let r = ExternRef::new(&mut scope, CountDrops(num_refs_dropped.clone()))?;
113
let args = [Val::ExternRef(Some(r))];
114
drop_ref.call(&mut scope, &args, &mut [])?;
115
}
116
117
// After doing a GC, all the refs should have been dropped.
118
store.gc(None)?;
119
assert_eq!(num_refs_dropped.load(SeqCst), 4096);
120
121
return Ok(());
122
}
123
124
#[test]
125
#[cfg_attr(miri, ignore)]
126
fn many_live_refs() -> Result<()> {
127
let mut wat = r#"
128
(module
129
;; Make new `externref`s.
130
(import "" "make_ref" (func $make_ref (result externref)))
131
132
;; Observe an `externref` so it is kept live.
133
(import "" "observe_ref" (func $observe_ref (param externref)))
134
135
(func (export "many_live_refs")
136
"#
137
.to_string();
138
139
// This is more than the initial `VMExternRefActivationsTable` capacity, so
140
// it will need to allocate additional bump chunks.
141
const NUM_LIVE_REFS: usize = 1024;
142
143
// Push `externref`s onto the stack.
144
for _ in 0..NUM_LIVE_REFS {
145
wat.push_str("(call $make_ref)\n");
146
}
147
148
// Pop `externref`s from the stack. Because we pass each of them to a
149
// function call here, they are all live references for the duration of
150
// their lifetimes.
151
for _ in 0..NUM_LIVE_REFS {
152
wat.push_str("(call $observe_ref)\n");
153
}
154
155
wat.push_str(
156
"
157
) ;; func
158
) ;; module
159
",
160
);
161
162
let (mut store, module) = ref_types_module(false, &wat)?;
163
164
let live_refs = Arc::new(AtomicUsize::new(0));
165
166
let make_ref = Func::wrap(&mut store, {
167
let live_refs = live_refs.clone();
168
move |mut caller: Caller<'_, _>| {
169
Ok(Some(ExternRef::new(
170
&mut caller,
171
CountLiveRefs::new(live_refs.clone()),
172
)?))
173
}
174
});
175
176
let observe_ref = Func::wrap(
177
&mut store,
178
|caller: Caller<'_, _>, r: Option<Rooted<ExternRef>>| {
179
let r = r
180
.unwrap()
181
.data(&caller)
182
.unwrap()
183
.unwrap()
184
.downcast_ref::<CountLiveRefs>()
185
.unwrap();
186
assert!(r.live_refs.load(SeqCst) > 0);
187
},
188
);
189
190
let instance = Instance::new(&mut store, &module, &[make_ref.into(), observe_ref.into()])?;
191
let many_live_refs = instance.get_func(&mut store, "many_live_refs").unwrap();
192
193
many_live_refs.call(&mut store, &[], &mut [])?;
194
195
store.as_context_mut().gc(None)?;
196
assert_eq!(live_refs.load(SeqCst), 0);
197
198
return Ok(());
199
200
struct CountLiveRefs {
201
live_refs: Arc<AtomicUsize>,
202
}
203
204
impl CountLiveRefs {
205
fn new(live_refs: Arc<AtomicUsize>) -> Self {
206
live_refs.fetch_add(1, SeqCst);
207
Self { live_refs }
208
}
209
}
210
211
impl Drop for CountLiveRefs {
212
fn drop(&mut self) {
213
self.live_refs.fetch_sub(1, SeqCst);
214
}
215
}
216
}
217
218
#[test]
219
#[cfg_attr(miri, ignore)]
220
fn drop_externref_via_table_set() -> Result<()> {
221
let (mut store, module) = ref_types_module(
222
false,
223
r#"
224
(module
225
(table $t 1 externref)
226
227
(func (export "table-set") (param externref)
228
(table.set $t (i32.const 0) (local.get 0))
229
)
230
)
231
"#,
232
)?;
233
234
let instance = Instance::new(&mut store, &module, &[])?;
235
let table_set = instance.get_func(&mut store, "table-set").unwrap();
236
237
let foo_is_dropped = Arc::new(AtomicBool::new(false));
238
let bar_is_dropped = Arc::new(AtomicBool::new(false));
239
240
{
241
let mut scope = RootScope::new(&mut store);
242
243
let foo = ExternRef::new(&mut scope, SetFlagOnDrop(foo_is_dropped.clone()))?;
244
let bar = ExternRef::new(&mut scope, SetFlagOnDrop(bar_is_dropped.clone()))?;
245
246
{
247
let args = vec![Val::ExternRef(Some(foo))];
248
table_set.call(&mut scope, &args, &mut [])?;
249
}
250
251
scope.as_context_mut().gc(None)?;
252
assert!(!foo_is_dropped.load(SeqCst));
253
assert!(!bar_is_dropped.load(SeqCst));
254
255
{
256
let args = vec![Val::ExternRef(Some(bar))];
257
table_set.call(&mut scope, &args, &mut [])?;
258
}
259
}
260
261
store.gc(None)?;
262
assert!(foo_is_dropped.load(SeqCst));
263
assert!(!bar_is_dropped.load(SeqCst));
264
265
table_set.call(&mut store, &[Val::ExternRef(None)], &mut [])?;
266
assert!(foo_is_dropped.load(SeqCst));
267
assert!(bar_is_dropped.load(SeqCst));
268
269
Ok(())
270
}
271
272
#[test]
273
#[cfg_attr(miri, ignore)]
274
fn global_drops_externref() -> Result<()> {
275
let _ = env_logger::try_init();
276
test_engine(&Engine::default())?;
277
278
let mut config = Config::new();
279
config.allocation_strategy(crate::small_pool_config());
280
test_engine(&Engine::new(&config)?)?;
281
282
return Ok(());
283
284
fn test_engine(engine: &Engine) -> Result<()> {
285
let mut store = Store::new(&engine, ());
286
let flag = Arc::new(AtomicBool::new(false));
287
let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
288
Global::new(
289
&mut store,
290
GlobalType::new(ValType::EXTERNREF, Mutability::Const),
291
externref.into(),
292
)?;
293
drop(store);
294
assert!(flag.load(SeqCst));
295
296
let mut store = Store::new(&engine, ());
297
let module = Module::new(
298
&engine,
299
r#"
300
(module
301
(global (mut externref) (ref.null extern))
302
303
(func (export "run") (param externref)
304
local.get 0
305
global.set 0
306
)
307
)
308
"#,
309
)?;
310
let instance = Instance::new(&mut store, &module, &[])?;
311
let run = instance.get_typed_func::<Option<Rooted<ExternRef>>, ()>(&mut store, "run")?;
312
let flag = Arc::new(AtomicBool::new(false));
313
let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
314
run.call(&mut store, Some(externref))?;
315
drop(store);
316
assert!(flag.load(SeqCst));
317
Ok(())
318
}
319
}
320
321
#[test]
322
#[cfg_attr(miri, ignore)]
323
fn table_drops_externref() -> Result<()> {
324
let _ = env_logger::try_init();
325
test_engine(&Engine::default())?;
326
327
let mut config = Config::new();
328
config.allocation_strategy(crate::small_pool_config());
329
test_engine(&Engine::new(&config)?)?;
330
331
return Ok(());
332
333
fn test_engine(engine: &Engine) -> Result<()> {
334
let mut store = Store::new(&engine, ());
335
let flag = Arc::new(AtomicBool::new(false));
336
let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
337
Table::new(
338
&mut store,
339
TableType::new(RefType::EXTERNREF, 1, None),
340
externref.into(),
341
)?;
342
drop(store);
343
assert!(flag.load(SeqCst));
344
345
let mut store = Store::new(&engine, ());
346
let module = Module::new(
347
&engine,
348
r#"
349
(module
350
(table 1 externref)
351
352
(func (export "run") (param externref)
353
i32.const 0
354
local.get 0
355
table.set 0
356
)
357
)
358
"#,
359
)?;
360
let instance = Instance::new(&mut store, &module, &[])?;
361
let run = instance.get_typed_func::<Option<Rooted<ExternRef>>, ()>(&mut store, "run")?;
362
let flag = Arc::new(AtomicBool::new(false));
363
let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
364
run.call(&mut store, Some(externref))?;
365
drop(store);
366
assert!(flag.load(SeqCst));
367
Ok(())
368
}
369
}
370
371
#[test]
372
fn global_init_no_leak() -> Result<()> {
373
let (mut store, module) = ref_types_module(
374
false,
375
r#"
376
(module
377
(import "" "" (global externref))
378
(global externref (global.get 0))
379
)
380
"#,
381
)?;
382
383
let flag = Arc::new(AtomicBool::new(false));
384
let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
385
let global = Global::new(
386
&mut store,
387
GlobalType::new(ValType::EXTERNREF, Mutability::Const),
388
externref.into(),
389
)?;
390
Instance::new(&mut store, &module, &[global.into()])?;
391
drop(store);
392
assert!(flag.load(SeqCst));
393
394
Ok(())
395
}
396
397
#[test]
398
#[cfg_attr(miri, ignore)]
399
fn no_gc_middle_of_args() -> Result<()> {
400
let (mut store, module) = ref_types_module(
401
false,
402
r#"
403
(module
404
(import "" "return_some" (func $return (result externref externref externref)))
405
(import "" "take_some" (func $take (param externref externref externref)))
406
(func (export "run")
407
(local i32)
408
i32.const 1000
409
local.set 0
410
loop
411
call $return
412
call $take
413
local.get 0
414
i32.const -1
415
i32.add
416
local.tee 0
417
br_if 0
418
end
419
)
420
)
421
"#,
422
)?;
423
424
let mut linker = Linker::new(store.engine());
425
linker.func_wrap("", "return_some", |mut caller: Caller<'_, _>| {
426
let a = Some(ExternRef::new(&mut caller, String::from("a"))?);
427
let b = Some(ExternRef::new(&mut caller, String::from("b"))?);
428
let c = Some(ExternRef::new(&mut caller, String::from("c"))?);
429
Ok((a, b, c))
430
})?;
431
linker.func_wrap(
432
"",
433
"take_some",
434
|caller: Caller<'_, _>,
435
a: Option<Rooted<ExternRef>>,
436
b: Option<Rooted<ExternRef>>,
437
c: Option<Rooted<ExternRef>>| {
438
let a = a.unwrap();
439
let b = b.unwrap();
440
let c = c.unwrap();
441
assert_eq!(
442
a.data(&caller)
443
.expect("rooted")
444
.expect("host data")
445
.downcast_ref::<String>()
446
.expect("is string"),
447
"a"
448
);
449
assert_eq!(
450
b.data(&caller)
451
.expect("rooted")
452
.expect("host data")
453
.downcast_ref::<String>()
454
.expect("is string"),
455
"b"
456
);
457
assert_eq!(
458
c.data(&caller)
459
.expect("rooted")
460
.expect("host data")
461
.downcast_ref::<String>()
462
.expect("is string"),
463
"c"
464
);
465
},
466
)?;
467
468
let instance = linker.instantiate(&mut store, &module)?;
469
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
470
func.call(&mut store, ())?;
471
472
Ok(())
473
}
474
475
#[test]
476
#[cfg_attr(miri, ignore)]
477
fn gc_and_tail_calls_and_stack_arguments() -> Result<()> {
478
// Test that GC refs in tail-calls' stack arguments get properly accounted
479
// for in stack maps.
480
//
481
// What we do _not_ want to happen is for tail callers to be responsible for
482
// including stack arguments in their stack maps (and therefore whether or
483
// not they get marked at runtime). If that was the case, then we could have
484
// the following scenario:
485
//
486
// * `f` calls `g` without any stack arguments,
487
// * `g` tail calls `h` with GC ref stack arguments,
488
// * and then `h` triggers a GC.
489
//
490
// Because `g`, who is responsible for including the GC refs in its stack
491
// map in this hypothetical scenario, is no longer on the stack, we never
492
// see its stack map, and therefore never mark the GC refs, and then we
493
// collect them too early, and then we can get user-after-free bugs. Not
494
// good! Note also that `f`, which is the frame that `h` will return to,
495
// _cannot_ be responsible for including these stack arguments in its stack
496
// map, because it has no idea what frame will be returning to it, and it
497
// could be any number of different functions using that frame for long (and
498
// indirect!) tail-call chains.
499
//
500
// In Cranelift we avoid this scenario because stack arguments are eagerly
501
// loaded into virtual registers, and then when we insert a GC safe point,
502
// we spill these virtual registers to the callee stack frame, and the stack
503
// map includes entries for these stack slots.
504
//
505
// Nonetheless, this test exercises the above scenario just in case we do
506
// something in the future like lazily load stack arguments into virtual
507
// registers, to make sure that everything shows up in stack maps like they
508
// are supposed to.
509
510
let (mut store, module) = ref_types_module(
511
false,
512
r#"
513
(module
514
(import "" "make_some" (func $make (result externref externref externref)))
515
(import "" "take_some" (func $take (param externref externref externref)))
516
(import "" "gc" (func $gc))
517
518
(func $stack_args (param externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref)
519
call $gc
520
;; Make sure all these GC refs are live, so that they need to
521
;; be put into the stack map.
522
local.get 0
523
local.get 1
524
local.get 2
525
call $take
526
local.get 3
527
local.get 4
528
local.get 5
529
call $take
530
local.get 6
531
local.get 7
532
local.get 8
533
call $take
534
local.get 9
535
local.get 10
536
local.get 11
537
call $take
538
local.get 12
539
local.get 13
540
local.get 14
541
call $take
542
local.get 15
543
local.get 16
544
local.get 17
545
call $take
546
local.get 18
547
local.get 19
548
local.get 20
549
call $take
550
local.get 21
551
local.get 22
552
local.get 23
553
call $take
554
local.get 24
555
local.get 25
556
local.get 26
557
call $take
558
local.get 27
559
local.get 28
560
local.get 29
561
call $take
562
)
563
564
(func $no_stack_args
565
call $make
566
call $make
567
call $make
568
call $make
569
call $make
570
call $make
571
call $make
572
call $make
573
call $make
574
call $make
575
return_call $stack_args
576
)
577
578
(func (export "run")
579
(local i32)
580
i32.const 1000
581
local.set 0
582
loop
583
call $no_stack_args
584
local.get 0
585
i32.const -1
586
i32.add
587
local.tee 0
588
br_if 0
589
end
590
)
591
)
592
"#,
593
)?;
594
595
let mut linker = Linker::new(store.engine());
596
linker.func_wrap("", "make_some", |mut caller: Caller<'_, _>| {
597
Ok((
598
Some(ExternRef::new(&mut caller, "a".to_string())?),
599
Some(ExternRef::new(&mut caller, "b".to_string())?),
600
Some(ExternRef::new(&mut caller, "c".to_string())?),
601
))
602
})?;
603
linker.func_wrap(
604
"",
605
"take_some",
606
|caller: Caller<'_, _>,
607
a: Option<Rooted<ExternRef>>,
608
b: Option<Rooted<ExternRef>>,
609
c: Option<Rooted<ExternRef>>| {
610
let a = a.unwrap();
611
let b = b.unwrap();
612
let c = c.unwrap();
613
assert_eq!(
614
a.data(&caller)
615
.unwrap()
616
.unwrap()
617
.downcast_ref::<String>()
618
.unwrap(),
619
"a"
620
);
621
assert_eq!(
622
b.data(&caller)
623
.unwrap()
624
.unwrap()
625
.downcast_ref::<String>()
626
.unwrap(),
627
"b"
628
);
629
assert_eq!(
630
c.data(&caller)
631
.unwrap()
632
.unwrap()
633
.downcast_ref::<String>()
634
.unwrap(),
635
"c"
636
);
637
},
638
)?;
639
linker.func_wrap("", "gc", |mut caller: Caller<()>| caller.gc(None))?;
640
641
let instance = linker.instantiate(&mut store, &module)?;
642
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
643
func.call(&mut store, ())?;
644
645
Ok(())
646
}
647
648
#[test]
649
#[cfg_attr(miri, ignore)]
650
fn no_leak_with_global_get_elem_segment() -> wasmtime::Result<()> {
651
let dropped = Arc::new(AtomicBool::new(false));
652
653
let engine = Engine::default();
654
let mut store = Store::new(&engine, ());
655
let module = Module::new(
656
&engine,
657
r#"
658
(module
659
(import "" "" (global $init externref))
660
(start $f)
661
(table $t 1 externref)
662
(elem $e externref (global.get $init))
663
664
(func $f
665
i32.const 0
666
i32.const 0
667
i32.const 1
668
table.init $t $e
669
670
i32.const 0
671
i32.const 0
672
i32.const 1
673
table.init $t $e
674
)
675
)
676
"#,
677
)?;
678
679
let externref = ExternRef::new(&mut store, SetFlagOnDrop(dropped.clone()))?;
680
let global = Global::new(
681
&mut store,
682
GlobalType::new(ValType::EXTERNREF, Mutability::Const),
683
externref.into(),
684
)?;
685
686
Instance::new(&mut store, &module, &[global.into()])?;
687
688
drop(store);
689
690
assert!(dropped.load(SeqCst));
691
Ok(())
692
}
693
694
#[test]
695
#[cfg_attr(miri, ignore)]
696
fn table_init_with_externref_global_get() -> wasmtime::Result<()> {
697
let dropped = Arc::new(AtomicBool::new(false));
698
699
let mut config = Config::new();
700
config.wasm_function_references(true);
701
let engine = Engine::new(&config)?;
702
let mut store = Store::new(&engine, ());
703
let module = Module::new(
704
&engine,
705
r#"
706
(module
707
(import "" "" (global $init externref))
708
(table $t 1 externref (global.get $init))
709
)
710
"#,
711
)?;
712
713
let externref = ExternRef::new(&mut store, SetFlagOnDrop(dropped.clone()))?;
714
let global = Global::new(
715
&mut store,
716
GlobalType::new(ValType::EXTERNREF, Mutability::Const),
717
externref.into(),
718
)?;
719
720
Instance::new(&mut store, &module, &[global.into()])?;
721
722
drop(store);
723
724
assert!(dropped.load(SeqCst));
725
Ok(())
726
}
727
728
#[test]
729
fn rooted_gets_collected_after_scope_exit() -> Result<()> {
730
let mut store = Store::<()>::default();
731
let flag = Arc::new(AtomicBool::new(false));
732
733
{
734
let mut scope = RootScope::new(&mut store);
735
let _externref = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
736
737
scope.as_context_mut().gc(None)?;
738
assert!(!flag.load(SeqCst), "not dropped when still rooted");
739
}
740
741
store.as_context_mut().gc(None)?;
742
assert!(flag.load(SeqCst), "dropped after being unrooted");
743
744
Ok(())
745
}
746
747
#[test]
748
fn owned_rooted_gets_collected_after_unrooting() -> Result<()> {
749
let mut store = Store::<()>::default();
750
let flag = Arc::new(AtomicBool::new(false));
751
752
let externref = {
753
let mut scope = RootScope::new(&mut store);
754
ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?.to_owned_rooted(&mut scope)?
755
};
756
757
store.gc(None)?;
758
assert!(!flag.load(SeqCst), "not dropped when still rooted");
759
760
drop(externref);
761
store.gc(None)?;
762
assert!(flag.load(SeqCst), "dropped after being unrooted");
763
764
Ok(())
765
}
766
767
#[test]
768
#[cfg_attr(miri, ignore)]
769
fn round_trip_gc_ref_through_typed_wasm_func() -> Result<()> {
770
let mut store = Store::<()>::default();
771
let module = Module::new(
772
store.engine(),
773
r#"
774
(module
775
(import "" "" (func $gc))
776
(func (export "f") (param externref) (result externref)
777
call $gc
778
local.get 0
779
)
780
)
781
"#,
782
)?;
783
let gc = Func::wrap(&mut store, |mut caller: Caller<'_, _>| caller.gc(None));
784
let instance = Instance::new(&mut store, &module, &[gc.into()])?;
785
let f = instance
786
.get_typed_func::<Option<Rooted<ExternRef>>, Option<Rooted<ExternRef>>>(&mut store, "f")?;
787
let x1 = ExternRef::new(&mut store, 1234)?;
788
let x2 = f.call(&mut store, Some(x1))?.unwrap();
789
assert!(Rooted::ref_eq(&store, &x1, &x2)?);
790
Ok(())
791
}
792
793
#[test]
794
#[cfg_attr(miri, ignore)]
795
fn round_trip_gc_ref_through_func_wrap() -> Result<()> {
796
let mut store = Store::<()>::default();
797
let f = Func::wrap(
798
&mut store,
799
|mut caller: Caller<'_, _>, x: Rooted<ExternRef>| {
800
caller.gc(None)?;
801
Ok(x)
802
},
803
);
804
let f = f.typed::<Rooted<ExternRef>, Rooted<ExternRef>>(&store)?;
805
let x1 = ExternRef::new(&mut store, 1234)?;
806
let x2 = f.call(&mut store, x1)?;
807
assert!(Rooted::ref_eq(&store, &x1, &x2)?);
808
Ok(())
809
}
810
811
#[test]
812
fn to_raw_from_raw_doesnt_leak() -> Result<()> {
813
let mut store = Store::<()>::default();
814
let flag = Arc::new(AtomicBool::new(false));
815
816
{
817
let mut scope = RootScope::new(&mut store);
818
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
819
let raw = x.to_raw(&mut scope)?;
820
let _x = ExternRef::from_raw(&mut scope, raw);
821
}
822
823
store.gc(None)?;
824
assert!(flag.load(SeqCst));
825
Ok(())
826
}
827
828
#[test]
829
fn table_fill_doesnt_leak() -> Result<()> {
830
let _ = env_logger::try_init();
831
832
let mut store = Store::<()>::default();
833
let flag = Arc::new(AtomicBool::new(false));
834
835
{
836
let mut scope = RootScope::new(&mut store);
837
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
838
let table = Table::new(
839
&mut scope,
840
TableType::new(RefType::EXTERNREF, 10, Some(10)),
841
x.into(),
842
)?;
843
table.fill(&mut scope, 0, Ref::Extern(None), 10)?;
844
}
845
846
store.gc(None)?;
847
assert!(flag.load(SeqCst));
848
Ok(())
849
}
850
851
#[test]
852
#[cfg_attr(miri, ignore)]
853
fn table_copy_doesnt_leak() -> Result<()> {
854
let _ = env_logger::try_init();
855
856
let mut store = Store::<()>::default();
857
let flag = Arc::new(AtomicBool::new(false));
858
859
{
860
let mut scope = RootScope::new(&mut store);
861
let table = Table::new(
862
&mut scope,
863
TableType::new(RefType::EXTERNREF, 10, Some(10)),
864
Ref::Extern(None),
865
)?;
866
867
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
868
table.fill(&mut scope, 2, x.into(), 3)?;
869
870
Table::copy(&mut scope, &table, 0, &table, 5, 5)?;
871
}
872
873
store.gc(None)?;
874
assert!(flag.load(SeqCst));
875
Ok(())
876
}
877
878
#[test]
879
#[cfg_attr(miri, ignore)]
880
fn table_set_doesnt_leak() -> Result<()> {
881
let _ = env_logger::try_init();
882
883
let mut store = Store::<()>::default();
884
let flag = Arc::new(AtomicBool::new(false));
885
886
{
887
let mut scope = RootScope::new(&mut store);
888
let table = Table::new(
889
&mut scope,
890
TableType::new(RefType::EXTERNREF, 10, Some(10)),
891
Ref::Extern(None),
892
)?;
893
894
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
895
table.set(&mut scope, 2, x.into())?;
896
table.set(&mut scope, 2, x.into())?;
897
table.set(&mut scope, 2, Ref::Extern(None))?;
898
}
899
900
store.gc(None)?;
901
assert!(flag.load(SeqCst));
902
Ok(())
903
}
904
905
#[test]
906
#[cfg_attr(miri, ignore)]
907
fn table_grow_doesnt_leak() -> Result<()> {
908
let _ = env_logger::try_init();
909
910
let mut store = Store::<()>::default();
911
let flag = Arc::new(AtomicBool::new(false));
912
913
{
914
let mut scope = RootScope::new(&mut store);
915
let table = Table::new(
916
&mut scope,
917
TableType::new(RefType::EXTERNREF, 10, Some(10)),
918
Ref::Extern(None),
919
)?;
920
921
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
922
table.grow(&mut scope, 0, x.into())?;
923
}
924
925
store.gc(None)?;
926
assert!(flag.load(SeqCst));
927
Ok(())
928
}
929
930
// This is a test that the argument to `table.init` is properly handled w.r.t.
931
// write barriers and such. This doesn't use `SetFlagOnDrop` because that would
932
// require initializing a table with an element initialized from a global and
933
// the global keeps the externref alive regardless. Instead this has a small GC
934
// heap and we continuously make more garbage than is in the heap and expect
935
// this to work as when it triggers a GC everything prior should get cleaned up.
936
#[test]
937
#[cfg_attr(miri, ignore)]
938
fn table_init_doesnt_leak() -> Result<()> {
939
const SIZE: u64 = 64 << 10;
940
941
let _ = env_logger::try_init();
942
943
let mut config = Config::new();
944
config.wasm_gc(true);
945
config.wasm_function_references(true);
946
config.memory_may_move(false);
947
config.memory_reservation(SIZE);
948
config.memory_reservation_for_growth(0);
949
let engine = Engine::new(&config)?;
950
let mut store = Store::new(&engine, ());
951
952
let module = Module::new(
953
store.engine(),
954
r#"
955
(module
956
(table 1 arrayref)
957
958
(type $a (array i31ref))
959
960
(func (export "run")
961
i32.const 0
962
i32.const 0
963
i32.const 1
964
table.init $e)
965
(elem $e arrayref (array.new $a (ref.i31 (i32.const 0)) (i32.const 200)))
966
)
967
"#,
968
)?;
969
970
let instance = Instance::new(&mut store, &module, &[])?;
971
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
972
for _ in 0..200 {
973
func.call(&mut store, ())?;
974
}
975
976
Ok(())
977
}
978
979
#[test]
980
fn ref_matches() -> Result<()> {
981
let mut store = Store::<()>::default();
982
let engine = store.engine().clone();
983
984
let func_ty = FuncType::new(&engine, None, None);
985
let func_ref_ty = RefType::new(true, HeapType::ConcreteFunc(func_ty.clone()));
986
let f = Func::new(&mut store, func_ty, |_, _, _| Ok(()));
987
988
let pre = StructRefPre::new(&mut store, StructType::new(&engine, [])?);
989
let s = StructRef::new(&mut store, &pre, &[])?.to_anyref();
990
991
let pre = ArrayRefPre::new(
992
&mut store,
993
ArrayType::new(&engine, FieldType::new(Mutability::Const, StorageType::I8)),
994
);
995
let a = ArrayRef::new(&mut store, &pre, &Val::I32(0), 0)?.to_anyref();
996
997
let i31 = AnyRef::from_i31(&mut store, I31::wrapping_i32(1234));
998
999
let e = ExternRef::new(&mut store, "hello")?;
1000
1001
for (val, ty, expected) in [
1002
// nulls to nullexternref
1003
(Ref::Extern(None), RefType::NULLEXTERNREF, true),
1004
(Ref::Any(None), RefType::NULLEXTERNREF, false),
1005
(Ref::Func(None), RefType::NULLEXTERNREF, false),
1006
// nulls to externref
1007
(Ref::Extern(None), RefType::EXTERNREF, true),
1008
(Ref::Any(None), RefType::EXTERNREF, false),
1009
(Ref::Func(None), RefType::EXTERNREF, false),
1010
// nulls to nullref
1011
(Ref::Extern(None), RefType::NULLREF, false),
1012
(Ref::Any(None), RefType::NULLREF, true),
1013
(Ref::Func(None), RefType::NULLREF, false),
1014
// nulls to structref
1015
(Ref::Extern(None), RefType::STRUCTREF, false),
1016
(Ref::Any(None), RefType::STRUCTREF, true),
1017
(Ref::Func(None), RefType::STRUCTREF, false),
1018
// nulls to arrayref
1019
(Ref::Extern(None), RefType::ARRAYREF, false),
1020
(Ref::Any(None), RefType::ARRAYREF, true),
1021
(Ref::Func(None), RefType::ARRAYREF, false),
1022
// nulls to i31ref
1023
(Ref::Extern(None), RefType::I31REF, false),
1024
(Ref::Any(None), RefType::I31REF, true),
1025
(Ref::Func(None), RefType::I31REF, false),
1026
// nulls to eqref
1027
(Ref::Extern(None), RefType::EQREF, false),
1028
(Ref::Any(None), RefType::EQREF, true),
1029
(Ref::Func(None), RefType::EQREF, false),
1030
// nulls to anyref
1031
(Ref::Extern(None), RefType::ANYREF, false),
1032
(Ref::Any(None), RefType::ANYREF, true),
1033
(Ref::Func(None), RefType::ANYREF, false),
1034
// non-null structref
1035
(Ref::Any(Some(s)), RefType::NULLFUNCREF, false),
1036
(Ref::Any(Some(s)), func_ref_ty.clone(), false),
1037
(Ref::Any(Some(s)), RefType::FUNCREF, false),
1038
(Ref::Any(Some(s)), RefType::NULLEXTERNREF, false),
1039
(Ref::Any(Some(s)), RefType::EXTERNREF, false),
1040
(Ref::Any(Some(s)), RefType::NULLREF, false),
1041
(Ref::Any(Some(s)), RefType::STRUCTREF, true),
1042
(Ref::Any(Some(s)), RefType::ARRAYREF, false),
1043
(Ref::Any(Some(s)), RefType::I31REF, false),
1044
(Ref::Any(Some(s)), RefType::EQREF, true),
1045
(Ref::Any(Some(s)), RefType::ANYREF, true),
1046
// non-null arrayref
1047
(Ref::Any(Some(a)), RefType::NULLFUNCREF, false),
1048
(Ref::Any(Some(a)), func_ref_ty.clone(), false),
1049
(Ref::Any(Some(a)), RefType::FUNCREF, false),
1050
(Ref::Any(Some(a)), RefType::NULLEXTERNREF, false),
1051
(Ref::Any(Some(a)), RefType::EXTERNREF, false),
1052
(Ref::Any(Some(a)), RefType::NULLREF, false),
1053
(Ref::Any(Some(a)), RefType::STRUCTREF, false),
1054
(Ref::Any(Some(a)), RefType::ARRAYREF, true),
1055
(Ref::Any(Some(a)), RefType::I31REF, false),
1056
(Ref::Any(Some(a)), RefType::EQREF, true),
1057
(Ref::Any(Some(a)), RefType::ANYREF, true),
1058
// non-null i31ref
1059
(Ref::Any(Some(i31)), RefType::NULLFUNCREF, false),
1060
(Ref::Any(Some(i31)), func_ref_ty.clone(), false),
1061
(Ref::Any(Some(i31)), RefType::FUNCREF, false),
1062
(Ref::Any(Some(i31)), RefType::NULLEXTERNREF, false),
1063
(Ref::Any(Some(i31)), RefType::EXTERNREF, false),
1064
(Ref::Any(Some(i31)), RefType::NULLREF, false),
1065
(Ref::Any(Some(i31)), RefType::STRUCTREF, false),
1066
(Ref::Any(Some(i31)), RefType::ARRAYREF, false),
1067
(Ref::Any(Some(i31)), RefType::I31REF, true),
1068
(Ref::Any(Some(i31)), RefType::EQREF, true),
1069
(Ref::Any(Some(i31)), RefType::ANYREF, true),
1070
// non-null funcref
1071
(Ref::Func(Some(f)), RefType::NULLFUNCREF, false),
1072
(Ref::Func(Some(f)), func_ref_ty.clone(), true),
1073
(Ref::Func(Some(f)), RefType::FUNCREF, true),
1074
(Ref::Func(Some(f)), RefType::NULLEXTERNREF, false),
1075
(Ref::Func(Some(f)), RefType::EXTERNREF, false),
1076
(Ref::Func(Some(f)), RefType::NULLREF, false),
1077
(Ref::Func(Some(f)), RefType::STRUCTREF, false),
1078
(Ref::Func(Some(f)), RefType::ARRAYREF, false),
1079
(Ref::Func(Some(f)), RefType::I31REF, false),
1080
(Ref::Func(Some(f)), RefType::EQREF, false),
1081
(Ref::Func(Some(f)), RefType::ANYREF, false),
1082
// non-null externref
1083
(Ref::Extern(Some(e)), RefType::NULLFUNCREF, false),
1084
(Ref::Extern(Some(e)), func_ref_ty.clone(), false),
1085
(Ref::Extern(Some(e)), RefType::FUNCREF, false),
1086
(Ref::Extern(Some(e)), RefType::NULLEXTERNREF, false),
1087
(Ref::Extern(Some(e)), RefType::EXTERNREF, true),
1088
(Ref::Extern(Some(e)), RefType::NULLREF, false),
1089
(Ref::Extern(Some(e)), RefType::STRUCTREF, false),
1090
(Ref::Extern(Some(e)), RefType::ARRAYREF, false),
1091
(Ref::Extern(Some(e)), RefType::I31REF, false),
1092
(Ref::Extern(Some(e)), RefType::EQREF, false),
1093
(Ref::Extern(Some(e)), RefType::ANYREF, false),
1094
] {
1095
let actual = val.matches_ty(&mut store, &ty)?;
1096
assert_eq!(
1097
actual, expected,
1098
"{val:?} matches {ty:?}? expected {expected}, got {actual}"
1099
);
1100
}
1101
1102
Ok(())
1103
}
1104
1105
#[test]
1106
#[cfg_attr(miri, ignore)]
1107
fn issue_9669() -> Result<()> {
1108
let _ = env_logger::try_init();
1109
1110
let mut config = Config::new();
1111
config.wasm_function_references(true);
1112
config.wasm_gc(true);
1113
config.collector(Collector::DeferredReferenceCounting);
1114
1115
let engine = Engine::new(&config)?;
1116
1117
let module = Module::new(
1118
&engine,
1119
r#"
1120
(module
1121
(type $empty (struct))
1122
(type $thing (struct
1123
(field $field1 (ref $empty))
1124
(field $field2 (ref $empty))
1125
))
1126
1127
(func (export "run")
1128
(local $object (ref $thing))
1129
1130
struct.new $empty
1131
struct.new $empty
1132
struct.new $thing
1133
1134
local.tee $object
1135
struct.get $thing $field1
1136
drop
1137
1138
local.get $object
1139
struct.get $thing $field2
1140
drop
1141
)
1142
)
1143
"#,
1144
)?;
1145
1146
let mut store = Store::new(&engine, ());
1147
let instance = Instance::new(&mut store, &module, &[])?;
1148
1149
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1150
func.call(&mut store, ())?;
1151
1152
Ok(())
1153
}
1154
1155
#[test]
1156
fn drc_transitive_drop_cons_list() -> Result<()> {
1157
let _ = env_logger::try_init();
1158
1159
let mut config = Config::new();
1160
config.wasm_function_references(true);
1161
config.wasm_gc(true);
1162
config.collector(Collector::DeferredReferenceCounting);
1163
1164
let engine = Engine::new(&config)?;
1165
1166
// Define a module that defines a recursive type (a `cons` list of
1167
// `externref`s) and exports a global of that type so that we can access it
1168
// from the host, because we don't yet have host APIs for defining recursive
1169
// types or rec groups.
1170
let module = Module::new(
1171
&engine,
1172
r#"
1173
(module
1174
(type $cons (struct (field externref) (field (ref null $cons))))
1175
(global (export "g") (ref null $cons) (ref.null $cons))
1176
)
1177
"#,
1178
)?;
1179
1180
let export = module.exports().nth(0).unwrap().ty();
1181
let global = export.unwrap_global();
1182
let ref_ty = global.content().unwrap_ref();
1183
let struct_ty = ref_ty.heap_type().unwrap_concrete_struct();
1184
1185
let mut store = Store::new(&engine, ());
1186
1187
let pre = StructRefPre::new(&mut store, struct_ty.clone());
1188
let num_refs_dropped = Arc::new(AtomicUsize::new(0));
1189
1190
let len = if cfg!(miri) { 2 } else { 100 };
1191
{
1192
let mut store = RootScope::new(&mut store);
1193
1194
let mut cdr = None;
1195
for _ in 0..len {
1196
let externref = ExternRef::new(&mut store, CountDrops(num_refs_dropped.clone()))?;
1197
let cons = StructRef::new(&mut store, &pre, &[externref.into(), cdr.into()])?;
1198
cdr = Some(cons);
1199
}
1200
1201
// Still holding the cons list alive at this point.
1202
assert_eq!(num_refs_dropped.load(SeqCst), 0);
1203
}
1204
1205
// Not holding the cons list alive anymore; should transitively drop
1206
// everything we created.
1207
store.gc(None)?;
1208
assert_eq!(num_refs_dropped.load(SeqCst), len);
1209
1210
Ok(())
1211
}
1212
1213
#[test]
1214
fn drc_transitive_drop_nested_arrays_tree() -> Result<()> {
1215
let _ = env_logger::try_init();
1216
1217
let mut config = Config::new();
1218
config.wasm_function_references(true);
1219
config.wasm_gc(true);
1220
config.collector(Collector::DeferredReferenceCounting);
1221
1222
let engine = Engine::new(&config)?;
1223
1224
let array_ty = ArrayType::new(
1225
&engine,
1226
FieldType::new(
1227
Mutability::Var,
1228
StorageType::ValType(ValType::Ref(RefType::ANYREF)),
1229
),
1230
);
1231
1232
let mut store = Store::new(&engine, ());
1233
let pre = ArrayRefPre::new(&mut store, array_ty);
1234
let num_refs_dropped = Arc::new(AtomicUsize::new(0));
1235
let mut expected = 0;
1236
1237
fn recursively_build_tree(
1238
mut store: &mut RootScope<&mut Store<()>>,
1239
pre: &ArrayRefPre,
1240
num_refs_dropped: &Arc<AtomicUsize>,
1241
expected: &mut usize,
1242
depth: u32,
1243
) -> Result<Rooted<AnyRef>> {
1244
let max = if cfg!(miri) { 1 } else { 3 };
1245
if depth >= max {
1246
*expected += 1;
1247
let e = ExternRef::new(&mut store, CountDrops(num_refs_dropped.clone()))?;
1248
AnyRef::convert_extern(&mut store, e)
1249
} else {
1250
let left = recursively_build_tree(store, pre, num_refs_dropped, expected, depth + 1)?;
1251
let right = recursively_build_tree(store, pre, num_refs_dropped, expected, depth + 1)?;
1252
let arr = ArrayRef::new_fixed(store, pre, &[left.into(), right.into()])?;
1253
Ok(arr.to_anyref())
1254
}
1255
}
1256
1257
{
1258
let mut store = RootScope::new(&mut store);
1259
let _tree = recursively_build_tree(&mut store, &pre, &num_refs_dropped, &mut expected, 0)?;
1260
1261
// Still holding the tree alive at this point.
1262
assert_eq!(num_refs_dropped.load(SeqCst), 0);
1263
}
1264
1265
// Not holding the tree alive anymore; should transitively drop everything
1266
// we created.
1267
store.gc(None)?;
1268
assert_eq!(num_refs_dropped.load(SeqCst), expected);
1269
1270
Ok(())
1271
}
1272
1273
#[test]
1274
#[cfg_attr(miri, ignore)]
1275
fn drc_traces_the_correct_number_of_gc_refs_in_arrays() -> Result<()> {
1276
let _ = env_logger::try_init();
1277
1278
let mut config = Config::new();
1279
config.wasm_function_references(true);
1280
config.wasm_gc(true);
1281
config.collector(Collector::DeferredReferenceCounting);
1282
1283
let engine = Engine::new(&config)?;
1284
let mut store = Store::new(&engine, ());
1285
1286
// The DRC collector was mistakenly reporting that arrays of GC refs had
1287
// `size_of(elems)` outgoing edges, rather than `len(elems)` edges. None of
1288
// our existing tests happened to trigger this bug because although we were
1289
// tricking the collector into tracing unallocated GC heap memory, it was
1290
// all zeroed out and was treated as null GC references. We can avoid that
1291
// in this regression test by first painting the heap with a large poison
1292
// value before we start allocating arrays, so that if the GC tries tracing
1293
// bogus heap memory, it finds very large GC ref heap indices and ultimately
1294
// tries to follow them outside the bounds of the GC heap, which (before
1295
// this bug was fixed) would lead to a panic.
1296
1297
let array_i8_ty = ArrayType::new(&engine, FieldType::new(Mutability::Var, StorageType::I8));
1298
let array_i8_pre = ArrayRefPre::new(&mut store, array_i8_ty);
1299
1300
{
1301
let mut store = RootScope::new(&mut store);
1302
1303
// Spray a poison pattern across the heap.
1304
let len = 1_000_000;
1305
let _poison = ArrayRef::new(&mut store, &array_i8_pre, &Val::I32(-1), len);
1306
}
1307
1308
// Make sure the poison array is collected.
1309
store.gc(None)?;
1310
1311
// Allocate and then collect an array of GC refs from Wasm. This should not
1312
// trick the collector into tracing any poison and panicking.
1313
let module = Module::new(
1314
&engine,
1315
r#"
1316
(module
1317
(type $ty (array (mut anyref)))
1318
(start $f)
1319
(func $f
1320
(drop (array.new $ty (ref.null any) (i32.const 1_000)))
1321
)
1322
)
1323
"#,
1324
)?;
1325
let _instance = Instance::new(&mut store, &module, &[])?;
1326
store.gc(None)?;
1327
1328
Ok(())
1329
}
1330
1331
// Test that we can completely fill the GC heap until we get an OOM. This
1332
// exercises growing the GC heap and that we configure compilation tunables and
1333
// runtime memories backing GC heaps correctly.
1334
#[test]
1335
#[cfg_attr(any(miri, not(target_pointer_width = "64")), ignore)]
1336
fn gc_heap_oom() -> Result<()> {
1337
if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
1338
return Ok(());
1339
}
1340
1341
let _ = env_logger::try_init();
1342
1343
for heap_size in [
1344
// Very small heap.
1345
1 << 16,
1346
// Bigger heap: 4 GiB
1347
1 << 32,
1348
] {
1349
for pooling in [true, false] {
1350
let mut config = Config::new();
1351
config.wasm_function_references(true);
1352
config.wasm_gc(true);
1353
config.collector(Collector::Null);
1354
config.memory_reservation(heap_size);
1355
config.memory_reservation_for_growth(0);
1356
config.memory_guard_size(0);
1357
config.memory_may_move(false);
1358
1359
if pooling {
1360
let mut pooling = crate::small_pool_config();
1361
pooling.max_memory_size(heap_size.try_into().unwrap());
1362
config.allocation_strategy(InstanceAllocationStrategy::Pooling(pooling));
1363
}
1364
1365
let engine = Engine::new(&config)?;
1366
1367
let module = Module::new(
1368
&engine,
1369
r#"
1370
(module
1371
(type $s (struct))
1372
(global $g (export "g") (mut i32) (i32.const 0))
1373
(func (export "run")
1374
loop
1375
struct.new $s
1376
1377
global.get $g
1378
i32.const 1
1379
i32.add
1380
global.set $g
1381
1382
br 0
1383
end
1384
)
1385
)
1386
"#,
1387
)?;
1388
1389
let mut store = Store::new(&engine, ());
1390
let instance = Instance::new(&mut store, &module, &[])?;
1391
1392
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1393
let err = run.call(&mut store, ()).expect_err("should oom");
1394
assert!(err.is::<Trap>(), "should get trap, got: {err:?}");
1395
let trap = err.downcast::<Trap>().unwrap();
1396
assert_eq!(trap, Trap::AllocationTooLarge);
1397
1398
let g = instance.get_global(&mut store, "g").unwrap();
1399
const SIZE_OF_NULL_GC_HEADER: u64 = 8;
1400
const FUDGE: u64 = 2;
1401
let actual = g.get(&mut store).unwrap_i32() as u64;
1402
let expected = heap_size / SIZE_OF_NULL_GC_HEADER;
1403
assert!(
1404
actual.abs_diff(expected) <= FUDGE,
1405
"actual approx= expected failed: \
1406
actual = {actual}, expected = {expected}, FUDGE={FUDGE}"
1407
);
1408
}
1409
}
1410
Ok(())
1411
}
1412
1413
#[test]
1414
#[cfg_attr(miri, ignore)]
1415
fn issue_10772() -> Result<()> {
1416
let mut store = crate::gc_store()?;
1417
let engine = store.engine().clone();
1418
1419
let module = Module::new(
1420
&engine,
1421
r#"
1422
(module
1423
(type $empty (struct))
1424
(type $tuple-concrete (struct (field (ref $empty))))
1425
(type $tuple-abstract (struct (field (ref struct))))
1426
(func (export "abstract") (param $t (ref $tuple-abstract))
1427
(drop (ref.cast (ref $tuple-concrete) (local.get $t)))
1428
)
1429
)
1430
"#,
1431
)?;
1432
1433
let linker = Linker::new(&engine);
1434
1435
let instance = linker.instantiate(&mut store, &module)?;
1436
let abstract_ = instance.get_func(&mut store, "abstract").unwrap();
1437
let empty_pre = StructRefPre::new(&mut store, StructType::new(&engine, [])?);
1438
let empty_struct = StructRef::new(&mut store, &empty_pre, &[])?;
1439
let tuple_pre = StructRefPre::new(
1440
&mut store,
1441
StructType::new(
1442
&engine,
1443
[FieldType::new(
1444
Mutability::Const,
1445
StorageType::ValType(ValType::Ref(RefType::new(false, HeapType::Struct))),
1446
)],
1447
)?,
1448
);
1449
let tuple_struct = StructRef::new(&mut store, &tuple_pre, &[empty_struct.into()])?;
1450
let tuple_any = Val::from(tuple_struct);
1451
1452
match abstract_.call(store, &[tuple_any], &mut []) {
1453
Ok(()) => panic!("should have trapped on cast failure"),
1454
Err(e) => {
1455
let trap = e.downcast::<Trap>().expect("should fail with a trap");
1456
assert_eq!(trap, Trap::CastFailure);
1457
}
1458
}
1459
1460
Ok(())
1461
}
1462
1463
#[test]
1464
fn drc_gc_inbetween_host_calls() -> Result<()> {
1465
let _ = env_logger::try_init();
1466
1467
let mut config = Config::new();
1468
config.wasm_function_references(true);
1469
config.wasm_gc(true);
1470
config.collector(Collector::DeferredReferenceCounting);
1471
1472
let engine = Engine::new(&config)?;
1473
1474
let mut store = Store::new(&engine, ());
1475
let func = Func::wrap(&mut store, |_: Option<Rooted<ExternRef>>| {});
1476
1477
let mut invoke_func = || {
1478
let inner_dropped = Arc::new(AtomicBool::new(false));
1479
{
1480
let mut scope = RootScope::new(&mut store);
1481
let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1482
func.call(&mut scope, &[r.into()], &mut [])?;
1483
}
1484
1485
assert!(!inner_dropped.load(SeqCst));
1486
store.gc(None)?;
1487
assert!(inner_dropped.load(SeqCst));
1488
wasmtime::error::Ok(())
1489
};
1490
1491
invoke_func()?;
1492
invoke_func()?;
1493
1494
Ok(())
1495
}
1496
1497
#[test]
1498
fn owned_rooted() -> Result<()> {
1499
let _ = env_logger::try_init();
1500
1501
let mut config = Config::new();
1502
config.wasm_function_references(true);
1503
config.wasm_gc(true);
1504
config.collector(Collector::DeferredReferenceCounting);
1505
1506
let engine = Engine::new(&config)?;
1507
1508
let mut store = Store::new(&engine, ());
1509
let inner_dropped = Arc::new(AtomicBool::new(false));
1510
let r = {
1511
let mut scope = RootScope::new(&mut store);
1512
let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1513
r.to_owned_rooted(&mut scope)?
1514
};
1515
assert!(!inner_dropped.load(SeqCst));
1516
store.gc(None)?;
1517
assert!(!inner_dropped.load(SeqCst));
1518
let r2 = r.clone();
1519
store.gc(None)?;
1520
assert!(!inner_dropped.load(SeqCst));
1521
drop(r);
1522
store.gc(None)?;
1523
assert!(!inner_dropped.load(SeqCst));
1524
drop(r2);
1525
store.gc(None)?;
1526
assert!(inner_dropped.load(SeqCst));
1527
1528
Ok(())
1529
}
1530
1531
#[test]
1532
#[cfg_attr(miri, ignore)]
1533
fn owned_rooted_lots_of_root_creation() -> Result<()> {
1534
let mut config = Config::new();
1535
config.wasm_function_references(true);
1536
config.wasm_gc(true);
1537
config.collector(Collector::DeferredReferenceCounting);
1538
1539
let engine = Engine::new(&config)?;
1540
1541
let mut store = Store::new(&engine, ());
1542
let inner_dropped = Arc::new(AtomicBool::new(false));
1543
let r = {
1544
let mut scope = RootScope::new(&mut store);
1545
let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1546
r.to_owned_rooted(&mut scope)?
1547
};
1548
assert!(!inner_dropped.load(SeqCst));
1549
store.gc(None)?;
1550
1551
for _ in 0..100_000 {
1552
let mut scope = RootScope::new(&mut store);
1553
// Go through a LIFO root then back to an owned root to create
1554
// a distinct root.
1555
let r2 = r.to_rooted(&mut scope);
1556
let r3 = r2.to_owned_rooted(&mut scope);
1557
drop(r3);
1558
}
1559
1560
store.gc(None)?;
1561
assert!(!inner_dropped.load(SeqCst));
1562
1563
drop(r);
1564
store.gc(None)?;
1565
assert!(inner_dropped.load(SeqCst));
1566
1567
Ok(())
1568
}
1569
1570
#[test]
1571
#[cfg_attr(miri, ignore)]
1572
fn runtime_table_init_oom() -> Result<()> {
1573
let mut config = Config::new();
1574
config.wasm_gc(true);
1575
config.wasm_function_references(true);
1576
config.memory_may_move(false);
1577
config.memory_reservation(64 << 10);
1578
config.memory_reservation_for_growth(0);
1579
let engine = Engine::new(&config)?;
1580
let mut store = Store::new(&engine, ());
1581
1582
let module = Module::new(
1583
store.engine(),
1584
r#"
1585
(module
1586
(table 100 arrayref)
1587
1588
(type $a (array i31ref))
1589
1590
(func (export "run")
1591
i32.const 0
1592
i32.const 0
1593
i32.const 5
1594
table.init $e)
1595
(elem $e arrayref
1596
(array.new_default $a (i32.const 100))
1597
(array.new_default $a (i32.const 10000))
1598
(array.new_default $a (i32.const 10000))
1599
(array.new_default $a (i32.const 10000))
1600
(array.new_default $a (i32.const 1000000))
1601
)
1602
)
1603
"#,
1604
)?;
1605
1606
let instance = Instance::new(&mut store, &module, &[])?;
1607
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1608
func.call(&mut store, ())
1609
.unwrap_err()
1610
.downcast::<GcHeapOutOfMemory<()>>()?;
1611
1612
Ok(())
1613
}
1614
1615
#[test]
1616
#[cfg_attr(miri, ignore)]
1617
fn instantiate_table_init_oom() -> Result<()> {
1618
let mut config = Config::new();
1619
config.wasm_gc(true);
1620
config.wasm_function_references(true);
1621
config.memory_may_move(false);
1622
config.memory_reservation(64 << 10);
1623
config.memory_reservation_for_growth(0);
1624
let engine = Engine::new(&config)?;
1625
let mut store = Store::new(&engine, ());
1626
1627
let module = Module::new(
1628
store.engine(),
1629
r#"
1630
(module
1631
(table 100 arrayref)
1632
1633
(type $a (array i31ref))
1634
1635
(elem (i32.const 0) arrayref
1636
(array.new_default $a (i32.const 100))
1637
(array.new_default $a (i32.const 10000))
1638
(array.new_default $a (i32.const 10000))
1639
(array.new_default $a (i32.const 10000))
1640
(array.new_default $a (i32.const 1000000))
1641
)
1642
)
1643
"#,
1644
)?;
1645
1646
Instance::new(&mut store, &module, &[])
1647
.unwrap_err()
1648
.downcast::<GcHeapOutOfMemory<()>>()?;
1649
1650
Ok(())
1651
}
1652
1653
#[test]
1654
#[cfg_attr(miri, ignore)]
1655
fn instantiate_table_init_expr_oom() -> Result<()> {
1656
let mut config = Config::new();
1657
config.wasm_gc(true);
1658
config.wasm_function_references(true);
1659
config.memory_may_move(false);
1660
config.memory_reservation(64 << 10);
1661
config.memory_reservation_for_growth(0);
1662
let engine = Engine::new(&config)?;
1663
let mut store = Store::new(&engine, ());
1664
1665
let module = Module::new(
1666
store.engine(),
1667
r#"
1668
(module
1669
(type $a (array i31ref))
1670
(table 100 (ref $a) (array.new_default $a (i32.const 100000)))
1671
)
1672
"#,
1673
)?;
1674
1675
Instance::new(&mut store, &module, &[])
1676
.unwrap_err()
1677
.downcast::<GcHeapOutOfMemory<()>>()?;
1678
1679
Ok(())
1680
}
1681
1682
#[test]
1683
#[cfg_attr(miri, ignore)]
1684
fn instantiate_global_init_oom() -> Result<()> {
1685
let mut config = Config::new();
1686
config.wasm_gc(true);
1687
config.wasm_function_references(true);
1688
config.memory_may_move(false);
1689
config.memory_reservation(64 << 10);
1690
config.memory_reservation_for_growth(0);
1691
let engine = Engine::new(&config)?;
1692
let mut store = Store::new(&engine, ());
1693
1694
let module = Module::new(
1695
store.engine(),
1696
r#"
1697
(module
1698
(table 100 arrayref)
1699
(type $a (array i31ref))
1700
(global (ref $a) (array.new_default $a (i32.const 10000000)))
1701
)
1702
"#,
1703
)?;
1704
1705
Instance::new(&mut store, &module, &[])
1706
.unwrap_err()
1707
.downcast::<GcHeapOutOfMemory<()>>()?;
1708
1709
Ok(())
1710
}
1711
1712
#[test]
1713
#[cfg_attr(miri, ignore)]
1714
fn array_new_elem_oom() -> Result<()> {
1715
let mut config = Config::new();
1716
config.wasm_gc(true);
1717
config.wasm_function_references(true);
1718
config.memory_may_move(false);
1719
config.memory_reservation(64 << 10);
1720
config.memory_reservation_for_growth(0);
1721
let engine = Engine::new(&config)?;
1722
let mut store = Store::new(&engine, ());
1723
1724
let module = Module::new(
1725
store.engine(),
1726
r#"
1727
(module
1728
(type $a (array (mut arrayref)))
1729
(type $i (array i31ref))
1730
1731
(func (export "run")
1732
i32.const 0
1733
i32.const 5
1734
array.new_elem $a $e
1735
drop)
1736
1737
(elem $e arrayref
1738
(array.new_default $i (i32.const 100))
1739
(array.new_default $i (i32.const 10000))
1740
(array.new_default $i (i32.const 10000))
1741
(array.new_default $i (i32.const 10000))
1742
(array.new_default $i (i32.const 1000000))
1743
)
1744
)
1745
"#,
1746
)?;
1747
1748
let instance = Instance::new(&mut store, &module, &[])?;
1749
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1750
func.call(&mut store, ())
1751
.unwrap_err()
1752
.downcast::<GcHeapOutOfMemory<()>>()?;
1753
1754
Ok(())
1755
}
1756
1757
#[test]
1758
#[cfg_attr(miri, ignore)]
1759
fn array_init_elem_oom() -> Result<()> {
1760
let mut config = Config::new();
1761
config.wasm_gc(true);
1762
config.wasm_function_references(true);
1763
config.memory_may_move(false);
1764
config.memory_reservation(64 << 10);
1765
config.memory_reservation_for_growth(0);
1766
let engine = Engine::new(&config)?;
1767
let mut store = Store::new(&engine, ());
1768
1769
let module = Module::new(
1770
store.engine(),
1771
r#"
1772
(module
1773
(type $a (array (mut arrayref)))
1774
(type $i (array i31ref))
1775
1776
(func (export "run")
1777
i32.const 5
1778
array.new_default $a
1779
i32.const 0
1780
i32.const 0
1781
i32.const 5
1782
array.init_elem $a $e)
1783
1784
(elem $e arrayref
1785
(array.new_default $i (i32.const 100))
1786
(array.new_default $i (i32.const 10000))
1787
(array.new_default $i (i32.const 10000))
1788
(array.new_default $i (i32.const 10000))
1789
(array.new_default $i (i32.const 1000000))
1790
)
1791
)
1792
"#,
1793
)?;
1794
1795
let instance = Instance::new(&mut store, &module, &[])?;
1796
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1797
func.call(&mut store, ())
1798
.unwrap_err()
1799
.downcast::<GcHeapOutOfMemory<()>>()?;
1800
1801
Ok(())
1802
}
1803
1804