foundry_evm_core/
ic.rs
1use alloy_primitives::map::rustc_hash::FxHashMap;
2use eyre::Result;
3use revm::interpreter::{
4 opcode::{PUSH0, PUSH1, PUSH32},
5 OpCode,
6};
7use revm_inspectors::opcode::immediate_size;
8use serde::Serialize;
9
10#[derive(Debug, Clone, Serialize)]
14#[serde(transparent)]
15pub struct PcIcMap {
16 pub inner: FxHashMap<u32, u32>,
17}
18
19impl PcIcMap {
20 pub fn new(code: &[u8]) -> Self {
22 Self { inner: make_map::<true>(code) }
23 }
24
25 pub fn len(&self) -> usize {
27 self.inner.len()
28 }
29
30 pub fn is_empty(&self) -> bool {
32 self.inner.is_empty()
33 }
34
35 pub fn get(&self, pc: u32) -> Option<u32> {
37 self.inner.get(&pc).copied()
38 }
39}
40
41pub struct IcPcMap {
45 pub inner: FxHashMap<u32, u32>,
46}
47
48impl IcPcMap {
49 pub fn new(code: &[u8]) -> Self {
51 Self { inner: make_map::<false>(code) }
52 }
53
54 pub fn len(&self) -> usize {
56 self.inner.len()
57 }
58
59 pub fn is_empty(&self) -> bool {
61 self.inner.is_empty()
62 }
63
64 pub fn get(&self, ic: u32) -> Option<u32> {
66 self.inner.get(&ic).copied()
67 }
68}
69
70fn make_map<const PC_FIRST: bool>(code: &[u8]) -> FxHashMap<u32, u32> {
71 assert!(code.len() <= u32::MAX as usize, "bytecode is too big");
72
73 let mut map = FxHashMap::with_capacity_and_hasher(code.len(), Default::default());
74
75 let mut pc = 0usize;
76 let mut cumulative_push_size = 0usize;
77 while pc < code.len() {
78 let ic = pc - cumulative_push_size;
79 if PC_FIRST {
80 map.insert(pc as u32, ic as u32);
81 } else {
82 map.insert(ic as u32, pc as u32);
83 }
84
85 if (PUSH1..=PUSH32).contains(&code[pc]) {
86 let push_size = (code[pc] - PUSH0) as usize;
88 pc += push_size;
89 cumulative_push_size += push_size;
90 }
91
92 pc += 1;
93 }
94
95 map.shrink_to_fit();
96
97 map
98}
99
100pub struct Instruction<'a> {
102 pub op: Option<OpCode>,
104 pub immediate: &'a [u8],
106 pub pc: u32,
108}
109
110pub fn decode_instructions(code: &[u8]) -> Result<Vec<Instruction<'_>>> {
112 assert!(code.len() <= u32::MAX as usize, "bytecode is too big");
113
114 let mut pc = 0usize;
115 let mut steps = Vec::new();
116
117 while pc < code.len() {
118 let op = OpCode::new(code[pc]);
119 pc += 1;
120 let immediate_size = op.map(|op| immediate_size(op, &code[pc..])).unwrap_or(0) as usize;
121
122 if pc + immediate_size > code.len() {
123 eyre::bail!("incomplete sequence of bytecode");
124 }
125
126 steps.push(Instruction { op, pc: pc as u32, immediate: &code[pc..pc + immediate_size] });
127
128 pc += immediate_size;
129 }
130
131 Ok(steps)
132}