1use 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#[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#[derive(Arbitrary, Debug, Clone, Copy)]
66enum HeapInit {
67 Empty,
69 Full,
71 AlmostFull,
73 Fragmented,
76}
77
78#[derive(Debug, Clone, Copy)]
80enum CreateMode {
81 GlobalGet,
83 TableGet,
85 Call,
87 LocalGet,
89 ArrayNew,
91 ArrayNewDefault,
93 ArrayNewFixed,
95 ArrayNewData,
97 ArrayNewElem,
99 StructNew,
101 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#[derive(Debug, Clone, Copy)]
147enum AccessMode {
148 RefTest,
150 StructGet,
152 StructGetS,
154 StructGetU,
156 StructSet,
158 ArrayGet,
160 ArrayGetS,
162 ArrayGetU,
164 ArraySet,
166 ArrayLen,
168 ArrayFill,
170 ArrayCopy,
172 ArrayInitData,
174 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#[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 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 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 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_g: Option<u32>,
282 global_root: Option<u32>,
283 global_root2: Option<u32>,
284
285 table_t: Option<u32>,
287
288 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 {
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 if self.run_impl_func.is_some() {
660 let mut locals = vec![
661 (1, ValType::I32), (1, ValType::I32), ];
664 if self.needs_before() {
665 locals.push((1, ValType::I32)); }
667 if matches!(self.cfg.access_mode, AccessMode::ArrayCopy) {
668 locals.push((1, self.obj_ref_type())); }
670 let mut body = Function::new(locals);
671 self.emit_local_get_body(&mut body);
672 finish(&mut code, body);
673 }
674
675 {
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), (1, ValType::I32), ];
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()), (1, ValType::I32), (1, ValType::I32), ];
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 !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 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 fn emit_normal_body(&self, body: &mut Function) {
1374 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 body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
1393
1394 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 self.emit_create(body);
1404 body.instruction(&Instruction::LocalSet(local_x));
1405
1406 self.emit_access(body, r, local_before, local_src);
1408 body.instruction(&Instruction::Call(self.gc_func.unwrap()));
1410 self.emit_assert(body, r, local_before);
1412
1413 body.instruction(&Instruction::I32Const(0));
1415 body.instruction(&Instruction::LocalSet(local_j));
1416
1417 body.instruction(&Instruction::Block(wasm_encoder::BlockType::Empty));
1419 body.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
1420
1421 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 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)); 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)); body.instruction(&Instruction::End); body.instruction(&Instruction::End); body.instruction(&Instruction::End); self.emit_access(body, r, local_before, local_src);
1452 self.emit_assert(body, r, local_before);
1453
1454 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 body.instruction(&Instruction::Br(0));
1462
1463 body.instruction(&Instruction::End); }
1465
1466 fn emit_idempotent_body(&self, body: &mut Function) {
1468 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); body.instruction(&Instruction::End); body.instruction(&Instruction::End); 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); }
1539
1540 fn emit_local_get_body(&self, body: &mut Function) {
1542 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); body.instruction(&Instruction::End); body.instruction(&Instruction::End); 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); }
1614}
1615
1616#[derive(Clone, Copy)]
1619enum ObjRef {
1620 Local(u32),
1622 Global,
1624 Table,
1626}
1627
1628fn sec(module: &mut Module, section: impl wasm_encoder::Section) {
1629 module.section(§ion);
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}