revm_bytecode/legacy/
analysis.rs1use super::JumpTable;
2use crate::opcode;
3use bitvec::{bitvec, order::Lsb0, vec::BitVec};
4use primitives::Bytes;
5use std::vec::Vec;
6
7pub fn analyze_legacy(bytecode: Bytes) -> (JumpTable, Bytes) {
13 if bytecode.is_empty() {
14 return (JumpTable::default(), Bytes::from_static(&[opcode::STOP]));
15 }
16
17 let mut jumps: BitVec<u8> = bitvec![u8, Lsb0; 0; bytecode.len()];
18 let range = bytecode.as_ptr_range();
19 let start = range.start;
20 let mut iterator = start;
21 let end = range.end;
22 let mut opcode = 0;
23
24 while iterator < end {
25 opcode = unsafe { *iterator };
26 if opcode == opcode::JUMPDEST {
27 unsafe { jumps.set_unchecked(iterator.offset_from(start) as usize, true) }
29 iterator = unsafe { iterator.add(1) };
30 } else {
31 let push_offset = opcode.wrapping_sub(opcode::PUSH1);
32 if push_offset < 32 {
33 iterator = unsafe { iterator.add(push_offset as usize + 2) };
35 } else {
36 iterator = unsafe { iterator.add(1) };
38 }
39 }
40 }
41
42 let overflow_padding = (iterator as usize) - (end as usize);
43
44 let stop_padding = opcode::OpCode::info_by_op(opcode)
45 .map(|o| !o.is_terminating() as usize)
46 .unwrap_or(1);
47
48 let padding = overflow_padding + stop_padding;
49
50 let bytecode = if padding > 0 {
51 let mut padded = Vec::with_capacity(bytecode.len() + padding);
52 padded.extend_from_slice(&bytecode);
53 padded.resize(padded.len() + padding, 0);
54 Bytes::from(padded)
55 } else {
56 bytecode
57 };
58
59 (JumpTable::new(jumps), bytecode)
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_bytecode_ends_with_stop_no_padding_needed() {
68 let bytecode = vec![
69 opcode::PUSH1,
70 0x01,
71 opcode::PUSH1,
72 0x02,
73 opcode::ADD,
74 opcode::STOP,
75 ];
76 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
77 assert_eq!(padded_bytecode.len(), bytecode.len());
78 }
79
80 #[test]
81 fn test_bytecode_ends_without_stop_requires_padding() {
82 let bytecode = vec![opcode::PUSH1, 0x01, opcode::PUSH1, 0x02, opcode::ADD];
83 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
84 assert_eq!(padded_bytecode.len(), bytecode.len() + 1);
85 }
86
87 #[test]
88 fn test_bytecode_ends_with_push16_requires_17_bytes_padding() {
89 let bytecode = vec![opcode::PUSH1, 0x01, opcode::PUSH16];
90 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
91 assert_eq!(padded_bytecode.len(), bytecode.len() + 17);
92 }
93
94 #[test]
95 fn test_bytecode_ends_with_push2_requires_2_bytes_padding() {
96 let bytecode = vec![opcode::PUSH1, 0x01, opcode::PUSH2, 0x02];
97 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
98 assert_eq!(padded_bytecode.len(), bytecode.len() + 2);
99 }
100
101 #[test]
102 fn test_empty_bytecode_requires_stop() {
103 let bytecode = vec![];
104 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
105 assert_eq!(padded_bytecode.len(), 1); }
107
108 #[test]
109 fn test_bytecode_with_jumpdest_at_start() {
110 let bytecode = vec![opcode::JUMPDEST, opcode::PUSH1, 0x01, opcode::STOP];
111 let (jump_table, _) = analyze_legacy(bytecode.clone().into());
112 assert!(jump_table.is_valid(0)); }
114
115 #[test]
116 fn test_bytecode_with_jumpdest_after_push() {
117 let bytecode = vec![opcode::PUSH1, 0x01, opcode::JUMPDEST, opcode::STOP];
118 let (jump_table, _) = analyze_legacy(bytecode.clone().into());
119 assert!(jump_table.is_valid(2)); }
121
122 #[test]
123 fn test_bytecode_with_multiple_jumpdests() {
124 let bytecode = vec![
125 opcode::JUMPDEST,
126 opcode::PUSH1,
127 0x01,
128 opcode::JUMPDEST,
129 opcode::STOP,
130 ];
131 let (jump_table, _) = analyze_legacy(bytecode.clone().into());
132 assert!(jump_table.is_valid(0)); assert!(jump_table.is_valid(3)); }
135
136 #[test]
137 fn test_bytecode_with_max_push32() {
138 let bytecode = vec![opcode::PUSH32];
139 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
140 assert_eq!(padded_bytecode.len(), bytecode.len() + 33); }
142
143 #[test]
144 fn test_bytecode_with_invalid_opcode() {
145 let bytecode = vec![0xFF, opcode::STOP]; let (jump_table, _) = analyze_legacy(bytecode.clone().into());
147 assert!(!jump_table.is_valid(0)); }
149
150 #[test]
151 fn test_bytecode_with_sequential_pushes() {
152 let bytecode = vec![
153 opcode::PUSH1,
154 0x01,
155 opcode::PUSH2,
156 0x02,
157 0x03,
158 opcode::PUSH4,
159 0x04,
160 0x05,
161 0x06,
162 0x07,
163 opcode::STOP,
164 ];
165 let (jump_table, padded_bytecode) = analyze_legacy(bytecode.clone().into());
166 assert_eq!(padded_bytecode.len(), bytecode.len());
167 assert!(!jump_table.is_valid(0)); assert!(!jump_table.is_valid(2)); assert!(!jump_table.is_valid(5)); }
171
172 #[test]
173 fn test_bytecode_with_jumpdest_in_push_data() {
174 let bytecode = vec![
175 opcode::PUSH2,
176 opcode::JUMPDEST, 0x02,
178 opcode::STOP,
179 ];
180 let (jump_table, _) = analyze_legacy(bytecode.clone().into());
181 assert!(!jump_table.is_valid(1)); }
183
184 #[test]
185 fn test_terminating_opcodes_behavior() {
186 let terminating_opcodes = [
188 opcode::STOP,
189 opcode::RETURN,
190 opcode::REVERT,
191 opcode::INVALID,
192 opcode::SELFDESTRUCT,
193 ];
194
195 for &terminating_opcode in &terminating_opcodes {
196 let bytecode = vec![opcode::PUSH1, 0x01, terminating_opcode];
197 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
198 assert_eq!(padded_bytecode.len(), bytecode.len());
199 }
200 }
201}