Skip to content

Commit

Permalink
feat: split decompilation to AST and Writer
Browse files Browse the repository at this point in the history
  • Loading branch information
i582 committed Jan 16, 2025
1 parent fe1c09b commit f0b7bf2
Show file tree
Hide file tree
Showing 12 changed files with 836 additions and 271 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tact-lang/opcode",
"version": "0.0.18",
"version": "0.0.19",
"main": "dist/index.js",
"repository": "https://github.com/tact-lang/ton-opcode.git",
"author": "Steve Korshakov <[email protected]>",
Expand All @@ -25,7 +25,8 @@
"ts-jest": "^29.0.5",
"ts-node": "^10.9.1",
"typescript": "^5.0.2",
"tvm-disassembler": "^3.0.0"
"tvm-disassembler": "^3.0.0",
"@scaleton/func-debug-symbols": "^0.1.4"
},
"peerDependencies": {
"@ton/core": ">=0.49.2",
Expand Down
124 changes: 124 additions & 0 deletions src/ast/AST.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {
BlockNode,
InstructionNode,
MethodNode,
ProcedureNode,
ProgramNode,
ReferenceNode,
ScalarNode,
NodeType,
ControlRegisterNode,
StackEntryNode,
GlobalVariableNode,
MethodReferenceNode,
} from './nodes';

export class AST {
static program(
methods: MethodNode[],
procedures: ProcedureNode[],
): ProgramNode {
return {
type: NodeType.PROGRAM,
methods,
procedures,
};
}

static method(
id: number,
body: BlockNode,
sourceHash: string,
sourceOffset: number,
): MethodNode {
return {
type: NodeType.METHOD,
id,
body,
hash: sourceHash,
offset: sourceOffset,
};
}

static procedure(hash: string, body: BlockNode): ProcedureNode {
return {
type: NodeType.PROCEDURE,
hash,
body,
};
}

static block(
instructions: InstructionNode[],
hash: string,
offset: number,
length: number,
): BlockNode {
return {
type: NodeType.BLOCK,
instructions,
hash,
offset,
length,
};
}

static instruction(
opcode: InstructionNode['opcode'],
args: InstructionNode['arguments'],
offset: number,
length: number,
hash: string,
): InstructionNode {
return {
type: NodeType.INSTRUCTION,
opcode,
arguments: args,
offset,
length,
hash,
};
}

static scalar(value: string | number | bigint): ScalarNode {
return {
type: NodeType.SCALAR,
value,
};
}

static reference(hash: string): ReferenceNode {
return {
type: NodeType.REFERENCE,
hash,
};
}

static controlRegister(index: number): ControlRegisterNode {
return {
type: NodeType.CONTROL_REGISTER,
value: index,
};
}

static stackEntry(index: number): StackEntryNode {
return {
type: NodeType.STACK_ENTRY,
value: index,
};
}

static globalVariable(index: number): GlobalVariableNode {
return {
type: NodeType.GLOBAL_VARIABLE,
value: index,
};
}

static methodReference(method: number): MethodReferenceNode {
return {
type: NodeType.METHOD_REFERENCE,
methodId: method,
};
}
}
2 changes: 2 additions & 0 deletions src/ast/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './nodes';
export * from './AST';
91 changes: 91 additions & 0 deletions src/ast/nodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {Cell} from '@ton/core';
import {OpCode} from '../codepage/opcodes.gen';

export enum NodeType {
PROGRAM,
METHOD,
BLOCK,
INSTRUCTION,
SCALAR,
REFERENCE,
PROCEDURE,
CONTROL_REGISTER,
STACK_ENTRY,
GLOBAL_VARIABLE,
METHOD_REFERENCE,
}

export type ControlRegisterNode = {
type: NodeType.CONTROL_REGISTER;
value: number;
};

export type StackEntryNode = {
type: NodeType.STACK_ENTRY;
value: number;
};

export type GlobalVariableNode = {
type: NodeType.GLOBAL_VARIABLE;
value: number;
};

export type ScalarNode = {
type: NodeType.SCALAR;
value: number | string | bigint | Cell;
};

export type ReferenceNode = {
type: NodeType.REFERENCE;
hash: string;
};

export type MethodReferenceNode = {
type: NodeType.METHOD_REFERENCE;
methodId: number;
};

export type InstructionNode = {
type: NodeType.INSTRUCTION;
opcode: OpCode['code'];
arguments: (
| ScalarNode
| BlockNode
| ReferenceNode
| StackEntryNode
| ControlRegisterNode
| GlobalVariableNode
| MethodReferenceNode
)[];
offset: number;
length: number;
hash: string;
};

export type BlockNode = {
type: NodeType.BLOCK;
instructions: InstructionNode[];
hash: string;
offset: number;
length: number;
};

export type MethodNode = {
type: NodeType.METHOD;
id: number;
body: BlockNode;
hash: string;
offset: number;
};

export type ProcedureNode = {
type: NodeType.PROCEDURE;
hash: string;
body: BlockNode;
};

export type ProgramNode = {
type: NodeType.PROGRAM;
methods: MethodNode[];
procedures: ProcedureNode[];
};
4 changes: 3 additions & 1 deletion src/codepage/opcodes.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export type OpCodeWithArgs =
| { code: 'SAVEALT', args: [number] }
| { code: 'SAVEBOTH', args: [number] }
| { code: 'CALL', args: [number] }
| { code: 'CALLDICT'; args: [number] }
| { code: 'JMP', args: [number] }
| { code: 'PREPARE', args: [number] }
| { code: 'THROW', args: [number] }
Expand Down Expand Up @@ -357,6 +358,7 @@ export type OpCodeNoArgs =
| { code: 'RETFALSE' }
| { code: 'RETBOOL' }
| { code: 'CALLCC' }
| { code: 'INLINECALLDICT' }
| { code: 'JMPXDATA' }
| { code: 'CALLXVARARGS' }
| { code: 'RETVARARGS' }
Expand Down Expand Up @@ -680,4 +682,4 @@ export type OpCodeNoArgs =
| { code: 'CHASHIX' }
| { code: 'CDEPTHIX' };

export type OpCode = OpCodeWithArgs | OpCodeNoArgs;
export type OpCode = OpCodeWithArgs | OpCodeNoArgs;
34 changes: 17 additions & 17 deletions src/decompiler/decompileAll.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { decompileAll } from "./decompileAll";
import * as fs from 'fs';
import { Printer } from "./printer";

describe('decompileAll', () => {
it('should decompile wallet v1', () => {
Expand All @@ -27,21 +26,22 @@ describe('decompileAll', () => {
expect(res).toMatchSnapshot();
});

it('should decompile highload wallet', () => {
let wallet = Buffer.from('te6ccgEBCAEAlwABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQC48oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhYhgBD0eG+lIJgC0wfUMAH7AJEy4gGz5lsBpMjLH8sfy//J7VQABNAwAgFIBgcAF7s5ztRNDTPzHXC/+AARuMl+1E0NcLH4', 'base64');
let res = decompileAll({ src: wallet });
expect(res).toMatchSnapshot();

// Check internals
let printer: Printer = (src) => {
if (typeof src === 'string') {
return src;
}
return JSON.stringify({ op: src.op, hash: src.hash, offset: src.offset, length: src.length, });
};
let snap = decompileAll({ src: wallet, printer });
expect(snap).toMatchSnapshot();
});
// TODO: add way to pass extra logic to recompilation
// it('should decompile highload wallet', () => {
// let wallet = Buffer.from('te6ccgEBCAEAlwABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQC48oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhYhgBD0eG+lIJgC0wfUMAH7AJEy4gGz5lsBpMjLH8sfy//J7VQABNAwAgFIBgcAF7s5ztRNDTPzHXC/+AARuMl+1E0NcLH4', 'base64');
// let res = decompileAll({ src: wallet });
// expect(res).toMatchSnapshot();
//
// // Check internals
// let printer: Printer = (src) => {
// if (typeof src === 'string') {
// return src;
// }
// return JSON.stringify({ op: src.op, hash: src.hash, offset: src.offset, length: src.length, });
// };
// let snap = decompileAll({ src: wallet, printer });
// expect(snap).toMatchSnapshot();
// });

it('should decompile wallet v4 speedtest', () => {
const wallet = Buffer.from('te6ccgECFAEAAtQAART/APSkE/S88sgLAQIBIAIDAgFIBAUE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8QERITAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNBgcCASAICQB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+gIZ9ADLaRfLH1Jgyz8gyYBA+wAGAIpQBIEBCPRZMO1E0IEBQNcgyAHPFvQAye1UAXKwjiOCEGRzdHKDHrFwgBhQBcsFUAPPFiP6AhPLassfyz/JgED7AJJfA+ICASAKCwBZvSQrb2omhAgKBrkPoCGEcNQICEekk30pkQzmkD6f+YN4EoAbeBAUiYcVnzGEAgFYDA0AEbjJftRNDXCx+AA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIA4PABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AAG7SB/oA1NQi+QAFyMoHFcv/ydB3dIAYyMsFywIizxZQBfoCFMtrEszMyXP7AMhAFIEBCPRR8qcCAHCBAQjXGPoA0z/IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBsgQEI1xj6ANM/MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAAr0AMntVA==', 'base64');
Expand Down Expand Up @@ -70,4 +70,4 @@ describe('decompileAll', () => {
let res = decompileAll({ src: mathlib });
expect(res).toMatchSnapshot();
});
});
});
Loading

0 comments on commit f0b7bf2

Please sign in to comment.