-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
119 lines (108 loc) · 3.77 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { readFileSync } from 'fs';
import { assert } from './src/assert';
import { Instruction, parseJumpdest, parsePushOperator } from './src/instruction';
import { fromGlobalTag, fromMnemonic } from './src/mapping';
import { TagToOffsetMapping } from './src/models';
import { sanitize } from './src/sanitizer';
import BN from 'bn.js';
const inPath = process.argv[2];
const outPath = '';
const inFile = readFileSync(inPath, 'utf-8').toUpperCase();
const sanitized = sanitize(inFile);
const tagMap: TagToOffsetMapping = {};
const lines = sanitized.split('\n');
let lineNumber = 1;
/// parse instructions
let instructions: Instruction[] = [];
for (const line of lines) {
// push handling
if(line.startsWith('PUSH')){
const instruction = parsePushOperator(line, lineNumber);
instructions.push(instruction);
}
else if(line.startsWith('JUMPDEST')){
const instruction = parseJumpdest(line, lineNumber);
instructions.push(instruction);
}else if(line.startsWith('@')){
const instruction: Instruction = {
type: 'TAG',
params: [line.trim()],
size: 0 // tag is just a tag
}
instructions.push(instruction);
}else{
if(!line){
lineNumber++;
continue;
}
const opcode = fromMnemonic[line];
assert(!!opcode, `opcode ${line} not found at line ${lineNumber}.`);
const instruction: Instruction = {
type: 'GENERAL',
opcode: opcode,
size: 1,
params: []
};
instructions.push(instruction);
}
lineNumber++;
}
/// create tag map
let pc = 0;
let afterRuntimeTag = false;
for (const instruction of instructions){
// console.log(pc, ":", instruction.opcode?.mnemonic)
if(instruction.type == 'TAG'){
if(!instruction.params){
throw new Error(`unexpected error at instruction ${JSON.stringify(instruction)}`);
}
if(afterRuntimeTag){
const runtimeTagOffset = tagMap['@RUNTIME'];
tagMap[instruction.params[0]] = pc - runtimeTagOffset;
}else{
tagMap[instruction.params[0]] = pc;
if(instruction.params[0] == '@RUNTIME'){
afterRuntimeTag = true;
}
}
// console.log(' -',tagMap[instruction.params[0]]);
}
pc += instruction.size;
}
/// parse bytes
let outCode = '';
for (const instruction of instructions){
if(instruction.type == 'PUSH'){ // PUSHx 0x...
outCode += instruction.opcode?.uint8;
outCode += instruction.params[0];
}else if(instruction.type == 'PUSH_TAG'){ // PUSH2 @tag
outCode += instruction.opcode?.uint8;
const tag = instruction.params[0];
if(!tagMap[tag]){
throw new Error(`tag '${tag} not defined.'`);
}
if(tagMap[tag] < 256){
outCode += '00'+Buffer.from([tagMap[tag]]).toString('hex');
}else{
outCode += Buffer.from([tagMap[tag]]).toString('hex');
}
}else if(instruction.type == 'PUSH_GLOBAL'){
outCode += instruction.opcode?.uint8;
const tag = instruction.params[0];
outCode += fromGlobalTag[tag];
}else if(instruction.type == 'TAG'){ // @tag
continue;
}else if(instruction.type == 'GENERAL'){ // OPCODE
outCode += instruction.opcode?.uint8;
}
}
// finalize
const codeSize = outCode.length/2;
const codeSizeHex = new BN(codeSize).toString('hex', 4).toUpperCase();
outCode = outCode.replaceAll('CDSZ', codeSizeHex);
if(tagMap['@RUNTIME']){
const runtimeLength = codeSize - tagMap['@RUNTIME']
const runtimeLengthHex = new BN(runtimeLength).toString('hex', 4).toUpperCase();
outCode = outCode.replaceAll('RT_L', runtimeLengthHex);
}
console.log(outCode);