1 |
|
2 | import { assertByteCode, Byte, Op, OpCode } from '@neo-one/client-common-esnext-esm';
|
3 | import { BinaryReader, utils } from './utils';
|
4 |
|
5 | const createHexString = (bytes: Buffer): string => {
|
6 | let mutableResult = '';
|
7 | bytes.forEach((byte) => {
|
8 | mutableResult += `${byte.toString(16).padStart(2, '0')}`;
|
9 | });
|
10 |
|
11 | return `0x${mutableResult}`;
|
12 | };
|
13 |
|
14 | interface Line {
|
15 | readonly pc: number;
|
16 | readonly value: string;
|
17 | }
|
18 |
|
19 | export const disassembleByteCode = (bytes: Buffer): ReadonlyArray<Line> => {
|
20 | const reader = new BinaryReader(bytes);
|
21 |
|
22 | const mutableResult: Array<[number, OpCode | 'UNKNOWN', string | undefined]> = [];
|
23 |
|
24 | while (reader.hasMore()) {
|
25 | const pc = reader.index;
|
26 | const byte = assertByteCode(reader.readUInt8());
|
27 |
|
28 | const pushBytes = byte >= Op.PUSHBYTES1 && byte <= Op.PUSHBYTES75;
|
29 | const pushData1 = byte === Op.PUSHDATA1;
|
30 | const pushData2 = byte === Op.PUSHDATA2;
|
31 | const pushData4 = byte === Op.PUSHDATA4;
|
32 |
|
33 | const opCode = Byte[byte];
|
34 |
|
35 | if (pushBytes || pushData1 || pushData2 || pushData4) {
|
36 | let numBytes;
|
37 | if (pushBytes) {
|
38 | numBytes = byte;
|
39 | } else if (pushData1) {
|
40 | numBytes = reader.readUInt8();
|
41 | } else if (pushData2) {
|
42 | numBytes = reader.readUInt16LE();
|
43 | } else {
|
44 | numBytes = reader.readInt32LE();
|
45 | }
|
46 | mutableResult.push([pc, opCode, createHexString(reader.readBytes(numBytes))]);
|
47 | } else if (byte === Op.JMP || byte === Op.JMPIF || byte === Op.JMPIFNOT || byte === Op.CALL) {
|
48 | mutableResult.push([pc, opCode, `${reader.readInt16LE()}`]);
|
49 | } else if (byte === Op.APPCALL || byte === Op.TAILCALL) {
|
50 | const mutableAppBytes = [...reader.readBytes(20)];
|
51 | mutableAppBytes.reverse();
|
52 | mutableResult.push([pc, opCode, createHexString(Buffer.from(mutableAppBytes))]);
|
53 | } else if (byte === Op.SYSCALL) {
|
54 | mutableResult.push([pc, opCode, utils.toASCII(reader.readVarBytesLE(252))]);
|
55 |
|
56 | } else if (byte === Op.CALL_E || byte === Op.CALL_ET) {
|
57 | const returnValueCount = reader.readBytes(1);
|
58 | const parametersCount = reader.readBytes(1);
|
59 | const mutableAppBytes = [...reader.readBytes(20)];
|
60 | mutableAppBytes.reverse();
|
61 | mutableResult.push([
|
62 | pc,
|
63 | opCode,
|
64 | `${createHexString(returnValueCount)} ${createHexString(parametersCount)} ${createHexString(
|
65 | Buffer.from(mutableAppBytes),
|
66 | )}`,
|
67 | ]);
|
68 | } else if (byte === Op.CALL_ED || byte === Op.CALL_EDT || byte === Op.CALL_I) {
|
69 | const returnValueCount = reader.readBytes(1);
|
70 | const parametersCount = reader.readBytes(1);
|
71 | mutableResult.push([pc, opCode, `${createHexString(returnValueCount)} ${createHexString(parametersCount)}`]);
|
72 | } else {
|
73 | mutableResult.push([pc, opCode, undefined]);
|
74 | }
|
75 | }
|
76 |
|
77 | return mutableResult.map(([index, opCode, val]) => ({
|
78 | pc: index,
|
79 | value: `${index.toString().padStart(4, '0')}:${opCode}${val === undefined ? '' : ` ${val}`}`,
|
80 | }));
|
81 | };
|
82 |
|
\ | No newline at end of file |