Skip to main content

wasmtime_fuzzing/generators/
gc_access.rs

1//! Generate Wasm modules that exercise GC object creation, field access, and
2//! assertion patterns across different heap initialization states.
3
4use arbitrary::{Arbitrary, Result, Unstructured};
5use std::borrow::Cow;
6use wasm_encoder::{
7    CodeSection, ConstExpr, DataSection, ElementSection, Elements, Encode, EntityType, ExportKind,
8    ExportSection, Function, FunctionSection, GlobalSection, GlobalType, ImportSection,
9    Instruction, Module, RefType, StartSection, TableSection, TableType, TypeSection, ValType,
10};
11
12/// The storage type for a GC object's field or element.
13#[derive(Arbitrary, Debug, Clone, Copy)]
14enum ElemType {
15    I8,
16    I32,
17    Anyref,
18    I31ref,
19}
20
21impl ElemType {
22    fn is_packed(self) -> bool {
23        matches!(self, ElemType::I8)
24    }
25
26    fn is_numeric(self) -> bool {
27        matches!(self, ElemType::I8 | ElemType::I32)
28    }
29
30    fn is_ref(self) -> bool {
31        matches!(self, ElemType::Anyref | ElemType::I31ref)
32    }
33
34    fn storage_type(self) -> wasm_encoder::StorageType {
35        match self {
36            ElemType::I8 => wasm_encoder::StorageType::I8,
37            ElemType::I32 => wasm_encoder::StorageType::Val(ValType::I32),
38            ElemType::Anyref | ElemType::I31ref => {
39                wasm_encoder::StorageType::Val(ValType::Ref(RefType::ANYREF))
40            }
41        }
42    }
43}
44
45#[derive(Arbitrary, Debug, Clone, Copy)]
46enum ObjectKind {
47    Array,
48    Struct,
49}
50
51#[derive(Arbitrary, Debug, Clone, Copy)]
52struct ObjectType {
53    kind: ObjectKind,
54    elem_type: ElemType,
55    mutable: bool,
56}
57
58impl ObjectType {
59    fn is_array(self) -> bool {
60        matches!(self.kind, ObjectKind::Array)
61    }
62}
63
64/// How the GC heap is initialized before the test's `run` function executes.
65#[derive(Arbitrary, Debug, Clone, Copy)]
66enum HeapInit {
67    /// The heap is left empty; no filler objects are allocated.
68    Empty,
69    /// The heap is filled to capacity with filler objects.
70    Full,
71    /// The heap is nearly full, leaving only a small amount of free space.
72    AlmostFull,
73    /// The heap is filled with two interleaved chains of filler objects, then
74    /// one chain is dropped and a GC is triggered, producing a fragmented heap.
75    Fragmented,
76}
77
78/// How the GC object under test is created in the `run` function.
79#[derive(Debug, Clone, Copy)]
80enum CreateMode {
81    /// Retrieve the object from an immutable global.
82    GlobalGet,
83    /// Retrieve the object from a table slot.
84    TableGet,
85    /// Create the object by calling a helper function.
86    Call,
87    /// Receive the object as a function parameter via `local.get`.
88    LocalGet,
89    /// Create an array with `array.new` (fill with a single value).
90    ArrayNew,
91    /// Create an array with `array.new_default` (zero/null-initialized).
92    ArrayNewDefault,
93    /// Create an array with `array.new_fixed` (each element pushed individually).
94    ArrayNewFixed,
95    /// Create an array with `array.new_data` from a passive data segment.
96    ArrayNewData,
97    /// Create an array with `array.new_elem` from a passive element segment.
98    ArrayNewElem,
99    /// Create a struct with `struct.new`.
100    StructNew,
101    /// Create a struct with `struct.new_default` (zero/null-initialized).
102    StructNewDefault,
103}
104
105impl CreateMode {
106    fn arbitrary(u: &mut Unstructured<'_>, object_type: &ObjectType) -> Result<Self> {
107        let mut modes: Vec<CreateMode> = vec![
108            CreateMode::GlobalGet,
109            CreateMode::TableGet,
110            CreateMode::Call,
111            CreateMode::LocalGet,
112        ];
113        if object_type.is_array() {
114            modes.push(CreateMode::ArrayNew);
115            modes.push(CreateMode::ArrayNewDefault);
116            modes.push(CreateMode::ArrayNewFixed);
117            if object_type.elem_type.is_ref() {
118                modes.push(CreateMode::ArrayNewElem);
119            } else {
120                modes.push(CreateMode::ArrayNewData);
121            }
122        } else {
123            modes.push(CreateMode::StructNew);
124            modes.push(CreateMode::StructNewDefault);
125        }
126        Ok(modes[u.int_in_range(0..=modes.len() - 1)?])
127    }
128
129    fn is_default(self) -> bool {
130        matches!(
131            self,
132            CreateMode::StructNewDefault | CreateMode::ArrayNewDefault
133        )
134    }
135
136    fn is_idempotent(self) -> bool {
137        matches!(
138            self,
139            CreateMode::GlobalGet | CreateMode::TableGet | CreateMode::LocalGet
140        )
141    }
142}
143
144/// The access operation performed on the GC object, exercised before and after
145/// a GC cycle to verify that the object survives collection correctly.
146#[derive(Debug, Clone, Copy)]
147enum AccessMode {
148    /// Test the object's runtime type with `ref.test`.
149    RefTest,
150    /// Read the struct field with `struct.get`.
151    StructGet,
152    /// Read a packed struct field with sign extension via `struct.get_s`.
153    StructGetS,
154    /// Read a packed struct field with zero extension via `struct.get_u`.
155    StructGetU,
156    /// Write the struct field with `struct.set`.
157    StructSet,
158    /// Read an array element with `array.get`.
159    ArrayGet,
160    /// Read a packed array element with sign extension via `array.get_s`.
161    ArrayGetS,
162    /// Read a packed array element with zero extension via `array.get_u`.
163    ArrayGetU,
164    /// Write an array element with `array.set`.
165    ArraySet,
166    /// Query the array length with `array.len`.
167    ArrayLen,
168    /// Fill the entire array with a value via `array.fill`.
169    ArrayFill,
170    /// Copy elements from a source array via `array.copy`.
171    ArrayCopy,
172    /// Initialize array elements from a passive data segment via `array.init_data`.
173    ArrayInitData,
174    /// Initialize array elements from a passive element segment via `array.init_elem`.
175    ArrayInitElem,
176}
177
178impl AccessMode {
179    fn arbitrary(u: &mut Unstructured<'_>, object_type: &ObjectType) -> Result<Self> {
180        let mut modes: Vec<AccessMode> = vec![AccessMode::RefTest];
181        if object_type.is_array() {
182            if object_type.elem_type.is_packed() {
183                modes.push(AccessMode::ArrayGetS);
184                modes.push(AccessMode::ArrayGetU);
185            } else {
186                modes.push(AccessMode::ArrayGet);
187            }
188            modes.push(AccessMode::ArrayLen);
189            if object_type.mutable {
190                modes.push(AccessMode::ArraySet);
191                modes.push(AccessMode::ArrayFill);
192                modes.push(AccessMode::ArrayCopy);
193                if object_type.elem_type.is_numeric() {
194                    modes.push(AccessMode::ArrayInitData);
195                } else if object_type.elem_type.is_ref() {
196                    modes.push(AccessMode::ArrayInitElem);
197                }
198            }
199        } else {
200            if object_type.elem_type.is_packed() {
201                modes.push(AccessMode::StructGetS);
202                modes.push(AccessMode::StructGetU);
203            } else {
204                modes.push(AccessMode::StructGet);
205            }
206            if object_type.mutable {
207                modes.push(AccessMode::StructSet);
208            }
209        }
210        Ok(modes[u.int_in_range(0..=modes.len() - 1)?])
211    }
212}
213
214/// A fuzz test case that generates a Wasm module exercising GC object creation,
215/// access, and assertion patterns across different heap initialization states.
216#[derive(Debug)]
217pub struct GcAccess {
218    object_type: ObjectType,
219    heap_init: HeapInit,
220    create_mode: CreateMode,
221    access_mode: AccessMode,
222    array_len: u32,
223    creation_value: i32,
224    mutation_value: i32,
225}
226
227impl<'a> Arbitrary<'a> for GcAccess {
228    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
229        let object_type: ObjectType = u.arbitrary()?;
230        let heap_init: HeapInit = u.arbitrary()?;
231        let create_mode = CreateMode::arbitrary(u, &object_type)?;
232        let access_mode = AccessMode::arbitrary(u, &object_type)?;
233        let array_len = u.int_in_range(1..=1024)?;
234        let creation_value: i32 = u.arbitrary()?;
235        let mutation_value: i32 = u.arbitrary()?;
236        Ok(GcAccess {
237            object_type,
238            heap_init,
239            create_mode,
240            access_mode,
241            array_len,
242            creation_value,
243            mutation_value,
244        })
245    }
246}
247
248impl GcAccess {
249    /// Generate the Wasm binary for this test case.
250    ///
251    /// The generated module imports `"wasmtime" "gc"` as a GC-triggering
252    /// function and exports a `"run"` function. The `"run"` function creates a
253    /// GC object, accesses it, triggers GC, then asserts the access result is
254    /// unchanged.
255    pub fn to_wasm(&self) -> Vec<u8> {
256        let mut emitter = Emitter::new(self);
257        emitter.emit()
258    }
259}
260
261struct Emitter<'a> {
262    cfg: &'a GcAccess,
263
264    // Type indices.
265    obj_type: Option<u32>,
266    filler_type: Option<u32>,
267    nested_obj_type: Option<u32>,
268    void_fn_type: Option<u32>,
269    make_obj_fn_type: Option<u32>,
270    run_impl_fn_type: Option<u32>,
271
272    // Function indices.
273    next_func_idx: u32,
274    gc_func: Option<u32>,
275    make_obj_func: Option<u32>,
276    init_func: Option<u32>,
277    run_impl_func: Option<u32>,
278    run_func: Option<u32>,
279
280    // Global indices.
281    global_g: Option<u32>,
282    global_root: Option<u32>,
283    global_root2: Option<u32>,
284
285    // Table index.
286    table_t: Option<u32>,
287
288    // Segment indices.
289    data_d: Option<u32>,
290    data_dinit: Option<u32>,
291    elem_e: Option<u32>,
292    elem_einit: Option<u32>,
293}
294
295impl<'a> Emitter<'a> {
296    fn new(cfg: &'a GcAccess) -> Self {
297        Emitter {
298            cfg,
299            obj_type: None,
300            filler_type: None,
301            nested_obj_type: None,
302            void_fn_type: None,
303            make_obj_fn_type: None,
304            run_impl_fn_type: None,
305            next_func_idx: 0,
306            gc_func: None,
307            make_obj_func: None,
308            init_func: None,
309            run_impl_func: None,
310            run_func: None,
311            global_g: None,
312            global_root: None,
313            global_root2: None,
314            table_t: None,
315            data_d: None,
316            data_dinit: None,
317            elem_e: None,
318            elem_einit: None,
319        }
320    }
321
322    fn obj_ref_type(&self) -> ValType {
323        ValType::Ref(RefType {
324            nullable: true,
325            heap_type: wasm_encoder::HeapType::Concrete(self.obj_type.unwrap()),
326        })
327    }
328
329    fn obj_ref_type_non_null(&self) -> ValType {
330        ValType::Ref(RefType {
331            nullable: false,
332            heap_type: wasm_encoder::HeapType::Concrete(self.obj_type.unwrap()),
333        })
334    }
335
336    fn emit(&mut self) -> Vec<u8> {
337        let mut module = Module::new();
338
339        let types = self.types();
340        sec(&mut module, types);
341        let imports = self.imports();
342        sec(&mut module, imports);
343        let functions = self.functions();
344        sec(&mut module, functions);
345        if let Some(s) = self.tables() {
346            sec(&mut module, s);
347        }
348        if let Some(s) = self.globals() {
349            sec(&mut module, s);
350        }
351        let exports = self.exports();
352        sec(&mut module, exports);
353        sec(
354            &mut module,
355            StartSection {
356                function_index: self.init_func.unwrap(),
357            },
358        );
359        if let Some(s) = self.elems() {
360            sec(&mut module, s);
361        }
362        let data = self.data();
363        if data.is_some() {
364            let count = self.data_d.is_some() as u32 + self.data_dinit.is_some() as u32;
365            sec(&mut module, wasm_encoder::DataCountSection { count });
366        }
367        let code = self.code();
368        sec(&mut module, code);
369        if let Some(s) = data {
370            sec(&mut module, s);
371        }
372
373        module.finish()
374    }
375
376    fn types(&mut self) -> TypeSection {
377        let mut types = TypeSection::new();
378
379        self.obj_type = Some(types.len());
380        let field = wasm_encoder::FieldType {
381            element_type: self.cfg.object_type.elem_type.storage_type(),
382            mutable: self.cfg.object_type.mutable,
383        };
384        let obj_composite = if self.cfg.object_type.is_array() {
385            wasm_encoder::CompositeInnerType::Array(wasm_encoder::ArrayType(field))
386        } else {
387            wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType {
388                fields: Box::new([field]),
389            })
390        };
391        types.ty().subtype(&wasm_encoder::SubType {
392            is_final: true,
393            supertype_idxs: Default::default(),
394            composite_type: wasm_encoder::CompositeType {
395                inner: obj_composite,
396                shared: false,
397                describes: None,
398                descriptor: None,
399            },
400        });
401
402        if !matches!(self.cfg.heap_init, HeapInit::Empty) {
403            self.filler_type = Some(types.len());
404            let fi = self.filler_type.unwrap();
405            types.ty().subtype(&wasm_encoder::SubType {
406                is_final: true,
407                supertype_idxs: Default::default(),
408                composite_type: wasm_encoder::CompositeType {
409                    inner: wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType {
410                        fields: Box::new([wasm_encoder::FieldType {
411                            element_type: wasm_encoder::StorageType::Val(ValType::Ref(RefType {
412                                nullable: true,
413                                heap_type: wasm_encoder::HeapType::Concrete(fi),
414                            })),
415                            mutable: true,
416                        }]),
417                    }),
418                    shared: false,
419                    describes: None,
420                    descriptor: None,
421                },
422            });
423        }
424
425        if self.cfg.object_type.elem_type.is_ref()
426            && !self.cfg.create_mode.is_default()
427            && matches!(self.cfg.object_type.elem_type, ElemType::Anyref)
428        {
429            self.nested_obj_type = Some(types.len());
430            types.ty().subtype(&wasm_encoder::SubType {
431                is_final: true,
432                supertype_idxs: Default::default(),
433                composite_type: wasm_encoder::CompositeType {
434                    inner: wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType {
435                        fields: Box::new([]),
436                    }),
437                    shared: false,
438                    describes: None,
439                    descriptor: None,
440                },
441            });
442        }
443
444        self.void_fn_type = Some(types.len());
445        types.ty().function(vec![], vec![]);
446
447        if matches!(self.cfg.create_mode, CreateMode::Call) {
448            self.make_obj_fn_type = Some(types.len());
449            types
450                .ty()
451                .function(vec![], vec![self.obj_ref_type_non_null()]);
452        }
453
454        if matches!(self.cfg.create_mode, CreateMode::LocalGet) {
455            self.run_impl_fn_type = Some(types.len());
456            types.ty().function(vec![self.obj_ref_type()], vec![]);
457        }
458
459        types
460    }
461
462    fn imports(&mut self) -> ImportSection {
463        let mut imports = ImportSection::new();
464        self.gc_func = Some(self.next_func_idx);
465        self.next_func_idx += 1;
466        imports.import(
467            "wasmtime",
468            "gc",
469            EntityType::Function(self.void_fn_type.unwrap()),
470        );
471        imports
472    }
473
474    fn functions(&mut self) -> FunctionSection {
475        let mut fns = FunctionSection::new();
476        if let Some(t) = self.make_obj_fn_type {
477            self.make_obj_func = Some(self.next_func_idx);
478            self.next_func_idx += 1;
479            fns.function(t);
480        }
481        self.init_func = Some(self.next_func_idx);
482        self.next_func_idx += 1;
483        fns.function(self.void_fn_type.unwrap());
484        if let Some(t) = self.run_impl_fn_type {
485            self.run_impl_func = Some(self.next_func_idx);
486            self.next_func_idx += 1;
487            fns.function(t);
488        }
489        self.run_func = Some(self.next_func_idx);
490        self.next_func_idx += 1;
491        fns.function(self.void_fn_type.unwrap());
492        fns
493    }
494
495    fn tables(&mut self) -> Option<TableSection> {
496        if !matches!(self.cfg.create_mode, CreateMode::TableGet) {
497            return None;
498        }
499        let mut tables = TableSection::new();
500        self.table_t = Some(tables.len());
501        tables.table_with_init(
502            TableType {
503                element_type: RefType {
504                    nullable: false,
505                    heap_type: wasm_encoder::HeapType::Concrete(self.obj_type.unwrap()),
506                },
507                minimum: 1,
508                maximum: Some(1),
509                table64: false,
510                shared: false,
511            },
512            &self.obj_const_expr(self.cfg.creation_value),
513        );
514        Some(tables)
515    }
516
517    fn globals(&mut self) -> Option<GlobalSection> {
518        let mut globals = GlobalSection::new();
519        let mut any = false;
520
521        if matches!(self.cfg.create_mode, CreateMode::GlobalGet) {
522            self.global_g = Some(globals.len());
523            any = true;
524            globals.global(
525                GlobalType {
526                    val_type: self.obj_ref_type_non_null(),
527                    mutable: false,
528                    shared: false,
529                },
530                &self.obj_const_expr(self.cfg.creation_value),
531            );
532        }
533
534        if let Some(fi) = self.filler_type {
535            if !matches!(self.cfg.heap_init, HeapInit::Empty) {
536                self.global_root = Some(globals.len());
537                any = true;
538                globals.global(
539                    GlobalType {
540                        val_type: ValType::Ref(RefType {
541                            nullable: true,
542                            heap_type: wasm_encoder::HeapType::Concrete(fi),
543                        }),
544                        mutable: true,
545                        shared: false,
546                    },
547                    &ConstExpr::ref_null(wasm_encoder::HeapType::Concrete(fi)),
548                );
549            }
550            if matches!(self.cfg.heap_init, HeapInit::Fragmented) {
551                self.global_root2 = Some(globals.len());
552                any = true;
553                globals.global(
554                    GlobalType {
555                        val_type: ValType::Ref(RefType {
556                            nullable: true,
557                            heap_type: wasm_encoder::HeapType::Concrete(fi),
558                        }),
559                        mutable: true,
560                        shared: false,
561                    },
562                    &ConstExpr::ref_null(wasm_encoder::HeapType::Concrete(fi)),
563                );
564            }
565        }
566
567        if any { Some(globals) } else { None }
568    }
569
570    fn exports(&self) -> ExportSection {
571        let mut exports = ExportSection::new();
572        exports.export("run", ExportKind::Func, self.run_func.unwrap());
573        exports
574    }
575
576    fn elems(&mut self) -> Option<ElementSection> {
577        let mut elems = ElementSection::new();
578        let mut any = false;
579
580        if matches!(self.cfg.create_mode, CreateMode::ArrayNewElem) {
581            self.elem_e = Some(elems.len());
582            any = true;
583            let exprs: Vec<ConstExpr> = (0..self.cfg.array_len)
584                .map(|_| self.ref_value_const_expr(self.cfg.creation_value))
585                .collect();
586            elems.segment(wasm_encoder::ElementSegment {
587                mode: wasm_encoder::ElementMode::Passive,
588                elements: Elements::Expressions(RefType::ANYREF, Cow::Owned(exprs)),
589            });
590        }
591
592        if matches!(self.cfg.access_mode, AccessMode::ArrayInitElem) {
593            self.elem_einit = Some(elems.len());
594            any = true;
595            let expr = self.ref_value_const_expr(self.cfg.mutation_value);
596            elems.segment(wasm_encoder::ElementSegment {
597                mode: wasm_encoder::ElementMode::Passive,
598                elements: Elements::Expressions(RefType::ANYREF, Cow::Owned(vec![expr])),
599            });
600        }
601
602        if any { Some(elems) } else { None }
603    }
604
605    fn data(&mut self) -> Option<DataSection> {
606        let mut data = DataSection::new();
607        let mut any = false;
608
609        if matches!(self.cfg.create_mode, CreateMode::ArrayNewData) {
610            self.data_d = Some(data.len());
611            any = true;
612            data.passive(self.data_bytes(self.cfg.creation_value, self.cfg.array_len as usize));
613        }
614        if matches!(self.cfg.access_mode, AccessMode::ArrayInitData) {
615            self.data_dinit = Some(data.len());
616            any = true;
617            data.passive(self.data_bytes(self.cfg.mutation_value, 1));
618        }
619
620        if any { Some(data) } else { None }
621    }
622
623    fn data_bytes(&self, val: i32, count: usize) -> Vec<u8> {
624        match self.cfg.object_type.elem_type {
625            ElemType::I32 => (val as u32)
626                .to_le_bytes()
627                .iter()
628                .copied()
629                .cycle()
630                .take(4 * count)
631                .collect(),
632            ElemType::I8 => vec![(val & 0xFF) as u8; count],
633            _ => unreachable!(),
634        }
635    }
636
637    fn code(&self) -> CodeSection {
638        let mut code = CodeSection::new();
639
640        if self.make_obj_func.is_some() {
641            let mut body = Function::new(vec![]);
642            self.emit_make_obj(&mut body);
643            finish(&mut code, body);
644        }
645
646        // init
647        {
648            let locals = if !matches!(self.cfg.heap_init, HeapInit::Empty) {
649                vec![(1, ValType::I32)]
650            } else {
651                vec![]
652            };
653            let mut body = Function::new(locals);
654            self.emit_init(&mut body);
655            finish(&mut code, body);
656        }
657
658        // run_impl (local_get mode)
659        if self.run_impl_func.is_some() {
660            let mut locals = vec![
661                (1, ValType::I32), // $i
662                (1, ValType::I32), // $j
663            ];
664            if self.needs_before() {
665                locals.push((1, ValType::I32)); // $before
666            }
667            if matches!(self.cfg.access_mode, AccessMode::ArrayCopy) {
668                locals.push((1, self.obj_ref_type())); // $src
669            }
670            let mut body = Function::new(locals);
671            self.emit_local_get_body(&mut body);
672            finish(&mut code, body);
673        }
674
675        // run
676        {
677            if self.run_impl_func.is_some() {
678                let mut body = Function::new(vec![]);
679                self.emit_make_obj(&mut body);
680                body.instruction(&Instruction::Call(self.run_impl_func.unwrap()));
681                finish(&mut code, body);
682            } else if self.cfg.create_mode.is_idempotent()
683                && !matches!(self.cfg.create_mode, CreateMode::LocalGet)
684            {
685                let mut locals = vec![
686                    (1, ValType::I32), // $i
687                    (1, ValType::I32), // $j
688                ];
689                if self.needs_before() {
690                    locals.push((1, ValType::I32));
691                }
692                if matches!(self.cfg.access_mode, AccessMode::ArrayCopy) {
693                    locals.push((1, self.obj_ref_type()));
694                }
695                let mut body = Function::new(locals);
696                self.emit_idempotent_body(&mut body);
697                finish(&mut code, body);
698            } else {
699                let mut locals = vec![
700                    (1, self.obj_ref_type()), // $x
701                    (1, ValType::I32),        // $i
702                    (1, ValType::I32),        // $j
703                ];
704                if self.needs_before() {
705                    locals.push((1, ValType::I32));
706                }
707                if matches!(self.cfg.access_mode, AccessMode::ArrayCopy) {
708                    locals.push((1, self.obj_ref_type()));
709                }
710                let mut body = Function::new(locals);
711                self.emit_normal_body(&mut body);
712                finish(&mut code, body);
713            }
714        }
715
716        code
717    }
718
719    fn needs_before(&self) -> bool {
720        match self.cfg.access_mode {
721            AccessMode::RefTest
722            | AccessMode::StructGetS
723            | AccessMode::StructGetU
724            | AccessMode::ArrayGetS
725            | AccessMode::ArrayGetU
726            | AccessMode::ArrayLen => true,
727            AccessMode::StructGet | AccessMode::ArrayGet => {
728                if self.cfg.object_type.elem_type.is_ref() {
729                    // anyref get with default creates drops the value; nested obj
730                    // asserts non-null; neither uses $before as i32
731                    !self.cfg.create_mode.is_default() && !self.uses_nested_obj()
732                } else {
733                    true
734                }
735            }
736            _ => false,
737        }
738    }
739
740    fn uses_nested_obj(&self) -> bool {
741        self.nested_obj_type.is_some()
742    }
743
744    fn emit_init(&self, body: &mut Function) {
745        let local_i = 0u32;
746        match self.cfg.heap_init {
747            HeapInit::Empty => {}
748            HeapInit::Full => self.emit_filler_loop(body, local_i, 4096),
749            HeapInit::AlmostFull => self.emit_filler_loop(body, local_i, 4094),
750            HeapInit::Fragmented => self.emit_fragmented(body, local_i),
751        }
752    }
753
754    fn emit_filler_loop(&self, body: &mut Function, local_i: u32, count: i32) {
755        let fi = self.filler_type.unwrap();
756        let root = self.global_root.unwrap();
757
758        body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
759        body.instruction(&Instruction::GlobalGet(root));
760        body.instruction(&Instruction::StructNew(fi));
761        body.instruction(&Instruction::GlobalSet(root));
762        body.instruction(&Instruction::LocalGet(local_i));
763        body.instruction(&Instruction::I32Const(1));
764        body.instruction(&Instruction::I32Add);
765        body.instruction(&Instruction::LocalSet(local_i));
766        body.instruction(&Instruction::LocalGet(local_i));
767        body.instruction(&Instruction::I32Const(count));
768        body.instruction(&Instruction::I32LtU);
769        body.instruction(&Instruction::BrIf(0));
770        body.instruction(&Instruction::End);
771    }
772
773    fn emit_fragmented(&self, body: &mut Function, local_i: u32) {
774        let fi = self.filler_type.unwrap();
775        let root = self.global_root.unwrap();
776        let root2 = self.global_root2.unwrap();
777
778        body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
779        body.instruction(&Instruction::GlobalGet(root));
780        body.instruction(&Instruction::StructNew(fi));
781        body.instruction(&Instruction::GlobalSet(root));
782        body.instruction(&Instruction::GlobalGet(root2));
783        body.instruction(&Instruction::StructNew(fi));
784        body.instruction(&Instruction::GlobalSet(root2));
785        body.instruction(&Instruction::LocalGet(local_i));
786        body.instruction(&Instruction::I32Const(1));
787        body.instruction(&Instruction::I32Add);
788        body.instruction(&Instruction::LocalSet(local_i));
789        body.instruction(&Instruction::LocalGet(local_i));
790        body.instruction(&Instruction::I32Const(2048));
791        body.instruction(&Instruction::I32LtU);
792        body.instruction(&Instruction::BrIf(0));
793        body.instruction(&Instruction::End);
794
795        body.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Concrete(fi)));
796        body.instruction(&Instruction::GlobalSet(root2));
797        body.instruction(&Instruction::Call(self.gc_func.unwrap()));
798    }
799
800    fn emit_make_obj(&self, body: &mut Function) {
801        let obj_type = self.obj_type.unwrap();
802        if self.cfg.object_type.is_array() {
803            self.emit_value(body, self.cfg.creation_value);
804            body.instruction(&Instruction::I32Const(self.cfg.array_len as i32));
805            body.instruction(&Instruction::ArrayNew(obj_type));
806        } else {
807            self.emit_value(body, self.cfg.creation_value);
808            body.instruction(&Instruction::StructNew(obj_type));
809        }
810    }
811
812    fn emit_create(&self, body: &mut Function) {
813        let obj_type = self.obj_type.unwrap();
814        match self.cfg.create_mode {
815            CreateMode::StructNew => {
816                self.emit_value(body, self.cfg.creation_value);
817                body.instruction(&Instruction::StructNew(obj_type));
818            }
819            CreateMode::StructNewDefault => {
820                body.instruction(&Instruction::StructNewDefault(obj_type));
821            }
822            CreateMode::ArrayNew => {
823                self.emit_value(body, self.cfg.creation_value);
824                body.instruction(&Instruction::I32Const(self.cfg.array_len as i32));
825                body.instruction(&Instruction::ArrayNew(obj_type));
826            }
827            CreateMode::ArrayNewDefault => {
828                body.instruction(&Instruction::I32Const(self.cfg.array_len as i32));
829                body.instruction(&Instruction::ArrayNewDefault(obj_type));
830            }
831            CreateMode::ArrayNewFixed => {
832                for _ in 0..self.cfg.array_len {
833                    self.emit_value(body, self.cfg.creation_value);
834                }
835                body.instruction(&Instruction::ArrayNewFixed {
836                    array_type_index: obj_type,
837                    array_size: self.cfg.array_len,
838                });
839            }
840            CreateMode::ArrayNewData => {
841                body.instruction(&Instruction::I32Const(0));
842                body.instruction(&Instruction::I32Const(self.cfg.array_len as i32));
843                body.instruction(&Instruction::ArrayNewData {
844                    array_type_index: obj_type,
845                    array_data_index: self.data_d.unwrap(),
846                });
847            }
848            CreateMode::ArrayNewElem => {
849                body.instruction(&Instruction::I32Const(0));
850                body.instruction(&Instruction::I32Const(self.cfg.array_len as i32));
851                body.instruction(&Instruction::ArrayNewElem {
852                    array_type_index: obj_type,
853                    array_elem_index: self.elem_e.unwrap(),
854                });
855            }
856            CreateMode::GlobalGet => {
857                body.instruction(&Instruction::GlobalGet(self.global_g.unwrap()));
858            }
859            CreateMode::TableGet => {
860                body.instruction(&Instruction::I32Const(0));
861                body.instruction(&Instruction::TableGet(self.table_t.unwrap()));
862            }
863            CreateMode::Call => {
864                body.instruction(&Instruction::Call(self.make_obj_func.unwrap()));
865            }
866            CreateMode::LocalGet => unreachable!(),
867        }
868    }
869
870    fn obj_const_expr(&self, val: i32) -> ConstExpr {
871        let obj_type = self.obj_type.unwrap();
872        let mut bytes = Vec::new();
873        if self.cfg.object_type.is_array() {
874            self.push_value_instrs(&mut bytes, val);
875            Instruction::I32Const(self.cfg.array_len as i32).encode(&mut bytes);
876            Instruction::ArrayNew(obj_type).encode(&mut bytes);
877        } else {
878            self.push_value_instrs(&mut bytes, val);
879            Instruction::StructNew(obj_type).encode(&mut bytes);
880        }
881        ConstExpr::raw(bytes)
882    }
883
884    fn ref_value_const_expr(&self, val: i32) -> ConstExpr {
885        let mut bytes = Vec::new();
886        if let Some(nested) = self.nested_obj_type {
887            Instruction::StructNew(nested).encode(&mut bytes);
888        } else {
889            Instruction::I32Const(val).encode(&mut bytes);
890            Instruction::RefI31.encode(&mut bytes);
891        }
892        ConstExpr::raw(bytes)
893    }
894
895    fn push_value_instrs(&self, buf: &mut Vec<u8>, val: i32) {
896        match self.cfg.object_type.elem_type {
897            ElemType::I8 | ElemType::I32 => {
898                Instruction::I32Const(val).encode(buf);
899            }
900            ElemType::I31ref => {
901                Instruction::I32Const(val).encode(buf);
902                Instruction::RefI31.encode(buf);
903            }
904            ElemType::Anyref => {
905                if self.cfg.create_mode.is_default() {
906                    Instruction::RefNull(wasm_encoder::HeapType::Abstract {
907                        shared: false,
908                        ty: wasm_encoder::AbstractHeapType::Any,
909                    })
910                    .encode(buf);
911                } else if let Some(nested) = self.nested_obj_type {
912                    Instruction::StructNew(nested).encode(buf);
913                } else {
914                    Instruction::I32Const(val).encode(buf);
915                    Instruction::RefI31.encode(buf);
916                }
917            }
918        }
919    }
920
921    fn emit_value(&self, body: &mut Function, val: i32) {
922        match self.cfg.object_type.elem_type {
923            ElemType::I8 | ElemType::I32 => {
924                body.instruction(&Instruction::I32Const(val));
925            }
926            ElemType::I31ref => {
927                body.instruction(&Instruction::I32Const(val));
928                body.instruction(&Instruction::RefI31);
929            }
930            ElemType::Anyref => {
931                if self.cfg.create_mode.is_default() {
932                    body.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Abstract {
933                        shared: false,
934                        ty: wasm_encoder::AbstractHeapType::Any,
935                    }));
936                } else if let Some(nested) = self.nested_obj_type {
937                    body.instruction(&Instruction::StructNew(nested));
938                } else {
939                    body.instruction(&Instruction::I32Const(val));
940                    body.instruction(&Instruction::RefI31);
941                }
942            }
943        }
944    }
945
946    /// Compute the i32 value we expect to read back from a numeric field
947    /// written with `val`, after packed storage truncation and unsigned
948    /// extension via `{array,struct}.get_u`.
949    fn expected_numeric_readback(&self, val: i32) -> i32 {
950        match self.cfg.object_type.elem_type {
951            ElemType::I8 => val & 0xFF,
952            ElemType::I32 => val,
953            _ => unreachable!(),
954        }
955    }
956
957    fn emit_mutation_value(&self, body: &mut Function) {
958        match self.cfg.object_type.elem_type {
959            ElemType::I8 | ElemType::I32 => {
960                body.instruction(&Instruction::I32Const(self.cfg.mutation_value));
961            }
962            ElemType::I31ref | ElemType::Anyref => {
963                body.instruction(&Instruction::I32Const(self.cfg.mutation_value));
964                body.instruction(&Instruction::RefI31);
965            }
966        }
967    }
968
969    fn emit_obj_ref(&self, body: &mut Function, r: ObjRef) {
970        match r {
971            ObjRef::Local(l) => {
972                body.instruction(&Instruction::LocalGet(l));
973            }
974            ObjRef::Global => {
975                body.instruction(&Instruction::GlobalGet(self.global_g.unwrap()));
976            }
977            ObjRef::Table => {
978                body.instruction(&Instruction::I32Const(0));
979                body.instruction(&Instruction::TableGet(self.table_t.unwrap()));
980            }
981        }
982    }
983
984    fn idempotent_ref(&self) -> ObjRef {
985        match self.cfg.create_mode {
986            CreateMode::GlobalGet => ObjRef::Global,
987            CreateMode::TableGet => ObjRef::Table,
988            _ => unreachable!(),
989        }
990    }
991
992    fn emit_access(
993        &self,
994        body: &mut Function,
995        r: ObjRef,
996        local_before: Option<u32>,
997        local_src: Option<u32>,
998    ) {
999        match self.cfg.access_mode {
1000            AccessMode::RefTest => {
1001                self.emit_obj_ref(body, r);
1002                body.instruction(&Instruction::RefTestNonNull(
1003                    wasm_encoder::HeapType::Concrete(self.obj_type.unwrap()),
1004                ));
1005                body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1006            }
1007            AccessMode::StructGet => self.emit_get_access(body, r, local_before),
1008            AccessMode::StructGetS => {
1009                self.emit_obj_ref(body, r);
1010                body.instruction(&Instruction::StructGetS {
1011                    struct_type_index: self.obj_type.unwrap(),
1012                    field_index: 0,
1013                });
1014                body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1015            }
1016            AccessMode::StructGetU => {
1017                self.emit_obj_ref(body, r);
1018                body.instruction(&Instruction::StructGetU {
1019                    struct_type_index: self.obj_type.unwrap(),
1020                    field_index: 0,
1021                });
1022                body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1023            }
1024            AccessMode::StructSet => {
1025                self.emit_obj_ref(body, r);
1026                self.emit_mutation_value(body);
1027                body.instruction(&Instruction::StructSet {
1028                    struct_type_index: self.obj_type.unwrap(),
1029                    field_index: 0,
1030                });
1031            }
1032            AccessMode::ArrayGet => self.emit_get_access(body, r, local_before),
1033            AccessMode::ArrayGetS => {
1034                self.emit_obj_ref(body, r);
1035                body.instruction(&Instruction::I32Const(0));
1036                body.instruction(&Instruction::ArrayGetS(self.obj_type.unwrap()));
1037                body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1038            }
1039            AccessMode::ArrayGetU => {
1040                self.emit_obj_ref(body, r);
1041                body.instruction(&Instruction::I32Const(0));
1042                body.instruction(&Instruction::ArrayGetU(self.obj_type.unwrap()));
1043                body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1044            }
1045            AccessMode::ArraySet => {
1046                self.emit_obj_ref(body, r);
1047                body.instruction(&Instruction::I32Const(0));
1048                self.emit_mutation_value(body);
1049                body.instruction(&Instruction::ArraySet(self.obj_type.unwrap()));
1050            }
1051            AccessMode::ArrayLen => {
1052                self.emit_obj_ref(body, r);
1053                body.instruction(&Instruction::ArrayLen);
1054                body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1055            }
1056            AccessMode::ArrayFill => {
1057                self.emit_obj_ref(body, r);
1058                body.instruction(&Instruction::I32Const(0));
1059                self.emit_mutation_value(body);
1060                body.instruction(&Instruction::I32Const(self.cfg.array_len as i32));
1061                body.instruction(&Instruction::ArrayFill(self.obj_type.unwrap()));
1062            }
1063            AccessMode::ArrayCopy => {
1064                let src = local_src.unwrap();
1065                self.emit_mutation_value(body);
1066                body.instruction(&Instruction::I32Const(self.cfg.array_len as i32));
1067                body.instruction(&Instruction::ArrayNew(self.obj_type.unwrap()));
1068                body.instruction(&Instruction::LocalSet(src));
1069                self.emit_obj_ref(body, r);
1070                body.instruction(&Instruction::I32Const(0));
1071                body.instruction(&Instruction::LocalGet(src));
1072                body.instruction(&Instruction::I32Const(0));
1073                body.instruction(&Instruction::I32Const(self.cfg.array_len as i32));
1074                body.instruction(&Instruction::ArrayCopy {
1075                    array_type_index_dst: self.obj_type.unwrap(),
1076                    array_type_index_src: self.obj_type.unwrap(),
1077                });
1078            }
1079            AccessMode::ArrayInitData => {
1080                self.emit_obj_ref(body, r);
1081                body.instruction(&Instruction::I32Const(0));
1082                body.instruction(&Instruction::I32Const(0));
1083                body.instruction(&Instruction::I32Const(1));
1084                body.instruction(&Instruction::ArrayInitData {
1085                    array_type_index: self.obj_type.unwrap(),
1086                    array_data_index: self.data_dinit.unwrap(),
1087                });
1088            }
1089            AccessMode::ArrayInitElem => {
1090                self.emit_obj_ref(body, r);
1091                body.instruction(&Instruction::I32Const(0));
1092                body.instruction(&Instruction::I32Const(0));
1093                body.instruction(&Instruction::I32Const(1));
1094                body.instruction(&Instruction::ArrayInitElem {
1095                    array_type_index: self.obj_type.unwrap(),
1096                    array_elem_index: self.elem_einit.unwrap(),
1097                });
1098            }
1099        }
1100    }
1101
1102    fn emit_get_access(&self, body: &mut Function, r: ObjRef, local_before: Option<u32>) {
1103        if self.cfg.object_type.elem_type.is_ref() {
1104            if self.cfg.create_mode.is_default() {
1105                self.emit_read_ref(body, r);
1106                body.instruction(&Instruction::Drop);
1107            } else if self.uses_nested_obj() {
1108                self.emit_read_ref(body, r);
1109                body.instruction(&Instruction::Drop);
1110            } else {
1111                self.emit_read_ref(body, r);
1112                body.instruction(&Instruction::RefCastNonNull(
1113                    wasm_encoder::HeapType::Abstract {
1114                        shared: false,
1115                        ty: wasm_encoder::AbstractHeapType::I31,
1116                    },
1117                ));
1118                body.instruction(&Instruction::I31GetU);
1119                body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1120            }
1121        } else if self.cfg.object_type.is_array() {
1122            self.emit_obj_ref(body, r);
1123            body.instruction(&Instruction::I32Const(0));
1124            body.instruction(&Instruction::ArrayGet(self.obj_type.unwrap()));
1125            body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1126        } else {
1127            self.emit_obj_ref(body, r);
1128            body.instruction(&Instruction::StructGet {
1129                struct_type_index: self.obj_type.unwrap(),
1130                field_index: 0,
1131            });
1132            body.instruction(&Instruction::LocalSet(local_before.unwrap()));
1133        }
1134    }
1135
1136    fn emit_assert(&self, body: &mut Function, r: ObjRef, local_before: Option<u32>) {
1137        match self.cfg.access_mode {
1138            AccessMode::RefTest => {
1139                body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1140                self.emit_obj_ref(body, r);
1141                body.instruction(&Instruction::RefTestNonNull(
1142                    wasm_encoder::HeapType::Concrete(self.obj_type.unwrap()),
1143                ));
1144                body.instruction(&Instruction::I32Ne);
1145                self.emit_trap_if(body);
1146            }
1147            AccessMode::StructGet | AccessMode::ArrayGet => {
1148                self.emit_get_assert(body, r, local_before)
1149            }
1150            AccessMode::StructGetS => {
1151                body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1152                self.emit_obj_ref(body, r);
1153                body.instruction(&Instruction::StructGetS {
1154                    struct_type_index: self.obj_type.unwrap(),
1155                    field_index: 0,
1156                });
1157                body.instruction(&Instruction::I32Ne);
1158                self.emit_trap_if(body);
1159            }
1160            AccessMode::StructGetU => {
1161                body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1162                self.emit_obj_ref(body, r);
1163                body.instruction(&Instruction::StructGetU {
1164                    struct_type_index: self.obj_type.unwrap(),
1165                    field_index: 0,
1166                });
1167                body.instruction(&Instruction::I32Ne);
1168                self.emit_trap_if(body);
1169            }
1170            AccessMode::StructSet => {
1171                self.emit_set_assert(body, r);
1172            }
1173            AccessMode::ArrayGetS => {
1174                body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1175                self.emit_obj_ref(body, r);
1176                body.instruction(&Instruction::I32Const(0));
1177                body.instruction(&Instruction::ArrayGetS(self.obj_type.unwrap()));
1178                body.instruction(&Instruction::I32Ne);
1179                self.emit_trap_if(body);
1180            }
1181            AccessMode::ArrayGetU => {
1182                body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1183                self.emit_obj_ref(body, r);
1184                body.instruction(&Instruction::I32Const(0));
1185                body.instruction(&Instruction::ArrayGetU(self.obj_type.unwrap()));
1186                body.instruction(&Instruction::I32Ne);
1187                self.emit_trap_if(body);
1188            }
1189            AccessMode::ArraySet => {
1190                self.emit_set_assert(body, r);
1191            }
1192            AccessMode::ArrayLen => {
1193                body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1194                self.emit_obj_ref(body, r);
1195                body.instruction(&Instruction::ArrayLen);
1196                body.instruction(&Instruction::I32Ne);
1197                self.emit_trap_if(body);
1198            }
1199            AccessMode::ArrayFill => {
1200                self.emit_mutation_assert(body, r);
1201            }
1202            AccessMode::ArrayCopy => {
1203                self.emit_mutation_assert(body, r);
1204            }
1205            AccessMode::ArrayInitData => {
1206                self.emit_read_field(body, r, 0);
1207                let expected = self.expected_numeric_readback(self.cfg.mutation_value);
1208                body.instruction(&Instruction::I32Const(expected));
1209                body.instruction(&Instruction::I32Ne);
1210                self.emit_trap_if(body);
1211            }
1212            AccessMode::ArrayInitElem => {
1213                self.emit_obj_ref(body, r);
1214                body.instruction(&Instruction::I32Const(0));
1215                body.instruction(&Instruction::ArrayGet(self.obj_type.unwrap()));
1216                body.instruction(&Instruction::RefCastNonNull(
1217                    wasm_encoder::HeapType::Abstract {
1218                        shared: false,
1219                        ty: wasm_encoder::AbstractHeapType::I31,
1220                    },
1221                ));
1222                body.instruction(&Instruction::I31GetU);
1223                body.instruction(&Instruction::I32Const(
1224                    self.cfg.mutation_value & 0x7FFF_FFFF,
1225                ));
1226                body.instruction(&Instruction::I32Ne);
1227                self.emit_trap_if(body);
1228            }
1229        }
1230    }
1231
1232    fn emit_get_assert(&self, body: &mut Function, r: ObjRef, local_before: Option<u32>) {
1233        if self.cfg.object_type.elem_type.is_ref() {
1234            if self.cfg.create_mode.is_default() {
1235                self.emit_read_ref(body, r);
1236                body.instruction(&Instruction::RefIsNull);
1237                body.instruction(&Instruction::I32Eqz);
1238                self.emit_trap_if(body);
1239            } else if self.uses_nested_obj() {
1240                self.emit_read_ref(body, r);
1241                body.instruction(&Instruction::RefIsNull);
1242                self.emit_trap_if(body);
1243            } else {
1244                body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1245                self.emit_read_ref(body, r);
1246                body.instruction(&Instruction::RefCastNonNull(
1247                    wasm_encoder::HeapType::Abstract {
1248                        shared: false,
1249                        ty: wasm_encoder::AbstractHeapType::I31,
1250                    },
1251                ));
1252                body.instruction(&Instruction::I31GetU);
1253                body.instruction(&Instruction::I32Ne);
1254                self.emit_trap_if(body);
1255            }
1256        } else if self.cfg.object_type.is_array() {
1257            body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1258            self.emit_obj_ref(body, r);
1259            body.instruction(&Instruction::I32Const(0));
1260            body.instruction(&Instruction::ArrayGet(self.obj_type.unwrap()));
1261            body.instruction(&Instruction::I32Ne);
1262            self.emit_trap_if(body);
1263        } else {
1264            body.instruction(&Instruction::LocalGet(local_before.unwrap()));
1265            self.emit_obj_ref(body, r);
1266            body.instruction(&Instruction::StructGet {
1267                struct_type_index: self.obj_type.unwrap(),
1268                field_index: 0,
1269            });
1270            body.instruction(&Instruction::I32Ne);
1271            self.emit_trap_if(body);
1272        }
1273    }
1274
1275    fn emit_set_assert(&self, body: &mut Function, r: ObjRef) {
1276        if self.cfg.object_type.elem_type.is_ref() {
1277            self.emit_read_ref(body, r);
1278            body.instruction(&Instruction::RefCastNonNull(
1279                wasm_encoder::HeapType::Abstract {
1280                    shared: false,
1281                    ty: wasm_encoder::AbstractHeapType::I31,
1282                },
1283            ));
1284            body.instruction(&Instruction::I31GetU);
1285            body.instruction(&Instruction::I32Const(
1286                self.cfg.mutation_value & 0x7FFF_FFFF,
1287            ));
1288            body.instruction(&Instruction::I32Ne);
1289            self.emit_trap_if(body);
1290        } else {
1291            self.emit_read_field(body, r, 0);
1292            let expected = self.expected_numeric_readback(self.cfg.mutation_value);
1293            body.instruction(&Instruction::I32Const(expected));
1294            body.instruction(&Instruction::I32Ne);
1295            self.emit_trap_if(body);
1296        }
1297    }
1298
1299    fn emit_mutation_assert(&self, body: &mut Function, r: ObjRef) {
1300        if self.cfg.object_type.elem_type.is_ref() {
1301            self.emit_obj_ref(body, r);
1302            body.instruction(&Instruction::I32Const(0));
1303            body.instruction(&Instruction::ArrayGet(self.obj_type.unwrap()));
1304            body.instruction(&Instruction::RefCastNonNull(
1305                wasm_encoder::HeapType::Abstract {
1306                    shared: false,
1307                    ty: wasm_encoder::AbstractHeapType::I31,
1308                },
1309            ));
1310            body.instruction(&Instruction::I31GetU);
1311            body.instruction(&Instruction::I32Const(
1312                self.cfg.mutation_value & 0x7FFF_FFFF,
1313            ));
1314        } else {
1315            self.emit_read_field(body, r, 0);
1316            let expected = self.expected_numeric_readback(self.cfg.mutation_value);
1317            body.instruction(&Instruction::I32Const(expected));
1318        }
1319        body.instruction(&Instruction::I32Ne);
1320        self.emit_trap_if(body);
1321    }
1322
1323    fn emit_read_ref(&self, body: &mut Function, r: ObjRef) {
1324        if self.cfg.object_type.is_array() {
1325            self.emit_obj_ref(body, r);
1326            body.instruction(&Instruction::I32Const(0));
1327            body.instruction(&Instruction::ArrayGet(self.obj_type.unwrap()));
1328        } else {
1329            self.emit_obj_ref(body, r);
1330            body.instruction(&Instruction::StructGet {
1331                struct_type_index: self.obj_type.unwrap(),
1332                field_index: 0,
1333            });
1334        }
1335    }
1336
1337    fn emit_read_field(&self, body: &mut Function, r: ObjRef, idx: u32) {
1338        if self.cfg.object_type.is_array() {
1339            self.emit_obj_ref(body, r);
1340            body.instruction(&Instruction::I32Const(idx as i32));
1341            if self.cfg.object_type.elem_type.is_packed() {
1342                body.instruction(&Instruction::ArrayGetU(self.obj_type.unwrap()));
1343            } else {
1344                body.instruction(&Instruction::ArrayGet(self.obj_type.unwrap()));
1345            }
1346        } else {
1347            self.emit_obj_ref(body, r);
1348            if self.cfg.object_type.elem_type.is_packed() {
1349                body.instruction(&Instruction::StructGetU {
1350                    struct_type_index: self.obj_type.unwrap(),
1351                    field_index: idx,
1352                });
1353            } else {
1354                body.instruction(&Instruction::StructGet {
1355                    struct_type_index: self.obj_type.unwrap(),
1356                    field_index: idx,
1357                });
1358            }
1359        }
1360    }
1361
1362    fn emit_trap_if(&self, body: &mut Function) {
1363        body.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
1364        body.instruction(&Instruction::Unreachable);
1365        body.instruction(&Instruction::End);
1366    }
1367
1368    /// Normal (non-idempotent, non-local_get) run body.
1369    ///
1370    /// Layout follows INSTRUCTIONS.md: outer loop with create + access + gc +
1371    /// assert, then inner loop with access + assert, then trailing access +
1372    /// assert.
1373    fn emit_normal_body(&self, body: &mut Function) {
1374        // locals: $x=0, $i=1, $j=2, [$before=3, [$src=4]]
1375        let local_x = 0u32;
1376        let local_i = 1u32;
1377        let local_j = 2u32;
1378        let local_before = if self.needs_before() {
1379            Some(3u32)
1380        } else {
1381            None
1382        };
1383        let local_src = if matches!(self.cfg.access_mode, AccessMode::ArrayCopy) {
1384            Some(if self.needs_before() { 4u32 } else { 3u32 })
1385        } else {
1386            None
1387        };
1388        let r = ObjRef::Local(local_x);
1389
1390        // i = 0 (already 0)
1391        // (loop $outer
1392        body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
1393
1394        //   (if (i32.ge_u $i 10) (return))
1395        body.instruction(&Instruction::LocalGet(local_i));
1396        body.instruction(&Instruction::I32Const(10));
1397        body.instruction(&Instruction::I32GeU);
1398        body.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
1399        body.instruction(&Instruction::Return);
1400        body.instruction(&Instruction::End);
1401
1402        //   (local.set $x CREATE)
1403        self.emit_create(body);
1404        body.instruction(&Instruction::LocalSet(local_x));
1405
1406        //   ACCESS
1407        self.emit_access(body, r, local_before, local_src);
1408        //   gc()
1409        body.instruction(&Instruction::Call(self.gc_func.unwrap()));
1410        //   ASSERT
1411        self.emit_assert(body, r, local_before);
1412
1413        //   j = 0
1414        body.instruction(&Instruction::I32Const(0));
1415        body.instruction(&Instruction::LocalSet(local_j));
1416
1417        //   (block $break (loop $inner
1418        body.instruction(&Instruction::Block(wasm_encoder::BlockType::Empty));
1419        body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
1420
1421        //     (if (i32.lt_u $j 10)
1422        body.instruction(&Instruction::LocalGet(local_j));
1423        body.instruction(&Instruction::I32Const(10));
1424        body.instruction(&Instruction::I32LtU);
1425        body.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
1426
1427        //       (then ACCESS; ASSERT; j += 1; br $inner)
1428        self.emit_access(body, r, local_before, local_src);
1429        self.emit_assert(body, r, local_before);
1430        body.instruction(&Instruction::LocalGet(local_j));
1431        body.instruction(&Instruction::I32Const(1));
1432        body.instruction(&Instruction::I32Add);
1433        body.instruction(&Instruction::LocalSet(local_j));
1434        body.instruction(&Instruction::Br(1)); // br $inner
1435
1436        //       (else ACCESS; ASSERT; j += 1; br $break)
1437        body.instruction(&Instruction::Else);
1438        self.emit_access(body, r, local_before, local_src);
1439        self.emit_assert(body, r, local_before);
1440        body.instruction(&Instruction::LocalGet(local_j));
1441        body.instruction(&Instruction::I32Const(1));
1442        body.instruction(&Instruction::I32Add);
1443        body.instruction(&Instruction::LocalSet(local_j));
1444        body.instruction(&Instruction::Br(2)); // br $break
1445
1446        body.instruction(&Instruction::End); // end if
1447        body.instruction(&Instruction::End); // end loop $inner
1448        body.instruction(&Instruction::End); // end block $break
1449
1450        //   ACCESS; ASSERT
1451        self.emit_access(body, r, local_before, local_src);
1452        self.emit_assert(body, r, local_before);
1453
1454        //   i += 1
1455        body.instruction(&Instruction::LocalGet(local_i));
1456        body.instruction(&Instruction::I32Const(1));
1457        body.instruction(&Instruction::I32Add);
1458        body.instruction(&Instruction::LocalSet(local_i));
1459
1460        //   br $outer
1461        body.instruction(&Instruction::Br(0));
1462
1463        body.instruction(&Instruction::End); // end loop $outer
1464    }
1465
1466    /// Idempotent (global_get / table_get) run body — no $x local.
1467    fn emit_idempotent_body(&self, body: &mut Function) {
1468        // locals: $i=0, $j=1, [$before=2, [$src=3]]
1469        let local_i = 0u32;
1470        let local_j = 1u32;
1471        let local_before = if self.needs_before() {
1472            Some(2u32)
1473        } else {
1474            None
1475        };
1476        let local_src = if matches!(self.cfg.access_mode, AccessMode::ArrayCopy) {
1477            Some(if self.needs_before() { 3u32 } else { 2u32 })
1478        } else {
1479            None
1480        };
1481        let r = self.idempotent_ref();
1482
1483        body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
1484
1485        body.instruction(&Instruction::LocalGet(local_i));
1486        body.instruction(&Instruction::I32Const(10));
1487        body.instruction(&Instruction::I32GeU);
1488        body.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
1489        body.instruction(&Instruction::Return);
1490        body.instruction(&Instruction::End);
1491
1492        self.emit_access(body, r, local_before, local_src);
1493        body.instruction(&Instruction::Call(self.gc_func.unwrap()));
1494        self.emit_assert(body, r, local_before);
1495
1496        body.instruction(&Instruction::I32Const(0));
1497        body.instruction(&Instruction::LocalSet(local_j));
1498
1499        body.instruction(&Instruction::Block(wasm_encoder::BlockType::Empty));
1500        body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
1501
1502        body.instruction(&Instruction::LocalGet(local_j));
1503        body.instruction(&Instruction::I32Const(10));
1504        body.instruction(&Instruction::I32LtU);
1505        body.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
1506
1507        self.emit_access(body, r, local_before, local_src);
1508        self.emit_assert(body, r, local_before);
1509        body.instruction(&Instruction::LocalGet(local_j));
1510        body.instruction(&Instruction::I32Const(1));
1511        body.instruction(&Instruction::I32Add);
1512        body.instruction(&Instruction::LocalSet(local_j));
1513        body.instruction(&Instruction::Br(1));
1514
1515        body.instruction(&Instruction::Else);
1516        self.emit_access(body, r, local_before, local_src);
1517        self.emit_assert(body, r, local_before);
1518        body.instruction(&Instruction::LocalGet(local_j));
1519        body.instruction(&Instruction::I32Const(1));
1520        body.instruction(&Instruction::I32Add);
1521        body.instruction(&Instruction::LocalSet(local_j));
1522        body.instruction(&Instruction::Br(2));
1523
1524        body.instruction(&Instruction::End); // end if
1525        body.instruction(&Instruction::End); // end loop $inner
1526        body.instruction(&Instruction::End); // end block $break
1527
1528        self.emit_access(body, r, local_before, local_src);
1529        self.emit_assert(body, r, local_before);
1530
1531        body.instruction(&Instruction::LocalGet(local_i));
1532        body.instruction(&Instruction::I32Const(1));
1533        body.instruction(&Instruction::I32Add);
1534        body.instruction(&Instruction::LocalSet(local_i));
1535
1536        body.instruction(&Instruction::Br(0));
1537        body.instruction(&Instruction::End); // end loop $outer
1538    }
1539
1540    /// local_get mode — param $p is the object ref.
1541    fn emit_local_get_body(&self, body: &mut Function) {
1542        // param: $p=0, locals: $i=1, $j=2, [$before=3, [$src=4]]
1543        let param_p = 0u32;
1544        let local_i = 1u32;
1545        let local_j = 2u32;
1546        let local_before = if self.needs_before() {
1547            Some(3u32)
1548        } else {
1549            None
1550        };
1551        let local_src = if matches!(self.cfg.access_mode, AccessMode::ArrayCopy) {
1552            Some(if self.needs_before() { 4u32 } else { 3u32 })
1553        } else {
1554            None
1555        };
1556        let r = ObjRef::Local(param_p);
1557
1558        body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
1559
1560        body.instruction(&Instruction::LocalGet(local_i));
1561        body.instruction(&Instruction::I32Const(10));
1562        body.instruction(&Instruction::I32GeU);
1563        body.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
1564        body.instruction(&Instruction::Return);
1565        body.instruction(&Instruction::End);
1566
1567        self.emit_access(body, r, local_before, local_src);
1568        body.instruction(&Instruction::Call(self.gc_func.unwrap()));
1569        self.emit_assert(body, r, local_before);
1570
1571        body.instruction(&Instruction::I32Const(0));
1572        body.instruction(&Instruction::LocalSet(local_j));
1573
1574        body.instruction(&Instruction::Block(wasm_encoder::BlockType::Empty));
1575        body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
1576
1577        body.instruction(&Instruction::LocalGet(local_j));
1578        body.instruction(&Instruction::I32Const(10));
1579        body.instruction(&Instruction::I32LtU);
1580        body.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
1581
1582        self.emit_access(body, r, local_before, local_src);
1583        self.emit_assert(body, r, local_before);
1584        body.instruction(&Instruction::LocalGet(local_j));
1585        body.instruction(&Instruction::I32Const(1));
1586        body.instruction(&Instruction::I32Add);
1587        body.instruction(&Instruction::LocalSet(local_j));
1588        body.instruction(&Instruction::Br(1));
1589
1590        body.instruction(&Instruction::Else);
1591        self.emit_access(body, r, local_before, local_src);
1592        self.emit_assert(body, r, local_before);
1593        body.instruction(&Instruction::LocalGet(local_j));
1594        body.instruction(&Instruction::I32Const(1));
1595        body.instruction(&Instruction::I32Add);
1596        body.instruction(&Instruction::LocalSet(local_j));
1597        body.instruction(&Instruction::Br(2));
1598
1599        body.instruction(&Instruction::End); // end if
1600        body.instruction(&Instruction::End); // end loop $inner
1601        body.instruction(&Instruction::End); // end block $break
1602
1603        self.emit_access(body, r, local_before, local_src);
1604        self.emit_assert(body, r, local_before);
1605
1606        body.instruction(&Instruction::LocalGet(local_i));
1607        body.instruction(&Instruction::I32Const(1));
1608        body.instruction(&Instruction::I32Add);
1609        body.instruction(&Instruction::LocalSet(local_i));
1610
1611        body.instruction(&Instruction::Br(0));
1612        body.instruction(&Instruction::End); // end loop $outer
1613    }
1614}
1615
1616/// How the test references the GC object when performing accesses and
1617/// assertions.
1618#[derive(Clone, Copy)]
1619enum ObjRef {
1620    /// The object is held in a local variable.
1621    Local(u32),
1622    /// The object is retrieved from an immutable global each time.
1623    Global,
1624    /// The object is retrieved from a table slot each time.
1625    Table,
1626}
1627
1628fn sec(module: &mut Module, section: impl wasm_encoder::Section) {
1629    module.section(&section);
1630}
1631
1632fn finish(code: &mut CodeSection, mut f: Function) {
1633    f.instruction(&Instruction::End);
1634    code.function(&f);
1635}
1636
1637#[cfg(test)]
1638mod tests {
1639    use super::*;
1640    use wasmparser::Validator;
1641
1642    #[test]
1643    fn gc_access_generates_valid_wasm() {
1644        crate::test::test_n_times(1024, |input: GcAccess, _u| {
1645            let wasm = input.to_wasm();
1646            validate(&wasm);
1647            Ok(())
1648        })
1649    }
1650
1651    fn validate(wasm: &[u8]) {
1652        let mut validator = Validator::new_with_features(wasmparser::WasmFeatures::all());
1653        let err = match validator.validate_all(wasm) {
1654            Ok(_) => return,
1655            Err(e) => e,
1656        };
1657        drop(std::fs::write("test.wasm", wasm));
1658        if let Ok(text) = wasmprinter::print_bytes(wasm) {
1659            drop(std::fs::write("test.wat", &text));
1660        }
1661        panic!("wasm failed to validate: {err}");
1662    }
1663}