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 padding = (iterator as usize) - (end as usize) + (opcode != opcode::STOP) as usize;
43 let bytecode = if padding > 0 {
44 let mut padded = Vec::with_capacity(bytecode.len() + padding);
45 padded.extend_from_slice(&bytecode);
46 padded.resize(padded.len() + padding, 0);
47 Bytes::from(padded)
48 } else {
49 bytecode
50 };
51
52 (JumpTable::new(jumps), bytecode)
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn test_bytecode_ends_with_stop_no_padding_needed() {
61 let bytecode = vec![
62 opcode::PUSH1,
63 0x01,
64 opcode::PUSH1,
65 0x02,
66 opcode::ADD,
67 opcode::STOP,
68 ];
69 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
70 assert_eq!(padded_bytecode.len(), bytecode.len());
71 }
72
73 #[test]
74 fn test_bytecode_ends_without_stop_requires_padding() {
75 let bytecode = vec![opcode::PUSH1, 0x01, opcode::PUSH1, 0x02, opcode::ADD];
76 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
77 assert_eq!(padded_bytecode.len(), bytecode.len() + 1);
78 }
79
80 #[test]
81 fn test_bytecode_ends_with_push16_requires_17_bytes_padding() {
82 let bytecode = vec![opcode::PUSH1, 0x01, opcode::PUSH16];
83 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
84 assert_eq!(padded_bytecode.len(), bytecode.len() + 17);
85 }
86
87 #[test]
88 fn test_bytecode_ends_with_push2_requires_2_bytes_padding() {
89 let bytecode = vec![opcode::PUSH1, 0x01, opcode::PUSH2, 0x02];
90 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
91 assert_eq!(padded_bytecode.len(), bytecode.len() + 2);
92 }
93
94 #[test]
95 fn test_empty_bytecode_requires_stop() {
96 let bytecode = vec![];
97 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
98 assert_eq!(padded_bytecode.len(), 1); }
100
101 #[test]
102 fn test_bytecode_with_jumpdest_at_start() {
103 let bytecode = vec![opcode::JUMPDEST, opcode::PUSH1, 0x01, opcode::STOP];
104 let (jump_table, _) = analyze_legacy(bytecode.clone().into());
105 assert!(jump_table.is_valid(0)); }
107
108 #[test]
109 fn test_bytecode_with_jumpdest_after_push() {
110 let bytecode = vec![opcode::PUSH1, 0x01, opcode::JUMPDEST, opcode::STOP];
111 let (jump_table, _) = analyze_legacy(bytecode.clone().into());
112 assert!(jump_table.is_valid(2)); }
114
115 #[test]
116 fn test_bytecode_with_multiple_jumpdests() {
117 let bytecode = vec![
118 opcode::JUMPDEST,
119 opcode::PUSH1,
120 0x01,
121 opcode::JUMPDEST,
122 opcode::STOP,
123 ];
124 let (jump_table, _) = analyze_legacy(bytecode.clone().into());
125 assert!(jump_table.is_valid(0)); assert!(jump_table.is_valid(3)); }
128
129 #[test]
130 fn test_bytecode_with_max_push32() {
131 let bytecode = vec![opcode::PUSH32];
132 let (_, padded_bytecode) = analyze_legacy(bytecode.clone().into());
133 assert_eq!(padded_bytecode.len(), bytecode.len() + 33); }
135
136 #[test]
137 fn test_bytecode_with_invalid_opcode() {
138 let bytecode = vec![0xFF, opcode::STOP]; let (jump_table, _) = analyze_legacy(bytecode.clone().into());
140 assert!(!jump_table.is_valid(0)); }
142
143 #[test]
144 fn test_bytecode_with_sequential_pushes() {
145 let bytecode = vec![
146 opcode::PUSH1,
147 0x01,
148 opcode::PUSH2,
149 0x02,
150 0x03,
151 opcode::PUSH4,
152 0x04,
153 0x05,
154 0x06,
155 0x07,
156 opcode::STOP,
157 ];
158 let (jump_table, padded_bytecode) = analyze_legacy(bytecode.clone().into());
159 assert_eq!(padded_bytecode.len(), bytecode.len());
160 assert!(!jump_table.is_valid(0)); assert!(!jump_table.is_valid(2)); assert!(!jump_table.is_valid(5)); }
164
165 #[test]
166 fn test_bytecode_with_jumpdest_in_push_data() {
167 let bytecode = vec![
168 opcode::PUSH2,
169 opcode::JUMPDEST, 0x02,
171 opcode::STOP,
172 ];
173 let (jump_table, _) = analyze_legacy(bytecode.clone().into());
174 assert!(!jump_table.is_valid(1)); }
176}