neo_solidity/cli/bytecode/bytecode_disasm/
disassemble.rs1pub(crate) fn disassemble_neovm_bytecode(bytecode: &[u8]) -> String {
2 let width = if bytecode.len() <= 0xFFFF { 4 } else { 6 };
3 let mut out = String::new();
4 let mut pc: usize = 0;
5
6 while pc < bytecode.len() {
7 let offset = pc;
8 let opcode = bytecode[pc];
9 pc += 1;
10
11 let opname = neo_solidity::runtime::spec::opcode_name(opcode)
12 .map(str::to_string)
13 .unwrap_or_else(|| format!("OP_{opcode:02X}"));
14
15 out.push_str(&format!("{offset:0width$X}: {opname}", width = width));
16
17 match opcode {
18 0x00 => {
20 if let Some(v) = take_i8(bytecode, &mut pc) {
22 out.push_str(&format!(" {v}"));
23 } else {
24 out.push_str(" <unexpected EOF>");
25 break;
26 }
27 }
28 0x01 => {
29 if let Some(v) = take_i16(bytecode, &mut pc) {
31 out.push_str(&format!(" {v}"));
32 } else {
33 out.push_str(" <unexpected EOF>");
34 break;
35 }
36 }
37 0x02 => {
38 if let Some(v) = take_i32(bytecode, &mut pc) {
40 out.push_str(&format!(" {v}"));
41 } else {
42 out.push_str(" <unexpected EOF>");
43 break;
44 }
45 }
46 0x03 => {
47 if let Some(v) = take_i64(bytecode, &mut pc) {
49 out.push_str(&format!(" {v}"));
50 } else {
51 out.push_str(" <unexpected EOF>");
52 break;
53 }
54 }
55 0x04 => {
56 if let Some(bytes) = take(bytecode, &mut pc, 16) {
58 out.push_str(&format!(" 0x{}", hex::encode(bytes)));
59 } else {
60 out.push_str(" <unexpected EOF>");
61 break;
62 }
63 }
64 0x05 => {
65 if let Some(bytes) = take(bytecode, &mut pc, 32) {
67 out.push_str(&format!(" 0x{}", hex::encode(bytes)));
68 } else {
69 out.push_str(" <unexpected EOF>");
70 break;
71 }
72 }
73 0x0A => {
74 if let Some(target) = take_u32(bytecode, &mut pc) {
76 out.push_str(&format!(" 0x{target:06X}"));
77 } else {
78 out.push_str(" <unexpected EOF>");
79 break;
80 }
81 }
82 0x0C => {
83 let Some(len) = take_u8(bytecode, &mut pc) else {
85 out.push_str(" <unexpected EOF>");
86 break;
87 };
88 let Some(data) = take(bytecode, &mut pc, len as usize) else {
89 out.push_str(" <unexpected EOF>");
90 break;
91 };
92 out.push_str(&format!(" len={len} 0x{}", hex::encode(data)));
93 }
94 0x0D => {
95 let Some(len) = take_u16(bytecode, &mut pc) else {
97 out.push_str(" <unexpected EOF>");
98 break;
99 };
100 let Some(data) = take(bytecode, &mut pc, len as usize) else {
101 out.push_str(" <unexpected EOF>");
102 break;
103 };
104 out.push_str(&format!(" len={len} 0x{}", hex::encode(data)));
105 }
106 0x0E => {
107 let Some(len) = take_u32(bytecode, &mut pc) else {
109 out.push_str(" <unexpected EOF>");
110 break;
111 };
112 let Some(data) = take(bytecode, &mut pc, len as usize) else {
113 out.push_str(" <unexpected EOF>");
114 break;
115 };
116 out.push_str(&format!(" len={len} 0x{}", hex::encode(data)));
117 }
118
119 0x23 | 0x25 | 0x27 | 0x29 | 0x2B | 0x2D | 0x2F | 0x31 | 0x33 | 0x35 | 0x3E => {
132 let Some(rel) = take_i32(bytecode, &mut pc) else {
133 out.push_str(" <unexpected EOF>");
134 break;
135 };
136 out.push_str(&format!(" -> {}", fmt_target(bytecode.len(), offset, rel)));
137 }
138 0x3C => {
139 let Some(catch_rel) = take_i32(bytecode, &mut pc) else {
141 out.push_str(" <unexpected EOF>");
142 break;
143 };
144 let Some(finally_rel) = take_i32(bytecode, &mut pc) else {
145 out.push_str(" <unexpected EOF>");
146 break;
147 };
148 if catch_rel != 0 {
149 out.push_str(&format!(
150 " catch={}",
151 fmt_target(bytecode.len(), offset, catch_rel)
152 ));
153 }
154 if finally_rel != 0 {
155 out.push_str(&format!(
156 " finally={}",
157 fmt_target(bytecode.len(), offset, finally_rel)
158 ));
159 }
160 }
161 0x3B => {
162 let Some(catch_rel) = take_i8(bytecode, &mut pc) else {
164 out.push_str(" <unexpected EOF>");
165 break;
166 };
167 let Some(finally_rel) = take_i8(bytecode, &mut pc) else {
168 out.push_str(" <unexpected EOF>");
169 break;
170 };
171 if catch_rel != 0 {
172 out.push_str(&format!(
173 " catch={}",
174 fmt_target(bytecode.len(), offset, catch_rel as i32)
175 ));
176 }
177 if finally_rel != 0 {
178 out.push_str(&format!(
179 " finally={}",
180 fmt_target(bytecode.len(), offset, finally_rel as i32)
181 ));
182 }
183 }
184 0x22 | 0x24 | 0x26 | 0x28 | 0x2A | 0x2C | 0x2E | 0x30 | 0x32 | 0x34 | 0x3D => {
196 let Some(rel) = take_i8(bytecode, &mut pc) else {
197 out.push_str(" <unexpected EOF>");
198 break;
199 };
200 out.push_str(&format!(
201 " -> {}",
202 fmt_target(bytecode.len(), offset, rel as i32)
203 ));
204 }
205
206 0x56 => {
208 let Some(count) = take_u8(bytecode, &mut pc) else {
210 out.push_str(" <unexpected EOF>");
211 break;
212 };
213 out.push_str(&format!(" {count}"));
214 }
215 0x57 => {
216 let Some(locals) = take_u8(bytecode, &mut pc) else {
218 out.push_str(" <unexpected EOF>");
219 break;
220 };
221 let Some(args) = take_u8(bytecode, &mut pc) else {
222 out.push_str(" <unexpected EOF>");
223 break;
224 };
225 out.push_str(&format!(" locals={locals} args={args}"));
226 }
227 0x5F | 0x67 | 0x6F | 0x77 | 0x7F | 0x87 => {
228 let Some(index) = take_u8(bytecode, &mut pc) else {
230 out.push_str(" <unexpected EOF>");
231 break;
232 };
233 out.push_str(&format!(" {index}"));
234 }
235
236 0x41 => {
238 let Some(id_bytes) = take(bytecode, &mut pc, 4) else {
239 out.push_str(" <unexpected EOF>");
240 break;
241 };
242 let id = [id_bytes[0], id_bytes[1], id_bytes[2], id_bytes[3]];
243 if let Some(name) = neo_solidity::runtime::spec::syscall_name(&id) {
244 out.push_str(&format!(" {name}"));
245 } else {
246 out.push_str(&format!(" 0x{}", hex::encode(id)));
247 }
248 }
249
250 _ => {}
251 }
252
253 out.push('\n');
254 }
255
256 out
257}