Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/gc.rs
1691 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<()>| {
640
caller.gc(None);
641
})?;
642
643
let instance = linker.instantiate(&mut store, &module)?;
644
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
645
func.call(&mut store, ())?;
646
647
Ok(())
648
}
649
650
#[test]
651
#[cfg_attr(miri, ignore)]
652
fn no_leak_with_global_get_elem_segment() -> anyhow::Result<()> {
653
let dropped = Arc::new(AtomicBool::new(false));
654
655
let engine = Engine::default();
656
let mut store = Store::new(&engine, ());
657
let module = Module::new(
658
&engine,
659
r#"
660
(module
661
(import "" "" (global $init externref))
662
(start $f)
663
(table $t 1 externref)
664
(elem $e externref (global.get $init))
665
666
(func $f
667
i32.const 0
668
i32.const 0
669
i32.const 1
670
table.init $t $e
671
672
i32.const 0
673
i32.const 0
674
i32.const 1
675
table.init $t $e
676
)
677
)
678
"#,
679
)?;
680
681
let externref = ExternRef::new(&mut store, SetFlagOnDrop(dropped.clone()))?;
682
let global = Global::new(
683
&mut store,
684
GlobalType::new(ValType::EXTERNREF, Mutability::Const),
685
externref.into(),
686
)?;
687
688
Instance::new(&mut store, &module, &[global.into()])?;
689
690
drop(store);
691
692
assert!(dropped.load(SeqCst));
693
Ok(())
694
}
695
696
#[test]
697
#[cfg_attr(miri, ignore)]
698
fn table_init_with_externref_global_get() -> anyhow::Result<()> {
699
let dropped = Arc::new(AtomicBool::new(false));
700
701
let mut config = Config::new();
702
config.wasm_function_references(true);
703
let engine = Engine::new(&config)?;
704
let mut store = Store::new(&engine, ());
705
let module = Module::new(
706
&engine,
707
r#"
708
(module
709
(import "" "" (global $init externref))
710
(table $t 1 externref (global.get $init))
711
)
712
"#,
713
)?;
714
715
let externref = ExternRef::new(&mut store, SetFlagOnDrop(dropped.clone()))?;
716
let global = Global::new(
717
&mut store,
718
GlobalType::new(ValType::EXTERNREF, Mutability::Const),
719
externref.into(),
720
)?;
721
722
Instance::new(&mut store, &module, &[global.into()])?;
723
724
drop(store);
725
726
assert!(dropped.load(SeqCst));
727
Ok(())
728
}
729
730
#[test]
731
fn rooted_gets_collected_after_scope_exit() -> Result<()> {
732
let mut store = Store::<()>::default();
733
let flag = Arc::new(AtomicBool::new(false));
734
735
{
736
let mut scope = RootScope::new(&mut store);
737
let _externref = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
738
739
scope.as_context_mut().gc(None);
740
assert!(!flag.load(SeqCst), "not dropped when still rooted");
741
}
742
743
store.as_context_mut().gc(None);
744
assert!(flag.load(SeqCst), "dropped after being unrooted");
745
746
Ok(())
747
}
748
749
#[test]
750
fn owned_rooted_gets_collected_after_unrooting() -> Result<()> {
751
let mut store = Store::<()>::default();
752
let flag = Arc::new(AtomicBool::new(false));
753
754
let externref = {
755
let mut scope = RootScope::new(&mut store);
756
ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?.to_owned_rooted(&mut scope)?
757
};
758
759
store.gc(None);
760
assert!(!flag.load(SeqCst), "not dropped when still rooted");
761
762
drop(externref);
763
store.gc(None);
764
assert!(flag.load(SeqCst), "dropped after being unrooted");
765
766
Ok(())
767
}
768
769
#[test]
770
#[cfg_attr(miri, ignore)]
771
fn round_trip_gc_ref_through_typed_wasm_func() -> Result<()> {
772
let mut store = Store::<()>::default();
773
let module = Module::new(
774
store.engine(),
775
r#"
776
(module
777
(import "" "" (func $gc))
778
(func (export "f") (param externref) (result externref)
779
call $gc
780
local.get 0
781
)
782
)
783
"#,
784
)?;
785
let gc = Func::wrap(&mut store, |mut caller: Caller<'_, _>| caller.gc(None));
786
let instance = Instance::new(&mut store, &module, &[gc.into()])?;
787
let f = instance
788
.get_typed_func::<Option<Rooted<ExternRef>>, Option<Rooted<ExternRef>>>(&mut store, "f")?;
789
let x1 = ExternRef::new(&mut store, 1234)?;
790
let x2 = f.call(&mut store, Some(x1))?.unwrap();
791
assert!(Rooted::ref_eq(&store, &x1, &x2)?);
792
Ok(())
793
}
794
795
#[test]
796
#[cfg_attr(miri, ignore)]
797
fn round_trip_gc_ref_through_func_wrap() -> Result<()> {
798
let mut store = Store::<()>::default();
799
let f = Func::wrap(
800
&mut store,
801
|mut caller: Caller<'_, _>, x: Rooted<ExternRef>| {
802
caller.gc(None);
803
x
804
},
805
);
806
let f = f.typed::<Rooted<ExternRef>, Rooted<ExternRef>>(&store)?;
807
let x1 = ExternRef::new(&mut store, 1234)?;
808
let x2 = f.call(&mut store, x1)?;
809
assert!(Rooted::ref_eq(&store, &x1, &x2)?);
810
Ok(())
811
}
812
813
#[test]
814
fn to_raw_from_raw_doesnt_leak() -> Result<()> {
815
let mut store = Store::<()>::default();
816
let flag = Arc::new(AtomicBool::new(false));
817
818
{
819
let mut scope = RootScope::new(&mut store);
820
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
821
let raw = x.to_raw(&mut scope)?;
822
let _x = ExternRef::from_raw(&mut scope, raw);
823
}
824
825
store.gc(None);
826
assert!(flag.load(SeqCst));
827
Ok(())
828
}
829
830
#[test]
831
fn table_fill_doesnt_leak() -> Result<()> {
832
let _ = env_logger::try_init();
833
834
let mut store = Store::<()>::default();
835
let flag = Arc::new(AtomicBool::new(false));
836
837
{
838
let mut scope = RootScope::new(&mut store);
839
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
840
let table = Table::new(
841
&mut scope,
842
TableType::new(RefType::EXTERNREF, 10, Some(10)),
843
x.into(),
844
)?;
845
table.fill(&mut scope, 0, Ref::Extern(None), 10)?;
846
}
847
848
store.gc(None);
849
assert!(flag.load(SeqCst));
850
Ok(())
851
}
852
853
#[test]
854
#[cfg_attr(miri, ignore)]
855
fn table_copy_doesnt_leak() -> Result<()> {
856
let _ = env_logger::try_init();
857
858
let mut store = Store::<()>::default();
859
let flag = Arc::new(AtomicBool::new(false));
860
861
{
862
let mut scope = RootScope::new(&mut store);
863
let table = Table::new(
864
&mut scope,
865
TableType::new(RefType::EXTERNREF, 10, Some(10)),
866
Ref::Extern(None),
867
)?;
868
869
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
870
table.fill(&mut scope, 2, x.into(), 3)?;
871
872
Table::copy(&mut scope, &table, 0, &table, 5, 5)?;
873
}
874
875
store.gc(None);
876
assert!(flag.load(SeqCst));
877
Ok(())
878
}
879
880
#[test]
881
#[cfg_attr(miri, ignore)]
882
fn table_set_doesnt_leak() -> Result<()> {
883
let _ = env_logger::try_init();
884
885
let mut store = Store::<()>::default();
886
let flag = Arc::new(AtomicBool::new(false));
887
888
{
889
let mut scope = RootScope::new(&mut store);
890
let table = Table::new(
891
&mut scope,
892
TableType::new(RefType::EXTERNREF, 10, Some(10)),
893
Ref::Extern(None),
894
)?;
895
896
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
897
table.set(&mut scope, 2, x.into())?;
898
table.set(&mut scope, 2, x.into())?;
899
table.set(&mut scope, 2, Ref::Extern(None))?;
900
}
901
902
store.gc(None);
903
assert!(flag.load(SeqCst));
904
Ok(())
905
}
906
907
#[test]
908
#[cfg_attr(miri, ignore)]
909
fn table_grow_doesnt_leak() -> Result<()> {
910
let _ = env_logger::try_init();
911
912
let mut store = Store::<()>::default();
913
let flag = Arc::new(AtomicBool::new(false));
914
915
{
916
let mut scope = RootScope::new(&mut store);
917
let table = Table::new(
918
&mut scope,
919
TableType::new(RefType::EXTERNREF, 10, Some(10)),
920
Ref::Extern(None),
921
)?;
922
923
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
924
table.grow(&mut scope, 0, x.into())?;
925
}
926
927
store.gc(None);
928
assert!(flag.load(SeqCst));
929
Ok(())
930
}
931
932
// This is a test that the argument to `table.init` is properly handled w.r.t.
933
// write barriers and such. This doesn't use `SetFlagOnDrop` because that would
934
// require initializing a table with an element initialized from a global and
935
// the global keeps the externref alive regardless. Instead this has a small GC
936
// heap and we continuously make more garbage than is in the heap and expect
937
// this to work as when it triggers a GC everything prior should get cleaned up.
938
#[test]
939
#[cfg_attr(miri, ignore)]
940
fn table_init_doesnt_leak() -> Result<()> {
941
const SIZE: u64 = 64 << 10;
942
943
let _ = env_logger::try_init();
944
945
let mut config = Config::new();
946
config.wasm_gc(true);
947
config.wasm_function_references(true);
948
config.memory_may_move(false);
949
config.memory_reservation(SIZE);
950
config.memory_reservation_for_growth(0);
951
let engine = Engine::new(&config)?;
952
let mut store = Store::new(&engine, ());
953
954
let module = Module::new(
955
store.engine(),
956
r#"
957
(module
958
(table 1 arrayref)
959
960
(type $a (array i31ref))
961
962
(func (export "run")
963
i32.const 0
964
i32.const 0
965
i32.const 1
966
table.init $e)
967
(elem $e arrayref (array.new $a (ref.i31 (i32.const 0)) (i32.const 200)))
968
)
969
"#,
970
)?;
971
972
let instance = Instance::new(&mut store, &module, &[])?;
973
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
974
for _ in 0..200 {
975
func.call(&mut store, ())?;
976
}
977
978
Ok(())
979
}
980
981
#[test]
982
fn ref_matches() -> Result<()> {
983
let mut store = Store::<()>::default();
984
let engine = store.engine().clone();
985
986
let func_ty = FuncType::new(&engine, None, None);
987
let func_ref_ty = RefType::new(true, HeapType::ConcreteFunc(func_ty.clone()));
988
let f = Func::new(&mut store, func_ty, |_, _, _| Ok(()));
989
990
let pre = StructRefPre::new(&mut store, StructType::new(&engine, [])?);
991
let s = StructRef::new(&mut store, &pre, &[])?.to_anyref();
992
993
let pre = ArrayRefPre::new(
994
&mut store,
995
ArrayType::new(&engine, FieldType::new(Mutability::Const, StorageType::I8)),
996
);
997
let a = ArrayRef::new(&mut store, &pre, &Val::I32(0), 0)?.to_anyref();
998
999
let i31 = AnyRef::from_i31(&mut store, I31::wrapping_i32(1234));
1000
1001
let e = ExternRef::new(&mut store, "hello")?;
1002
1003
for (val, ty, expected) in [
1004
// nulls to nullexternref
1005
(Ref::Extern(None), RefType::NULLEXTERNREF, true),
1006
(Ref::Any(None), RefType::NULLEXTERNREF, false),
1007
(Ref::Func(None), RefType::NULLEXTERNREF, false),
1008
// nulls to externref
1009
(Ref::Extern(None), RefType::EXTERNREF, true),
1010
(Ref::Any(None), RefType::EXTERNREF, false),
1011
(Ref::Func(None), RefType::EXTERNREF, false),
1012
// nulls to nullref
1013
(Ref::Extern(None), RefType::NULLREF, false),
1014
(Ref::Any(None), RefType::NULLREF, true),
1015
(Ref::Func(None), RefType::NULLREF, false),
1016
// nulls to structref
1017
(Ref::Extern(None), RefType::STRUCTREF, false),
1018
(Ref::Any(None), RefType::STRUCTREF, true),
1019
(Ref::Func(None), RefType::STRUCTREF, false),
1020
// nulls to arrayref
1021
(Ref::Extern(None), RefType::ARRAYREF, false),
1022
(Ref::Any(None), RefType::ARRAYREF, true),
1023
(Ref::Func(None), RefType::ARRAYREF, false),
1024
// nulls to i31ref
1025
(Ref::Extern(None), RefType::I31REF, false),
1026
(Ref::Any(None), RefType::I31REF, true),
1027
(Ref::Func(None), RefType::I31REF, false),
1028
// nulls to eqref
1029
(Ref::Extern(None), RefType::EQREF, false),
1030
(Ref::Any(None), RefType::EQREF, true),
1031
(Ref::Func(None), RefType::EQREF, false),
1032
// nulls to anyref
1033
(Ref::Extern(None), RefType::ANYREF, false),
1034
(Ref::Any(None), RefType::ANYREF, true),
1035
(Ref::Func(None), RefType::ANYREF, false),
1036
// non-null structref
1037
(Ref::Any(Some(s)), RefType::NULLFUNCREF, false),
1038
(Ref::Any(Some(s)), func_ref_ty.clone(), false),
1039
(Ref::Any(Some(s)), RefType::FUNCREF, false),
1040
(Ref::Any(Some(s)), RefType::NULLEXTERNREF, false),
1041
(Ref::Any(Some(s)), RefType::EXTERNREF, false),
1042
(Ref::Any(Some(s)), RefType::NULLREF, false),
1043
(Ref::Any(Some(s)), RefType::STRUCTREF, true),
1044
(Ref::Any(Some(s)), RefType::ARRAYREF, false),
1045
(Ref::Any(Some(s)), RefType::I31REF, false),
1046
(Ref::Any(Some(s)), RefType::EQREF, true),
1047
(Ref::Any(Some(s)), RefType::ANYREF, true),
1048
// non-null arrayref
1049
(Ref::Any(Some(a)), RefType::NULLFUNCREF, false),
1050
(Ref::Any(Some(a)), func_ref_ty.clone(), false),
1051
(Ref::Any(Some(a)), RefType::FUNCREF, false),
1052
(Ref::Any(Some(a)), RefType::NULLEXTERNREF, false),
1053
(Ref::Any(Some(a)), RefType::EXTERNREF, false),
1054
(Ref::Any(Some(a)), RefType::NULLREF, false),
1055
(Ref::Any(Some(a)), RefType::STRUCTREF, false),
1056
(Ref::Any(Some(a)), RefType::ARRAYREF, true),
1057
(Ref::Any(Some(a)), RefType::I31REF, false),
1058
(Ref::Any(Some(a)), RefType::EQREF, true),
1059
(Ref::Any(Some(a)), RefType::ANYREF, true),
1060
// non-null i31ref
1061
(Ref::Any(Some(i31)), RefType::NULLFUNCREF, false),
1062
(Ref::Any(Some(i31)), func_ref_ty.clone(), false),
1063
(Ref::Any(Some(i31)), RefType::FUNCREF, false),
1064
(Ref::Any(Some(i31)), RefType::NULLEXTERNREF, false),
1065
(Ref::Any(Some(i31)), RefType::EXTERNREF, false),
1066
(Ref::Any(Some(i31)), RefType::NULLREF, false),
1067
(Ref::Any(Some(i31)), RefType::STRUCTREF, false),
1068
(Ref::Any(Some(i31)), RefType::ARRAYREF, false),
1069
(Ref::Any(Some(i31)), RefType::I31REF, true),
1070
(Ref::Any(Some(i31)), RefType::EQREF, true),
1071
(Ref::Any(Some(i31)), RefType::ANYREF, true),
1072
// non-null funcref
1073
(Ref::Func(Some(f)), RefType::NULLFUNCREF, false),
1074
(Ref::Func(Some(f)), func_ref_ty.clone(), true),
1075
(Ref::Func(Some(f)), RefType::FUNCREF, true),
1076
(Ref::Func(Some(f)), RefType::NULLEXTERNREF, false),
1077
(Ref::Func(Some(f)), RefType::EXTERNREF, false),
1078
(Ref::Func(Some(f)), RefType::NULLREF, false),
1079
(Ref::Func(Some(f)), RefType::STRUCTREF, false),
1080
(Ref::Func(Some(f)), RefType::ARRAYREF, false),
1081
(Ref::Func(Some(f)), RefType::I31REF, false),
1082
(Ref::Func(Some(f)), RefType::EQREF, false),
1083
(Ref::Func(Some(f)), RefType::ANYREF, false),
1084
// non-null externref
1085
(Ref::Extern(Some(e)), RefType::NULLFUNCREF, false),
1086
(Ref::Extern(Some(e)), func_ref_ty.clone(), false),
1087
(Ref::Extern(Some(e)), RefType::FUNCREF, false),
1088
(Ref::Extern(Some(e)), RefType::NULLEXTERNREF, false),
1089
(Ref::Extern(Some(e)), RefType::EXTERNREF, true),
1090
(Ref::Extern(Some(e)), RefType::NULLREF, false),
1091
(Ref::Extern(Some(e)), RefType::STRUCTREF, false),
1092
(Ref::Extern(Some(e)), RefType::ARRAYREF, false),
1093
(Ref::Extern(Some(e)), RefType::I31REF, false),
1094
(Ref::Extern(Some(e)), RefType::EQREF, false),
1095
(Ref::Extern(Some(e)), RefType::ANYREF, false),
1096
] {
1097
let actual = val.matches_ty(&mut store, &ty)?;
1098
assert_eq!(
1099
actual, expected,
1100
"{val:?} matches {ty:?}? expected {expected}, got {actual}"
1101
);
1102
}
1103
1104
Ok(())
1105
}
1106
1107
#[test]
1108
#[cfg_attr(miri, ignore)]
1109
fn issue_9669() -> Result<()> {
1110
let _ = env_logger::try_init();
1111
1112
let mut config = Config::new();
1113
config.wasm_function_references(true);
1114
config.wasm_gc(true);
1115
config.collector(Collector::DeferredReferenceCounting);
1116
1117
let engine = Engine::new(&config)?;
1118
1119
let module = Module::new(
1120
&engine,
1121
r#"
1122
(module
1123
(type $empty (struct))
1124
(type $thing (struct
1125
(field $field1 (ref $empty))
1126
(field $field2 (ref $empty))
1127
))
1128
1129
(func (export "run")
1130
(local $object (ref $thing))
1131
1132
struct.new $empty
1133
struct.new $empty
1134
struct.new $thing
1135
1136
local.tee $object
1137
struct.get $thing $field1
1138
drop
1139
1140
local.get $object
1141
struct.get $thing $field2
1142
drop
1143
)
1144
)
1145
"#,
1146
)?;
1147
1148
let mut store = Store::new(&engine, ());
1149
let instance = Instance::new(&mut store, &module, &[])?;
1150
1151
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1152
func.call(&mut store, ())?;
1153
1154
Ok(())
1155
}
1156
1157
#[test]
1158
fn drc_transitive_drop_cons_list() -> Result<()> {
1159
let _ = env_logger::try_init();
1160
1161
let mut config = Config::new();
1162
config.wasm_function_references(true);
1163
config.wasm_gc(true);
1164
config.collector(Collector::DeferredReferenceCounting);
1165
1166
let engine = Engine::new(&config)?;
1167
1168
// Define a module that defines a recursive type (a `cons` list of
1169
// `externref`s) and exports a global of that type so that we can access it
1170
// from the host, because we don't yet have host APIs for defining recursive
1171
// types or rec groups.
1172
let module = Module::new(
1173
&engine,
1174
r#"
1175
(module
1176
(type $cons (struct (field externref) (field (ref null $cons))))
1177
(global (export "g") (ref null $cons) (ref.null $cons))
1178
)
1179
"#,
1180
)?;
1181
1182
let export = module.exports().nth(0).unwrap().ty();
1183
let global = export.unwrap_global();
1184
let ref_ty = global.content().unwrap_ref();
1185
let struct_ty = ref_ty.heap_type().unwrap_concrete_struct();
1186
1187
let mut store = Store::new(&engine, ());
1188
1189
let pre = StructRefPre::new(&mut store, struct_ty.clone());
1190
let num_refs_dropped = Arc::new(AtomicUsize::new(0));
1191
1192
let len = if cfg!(miri) { 2 } else { 100 };
1193
{
1194
let mut store = RootScope::new(&mut store);
1195
1196
let mut cdr = None;
1197
for _ in 0..len {
1198
let externref = ExternRef::new(&mut store, CountDrops(num_refs_dropped.clone()))?;
1199
let cons = StructRef::new(&mut store, &pre, &[externref.into(), cdr.into()])?;
1200
cdr = Some(cons);
1201
}
1202
1203
// Still holding the cons list alive at this point.
1204
assert_eq!(num_refs_dropped.load(SeqCst), 0);
1205
}
1206
1207
// Not holding the cons list alive anymore; should transitively drop
1208
// everything we created.
1209
store.gc(None);
1210
assert_eq!(num_refs_dropped.load(SeqCst), len);
1211
1212
Ok(())
1213
}
1214
1215
#[test]
1216
fn drc_transitive_drop_nested_arrays_tree() -> Result<()> {
1217
let _ = env_logger::try_init();
1218
1219
let mut config = Config::new();
1220
config.wasm_function_references(true);
1221
config.wasm_gc(true);
1222
config.collector(Collector::DeferredReferenceCounting);
1223
1224
let engine = Engine::new(&config)?;
1225
1226
let array_ty = ArrayType::new(
1227
&engine,
1228
FieldType::new(
1229
Mutability::Var,
1230
StorageType::ValType(ValType::Ref(RefType::ANYREF)),
1231
),
1232
);
1233
1234
let mut store = Store::new(&engine, ());
1235
let pre = ArrayRefPre::new(&mut store, array_ty);
1236
let num_refs_dropped = Arc::new(AtomicUsize::new(0));
1237
let mut expected = 0;
1238
1239
fn recursively_build_tree(
1240
mut store: &mut RootScope<&mut Store<()>>,
1241
pre: &ArrayRefPre,
1242
num_refs_dropped: &Arc<AtomicUsize>,
1243
expected: &mut usize,
1244
depth: u32,
1245
) -> Result<Rooted<AnyRef>> {
1246
let max = if cfg!(miri) { 1 } else { 3 };
1247
if depth >= max {
1248
*expected += 1;
1249
let e = ExternRef::new(&mut store, CountDrops(num_refs_dropped.clone()))?;
1250
AnyRef::convert_extern(&mut store, e)
1251
} else {
1252
let left = recursively_build_tree(store, pre, num_refs_dropped, expected, depth + 1)?;
1253
let right = recursively_build_tree(store, pre, num_refs_dropped, expected, depth + 1)?;
1254
let arr = ArrayRef::new_fixed(store, pre, &[left.into(), right.into()])?;
1255
Ok(arr.to_anyref())
1256
}
1257
}
1258
1259
{
1260
let mut store = RootScope::new(&mut store);
1261
let _tree = recursively_build_tree(&mut store, &pre, &num_refs_dropped, &mut expected, 0)?;
1262
1263
// Still holding the tree alive at this point.
1264
assert_eq!(num_refs_dropped.load(SeqCst), 0);
1265
}
1266
1267
// Not holding the tree alive anymore; should transitively drop everything
1268
// we created.
1269
store.gc(None);
1270
assert_eq!(num_refs_dropped.load(SeqCst), expected);
1271
1272
Ok(())
1273
}
1274
1275
#[test]
1276
#[cfg_attr(miri, ignore)]
1277
fn drc_traces_the_correct_number_of_gc_refs_in_arrays() -> Result<()> {
1278
let _ = env_logger::try_init();
1279
1280
let mut config = Config::new();
1281
config.wasm_function_references(true);
1282
config.wasm_gc(true);
1283
config.collector(Collector::DeferredReferenceCounting);
1284
1285
let engine = Engine::new(&config)?;
1286
let mut store = Store::new(&engine, ());
1287
1288
// The DRC collector was mistakenly reporting that arrays of GC refs had
1289
// `size_of(elems)` outgoing edges, rather than `len(elems)` edges. None of
1290
// our existing tests happened to trigger this bug because although we were
1291
// tricking the collector into tracing unallocated GC heap memory, it was
1292
// all zeroed out and was treated as null GC references. We can avoid that
1293
// in this regression test by first painting the heap with a large poison
1294
// value before we start allocating arrays, so that if the GC tries tracing
1295
// bogus heap memory, it finds very large GC ref heap indices and ultimately
1296
// tries to follow them outside the bounds of the GC heap, which (before
1297
// this bug was fixed) would lead to a panic.
1298
1299
let array_i8_ty = ArrayType::new(&engine, FieldType::new(Mutability::Var, StorageType::I8));
1300
let array_i8_pre = ArrayRefPre::new(&mut store, array_i8_ty);
1301
1302
{
1303
let mut store = RootScope::new(&mut store);
1304
1305
// Spray a poison pattern across the heap.
1306
let len = 1_000_000;
1307
let _poison = ArrayRef::new(&mut store, &array_i8_pre, &Val::I32(-1), len);
1308
}
1309
1310
// Make sure the poison array is collected.
1311
store.gc(None);
1312
1313
// Allocate and then collect an array of GC refs from Wasm. This should not
1314
// trick the collector into tracing any poison and panicking.
1315
let module = Module::new(
1316
&engine,
1317
r#"
1318
(module
1319
(type $ty (array (mut anyref)))
1320
(start $f)
1321
(func $f
1322
(drop (array.new $ty (ref.null any) (i32.const 1_000)))
1323
)
1324
)
1325
"#,
1326
)?;
1327
let _instance = Instance::new(&mut store, &module, &[])?;
1328
store.gc(None);
1329
1330
Ok(())
1331
}
1332
1333
// Test that we can completely fill the GC heap until we get an OOM. This
1334
// exercises growing the GC heap and that we configure compilation tunables and
1335
// runtime memories backing GC heaps correctly.
1336
#[test]
1337
#[cfg_attr(any(miri, not(target_pointer_width = "64")), ignore)]
1338
fn gc_heap_oom() -> Result<()> {
1339
if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
1340
return Ok(());
1341
}
1342
1343
let _ = env_logger::try_init();
1344
1345
for heap_size in [
1346
// Very small heap.
1347
1 << 16,
1348
// Bigger heap: 4 GiB
1349
1 << 32,
1350
] {
1351
for pooling in [true, false] {
1352
let mut config = Config::new();
1353
config.wasm_function_references(true);
1354
config.wasm_gc(true);
1355
config.collector(Collector::Null);
1356
config.memory_reservation(heap_size);
1357
config.memory_reservation_for_growth(0);
1358
config.memory_guard_size(0);
1359
config.memory_may_move(false);
1360
1361
if pooling {
1362
let mut pooling = crate::small_pool_config();
1363
pooling.max_memory_size(heap_size.try_into().unwrap());
1364
config.allocation_strategy(InstanceAllocationStrategy::Pooling(pooling));
1365
}
1366
1367
let engine = Engine::new(&config)?;
1368
1369
let module = Module::new(
1370
&engine,
1371
r#"
1372
(module
1373
(type $s (struct))
1374
(global $g (export "g") (mut i32) (i32.const 0))
1375
(func (export "run")
1376
loop
1377
struct.new $s
1378
1379
global.get $g
1380
i32.const 1
1381
i32.add
1382
global.set $g
1383
1384
br 0
1385
end
1386
)
1387
)
1388
"#,
1389
)?;
1390
1391
let mut store = Store::new(&engine, ());
1392
let instance = Instance::new(&mut store, &module, &[])?;
1393
1394
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1395
let err = run.call(&mut store, ()).expect_err("should oom");
1396
assert!(err.is::<Trap>(), "should get trap, got: {err:?}");
1397
let trap = err.downcast::<Trap>().unwrap();
1398
assert_eq!(trap, Trap::AllocationTooLarge);
1399
1400
let g = instance.get_global(&mut store, "g").unwrap();
1401
const SIZE_OF_NULL_GC_HEADER: u64 = 8;
1402
const FUDGE: u64 = 2;
1403
let actual = g.get(&mut store).unwrap_i32() as u64;
1404
let expected = heap_size / SIZE_OF_NULL_GC_HEADER;
1405
assert!(
1406
actual.abs_diff(expected) <= FUDGE,
1407
"actual approx= expected failed: \
1408
actual = {actual}, expected = {expected}, FUDGE={FUDGE}"
1409
);
1410
}
1411
}
1412
Ok(())
1413
}
1414
1415
#[test]
1416
#[cfg_attr(miri, ignore)]
1417
fn issue_10772() -> Result<()> {
1418
let mut store = crate::gc_store()?;
1419
let engine = store.engine().clone();
1420
1421
let module = Module::new(
1422
&engine,
1423
r#"
1424
(module
1425
(type $empty (struct))
1426
(type $tuple-concrete (struct (field (ref $empty))))
1427
(type $tuple-abstract (struct (field (ref struct))))
1428
(func (export "abstract") (param $t (ref $tuple-abstract))
1429
(drop (ref.cast (ref $tuple-concrete) (local.get $t)))
1430
)
1431
)
1432
"#,
1433
)?;
1434
1435
let linker = Linker::new(&engine);
1436
1437
let instance = linker.instantiate(&mut store, &module)?;
1438
let abstract_ = instance.get_func(&mut store, "abstract").unwrap();
1439
let empty_pre = StructRefPre::new(&mut store, StructType::new(&engine, [])?);
1440
let empty_struct = StructRef::new(&mut store, &empty_pre, &[])?;
1441
let tuple_pre = StructRefPre::new(
1442
&mut store,
1443
StructType::new(
1444
&engine,
1445
[FieldType::new(
1446
Mutability::Const,
1447
StorageType::ValType(ValType::Ref(RefType::new(false, HeapType::Struct))),
1448
)],
1449
)?,
1450
);
1451
let tuple_struct = StructRef::new(&mut store, &tuple_pre, &[empty_struct.into()])?;
1452
let tuple_any = Val::from(tuple_struct);
1453
1454
match abstract_.call(store, &[tuple_any], &mut []) {
1455
Ok(()) => panic!("should have trapped on cast failure"),
1456
Err(e) => {
1457
let trap = e.downcast::<Trap>().expect("should fail with a trap");
1458
assert_eq!(trap, Trap::CastFailure);
1459
}
1460
}
1461
1462
Ok(())
1463
}
1464
1465
#[test]
1466
fn drc_gc_inbetween_host_calls() -> Result<()> {
1467
let _ = env_logger::try_init();
1468
1469
let mut config = Config::new();
1470
config.wasm_function_references(true);
1471
config.wasm_gc(true);
1472
config.collector(Collector::DeferredReferenceCounting);
1473
1474
let engine = Engine::new(&config)?;
1475
1476
let mut store = Store::new(&engine, ());
1477
let func = Func::wrap(&mut store, |_: Option<Rooted<ExternRef>>| {});
1478
1479
let mut invoke_func = || {
1480
let inner_dropped = Arc::new(AtomicBool::new(false));
1481
{
1482
let mut scope = RootScope::new(&mut store);
1483
let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1484
func.call(&mut scope, &[r.into()], &mut [])?;
1485
}
1486
1487
assert!(!inner_dropped.load(SeqCst));
1488
store.gc(None);
1489
assert!(inner_dropped.load(SeqCst));
1490
anyhow::Ok(())
1491
};
1492
1493
invoke_func()?;
1494
invoke_func()?;
1495
1496
Ok(())
1497
}
1498
1499
#[test]
1500
fn owned_rooted() -> Result<()> {
1501
let _ = env_logger::try_init();
1502
1503
let mut config = Config::new();
1504
config.wasm_function_references(true);
1505
config.wasm_gc(true);
1506
config.collector(Collector::DeferredReferenceCounting);
1507
1508
let engine = Engine::new(&config)?;
1509
1510
let mut store = Store::new(&engine, ());
1511
let inner_dropped = Arc::new(AtomicBool::new(false));
1512
let r = {
1513
let mut scope = RootScope::new(&mut store);
1514
let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1515
r.to_owned_rooted(&mut scope)?
1516
};
1517
assert!(!inner_dropped.load(SeqCst));
1518
store.gc(None);
1519
assert!(!inner_dropped.load(SeqCst));
1520
let r2 = r.clone();
1521
store.gc(None);
1522
assert!(!inner_dropped.load(SeqCst));
1523
drop(r);
1524
store.gc(None);
1525
assert!(!inner_dropped.load(SeqCst));
1526
drop(r2);
1527
store.gc(None);
1528
assert!(inner_dropped.load(SeqCst));
1529
1530
Ok(())
1531
}
1532
1533
#[test]
1534
#[cfg_attr(miri, ignore)]
1535
fn owned_rooted_lots_of_root_creation() -> Result<()> {
1536
let mut config = Config::new();
1537
config.wasm_function_references(true);
1538
config.wasm_gc(true);
1539
config.collector(Collector::DeferredReferenceCounting);
1540
1541
let engine = Engine::new(&config)?;
1542
1543
let mut store = Store::new(&engine, ());
1544
let inner_dropped = Arc::new(AtomicBool::new(false));
1545
let r = {
1546
let mut scope = RootScope::new(&mut store);
1547
let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1548
r.to_owned_rooted(&mut scope)?
1549
};
1550
assert!(!inner_dropped.load(SeqCst));
1551
store.gc(None);
1552
1553
for _ in 0..100_000 {
1554
let mut scope = RootScope::new(&mut store);
1555
// Go through a LIFO root then back to an owned root to create
1556
// a distinct root.
1557
let r2 = r.to_rooted(&mut scope);
1558
let r3 = r2.to_owned_rooted(&mut scope);
1559
drop(r3);
1560
}
1561
1562
store.gc(None);
1563
assert!(!inner_dropped.load(SeqCst));
1564
1565
drop(r);
1566
store.gc(None);
1567
assert!(inner_dropped.load(SeqCst));
1568
1569
Ok(())
1570
}
1571
1572
#[test]
1573
#[cfg_attr(miri, ignore)]
1574
fn runtime_table_init_oom() -> Result<()> {
1575
let mut config = Config::new();
1576
config.wasm_gc(true);
1577
config.wasm_function_references(true);
1578
config.memory_may_move(false);
1579
config.memory_reservation(64 << 10);
1580
config.memory_reservation_for_growth(0);
1581
let engine = Engine::new(&config)?;
1582
let mut store = Store::new(&engine, ());
1583
1584
let module = Module::new(
1585
store.engine(),
1586
r#"
1587
(module
1588
(table 100 arrayref)
1589
1590
(type $a (array i31ref))
1591
1592
(func (export "run")
1593
i32.const 0
1594
i32.const 0
1595
i32.const 5
1596
table.init $e)
1597
(elem $e arrayref
1598
(array.new_default $a (i32.const 100))
1599
(array.new_default $a (i32.const 10000))
1600
(array.new_default $a (i32.const 10000))
1601
(array.new_default $a (i32.const 10000))
1602
(array.new_default $a (i32.const 1000000))
1603
)
1604
)
1605
"#,
1606
)?;
1607
1608
let instance = Instance::new(&mut store, &module, &[])?;
1609
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1610
func.call(&mut store, ())
1611
.unwrap_err()
1612
.downcast::<GcHeapOutOfMemory<()>>()?;
1613
1614
Ok(())
1615
}
1616
1617
#[test]
1618
#[cfg_attr(miri, ignore)]
1619
fn instantiate_table_init_oom() -> Result<()> {
1620
let mut config = Config::new();
1621
config.wasm_gc(true);
1622
config.wasm_function_references(true);
1623
config.memory_may_move(false);
1624
config.memory_reservation(64 << 10);
1625
config.memory_reservation_for_growth(0);
1626
let engine = Engine::new(&config)?;
1627
let mut store = Store::new(&engine, ());
1628
1629
let module = Module::new(
1630
store.engine(),
1631
r#"
1632
(module
1633
(table 100 arrayref)
1634
1635
(type $a (array i31ref))
1636
1637
(elem (i32.const 0) arrayref
1638
(array.new_default $a (i32.const 100))
1639
(array.new_default $a (i32.const 10000))
1640
(array.new_default $a (i32.const 10000))
1641
(array.new_default $a (i32.const 10000))
1642
(array.new_default $a (i32.const 1000000))
1643
)
1644
)
1645
"#,
1646
)?;
1647
1648
Instance::new(&mut store, &module, &[])
1649
.unwrap_err()
1650
.downcast::<GcHeapOutOfMemory<()>>()?;
1651
1652
Ok(())
1653
}
1654
1655
#[test]
1656
#[cfg_attr(miri, ignore)]
1657
fn instantiate_table_init_expr_oom() -> Result<()> {
1658
let mut config = Config::new();
1659
config.wasm_gc(true);
1660
config.wasm_function_references(true);
1661
config.memory_may_move(false);
1662
config.memory_reservation(64 << 10);
1663
config.memory_reservation_for_growth(0);
1664
let engine = Engine::new(&config)?;
1665
let mut store = Store::new(&engine, ());
1666
1667
let module = Module::new(
1668
store.engine(),
1669
r#"
1670
(module
1671
(type $a (array i31ref))
1672
(table 100 (ref $a) (array.new_default $a (i32.const 100000)))
1673
)
1674
"#,
1675
)?;
1676
1677
Instance::new(&mut store, &module, &[])
1678
.unwrap_err()
1679
.downcast::<GcHeapOutOfMemory<()>>()?;
1680
1681
Ok(())
1682
}
1683
1684
#[test]
1685
#[cfg_attr(miri, ignore)]
1686
fn instantiate_global_init_oom() -> Result<()> {
1687
let mut config = Config::new();
1688
config.wasm_gc(true);
1689
config.wasm_function_references(true);
1690
config.memory_may_move(false);
1691
config.memory_reservation(64 << 10);
1692
config.memory_reservation_for_growth(0);
1693
let engine = Engine::new(&config)?;
1694
let mut store = Store::new(&engine, ());
1695
1696
let module = Module::new(
1697
store.engine(),
1698
r#"
1699
(module
1700
(table 100 arrayref)
1701
(type $a (array i31ref))
1702
(global (ref $a) (array.new_default $a (i32.const 10000000)))
1703
)
1704
"#,
1705
)?;
1706
1707
Instance::new(&mut store, &module, &[])
1708
.unwrap_err()
1709
.downcast::<GcHeapOutOfMemory<()>>()?;
1710
1711
Ok(())
1712
}
1713
1714
#[test]
1715
#[cfg_attr(miri, ignore)]
1716
fn array_new_elem_oom() -> Result<()> {
1717
let mut config = Config::new();
1718
config.wasm_gc(true);
1719
config.wasm_function_references(true);
1720
config.memory_may_move(false);
1721
config.memory_reservation(64 << 10);
1722
config.memory_reservation_for_growth(0);
1723
let engine = Engine::new(&config)?;
1724
let mut store = Store::new(&engine, ());
1725
1726
let module = Module::new(
1727
store.engine(),
1728
r#"
1729
(module
1730
(type $a (array (mut arrayref)))
1731
(type $i (array i31ref))
1732
1733
(func (export "run")
1734
i32.const 0
1735
i32.const 5
1736
array.new_elem $a $e
1737
drop)
1738
1739
(elem $e arrayref
1740
(array.new_default $i (i32.const 100))
1741
(array.new_default $i (i32.const 10000))
1742
(array.new_default $i (i32.const 10000))
1743
(array.new_default $i (i32.const 10000))
1744
(array.new_default $i (i32.const 1000000))
1745
)
1746
)
1747
"#,
1748
)?;
1749
1750
let instance = Instance::new(&mut store, &module, &[])?;
1751
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1752
func.call(&mut store, ())
1753
.unwrap_err()
1754
.downcast::<GcHeapOutOfMemory<()>>()?;
1755
1756
Ok(())
1757
}
1758
1759
#[test]
1760
#[cfg_attr(miri, ignore)]
1761
fn array_init_elem_oom() -> Result<()> {
1762
let mut config = Config::new();
1763
config.wasm_gc(true);
1764
config.wasm_function_references(true);
1765
config.memory_may_move(false);
1766
config.memory_reservation(64 << 10);
1767
config.memory_reservation_for_growth(0);
1768
let engine = Engine::new(&config)?;
1769
let mut store = Store::new(&engine, ());
1770
1771
let module = Module::new(
1772
store.engine(),
1773
r#"
1774
(module
1775
(type $a (array (mut arrayref)))
1776
(type $i (array i31ref))
1777
1778
(func (export "run")
1779
i32.const 5
1780
array.new_default $a
1781
i32.const 0
1782
i32.const 0
1783
i32.const 5
1784
array.init_elem $a $e)
1785
1786
(elem $e arrayref
1787
(array.new_default $i (i32.const 100))
1788
(array.new_default $i (i32.const 10000))
1789
(array.new_default $i (i32.const 10000))
1790
(array.new_default $i (i32.const 10000))
1791
(array.new_default $i (i32.const 1000000))
1792
)
1793
)
1794
"#,
1795
)?;
1796
1797
let instance = Instance::new(&mut store, &module, &[])?;
1798
let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1799
func.call(&mut store, ())
1800
.unwrap_err()
1801
.downcast::<GcHeapOutOfMemory<()>>()?;
1802
1803
Ok(())
1804
}
1805
1806