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/// Maps from program counter to instruction counter.
11///
12/// Inverse of [`IcPcMap`].
13#[derive(Debug, Clone, Serialize)]
14#[serde(transparent)]
15pub struct PcIcMap {
16    pub inner: FxHashMap<u32, u32>,
17}
18
19impl PcIcMap {
20    /// Creates a new `PcIcMap` for the given code.
21    pub fn new(code: &[u8]) -> Self {
22        Self { inner: make_map::<true>(code) }
23    }
24
25    /// Returns the length of the map.
26    pub fn len(&self) -> usize {
27        self.inner.len()
28    }
29
30    /// Returns `true` if the map is empty.
31    pub fn is_empty(&self) -> bool {
32        self.inner.is_empty()
33    }
34
35    /// Returns the instruction counter for the given program counter.
36    pub fn get(&self, pc: u32) -> Option<u32> {
37        self.inner.get(&pc).copied()
38    }
39}
40
41/// Map from instruction counter to program counter.
42///
43/// Inverse of [`PcIcMap`].
44pub struct IcPcMap {
45    pub inner: FxHashMap<u32, u32>,
46}
47
48impl IcPcMap {
49    /// Creates a new `IcPcMap` for the given code.
50    pub fn new(code: &[u8]) -> Self {
51        Self { inner: make_map::<false>(code) }
52    }
53
54    /// Returns the length of the map.
55    pub fn len(&self) -> usize {
56        self.inner.len()
57    }
58
59    /// Returns `true` if the map is empty.
60    pub fn is_empty(&self) -> bool {
61        self.inner.is_empty()
62    }
63
64    /// Returns the program counter for the given instruction counter.
65    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            // Skip the push bytes.
87            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
100/// Represents a single instruction consisting of the opcode and its immediate data.
101pub struct Instruction<'a> {
102    /// OpCode, if it could be decoded.
103    pub op: Option<OpCode>,
104    /// Immediate data following the opcode.
105    pub immediate: &'a [u8],
106    /// Program counter of the opcode.
107    pub pc: u32,
108}
109
110/// Decodes raw opcode bytes into [`Instruction`]s.
111pub 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}