1#[cfg(test)]
27mod validation;
28
29use crate::{
30 Module,
31 module::{Function, Import, Instruction, ModuleBuilder},
32};
33use alloc::vec::Vec;
34use core::{cmp::min, mem, num::NonZeroU32};
35use wasmparser::{FuncType, TypeRef, ValType};
36
37#[derive(Debug, derive_more::From)]
38pub enum GasMeteringError {
39 Counter(CounterError),
40 LocalsInitCost,
41 NoActiveControlBlock,
42 MissingInstructionRule(Instruction),
43 ActiveIndexRelativeDepthUnderflow,
44 UnusedBlock,
45 LocalsCountOverflow,
46 ActiveIndexLabelUnderflow,
47}
48
49pub trait Rules {
51 fn instruction_cost(&self, instruction: &Instruction) -> Option<u32>;
57
58 fn memory_grow_cost(&self) -> MemoryGrowCost;
67
68 fn call_per_local_cost(&self) -> u32;
70}
71
72#[derive(Debug, PartialEq, Eq, Copy, Clone)]
74pub enum MemoryGrowCost {
75 Free,
84 Linear(NonZeroU32),
86}
87
88impl MemoryGrowCost {
89 fn enabled(&self) -> bool {
91 match self {
92 Self::Free => false,
93 Self::Linear(_) => true,
94 }
95 }
96}
97
98pub struct ConstantCostRules {
108 instruction_cost: u32,
109 memory_grow_cost: u32,
110 call_per_local_cost: u32,
111}
112
113impl ConstantCostRules {
114 pub fn new(instruction_cost: u32, memory_grow_cost: u32, call_per_local_cost: u32) -> Self {
119 Self {
120 instruction_cost,
121 memory_grow_cost,
122 call_per_local_cost,
123 }
124 }
125}
126
127impl Default for ConstantCostRules {
128 fn default() -> Self {
130 Self {
131 instruction_cost: 1,
132 memory_grow_cost: 0,
133 call_per_local_cost: 1,
134 }
135 }
136}
137
138impl Rules for ConstantCostRules {
139 fn instruction_cost(&self, _instruction: &Instruction) -> Option<u32> {
140 Some(self.instruction_cost)
141 }
142
143 fn memory_grow_cost(&self) -> MemoryGrowCost {
144 NonZeroU32::new(self.memory_grow_cost).map_or(MemoryGrowCost::Free, MemoryGrowCost::Linear)
145 }
146
147 fn call_per_local_cost(&self) -> u32 {
148 self.call_per_local_cost
149 }
150}
151
152pub fn inject<R: Rules>(
188 module: Module,
189 rules: &R,
190 gas_module_name: &'static str,
191) -> Result<Module, GasMeteringError> {
192 let gas_func = module.import_count(|ty| matches!(ty, TypeRef::Func(_)));
194
195 let mut mbuilder = ModuleBuilder::from_module(module);
196
197 let import_sig = mbuilder.push_type(FuncType::new([ValType::I32], []));
198 mbuilder.push_import(Import::func(gas_module_name, "gas", import_sig));
199
200 let module = mbuilder
201 .shift_func_index(gas_func as u32)
202 .shift_all()
203 .build();
204
205 post_injection_handler(module, rules, gas_func)
206}
207
208pub fn post_injection_handler<R: Rules>(
212 mut module: Module,
213 rules: &R,
214 gas_charge_index: usize,
215) -> Result<Module, GasMeteringError> {
216 let import_count = module.import_count(|ty| matches!(ty, TypeRef::Func(_)));
220 let total_func = module.functions_space() as u32;
221 let mut need_grow_counter = false;
222
223 if let Some(code_section) = &mut module.code_section {
224 for (i, func_body) in code_section.iter_mut().enumerate() {
225 if i + import_count == gas_charge_index {
226 continue;
227 }
228
229 let locals_count = func_body
230 .locals
231 .iter()
232 .try_fold(0u32, |count, val_type| count.checked_add(val_type.0))
233 .ok_or(GasMeteringError::LocalsCountOverflow)?;
234 inject_counter(
235 &mut func_body.instructions,
236 rules,
237 locals_count,
238 gas_charge_index as u32,
239 )?;
240
241 if rules.memory_grow_cost().enabled()
242 && inject_grow_counter(&mut func_body.instructions, total_func) > 0
243 {
244 need_grow_counter = true;
245 }
246 }
247 }
248
249 match need_grow_counter {
250 true => Ok(add_grow_counter(module, rules, gas_charge_index as u32)),
251 false => Ok(module),
252 }
253}
254
255#[derive(Debug)]
273struct ControlBlock {
274 lowest_forward_br_target: usize,
283
284 active_metered_block: MeteredBlock,
286
287 is_loop: bool,
290}
291
292#[derive(Debug)]
296struct MeteredBlock {
297 start_pos: usize,
299 cost: BlockCostCounter,
301}
302
303#[derive(Debug, PartialEq, PartialOrd)]
305#[cfg_attr(test, derive(Copy, Clone, Default))]
306struct BlockCostCounter {
307 overflows: usize,
324 accumulator: u32,
326}
327
328impl BlockCostCounter {
329 const MAX_GAS_ARG: u32 = u32::MAX;
335
336 fn zero() -> Self {
337 Self::initialize(0)
338 }
339
340 fn initialize(initial_cost: u32) -> Self {
341 Self {
342 overflows: 0,
343 accumulator: initial_cost,
344 }
345 }
346
347 fn add(&mut self, counter: BlockCostCounter) {
348 self.overflows = self.overflows.saturating_add(counter.overflows);
350 self.increment(counter.accumulator)
351 }
352
353 fn increment(&mut self, val: u32) {
354 if let Some(res) = self.accumulator.checked_add(val) {
355 self.accumulator = res;
356 } else {
357 self.accumulator = val - (u32::MAX - self.accumulator);
359 self.overflows = self.overflows.saturating_add(1);
361 }
362 }
363
364 fn block_costs(&self) -> (usize, u32) {
368 (self.overflows, self.accumulator)
369 }
370
371 fn costs_num(&self) -> usize {
374 if self.accumulator != 0 {
375 self.overflows + 1
376 } else {
377 self.overflows
378 }
379 }
380}
381
382#[derive(Debug)]
383pub enum CounterError {
384 StackLast,
385 StackPop,
386 StackGet,
387}
388
389struct Counter {
392 stack: Vec<ControlBlock>,
398
399 finalized_blocks: Vec<MeteredBlock>,
401}
402
403impl Counter {
404 fn new() -> Counter {
405 Counter {
406 stack: Vec::new(),
407 finalized_blocks: Vec::new(),
408 }
409 }
410
411 fn begin_control_block(&mut self, cursor: usize, is_loop: bool) {
413 let index = self.stack.len();
414 self.stack.push(ControlBlock {
415 lowest_forward_br_target: index,
416 active_metered_block: MeteredBlock {
417 start_pos: cursor,
418 cost: BlockCostCounter::zero(),
419 },
420 is_loop,
421 })
422 }
423
424 fn finalize_control_block(&mut self, cursor: usize) -> Result<(), CounterError> {
427 self.finalize_metered_block(cursor)?;
430
431 let closing_control_block = self.stack.pop().ok_or(CounterError::StackPop)?;
433 let closing_control_index = self.stack.len();
434
435 if self.stack.is_empty() {
436 return Ok(());
437 }
438
439 {
441 let control_block = self.stack.last_mut().ok_or(CounterError::StackLast)?;
442 control_block.lowest_forward_br_target = min(
443 control_block.lowest_forward_br_target,
444 closing_control_block.lowest_forward_br_target,
445 );
446 }
447
448 let may_br_out = closing_control_block.lowest_forward_br_target < closing_control_index;
451 if may_br_out {
452 self.finalize_metered_block(cursor)?;
453 }
454
455 Ok(())
456 }
457
458 fn finalize_metered_block(&mut self, cursor: usize) -> Result<(), CounterError> {
462 let closing_metered_block = {
463 let control_block = self.stack.last_mut().ok_or(CounterError::StackLast)?;
464 mem::replace(
465 &mut control_block.active_metered_block,
466 MeteredBlock {
467 start_pos: cursor + 1,
468 cost: BlockCostCounter::zero(),
469 },
470 )
471 };
472
473 let last_index = self.stack.len() - 1;
479 if last_index > 0 {
480 let prev_control_block = self
481 .stack
482 .get_mut(last_index - 1)
483 .expect("last_index is greater than 0; last_index is stack size - 1; qed");
484 let prev_metered_block = &mut prev_control_block.active_metered_block;
485 if closing_metered_block.start_pos == prev_metered_block.start_pos {
486 prev_metered_block.cost.add(closing_metered_block.cost);
487 return Ok(());
488 }
489 }
490
491 if closing_metered_block.cost > BlockCostCounter::zero() {
492 self.finalized_blocks.push(closing_metered_block);
493 }
494 Ok(())
495 }
496
497 fn branch(&mut self, cursor: usize, indices: &[usize]) -> Result<(), CounterError> {
502 self.finalize_metered_block(cursor)?;
503
504 for &index in indices {
506 let target_is_loop = {
507 let target_block = self.stack.get(index).ok_or(CounterError::StackGet)?;
508 target_block.is_loop
509 };
510 if target_is_loop {
511 continue;
512 }
513
514 let control_block = self.stack.last_mut().ok_or(CounterError::StackLast)?;
515 control_block.lowest_forward_br_target =
516 min(control_block.lowest_forward_br_target, index);
517 }
518
519 Ok(())
520 }
521
522 fn active_control_block_index(&self) -> Option<usize> {
524 self.stack.len().checked_sub(1)
525 }
526
527 fn active_metered_block(&mut self) -> Result<&mut MeteredBlock, CounterError> {
529 let top_block = self.stack.last_mut().ok_or(CounterError::StackLast)?;
530 Ok(&mut top_block.active_metered_block)
531 }
532
533 fn increment(&mut self, val: u32) -> Result<(), CounterError> {
535 let top_block = self.active_metered_block()?;
536 top_block.cost.increment(val);
537 Ok(())
538 }
539}
540
541fn inject_grow_counter(instructions: &mut Vec<Instruction>, grow_counter_func: u32) -> usize {
542 use Instruction::*;
543 let mut counter = 0;
544 for instruction in instructions {
545 if let MemoryGrow(_) = *instruction {
546 *instruction = Call(grow_counter_func);
547 counter += 1;
548 }
549 }
550 counter
551}
552
553fn add_grow_counter<R: Rules>(module: Module, rules: &R, gas_func: u32) -> Module {
554 use Instruction::*;
555
556 let cost = match rules.memory_grow_cost() {
557 MemoryGrowCost::Free => return module,
558 MemoryGrowCost::Linear(val) => val.get(),
559 };
560
561 let mut b = ModuleBuilder::from_module(module);
562 b.add_func(
563 FuncType::new([ValType::I32], [ValType::I32]),
564 Function::from_instructions([
565 LocalGet(0),
566 LocalGet(0),
567 I32Const(cost as i32),
568 I32Mul,
569 Call(gas_func),
572 MemoryGrow(0),
573 End,
574 ]),
575 );
576
577 b.build()
578}
579
580fn determine_metered_blocks<R: Rules>(
581 instructions: &[Instruction],
582 rules: &R,
583 locals_count: u32,
584) -> Result<Vec<MeteredBlock>, GasMeteringError> {
585 use Instruction::*;
586
587 let mut counter = Counter::new();
588
589 counter.begin_control_block(0, false);
591
592 let locals_init_cost = rules
594 .call_per_local_cost()
595 .checked_mul(locals_count)
596 .ok_or(GasMeteringError::LocalsInitCost)?;
597 counter.increment(locals_init_cost)?;
598
599 for (cursor, instruction) in instructions.iter().enumerate() {
600 let instruction_cost = rules
601 .instruction_cost(instruction)
602 .ok_or_else(|| GasMeteringError::MissingInstructionRule(instruction.clone()))?;
603 match instruction {
604 Block { .. } => {
605 counter.increment(instruction_cost)?;
606
607 let top_block_start_pos = counter.active_metered_block()?.start_pos;
612 counter.begin_control_block(top_block_start_pos, false);
613 }
614 If { .. } => {
615 counter.increment(instruction_cost)?;
616 counter.begin_control_block(cursor + 1, false);
617 }
618 Loop { .. } => {
619 counter.increment(instruction_cost)?;
620 counter.begin_control_block(cursor + 1, true);
621 }
622 End => {
623 counter.finalize_control_block(cursor)?;
624 }
625 Else => {
626 counter.finalize_metered_block(cursor)?;
627 }
628 Br(relative_depth) | BrIf(relative_depth) => {
629 counter.increment(instruction_cost)?;
630
631 let active_index = counter
633 .active_control_block_index()
634 .ok_or(GasMeteringError::NoActiveControlBlock)?;
635 let target_index = active_index
636 .checked_sub(*relative_depth as usize)
637 .ok_or(GasMeteringError::ActiveIndexRelativeDepthUnderflow)?;
638 counter.branch(cursor, &[target_index])?;
639 }
640 BrTable(targets) => {
641 counter.increment(instruction_cost)?;
642
643 let active_index = counter
644 .active_control_block_index()
645 .ok_or(GasMeteringError::NoActiveControlBlock)?;
646 let target_indices = [targets.default]
647 .into_iter()
648 .chain(targets.targets.clone())
649 .map(|label| active_index.checked_sub(label as usize))
650 .collect::<Option<Vec<_>>>()
651 .ok_or(GasMeteringError::ActiveIndexLabelUnderflow)?;
652 counter.branch(cursor, target_indices.as_slice())?;
653 }
654 Return => {
655 counter.increment(instruction_cost)?;
656 counter.branch(cursor, &[0])?;
657 }
658 _ => {
659 counter.increment(instruction_cost)?;
661 }
662 }
663 }
664
665 counter
666 .finalized_blocks
667 .sort_unstable_by_key(|block| block.start_pos);
668 Ok(counter.finalized_blocks)
669}
670
671fn inject_counter<R: Rules>(
672 instructions: &mut Vec<Instruction>,
673 rules: &R,
674 locals_count: u32,
675 gas_func: u32,
676) -> Result<(), GasMeteringError> {
677 let blocks = determine_metered_blocks(instructions, rules, locals_count)?;
678 insert_metering_calls(instructions, blocks, gas_func)
679}
680
681fn insert_metering_calls(
683 instructions: &mut Vec<Instruction>,
684 blocks: Vec<MeteredBlock>,
685 gas_func: u32,
686) -> Result<(), GasMeteringError> {
687 let block_cost_instrs = calculate_blocks_costs_num(&blocks);
688 let new_instrs_len = instructions.len() + 2 * block_cost_instrs;
691 let original_instrs = mem::replace(instructions, Vec::with_capacity(new_instrs_len));
692 let new_instrs = instructions;
693
694 let mut block_iter = blocks.into_iter().peekable();
695 for (original_pos, instr) in original_instrs.into_iter().enumerate() {
696 let used_block = if let Some(block) = block_iter.peek() {
698 if block.start_pos == original_pos {
699 insert_gas_call(new_instrs, block, gas_func);
700 true
701 } else {
702 false
703 }
704 } else {
705 false
706 };
707
708 if used_block {
709 block_iter.next();
710 }
711
712 new_instrs.push(instr);
714 }
715
716 if block_iter.next().is_some() {
717 return Err(GasMeteringError::UnusedBlock);
718 }
719
720 Ok(())
721}
722
723fn calculate_blocks_costs_num(blocks: &[MeteredBlock]) -> usize {
725 blocks.iter().map(|block| block.cost.costs_num()).sum()
726}
727
728fn insert_gas_call(new_instrs: &mut Vec<Instruction>, current_block: &MeteredBlock, gas_func: u32) {
729 use Instruction::*;
730
731 let (mut overflows_num, current_cost) = current_block.cost.block_costs();
732 while overflows_num != 0 {
734 new_instrs.push(I32Const(BlockCostCounter::MAX_GAS_ARG as i32));
735 new_instrs.push(Call(gas_func));
736 overflows_num -= 1;
737 }
738 if current_cost != 0 {
740 new_instrs.push(I32Const(current_cost as i32));
741 new_instrs.push(Call(gas_func));
742 }
743}
744
745#[cfg(test)]
746mod tests {
747 use super::*;
748 use crate::{
749 module::{ConstExpr, Global, Instruction::*},
750 test_gas_counter_injection,
751 tests::parse_wat,
752 };
753 use wasmparser::{BlockType, GlobalType};
754
755 fn get_function_body(module: &Module, index: usize) -> Option<&[Instruction]> {
756 module
757 .code_section
758 .as_ref()
759 .and_then(|code_section| code_section.get(index))
760 .map(|func_body| func_body.instructions.as_slice())
761 }
762
763 fn prebuilt_simple_module() -> Module {
764 let mut mbuilder = ModuleBuilder::default();
765
766 mbuilder.push_global(Global {
767 ty: GlobalType {
768 content_type: ValType::I32,
769 mutable: false,
770 shared: false,
771 },
772 init_expr: ConstExpr::default(),
773 });
774
775 mbuilder.add_func(FuncType::new([ValType::I32], []), Function::default());
776
777 mbuilder.add_func(
778 FuncType::new([ValType::I32], []),
779 Function::from_instructions([
780 Call(0),
781 If(BlockType::Empty),
782 Call(0),
783 Call(0),
784 Call(0),
785 Else,
786 Call(0),
787 Call(0),
788 End,
789 Call(0),
790 End,
791 ]),
792 );
793
794 mbuilder.build()
795 }
796
797 #[test]
798 fn simple_grow() {
799 let module = parse_wat(
800 r#"(module
801 (func (result i32)
802 global.get 0
803 memory.grow)
804 (global i32 (i32.const 42))
805 (memory 0 1)
806 )"#,
807 );
808
809 let injected_module = inject(module, &ConstantCostRules::new(1, 10_000, 1), "env").unwrap();
810
811 assert_eq!(
812 get_function_body(&injected_module, 0).unwrap(),
813 [I32Const(2), Call(0), GlobalGet(0), Call(2), End]
814 );
815 assert_eq!(
816 get_function_body(&injected_module, 1).unwrap(),
817 [
818 LocalGet(0),
819 LocalGet(0),
820 I32Const(10000),
821 I32Mul,
822 Call(0),
823 MemoryGrow(0),
824 End,
825 ]
826 );
827
828 let binary = injected_module.serialize().expect("serialization failed");
829 wasmparser::validate(&binary).unwrap();
830 }
831
832 #[test]
833 fn grow_no_gas_no_track() {
834 let module = parse_wat(
835 r"(module
836 (func (result i32)
837 global.get 0
838 memory.grow)
839 (global i32 (i32.const 42))
840 (memory 0 1)
841 )",
842 );
843
844 let injected_module = inject(module, &ConstantCostRules::default(), "env").unwrap();
845
846 assert_eq!(
847 get_function_body(&injected_module, 0).unwrap(),
848 [I32Const(2), Call(0), GlobalGet(0), MemoryGrow(0), End]
849 );
850
851 assert_eq!(injected_module.functions_space(), 2);
852
853 let binary = injected_module.serialize().expect("serialization failed");
854 wasmparser::validate(&binary).unwrap();
855 }
856
857 #[test]
858 fn call_index() {
859 let injected_module = inject(
860 prebuilt_simple_module(),
861 &ConstantCostRules::default(),
862 "env",
863 )
864 .unwrap();
865
866 assert_eq!(
867 get_function_body(&injected_module, 1).unwrap(),
868 &vec![
869 I32Const(3),
870 Call(0),
871 Call(1),
872 If(BlockType::Empty),
873 I32Const(3),
874 Call(0),
875 Call(1),
876 Call(1),
877 Call(1),
878 Else,
879 I32Const(2),
880 Call(0),
881 Call(1),
882 Call(1),
883 End,
884 Call(1),
885 End
886 ][..]
887 );
888 }
889
890 #[test]
891 fn cost_overflow() {
892 let instruction_cost = u32::MAX / 2;
893 let injected_module = inject(
894 prebuilt_simple_module(),
895 &ConstantCostRules::new(instruction_cost, 0, instruction_cost),
896 "env",
897 )
898 .unwrap();
899
900 assert_eq!(
901 get_function_body(&injected_module, 1).unwrap(),
902 &vec![
903 I32Const(-1),
907 Call(0),
908 I32Const((instruction_cost - 1) as i32),
909 Call(0),
910 Call(1),
911 If(BlockType::Empty),
912 I32Const(-1),
914 Call(0),
915 I32Const((instruction_cost - 1) as i32),
916 Call(0),
917 Call(1),
918 Call(1),
919 Call(1),
920 Else,
921 I32Const(-2),
923 Call(0),
924 Call(1),
925 Call(1),
926 End,
927 Call(1),
928 End
929 ][..]
930 );
931 }
932
933 macro_rules! test_gas_counter_injection {
934 (name = $name:ident; input = $input:expr; expected = $expected:expr) => {
935 #[test]
936 fn $name() {
937 let input_module = parse_wat($input);
938 let expected_module = parse_wat($expected);
939
940 let injected_module = inject(input_module, &ConstantCostRules::default(), "env")
941 .expect("inject_gas_counter call failed");
942
943 let actual_func_body = get_function_body(&injected_module, 0)
944 .expect("injected module must have a function body");
945 let expected_func_body = get_function_body(&expected_module, 0)
946 .expect("post-module must have a function body");
947
948 assert_eq!(actual_func_body, expected_func_body);
949 }
950 };
951 }
952
953 test_gas_counter_injection! {
954 name = simple;
955 input = r#"
956 (module
957 (func (result i32)
958 (global.get 0)))
959 "#;
960 expected = r#"
961 (module
962 (func (result i32)
963 (call 0 (i32.const 1))
964 (global.get 0)))
965 "#
966 }
967
968 test_gas_counter_injection! {
969 name = nested;
970 input = r#"
971 (module
972 (func (result i32)
973 (global.get 0)
974 (block
975 (global.get 0)
976 (global.get 0)
977 (global.get 0))
978 (global.get 0)))
979 "#;
980 expected = r#"
981 (module
982 (func (result i32)
983 (call 0 (i32.const 6))
984 (global.get 0)
985 (block
986 (global.get 0)
987 (global.get 0)
988 (global.get 0))
989 (global.get 0)))
990 "#
991 }
992
993 test_gas_counter_injection! {
994 name = ifelse;
995 input = r#"
996 (module
997 (func (result i32)
998 (global.get 0)
999 (if
1000 (then
1001 (global.get 0)
1002 (global.get 0)
1003 (global.get 0))
1004 (else
1005 (global.get 0)
1006 (global.get 0)))
1007 (global.get 0)))
1008 "#;
1009 expected = r#"
1010 (module
1011 (func (result i32)
1012 (call 0 (i32.const 3))
1013 (global.get 0)
1014 (if
1015 (then
1016 (call 0 (i32.const 3))
1017 (global.get 0)
1018 (global.get 0)
1019 (global.get 0))
1020 (else
1021 (call 0 (i32.const 2))
1022 (global.get 0)
1023 (global.get 0)))
1024 (global.get 0)))
1025 "#
1026 }
1027
1028 test_gas_counter_injection! {
1029 name = branch_innermost;
1030 input = r#"
1031 (module
1032 (func (result i32)
1033 (global.get 0)
1034 (block
1035 (global.get 0)
1036 (drop)
1037 (br 0)
1038 (global.get 0)
1039 (drop))
1040 (global.get 0)))
1041 "#;
1042 expected = r#"
1043 (module
1044 (func (result i32)
1045 (call 0 (i32.const 6))
1046 (global.get 0)
1047 (block
1048 (global.get 0)
1049 (drop)
1050 (br 0)
1051 (call 0 (i32.const 2))
1052 (global.get 0)
1053 (drop))
1054 (global.get 0)))
1055 "#
1056 }
1057
1058 test_gas_counter_injection! {
1059 name = branch_outer_block;
1060 input = r#"
1061 (module
1062 (func (result i32)
1063 (global.get 0)
1064 (block
1065 (global.get 0)
1066 (if
1067 (then
1068 (global.get 0)
1069 (global.get 0)
1070 (drop)
1071 (br_if 1)))
1072 (global.get 0)
1073 (drop))
1074 (global.get 0)))
1075 "#;
1076 expected = r#"
1077 (module
1078 (func (result i32)
1079 (call 0 (i32.const 5))
1080 (global.get 0)
1081 (block
1082 (global.get 0)
1083 (if
1084 (then
1085 (call 0 (i32.const 4))
1086 (global.get 0)
1087 (global.get 0)
1088 (drop)
1089 (br_if 1)))
1090 (call 0 (i32.const 2))
1091 (global.get 0)
1092 (drop))
1093 (global.get 0)))
1094 "#
1095 }
1096
1097 test_gas_counter_injection! {
1098 name = branch_outer_loop;
1099 input = r#"
1100 (module
1101 (func (result i32)
1102 (global.get 0)
1103 (loop
1104 (global.get 0)
1105 (if
1106 (then
1107 (global.get 0)
1108 (br_if 0))
1109 (else
1110 (global.get 0)
1111 (global.get 0)
1112 (drop)
1113 (br_if 1)))
1114 (global.get 0)
1115 (drop))
1116 (global.get 0)))
1117 "#;
1118 expected = r#"
1119 (module
1120 (func (result i32)
1121 (call 0 (i32.const 3))
1122 (global.get 0)
1123 (loop
1124 (call 0 (i32.const 4))
1125 (global.get 0)
1126 (if
1127 (then
1128 (call 0 (i32.const 2))
1129 (global.get 0)
1130 (br_if 0))
1131 (else
1132 (call 0 (i32.const 4))
1133 (global.get 0)
1134 (global.get 0)
1135 (drop)
1136 (br_if 1)))
1137 (global.get 0)
1138 (drop))
1139 (global.get 0)))
1140 "#
1141 }
1142
1143 test_gas_counter_injection! {
1144 name = return_from_func;
1145 input = r#"
1146 (module
1147 (func (result i32)
1148 (global.get 0)
1149 (if
1150 (then
1151 (return)))
1152 (global.get 0)))
1153 "#;
1154 expected = r#"
1155 (module
1156 (func (result i32)
1157 (call 0 (i32.const 2))
1158 (global.get 0)
1159 (if
1160 (then
1161 (call 0 (i32.const 1))
1162 (return)))
1163 (call 0 (i32.const 1))
1164 (global.get 0)))
1165 "#
1166 }
1167
1168 test_gas_counter_injection! {
1169 name = branch_from_if_not_else;
1170 input = r#"
1171 (module
1172 (func (result i32)
1173 (global.get 0)
1174 (block
1175 (global.get 0)
1176 (if
1177 (then (br 1))
1178 (else (br 0)))
1179 (global.get 0)
1180 (drop))
1181 (global.get 0)))
1182 "#;
1183 expected = r#"
1184 (module
1185 (func (result i32)
1186 (call 0 (i32.const 5))
1187 (global.get 0)
1188 (block
1189 (global.get 0)
1190 (if
1191 (then
1192 (call 0 (i32.const 1))
1193 (br 1))
1194 (else
1195 (call 0 (i32.const 1))
1196 (br 0)))
1197 (call 0 (i32.const 2))
1198 (global.get 0)
1199 (drop))
1200 (global.get 0)))
1201 "#
1202 }
1203
1204 test_gas_counter_injection! {
1205 name = empty_loop;
1206 input = r#"
1207 (module
1208 (func
1209 (loop
1210 (br 0)
1211 )
1212 unreachable
1213 )
1214 )
1215 "#;
1216 expected = r#"
1217 (module
1218 (func
1219 (call 0 (i32.const 2))
1220 (loop
1221 (call 0 (i32.const 1))
1222 (br 0)
1223 )
1224 unreachable
1225 )
1226 )
1227 "#
1228 }
1229}