1 //! Operations for the `gc` operations.
2 
3 use crate::generators::gc_ops::types::StackType;
4 use crate::generators::gc_ops::{
5     limits::GcOpsLimits,
6     types::{CompositeType, RecGroupId, StructType, TypeId, Types},
7 };
8 use serde::{Deserialize, Serialize};
9 use std::collections::BTreeMap;
10 use wasm_encoder::{
11     CodeSection, ConstExpr, EntityType, ExportKind, ExportSection, Function, FunctionSection,
12     GlobalSection, ImportSection, Instruction, Module, RefType, TableSection, TableType,
13     TypeSection, ValType,
14 };
15 
16 /// The base offsets and indices for various Wasm entities within
17 /// their index spaces in the the encoded Wasm binary.
18 #[derive(Clone, Copy)]
19 struct WasmEncodingBases {
20     struct_type_base: u32,
21     typed_first_func_index: u32,
22     struct_local_idx: u32,
23     typed_local_base: u32,
24     struct_global_idx: u32,
25     typed_global_base: u32,
26     struct_table_idx: u32,
27     typed_table_base: u32,
28 }
29 
30 /// A description of a Wasm module that makes a series of `externref` table
31 /// operations.
32 #[derive(Debug, Default, Serialize, Deserialize)]
33 pub struct GcOps {
34     pub(crate) limits: GcOpsLimits,
35     pub(crate) ops: Vec<GcOp>,
36     pub(crate) types: Types,
37 }
38 
39 impl GcOps {
40     /// Serialize this module into a Wasm binary.
41     ///
42     /// The module requires several function imports. See this function's
43     /// implementation for their exact types.
44     ///
45     /// The single export of the module is a function "run" that takes
46     /// `self.num_params` parameters of type `externref`.
47     ///
48     /// The "run" function does not terminate; you should run it with limited
49     /// fuel. It also is not guaranteed to avoid traps: it may access
50     /// out-of-bounds of the table.
to_wasm_binary(&mut self) -> Vec<u8>51     pub fn to_wasm_binary(&mut self) -> Vec<u8> {
52         self.fixup();
53 
54         let mut module = Module::new();
55 
56         // Encode the types for all functions that we are using.
57         let mut types = TypeSection::new();
58 
59         // 0: "gc"
60         types.ty().function(
61             vec![],
62             // Return a bunch of stuff from `gc` so that we exercise GCing when
63             // there is return pointer space allocated on the stack. This is
64             // especially important because the x64 backend currently
65             // dynamically adjusts the stack pointer for each call that uses
66             // return pointers rather than statically allocating space in the
67             // stack frame.
68             vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF],
69         );
70 
71         // 1: "run"
72         let mut params: Vec<ValType> = Vec::with_capacity(
73             usize::try_from(self.limits.num_params).expect("num_params is too large"),
74         );
75         for _i in 0..self.limits.num_params {
76             params.push(ValType::EXTERNREF);
77         }
78         let params_len =
79             u32::try_from(params.len()).expect("params len should be within u32 range");
80         let results = vec![];
81         types.ty().function(params, results);
82 
83         // 2: `take_refs`
84         types.ty().function(
85             vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF],
86             vec![],
87         );
88 
89         // 3: `make_refs`
90         types.ty().function(
91             vec![],
92             vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF],
93         );
94 
95         // 4: `take_struct`
96         types.ty().function(
97             vec![ValType::Ref(RefType {
98                 nullable: true,
99                 heap_type: wasm_encoder::HeapType::Abstract {
100                     shared: false,
101                     ty: wasm_encoder::AbstractHeapType::Struct,
102                 },
103             })],
104             vec![],
105         );
106 
107         let struct_type_base: u32 = types.len();
108 
109         // Sort all types globally so supertypes come before subtypes.
110         // This is used to order types *within* each rec group.
111         let mut type_order = Vec::new();
112         self.types.sort_types_topo(&mut type_order);
113 
114         // Build a position map so we can sort each group's members
115         // according to the global type order.
116         let type_position: BTreeMap<TypeId, usize> = type_order
117             .iter()
118             .enumerate()
119             .map(|(i, &id)| (id, i))
120             .collect();
121 
122         // Topological sort of rec groups: if a type in group G has a
123         // supertype in group H, then H appears before G.
124         let mut group_order = Vec::new();
125         self.types.sort_rec_groups_topo(&mut group_order);
126 
127         // For each group, collect its members sorted by the global type order.
128         let mut group_members: BTreeMap<RecGroupId, Vec<TypeId>> = BTreeMap::new();
129         for &gid in &group_order {
130             if let Some(member_set) = self.types.rec_groups.get(&gid) {
131                 let mut members: Vec<TypeId> = member_set.iter().copied().collect();
132                 members.sort_by_key(|tid| type_position.get(tid).copied().unwrap_or(usize::MAX));
133                 group_members.insert(gid, members);
134             }
135         }
136 
137         // Build the type-id-to-wasm-index map directly.
138         let mut type_ids_to_index: BTreeMap<TypeId, u32> = BTreeMap::new();
139         let mut next_idx = struct_type_base;
140         for g in &group_order {
141             if let Some(members) = group_members.get(g) {
142                 for &tid in members {
143                     type_ids_to_index.insert(tid, next_idx);
144                     next_idx += 1;
145                 }
146             }
147         }
148 
149         let encode_ty_id = |ty_id: &TypeId| -> wasm_encoder::SubType {
150             let def = &self.types.type_defs[ty_id];
151             match &def.composite_type {
152                 CompositeType::Struct(StructType {}) => wasm_encoder::SubType {
153                     is_final: def.is_final,
154                     supertype_idx: def.supertype.map(|st| type_ids_to_index[&st]),
155                     composite_type: wasm_encoder::CompositeType {
156                         inner: wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType {
157                             fields: Box::new([]),
158                         }),
159                         shared: false,
160                         describes: None,
161                         descriptor: None,
162                     },
163                 },
164             }
165         };
166 
167         let mut struct_count = 0;
168 
169         // Emit rec groups in the derived order.
170         for g in &group_order {
171             let type_ids = group_members.get(g).map(|v| v.as_slice()).unwrap_or(&[]);
172             let members: Vec<wasm_encoder::SubType> = type_ids.iter().map(encode_ty_id).collect();
173             types.ty().rec(members);
174             struct_count += u32::try_from(type_ids.len()).unwrap();
175         }
176 
177         let typed_fn_type_base: u32 = struct_type_base + struct_count;
178 
179         for i in 0..struct_count {
180             let concrete = struct_type_base + i;
181             types.ty().function(
182                 vec![ValType::Ref(RefType {
183                     nullable: true,
184                     heap_type: wasm_encoder::HeapType::Concrete(concrete),
185                 })],
186                 vec![],
187             );
188         }
189 
190         // Import the GC function.
191         let mut imports = ImportSection::new();
192         imports.import("", "gc", EntityType::Function(0));
193         imports.import("", "take_refs", EntityType::Function(2));
194         imports.import("", "make_refs", EntityType::Function(3));
195         imports.import("", "take_struct", EntityType::Function(4));
196 
197         // For each of our concrete struct types, define a function
198         // import that takes an argument of that concrete type.
199         let typed_first_func_index: u32 = imports.len();
200 
201         for i in 0..struct_count {
202             let ty_idx = typed_fn_type_base + i;
203             let name = format!("take_struct_{}", struct_type_base + i);
204             imports.import("", &name, EntityType::Function(ty_idx));
205         }
206 
207         // Define our table.
208         let mut tables = TableSection::new();
209         tables.table(TableType {
210             element_type: RefType::EXTERNREF,
211             minimum: u64::from(self.limits.table_size),
212             maximum: None,
213             table64: false,
214             shared: false,
215         });
216 
217         let struct_table_idx = tables.len();
218         tables.table(TableType {
219             element_type: RefType {
220                 nullable: true,
221                 heap_type: wasm_encoder::HeapType::Abstract {
222                     shared: false,
223                     ty: wasm_encoder::AbstractHeapType::Struct,
224                 },
225             },
226             minimum: u64::from(self.limits.table_size),
227             maximum: None,
228             table64: false,
229             shared: false,
230         });
231 
232         let typed_table_base = tables.len();
233         for i in 0..struct_count {
234             let concrete = struct_type_base + i;
235             tables.table(TableType {
236                 element_type: RefType {
237                     nullable: true,
238                     heap_type: wasm_encoder::HeapType::Concrete(concrete),
239                 },
240                 minimum: u64::from(self.limits.table_size),
241                 maximum: None,
242                 table64: false,
243                 shared: false,
244             });
245         }
246 
247         // Define our globals.
248         let mut globals = GlobalSection::new();
249         for _ in 0..self.limits.num_globals {
250             globals.global(
251                 wasm_encoder::GlobalType {
252                     val_type: wasm_encoder::ValType::EXTERNREF,
253                     mutable: true,
254                     shared: false,
255                 },
256                 &ConstExpr::ref_null(wasm_encoder::HeapType::EXTERN),
257             );
258         }
259 
260         // Add exactly one (ref.null struct) global.
261         let struct_global_idx = globals.len();
262         globals.global(
263             wasm_encoder::GlobalType {
264                 val_type: ValType::Ref(RefType {
265                     nullable: true,
266                     heap_type: wasm_encoder::HeapType::Abstract {
267                         shared: false,
268                         ty: wasm_encoder::AbstractHeapType::Struct,
269                     },
270                 }),
271                 mutable: true,
272                 shared: false,
273             },
274             &ConstExpr::ref_null(wasm_encoder::HeapType::Abstract {
275                 shared: false,
276                 ty: wasm_encoder::AbstractHeapType::Struct,
277             }),
278         );
279 
280         // Add one typed (ref <type>) global per struct type.
281         let typed_global_base = globals.len();
282         for i in 0..struct_count {
283             let concrete = struct_type_base + i;
284             globals.global(
285                 wasm_encoder::GlobalType {
286                     val_type: ValType::Ref(RefType {
287                         nullable: true,
288                         heap_type: wasm_encoder::HeapType::Concrete(concrete),
289                     }),
290                     mutable: true,
291                     shared: false,
292                 },
293                 &ConstExpr::ref_null(wasm_encoder::HeapType::Concrete(concrete)),
294             );
295         }
296 
297         // Define the "run" function export.
298         let mut functions = FunctionSection::new();
299         let mut exports = ExportSection::new();
300 
301         let run_defined_idx = functions.len();
302         functions.function(1);
303         let run_func_index = imports.len() + run_defined_idx;
304         exports.export("run", ExportKind::Func, run_func_index);
305 
306         // Give ourselves one scratch local that we can use in various `GcOp`
307         // implementations.
308         let mut local_decls: Vec<(u32, ValType)> = vec![(1, ValType::EXTERNREF)];
309 
310         let scratch_local = params_len;
311         let struct_local_idx = scratch_local + 1;
312         local_decls.push((
313             1,
314             ValType::Ref(RefType {
315                 nullable: true,
316                 heap_type: wasm_encoder::HeapType::Abstract {
317                     shared: false,
318                     ty: wasm_encoder::AbstractHeapType::Struct,
319                 },
320             }),
321         ));
322 
323         let typed_local_base: u32 = struct_local_idx + 1;
324         for i in 0..struct_count {
325             let concrete = struct_type_base + i;
326             local_decls.push((
327                 1,
328                 ValType::Ref(RefType {
329                     nullable: true,
330                     heap_type: wasm_encoder::HeapType::Concrete(concrete),
331                 }),
332             ));
333         }
334 
335         let storage_bases = WasmEncodingBases {
336             struct_type_base,
337             typed_first_func_index,
338             struct_local_idx,
339             typed_local_base,
340             struct_global_idx,
341             typed_global_base,
342             struct_table_idx,
343             typed_table_base,
344         };
345 
346         let mut func = Function::new(local_decls);
347         func.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
348         for op in &self.ops {
349             op.encode(&mut func, scratch_local, storage_bases);
350         }
351         func.instruction(&Instruction::Br(0));
352         func.instruction(&Instruction::End);
353         func.instruction(&Instruction::End);
354 
355         let mut code = CodeSection::new();
356         code.function(&func);
357 
358         module
359             .section(&types)
360             .section(&imports)
361             .section(&functions)
362             .section(&tables)
363             .section(&globals)
364             .section(&exports)
365             .section(&code);
366 
367         module.finish()
368     }
369 
370     /// Fixes this test case such that it becomes valid.
371     ///
372     /// This is necessary because a random mutation (e.g. removing an op in the
373     /// middle of our sequence) might have made it so that subsequent ops won't
374     /// have their expected operand types on the Wasm stack
375     /// anymore. Furthermore, because we serialize and deserialize test cases,
376     /// and libFuzzer will occasionally mutate those serialized bytes directly,
377     /// rather than use one of our custom mutations, we have no guarantee that
378     /// pre-mutation test cases are even valid! Therefore, we always call this
379     /// method before translating this "AST"-style representation into a raw
380     /// Wasm binary.
fixup(&mut self)381     pub fn fixup(&mut self) {
382         self.limits.fixup();
383         self.types.fixup(&self.limits);
384 
385         let mut new_ops = Vec::with_capacity(self.ops.len());
386         let mut stack: Vec<StackType> = Vec::new();
387         let num_types = u32::try_from(self.types.type_defs.len()).unwrap();
388 
389         let mut operand_types = Vec::new();
390         for op in &self.ops {
391             let Some(op) = op.fixup(&self.limits, num_types) else {
392                 continue;
393             };
394 
395             debug_assert!(operand_types.is_empty());
396             op.operand_types(&mut operand_types);
397             for ty in operand_types.drain(..) {
398                 StackType::fixup(ty, &mut stack, &mut new_ops, num_types);
399             }
400 
401             // Finally, emit the op itself (updates stack abstractly)
402             let mut result_types = Vec::new();
403             StackType::emit(op, &mut stack, &mut new_ops, num_types, &mut result_types);
404         }
405 
406         // Drop any remaining values on the operand stack.
407         for _ in 0..stack.len() {
408             new_ops.push(GcOp::Drop);
409         }
410 
411         log::trace!("ops after fixup: {new_ops:#?}");
412         self.ops = new_ops;
413     }
414 
415     /// Attempts to remove the last opcode from the sequence.
416     ///
417     /// Returns `true` if an opcode was successfully removed, or `false` if the
418     /// list was already empty.
pop(&mut self) -> bool419     pub fn pop(&mut self) -> bool {
420         self.ops.pop().is_some()
421     }
422 }
423 
424 macro_rules! for_each_gc_op {
425     ( $mac:ident ) => {
426         $mac! {
427             #[operands([])]
428             #[results([ExternRef, ExternRef, ExternRef])]
429             Gc,
430 
431             #[operands([])]
432             #[results([ExternRef, ExternRef, ExternRef])]
433             MakeRefs,
434 
435             #[operands([Some(ExternRef), Some(ExternRef), Some(ExternRef)])]
436             #[results([])]
437             TakeRefs,
438 
439             #[operands([])]
440             #[results([ExternRef])]
441             #[fixup(|limits, _num_types| {
442                 // Add one to make sure that out-of-bounds table accesses are
443                 // possible, but still rare.
444                 elem_index = elem_index % (limits.table_size + 1);
445             })]
446             TableGet { elem_index: u32 },
447 
448             #[operands([Some(ExternRef)])]
449             #[results([])]
450             #[fixup(|limits, _num_types| {
451                 // Add one to make sure that out-of-bounds table accesses are
452                 // possible, but still rare.
453                 elem_index = elem_index % (limits.table_size + 1);
454             })]
455             TableSet { elem_index: u32 },
456 
457             #[operands([])]
458             #[results([ExternRef])]
459             #[fixup(|limits, _num_types| {
460                 global_index = global_index.checked_rem(limits.num_globals)?;
461             })]
462             GlobalGet { global_index:  u32 },
463 
464             #[operands([Some(ExternRef)])]
465             #[results([])]
466             #[fixup(|limits, _num_types| {
467                 global_index = global_index.checked_rem(limits.num_globals)?;
468             })]
469             GlobalSet { global_index: u32 },
470 
471             #[operands([])]
472             #[results([ExternRef])]
473             #[fixup(|limits, _num_types| {
474                 local_index = local_index.checked_rem(limits.num_params)?;
475             })]
476             LocalGet { local_index: u32 },
477 
478             #[operands([Some(ExternRef)])]
479             #[results([])]
480             #[fixup(|limits, _num_types| {
481                 local_index = local_index.checked_rem(limits.num_params)?;
482             })]
483             LocalSet { local_index: u32 },
484 
485             #[operands([])]
486             #[results([Struct(Some(type_index))])]
487             #[fixup(|_limits, num_types| {
488                 type_index = type_index.checked_rem(num_types)?;
489             })]
490             StructNew { type_index: u32 },
491 
492             #[operands([Some(Struct(None))])]
493             #[results([])]
494             TakeStructCall,
495 
496             #[operands([Some(Struct(Some(type_index)))])]
497             #[results([])]
498             #[fixup(|_limits, num_types| {
499                 type_index = type_index.checked_rem(num_types)?;
500             })]
501             TakeTypedStructCall { type_index: u32 },
502 
503             #[operands([Some(Struct(None))])]
504             #[results([])]
505             StructLocalSet,
506 
507             #[operands([])]
508             #[results([Struct(None)])]
509             StructLocalGet,
510 
511             #[operands([Some(Struct(Some(type_index)))])]
512             #[results([])]
513             #[fixup(|_limits, num_types| {
514                 type_index = type_index.checked_rem(num_types)?;
515             })]
516             TypedStructLocalSet { type_index: u32 },
517 
518             #[operands([])]
519             #[results([Struct(Some(type_index))])]
520             #[fixup(|_limits, num_types| {
521                 type_index = type_index.checked_rem(num_types)?;
522             })]
523             TypedStructLocalGet { type_index: u32 },
524 
525             #[operands([Some(Struct(None))])]
526             #[results([])]
527             StructGlobalSet,
528 
529             #[operands([])]
530             #[results([Struct(None)])]
531             StructGlobalGet,
532 
533             #[operands([Some(Struct(Some(type_index)))])]
534             #[results([])]
535             #[fixup(|_limits, num_types| {
536                 type_index = type_index.checked_rem(num_types)?;
537             })]
538             TypedStructGlobalSet { type_index: u32 },
539 
540             #[operands([])]
541             #[results([Struct(Some(type_index))])]
542             #[fixup(|_limits, num_types| {
543                 type_index = type_index.checked_rem(num_types)?;
544             })]
545             TypedStructGlobalGet { type_index: u32 },
546 
547             #[operands([Some(Struct(None))])]
548             #[results([])]
549             #[fixup(|limits, _num_types| {
550                 // Add one to make sure that out-of-bounds table accesses are
551                 // possible, but still rare.
552                 elem_index = elem_index % (limits.table_size + 1);
553             })]
554             StructTableSet { elem_index: u32 },
555 
556             #[operands([])]
557             #[results([Struct(None)])]
558             #[fixup(|limits, _num_types| {
559                 // Add one to make sure that out-of-bounds table accesses are
560                 // possible, but still rare.
561                 elem_index = elem_index % (limits.table_size + 1);
562             })]
563             StructTableGet { elem_index: u32 },
564 
565             #[operands([Some(Struct(Some(type_index)))])]
566             #[results([])]
567             #[fixup(|limits, num_types| {
568                 // Add one to make sure that out-of-bounds table accesses are
569                 // possible, but still rare.
570                 elem_index = elem_index % (limits.table_size + 1);
571                 type_index = type_index.checked_rem(num_types)?;
572             })]
573             TypedStructTableSet { elem_index: u32, type_index: u32 },
574 
575             #[operands([])]
576             #[results([Struct(Some(type_index))])]
577             #[fixup(|limits, num_types| {
578                 // Add one to make sure that out-of-bounds table accesses are
579                 // possible, but still rare.
580                 elem_index = elem_index % (limits.table_size + 1);
581                 type_index = type_index.checked_rem(num_types)?;
582             })]
583             TypedStructTableGet { elem_index: u32, type_index: u32 },
584 
585             #[operands([None])]
586             #[results([])]
587             Drop,
588 
589             #[operands([])]
590             #[results([ExternRef])]
591             NullExtern,
592 
593             #[operands([])]
594             #[results([Struct(None)])]
595             NullStruct,
596 
597             #[operands([])]
598             #[results([Struct(Some(type_index))])]
599             #[fixup(|_limits, num_types| {
600                 type_index = type_index.checked_rem(num_types)?;
601             })]
602             NullTypedStruct { type_index: u32 },
603         }
604     };
605 }
606 
607 macro_rules! define_gc_op_variants {
608     (
609         $(
610             $( #[$attr:meta] )*
611             $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
612         )*
613     ) => {
614         /// The operations that can be performed by the `gc` function.
615         #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
616         #[allow(missing_docs, reason = "self-describing")]
617         pub enum GcOp {
618             $(
619                 $op $( { $( $field : $field_ty ),* } )? ,
620             )*
621         }
622     };
623 }
624 for_each_gc_op!(define_gc_op_variants);
625 
626 macro_rules! define_op_names {
627     (
628         $(
629             $( #[$attr:meta] )*
630             $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
631         )*
632     ) => {
633         #[cfg(test)]
634         pub(crate) const OP_NAMES: &[&str] = &[
635             $(stringify!($op)),*
636         ];
637     }
638 }
639 for_each_gc_op!(define_op_names);
640 
641 impl GcOp {
642     #[cfg(test)]
name(&self) -> &'static str643     pub(crate) fn name(&self) -> &'static str {
644         macro_rules! define_gc_op_name {
645             (
646                 $(
647                     $( #[$attr:meta] )*
648                     $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
649                 )*
650             ) => {
651                 match self {
652                     $(
653                         Self::$op $( { $($field: _),* } )? => stringify!($op),
654                     )*
655                 }
656             };
657         }
658         for_each_gc_op!(define_gc_op_name)
659     }
660 
operand_types(&self, out: &mut Vec<Option<StackType>>)661     pub(crate) fn operand_types(&self, out: &mut Vec<Option<StackType>>) {
662         macro_rules! define_gc_op_operand_types {
663             (
664                 $(
665                     #[operands($operands:expr)]
666                     $( #[$attr:meta] )*
667                     $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
668                 )*
669             ) => {{
670                 use StackType::*;
671                 match self {
672                     $(
673                         Self::$op $( { $($field),* } )? => {
674                             $(
675                                 $(
676                                     #[allow(unused, reason = "macro code")]
677                                     let $field = *$field;
678                                 )*
679                             )?
680                             let operands: [Option<StackType>; _] = $operands;
681                             out.extend(operands);
682                         }
683                     )*
684                 }
685             }};
686         }
687         for_each_gc_op!(define_gc_op_operand_types)
688     }
689 
result_types(&self, out: &mut Vec<StackType>)690     pub(crate) fn result_types(&self, out: &mut Vec<StackType>) {
691         macro_rules! define_gc_op_result_types {
692             (
693                 $(
694                     #[operands($operands:expr)]
695                     #[results($results:expr)]
696                     $( #[$attr:meta] )*
697                     $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
698                 )*
699             ) => {{
700                 use StackType::*;
701                 match self {
702                     $(
703                         Self::$op $( { $($field),* } )? => {
704                             $(
705                                 $(
706                                     #[allow(unused, reason = "macro code")]
707                                     let $field = *$field;
708                                 )*
709                             )?
710                             let results: [StackType; _] = $results;
711                             out.extend(results);
712                         }
713                     )*
714                 }
715             }};
716         }
717         for_each_gc_op!(define_gc_op_result_types)
718     }
719 
fixup(&self, limits: &GcOpsLimits, num_types: u32) -> Option<Self>720     pub(crate) fn fixup(&self, limits: &GcOpsLimits, num_types: u32) -> Option<Self> {
721         macro_rules! define_gc_op_fixup {
722             (
723                 $(
724                     #[operands($operands:expr)]
725                     #[results($results:expr)]
726                     $( #[fixup(|$limits:ident, $num_types:ident| $fixup:expr)] )?
727                     $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
728                 )*
729             ) => {{
730                 match self {
731                     $(
732                         Self::$op $( { $($field),* } )? => {
733                             $(
734                                 $(
735                                     #[allow(unused_mut, reason = "macro code")]
736                                     let mut $field = *$field;
737                                 )*
738                                 let $limits = limits;
739                                 let $num_types = num_types;
740                                 $fixup;
741                             )?
742                             Some(Self::$op $( { $( $field ),* } )? )
743                         }
744                     )*
745                 }
746             }};
747         }
748         for_each_gc_op!(define_gc_op_fixup)
749     }
750 
generate(ctx: &mut mutatis::Context) -> mutatis::Result<GcOp>751     pub(crate) fn generate(ctx: &mut mutatis::Context) -> mutatis::Result<GcOp> {
752         macro_rules! define_gc_op_generate {
753             (
754                 $(
755                     $( #[$attr:meta] )*
756                     $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
757                 )*
758             ) => {{
759                 let choices: &[fn(&mut mutatis::Context) -> mutatis::Result<GcOp>] = &[
760                     $(
761                         |_ctx| Ok(GcOp::$op $( {
762                             $(
763                                 $field: {
764                                     let mut mutator = <$field_ty as mutatis::DefaultMutate>::DefaultMutate::default();
765                                     mutatis::Generate::<$field_ty>::generate(&mut mutator, _ctx)?
766                                 }
767                             ),*
768                         } )? ),
769                     )*
770                 ];
771 
772                 let f = *ctx.rng()
773                     .choose(choices)
774                     .unwrap();
775                 (f)(ctx)
776             }};
777         }
778         for_each_gc_op!(define_gc_op_generate)
779     }
780 
encode(&self, func: &mut Function, scratch_local: u32, encoding_bases: WasmEncodingBases)781     fn encode(&self, func: &mut Function, scratch_local: u32, encoding_bases: WasmEncodingBases) {
782         let gc_func_idx = 0;
783         let take_refs_func_idx = 1;
784         let make_refs_func_idx = 2;
785         let take_structref_idx = 3;
786 
787         match *self {
788             Self::Gc => {
789                 func.instruction(&Instruction::Call(gc_func_idx));
790             }
791             Self::MakeRefs => {
792                 func.instruction(&Instruction::Call(make_refs_func_idx));
793             }
794             Self::TakeRefs => {
795                 func.instruction(&Instruction::Call(take_refs_func_idx));
796             }
797             Self::TableGet { elem_index: x } => {
798                 func.instruction(&Instruction::I32Const(x.cast_signed()));
799                 func.instruction(&Instruction::TableGet(0));
800             }
801             Self::TableSet { elem_index: x } => {
802                 func.instruction(&Instruction::LocalSet(scratch_local));
803                 func.instruction(&Instruction::I32Const(x.cast_signed()));
804                 func.instruction(&Instruction::LocalGet(scratch_local));
805                 func.instruction(&Instruction::TableSet(0));
806             }
807             Self::GlobalGet { global_index: x } => {
808                 func.instruction(&Instruction::GlobalGet(x));
809             }
810             Self::GlobalSet { global_index: x } => {
811                 func.instruction(&Instruction::GlobalSet(x));
812             }
813             Self::LocalGet { local_index: x } => {
814                 func.instruction(&Instruction::LocalGet(x));
815             }
816             Self::LocalSet { local_index: x } => {
817                 func.instruction(&Instruction::LocalSet(x));
818             }
819             Self::Drop => {
820                 func.instruction(&Instruction::Drop);
821             }
822             Self::NullExtern => {
823                 func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::EXTERN));
824             }
825             Self::NullStruct => {
826                 func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Abstract {
827                     shared: false,
828                     ty: wasm_encoder::AbstractHeapType::Struct,
829                 }));
830             }
831             Self::NullTypedStruct { type_index } => {
832                 func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Concrete(
833                     encoding_bases.struct_type_base + type_index,
834                 )));
835             }
836             Self::StructNew { type_index: x } => {
837                 func.instruction(&Instruction::StructNew(encoding_bases.struct_type_base + x));
838             }
839             Self::TakeStructCall => {
840                 func.instruction(&Instruction::Call(take_structref_idx));
841             }
842             Self::TakeTypedStructCall { type_index: x } => {
843                 let f = encoding_bases.typed_first_func_index + x;
844                 func.instruction(&Instruction::Call(f));
845             }
846             Self::StructLocalGet => {
847                 func.instruction(&Instruction::LocalGet(encoding_bases.struct_local_idx));
848             }
849             Self::TypedStructLocalGet { type_index: x } => {
850                 func.instruction(&Instruction::LocalGet(encoding_bases.typed_local_base + x));
851             }
852             Self::StructLocalSet => {
853                 func.instruction(&Instruction::LocalSet(encoding_bases.struct_local_idx));
854             }
855             Self::TypedStructLocalSet { type_index: x } => {
856                 func.instruction(&Instruction::LocalSet(encoding_bases.typed_local_base + x));
857             }
858             Self::StructGlobalGet => {
859                 func.instruction(&Instruction::GlobalGet(encoding_bases.struct_global_idx));
860             }
861             Self::TypedStructGlobalGet { type_index: x } => {
862                 func.instruction(&Instruction::GlobalGet(
863                     encoding_bases.typed_global_base + x,
864                 ));
865             }
866             Self::StructGlobalSet => {
867                 func.instruction(&Instruction::GlobalSet(encoding_bases.struct_global_idx));
868             }
869             Self::TypedStructGlobalSet { type_index: x } => {
870                 func.instruction(&Instruction::GlobalSet(
871                     encoding_bases.typed_global_base + x,
872                 ));
873             }
874             Self::StructTableGet { elem_index } => {
875                 func.instruction(&Instruction::I32Const(elem_index.cast_signed()));
876                 func.instruction(&Instruction::TableGet(encoding_bases.struct_table_idx));
877             }
878             Self::TypedStructTableGet {
879                 elem_index,
880                 type_index,
881             } => {
882                 func.instruction(&Instruction::I32Const(elem_index.cast_signed()));
883                 func.instruction(&Instruction::TableGet(
884                     encoding_bases.typed_table_base + type_index,
885                 ));
886             }
887             Self::StructTableSet { elem_index } => {
888                 // Use struct_local_idx (anyref) to temporarily store the value before table.set
889                 func.instruction(&Instruction::LocalSet(encoding_bases.struct_local_idx));
890                 func.instruction(&Instruction::I32Const(elem_index.cast_signed()));
891                 func.instruction(&Instruction::LocalGet(encoding_bases.struct_local_idx));
892                 func.instruction(&Instruction::TableSet(encoding_bases.struct_table_idx));
893             }
894             Self::TypedStructTableSet {
895                 elem_index,
896                 type_index,
897             } => {
898                 func.instruction(&Instruction::LocalSet(
899                     encoding_bases.typed_local_base + type_index,
900                 ));
901                 func.instruction(&Instruction::I32Const(elem_index.cast_signed()));
902                 func.instruction(&Instruction::LocalGet(
903                     encoding_bases.typed_local_base + type_index,
904                 ));
905                 func.instruction(&Instruction::TableSet(
906                     encoding_bases.typed_table_base + type_index,
907                 ));
908             }
909         }
910     }
911 }
912