neo_devpack_solidity/cli/bytecode/bytecode_disasm/
disassemble.rs1pub fn disassemble_neovm_bytecode(bytecode: &[u8]) -> String {
7 let width = if bytecode.len() <= 0xFFFF { 4 } else { 6 };
8 let mut out = String::new();
9 let mut pc: usize = 0;
10
11 while pc < bytecode.len() {
12 let offset = pc;
13 let opcode = bytecode[pc];
14 pc += 1;
15
16 let opname = neo_devpack_solidity::runtime::spec::opcode_name(opcode)
17 .map(str::to_string)
18 .unwrap_or_else(|| format!("OP_{opcode:02X}"));
19
20 out.push_str(&format!("{offset:0width$X}: {opname}"));
21
22 match opcode {
23 0x00 => {
25 if let Some(v) = take_i8(bytecode, &mut pc) {
27 out.push_str(&format!(" {v}"));
28 } else {
29 out.push_str(" <unexpected EOF>");
30 break;
31 }
32 }
33 0x01 => {
34 if let Some(v) = take_i16(bytecode, &mut pc) {
36 out.push_str(&format!(" {v}"));
37 } else {
38 out.push_str(" <unexpected EOF>");
39 break;
40 }
41 }
42 0x02 => {
43 if let Some(v) = take_i32(bytecode, &mut pc) {
45 out.push_str(&format!(" {v}"));
46 } else {
47 out.push_str(" <unexpected EOF>");
48 break;
49 }
50 }
51 0x03 => {
52 if let Some(v) = take_i64(bytecode, &mut pc) {
54 out.push_str(&format!(" {v}"));
55 } else {
56 out.push_str(" <unexpected EOF>");
57 break;
58 }
59 }
60 0x04 => {
61 if let Some(bytes) = take(bytecode, &mut pc, 16) {
63 out.push_str(&format!(" 0x{}", hex::encode(bytes)));
64 } else {
65 out.push_str(" <unexpected EOF>");
66 break;
67 }
68 }
69 0x05 => {
70 if let Some(bytes) = take(bytecode, &mut pc, 32) {
72 out.push_str(&format!(" 0x{}", hex::encode(bytes)));
73 } else {
74 out.push_str(" <unexpected EOF>");
75 break;
76 }
77 }
78 0x0A => {
79 if let Some(raw) = take_u32(bytecode, &mut pc) {
81 out.push_str(&format!(" {}", raw as i32));
82 } else {
83 out.push_str(" <unexpected EOF>");
84 break;
85 }
86 }
87 0x0C => {
88 let Some(len) = take_u8(bytecode, &mut pc) else {
90 out.push_str(" <unexpected EOF>");
91 break;
92 };
93 let Some(data) = take(bytecode, &mut pc, len as usize) else {
94 out.push_str(" <unexpected EOF>");
95 break;
96 };
97 out.push_str(&format!(" len={len} 0x{}", hex::encode(data)));
98 }
99 0x0D => {
100 let Some(len) = take_u16(bytecode, &mut pc) else {
102 out.push_str(" <unexpected EOF>");
103 break;
104 };
105 let Some(data) = take(bytecode, &mut pc, len as usize) else {
106 out.push_str(" <unexpected EOF>");
107 break;
108 };
109 out.push_str(&format!(" len={len} 0x{}", hex::encode(data)));
110 }
111 0x0E => {
112 let Some(len) = take_u32(bytecode, &mut pc) else {
114 out.push_str(" <unexpected EOF>");
115 break;
116 };
117 let Some(data) = take(bytecode, &mut pc, len as usize) else {
118 out.push_str(" <unexpected EOF>");
119 break;
120 };
121 out.push_str(&format!(" len={len} 0x{}", hex::encode(data)));
122 }
123
124 0x23 | 0x25 | 0x27 | 0x29 | 0x2B | 0x2D | 0x2F | 0x31 | 0x33 | 0x35 | 0x3E => {
137 let Some(rel) = take_i32(bytecode, &mut pc) else {
138 out.push_str(" <unexpected EOF>");
139 break;
140 };
141 out.push_str(&format!(" -> {}", fmt_target(bytecode.len(), offset, rel)));
142 }
143 0x3C => {
144 let Some(catch_rel) = take_i32(bytecode, &mut pc) else {
146 out.push_str(" <unexpected EOF>");
147 break;
148 };
149 let Some(finally_rel) = take_i32(bytecode, &mut pc) else {
150 out.push_str(" <unexpected EOF>");
151 break;
152 };
153 if catch_rel != 0 {
154 out.push_str(&format!(
155 " catch={}",
156 fmt_target(bytecode.len(), offset, catch_rel)
157 ));
158 }
159 if finally_rel != 0 {
160 out.push_str(&format!(
161 " finally={}",
162 fmt_target(bytecode.len(), offset, finally_rel)
163 ));
164 }
165 }
166 0x3B => {
167 let Some(catch_rel) = take_i8(bytecode, &mut pc) else {
169 out.push_str(" <unexpected EOF>");
170 break;
171 };
172 let Some(finally_rel) = take_i8(bytecode, &mut pc) else {
173 out.push_str(" <unexpected EOF>");
174 break;
175 };
176 if catch_rel != 0 {
177 out.push_str(&format!(
178 " catch={}",
179 fmt_target(bytecode.len(), offset, catch_rel as i32)
180 ));
181 }
182 if finally_rel != 0 {
183 out.push_str(&format!(
184 " finally={}",
185 fmt_target(bytecode.len(), offset, finally_rel as i32)
186 ));
187 }
188 }
189 0x22 | 0x24 | 0x26 | 0x28 | 0x2A | 0x2C | 0x2E | 0x30 | 0x32 | 0x34 | 0x3D => {
201 let Some(rel) = take_i8(bytecode, &mut pc) else {
202 out.push_str(" <unexpected EOF>");
203 break;
204 };
205 out.push_str(&format!(
206 " -> {}",
207 fmt_target(bytecode.len(), offset, rel as i32)
208 ));
209 }
210
211 0x56 => {
213 let Some(count) = take_u8(bytecode, &mut pc) else {
215 out.push_str(" <unexpected EOF>");
216 break;
217 };
218 out.push_str(&format!(" {count}"));
219 }
220 0x57 => {
221 let Some(locals) = take_u8(bytecode, &mut pc) else {
223 out.push_str(" <unexpected EOF>");
224 break;
225 };
226 let Some(args) = take_u8(bytecode, &mut pc) else {
227 out.push_str(" <unexpected EOF>");
228 break;
229 };
230 out.push_str(&format!(" locals={locals} args={args}"));
231 }
232 0x5F | 0x67 | 0x6F | 0x77 | 0x7F | 0x87 => {
233 let Some(index) = take_u8(bytecode, &mut pc) else {
235 out.push_str(" <unexpected EOF>");
236 break;
237 };
238 out.push_str(&format!(" {index}"));
239 }
240
241 0x41 => {
243 let Some(id_bytes) = take(bytecode, &mut pc, 4) else {
244 out.push_str(" <unexpected EOF>");
245 break;
246 };
247 let id = [id_bytes[0], id_bytes[1], id_bytes[2], id_bytes[3]];
248 if let Some(name) = neo_devpack_solidity::runtime::spec::syscall_name(&id) {
249 out.push_str(&format!(" {name}"));
250 } else {
251 out.push_str(&format!(" 0x{}", hex::encode(id)));
252 }
253 }
254
255 0x37 => {
258 let Some(idx_bytes) = take(bytecode, &mut pc, 2) else {
259 out.push_str(" <unexpected EOF>");
260 break;
261 };
262 let idx = u16::from_le_bytes([idx_bytes[0], idx_bytes[1]]);
263 out.push_str(&format!(" token={idx}"));
264 }
265
266 0xDB | 0xD9 => {
267 let Some(type_byte) = take(bytecode, &mut pc, 1) else {
273 out.push_str(" <unexpected EOF>");
274 break;
275 };
276 out.push_str(&format!(" type=0x{:02x}", type_byte[0]));
277 }
278
279 _ => {}
280 }
281
282 out.push('\n');
283 }
284
285 out
286}