rtvm_primitives/
bytecode.rs

1pub mod eof;
2pub mod legacy;
3
4pub use eof::Eof;
5pub use legacy::{JumpTable, LegacyAnalyzedBytecode};
6
7use crate::{keccak256, Bytes, B256, KECCAK_EMPTY};
8
9/// State of the [`Bytecode`] analysis.
10#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub enum Bytecode {
13    /// No analysis has been performed.
14    LegacyRaw(Bytes),
15    /// The bytecode has been analyzed for valid jump destinations.
16    LegacyAnalyzed(LegacyAnalyzedBytecode),
17    /// Ethereum Object Format
18    Eof(Eof),
19}
20
21impl Default for Bytecode {
22    #[inline]
23    fn default() -> Self {
24        // Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode.
25        Self::new()
26    }
27}
28
29impl Bytecode {
30    // Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode.
31    #[inline]
32    pub fn new() -> Self {
33        Self::LegacyAnalyzed(LegacyAnalyzedBytecode::default())
34    }
35
36    /// Return jump table if bytecode is analyzed
37    #[inline]
38    pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
39        match &self {
40            Self::LegacyAnalyzed(analyzed) => Some(analyzed.jump_table()),
41            _ => None,
42        }
43    }
44
45    /// Calculate hash of the bytecode.
46    pub fn hash_slow(&self) -> B256 {
47        if self.is_empty() {
48            KECCAK_EMPTY
49        } else {
50            keccak256(self.original_byte_slice())
51        }
52    }
53
54    /// Return reference to the EOF if bytecode is EOF.
55    #[inline]
56    pub const fn eof(&self) -> Option<&Eof> {
57        match self {
58            Self::Eof(eof) => Some(eof),
59            _ => None,
60        }
61    }
62
63    /// Return true if bytecode is EOF.
64    #[inline]
65    pub const fn is_eof(&self) -> bool {
66        matches!(self, Self::Eof(_))
67    }
68
69    /// Creates a new raw [`Bytecode`].
70    #[inline]
71    pub fn new_raw(bytecode: Bytes) -> Self {
72        Self::LegacyRaw(bytecode)
73    }
74
75    /// Create new checked bytecode.
76    ///
77    /// # Safety
78    ///
79    /// Bytecode needs to end with STOP (0x00) opcode as checked bytecode assumes
80    /// that it is safe to iterate over bytecode without checking lengths.
81    pub unsafe fn new_analyzed(
82        bytecode: Bytes,
83        original_len: usize,
84        jump_table: JumpTable,
85    ) -> Self {
86        Self::LegacyAnalyzed(LegacyAnalyzedBytecode::new(
87            bytecode,
88            original_len,
89            jump_table,
90        ))
91    }
92
93    /// Returns a reference to the bytecode.
94    /// In case of EOF this will be the first code section.
95    #[inline]
96    pub fn bytecode_bytes(&self) -> Bytes {
97        match self {
98            Self::LegacyRaw(bytes) => bytes.clone(),
99            Self::LegacyAnalyzed(analyzed) => analyzed.bytes(),
100            Self::Eof(eof) => eof
101                .body
102                .code(0)
103                .expect("Valid EOF has at least one code section")
104                .clone(),
105        }
106    }
107
108    /// Returns false if bytecode can't be executed in Interpreter.
109    pub fn is_execution_ready(&self) -> bool {
110        !matches!(self, Self::LegacyRaw(_))
111    }
112
113    /// Returns a reference to the original bytecode.
114    #[inline]
115    pub fn original_bytes(&self) -> Bytes {
116        match self {
117            Self::LegacyRaw(bytes) => bytes.clone(),
118            Self::LegacyAnalyzed(analyzed) => analyzed.original_bytes(),
119            Self::Eof(eof) => eof.raw().clone(),
120        }
121    }
122
123    /// Returns the original bytecode as a byte slice.
124    #[inline]
125    pub fn original_byte_slice(&self) -> &[u8] {
126        match self {
127            Self::LegacyRaw(bytes) => bytes,
128            Self::LegacyAnalyzed(analyzed) => analyzed.original_byte_slice(),
129            Self::Eof(eof) => eof.raw(),
130        }
131    }
132
133    /// Returns the length of the raw bytes.
134    #[inline]
135    pub fn len(&self) -> usize {
136        match self {
137            Self::LegacyRaw(bytes) => bytes.len(),
138            Self::LegacyAnalyzed(analyzed) => analyzed.original_len(),
139            Self::Eof(eof) => eof.size(),
140        }
141    }
142
143    /// Returns whether the bytecode is empty.
144    #[inline]
145    pub fn is_empty(&self) -> bool {
146        self.len() == 0
147    }
148}