simple-javascript-obf 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/node.js.yml +31 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/THIRD_PARTY_NOTICES.md +82 -0
- package/bin/js-obf +228 -0
- package/bin/obf.sh +257 -0
- package/package.json +26 -0
- package/src/index.js +106 -0
- package/src/options.js +128 -0
- package/src/pipeline.js +56 -0
- package/src/plugins/antiHook.js +123 -0
- package/src/plugins/controlFlowFlatten.js +203 -0
- package/src/plugins/deadCode.js +82 -0
- package/src/plugins/encodeMembers.js +44 -0
- package/src/plugins/entry.js +31 -0
- package/src/plugins/rename.js +100 -0
- package/src/plugins/stringEncode.js +494 -0
- package/src/plugins/vm/ast-utils.js +58 -0
- package/src/plugins/vm/compiler.js +113 -0
- package/src/plugins/vm/constants.js +72 -0
- package/src/plugins/vm/emit.js +916 -0
- package/src/plugins/vm/encoding.js +252 -0
- package/src/plugins/vm/index.js +366 -0
- package/src/plugins/vm/mapping.js +24 -0
- package/src/plugins/vm/normalize.js +692 -0
- package/src/plugins/vm/runtime.js +1145 -0
- package/src/plugins/vm.js +1 -0
- package/src/utils/names.js +55 -0
- package/src/utils/reserved.js +57 -0
- package/src/utils/rng.js +55 -0
- package/src/utils/stream.js +97 -0
- package/src/utils/string.js +13 -0
- package/test/bench-runner.js +78 -0
- package/test/benchmark-source.js +35 -0
- package/test/benchmark-vm.js +160 -0
- package/test/dist/bench.obf.js +1 -0
- package/test/dist/bench.original.js +35 -0
- package/test/dist/bench.vm.js +1 -0
- package/test/dist/sample-input.obf.js +1 -0
- package/test/dist/sample-input.vm.js +1 -0
- package/test/generate-obf.js +38 -0
- package/test/obf-smoke.js +129 -0
- package/test/sample-input.js +23 -0
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
const generate = require("@babel/generator").default;
|
|
2
|
+
|
|
3
|
+
const { BIN_OPS, UNARY_OPS, OPCODES } = require("./constants");
|
|
4
|
+
const { createLabel } = require("./compiler");
|
|
5
|
+
const { containsSuper, isSimpleLiteral, literalValue } = require("./ast-utils");
|
|
6
|
+
|
|
7
|
+
function hasWithStack(state) {
|
|
8
|
+
return Array.isArray(state.withStack) && state.withStack.length > 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function emitWithGet(name, compiler, state) {
|
|
12
|
+
if (!hasWithStack(state)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const nameIdx = compiler.addConst(name);
|
|
16
|
+
const inIndex = BIN_OPS.indexOf("in");
|
|
17
|
+
const end = createLabel();
|
|
18
|
+
for (let i = state.withStack.length - 1; i >= 0; i -= 1) {
|
|
19
|
+
const withName = state.withStack[i];
|
|
20
|
+
const withIdx = compiler.addConst(withName);
|
|
21
|
+
const next = createLabel();
|
|
22
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
23
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
24
|
+
compiler.emit(OPCODES.BIN_OP, inIndex);
|
|
25
|
+
compiler.emitJump(OPCODES.JMP_IF_FALSE, next);
|
|
26
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
27
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
28
|
+
compiler.emit(OPCODES.GET_PROP);
|
|
29
|
+
compiler.emitJump(OPCODES.JMP, end);
|
|
30
|
+
compiler.mark(next);
|
|
31
|
+
}
|
|
32
|
+
compiler.emit(OPCODES.GET_GLOBAL, nameIdx);
|
|
33
|
+
compiler.mark(end);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function emitMemberAssignment(expr, compiler, state) {
|
|
38
|
+
const left = expr.left;
|
|
39
|
+
if (left.type !== "MemberExpression") {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const objTemp = compiler.createTempName();
|
|
43
|
+
const propTemp = compiler.createTempName();
|
|
44
|
+
const objIdx = compiler.addConst(objTemp);
|
|
45
|
+
const propIdx = compiler.addConst(propTemp);
|
|
46
|
+
|
|
47
|
+
if (!compileExpression(left.object, compiler, state)) return false;
|
|
48
|
+
compiler.emit(OPCODES.SET_VAR, objIdx);
|
|
49
|
+
compiler.emit(OPCODES.POP);
|
|
50
|
+
|
|
51
|
+
if (left.computed) {
|
|
52
|
+
if (!compileExpression(left.property, compiler, state)) return false;
|
|
53
|
+
} else {
|
|
54
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(left.property.name));
|
|
55
|
+
}
|
|
56
|
+
compiler.emit(OPCODES.SET_VAR, propIdx);
|
|
57
|
+
compiler.emit(OPCODES.POP);
|
|
58
|
+
|
|
59
|
+
if (expr.operator === "=") {
|
|
60
|
+
compiler.emit(OPCODES.GET_VAR, objIdx);
|
|
61
|
+
compiler.emit(OPCODES.GET_VAR, propIdx);
|
|
62
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
63
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const op = expr.operator.slice(0, -1);
|
|
68
|
+
const opIndex = BIN_OPS.indexOf(op);
|
|
69
|
+
if (opIndex === -1) return false;
|
|
70
|
+
|
|
71
|
+
compiler.emit(OPCODES.GET_VAR, objIdx);
|
|
72
|
+
compiler.emit(OPCODES.GET_VAR, propIdx);
|
|
73
|
+
compiler.emit(OPCODES.GET_PROP);
|
|
74
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
75
|
+
compiler.emit(OPCODES.BIN_OP, opIndex);
|
|
76
|
+
|
|
77
|
+
const valueTemp = compiler.createTempName();
|
|
78
|
+
const valueIdx = compiler.addConst(valueTemp);
|
|
79
|
+
compiler.emit(OPCODES.SET_VAR, valueIdx);
|
|
80
|
+
compiler.emit(OPCODES.POP);
|
|
81
|
+
|
|
82
|
+
compiler.emit(OPCODES.GET_VAR, objIdx);
|
|
83
|
+
compiler.emit(OPCODES.GET_VAR, propIdx);
|
|
84
|
+
compiler.emit(OPCODES.GET_VAR, valueIdx);
|
|
85
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function emitMemberUpdate(expr, compiler, state) {
|
|
90
|
+
const arg = expr.argument;
|
|
91
|
+
if (arg.type !== "MemberExpression") {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const isPrefix = expr.prefix;
|
|
95
|
+
const delta = expr.operator === "++" ? 1 : -1;
|
|
96
|
+
const objTemp = compiler.createTempName();
|
|
97
|
+
const propTemp = compiler.createTempName();
|
|
98
|
+
const objIdx = compiler.addConst(objTemp);
|
|
99
|
+
const propIdx = compiler.addConst(propTemp);
|
|
100
|
+
|
|
101
|
+
if (!compileExpression(arg.object, compiler, state)) return false;
|
|
102
|
+
compiler.emit(OPCODES.SET_VAR, objIdx);
|
|
103
|
+
compiler.emit(OPCODES.POP);
|
|
104
|
+
|
|
105
|
+
if (arg.computed) {
|
|
106
|
+
if (!compileExpression(arg.property, compiler, state)) return false;
|
|
107
|
+
} else {
|
|
108
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(arg.property.name));
|
|
109
|
+
}
|
|
110
|
+
compiler.emit(OPCODES.SET_VAR, propIdx);
|
|
111
|
+
compiler.emit(OPCODES.POP);
|
|
112
|
+
|
|
113
|
+
compiler.emit(OPCODES.GET_VAR, objIdx);
|
|
114
|
+
compiler.emit(OPCODES.GET_VAR, propIdx);
|
|
115
|
+
compiler.emit(OPCODES.GET_PROP);
|
|
116
|
+
|
|
117
|
+
const oldTemp = compiler.createTempName();
|
|
118
|
+
const oldIdx = compiler.addConst(oldTemp);
|
|
119
|
+
compiler.emit(OPCODES.SET_VAR, oldIdx);
|
|
120
|
+
compiler.emit(OPCODES.POP);
|
|
121
|
+
|
|
122
|
+
compiler.emit(OPCODES.GET_VAR, oldIdx);
|
|
123
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(delta));
|
|
124
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("+"));
|
|
125
|
+
|
|
126
|
+
const newTemp = compiler.createTempName();
|
|
127
|
+
const newIdx = compiler.addConst(newTemp);
|
|
128
|
+
compiler.emit(OPCODES.SET_VAR, newIdx);
|
|
129
|
+
compiler.emit(OPCODES.POP);
|
|
130
|
+
|
|
131
|
+
compiler.emit(OPCODES.GET_VAR, objIdx);
|
|
132
|
+
compiler.emit(OPCODES.GET_VAR, propIdx);
|
|
133
|
+
compiler.emit(OPCODES.GET_VAR, newIdx);
|
|
134
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
135
|
+
|
|
136
|
+
if (!isPrefix) {
|
|
137
|
+
compiler.emit(OPCODES.POP);
|
|
138
|
+
compiler.emit(OPCODES.GET_VAR, oldIdx);
|
|
139
|
+
} else {
|
|
140
|
+
compiler.emit(OPCODES.GET_VAR, newIdx);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function emitWithAssignment(expr, compiler, state) {
|
|
147
|
+
if (!hasWithStack(state)) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (expr.left.type !== "Identifier") {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const name = expr.left.name;
|
|
154
|
+
if (state.locals.has(name)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const nameIdx = compiler.addConst(name);
|
|
159
|
+
const inIndex = BIN_OPS.indexOf("in");
|
|
160
|
+
const end = createLabel();
|
|
161
|
+
for (let i = state.withStack.length - 1; i >= 0; i -= 1) {
|
|
162
|
+
const withName = state.withStack[i];
|
|
163
|
+
const withIdx = compiler.addConst(withName);
|
|
164
|
+
const next = createLabel();
|
|
165
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
166
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
167
|
+
compiler.emit(OPCODES.BIN_OP, inIndex);
|
|
168
|
+
compiler.emitJump(OPCODES.JMP_IF_FALSE, next);
|
|
169
|
+
if (expr.operator === "=") {
|
|
170
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
171
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
172
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
173
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
174
|
+
} else {
|
|
175
|
+
const op = expr.operator.slice(0, -1);
|
|
176
|
+
const opIndex = BIN_OPS.indexOf(op);
|
|
177
|
+
if (opIndex === -1) return false;
|
|
178
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
179
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
180
|
+
compiler.emit(OPCODES.GET_PROP);
|
|
181
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
182
|
+
compiler.emit(OPCODES.BIN_OP, opIndex);
|
|
183
|
+
|
|
184
|
+
const valueTemp = compiler.createTempName();
|
|
185
|
+
const valueIdx = compiler.addConst(valueTemp);
|
|
186
|
+
compiler.emit(OPCODES.SET_VAR, valueIdx);
|
|
187
|
+
compiler.emit(OPCODES.POP);
|
|
188
|
+
|
|
189
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
190
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
191
|
+
compiler.emit(OPCODES.GET_VAR, valueIdx);
|
|
192
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
193
|
+
}
|
|
194
|
+
compiler.emitJump(OPCODES.JMP, end);
|
|
195
|
+
compiler.mark(next);
|
|
196
|
+
}
|
|
197
|
+
if (expr.operator === "=") {
|
|
198
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
199
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
200
|
+
} else {
|
|
201
|
+
const op = expr.operator.slice(0, -1);
|
|
202
|
+
const opIndex = BIN_OPS.indexOf(op);
|
|
203
|
+
if (opIndex === -1) return false;
|
|
204
|
+
compiler.emit(OPCODES.GET_GLOBAL, nameIdx);
|
|
205
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
206
|
+
compiler.emit(OPCODES.BIN_OP, opIndex);
|
|
207
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
208
|
+
}
|
|
209
|
+
compiler.mark(end);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function emitWithUpdate(expr, compiler, state) {
|
|
214
|
+
if (!hasWithStack(state)) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
if (expr.argument.type !== "Identifier") {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const name = expr.argument.name;
|
|
221
|
+
if (state.locals.has(name)) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
const nameIdx = compiler.addConst(name);
|
|
225
|
+
const inIndex = BIN_OPS.indexOf("in");
|
|
226
|
+
const end = createLabel();
|
|
227
|
+
const isPrefix = expr.prefix;
|
|
228
|
+
const delta = expr.operator === "++" ? 1 : -1;
|
|
229
|
+
for (let i = state.withStack.length - 1; i >= 0; i -= 1) {
|
|
230
|
+
const withName = state.withStack[i];
|
|
231
|
+
const withIdx = compiler.addConst(withName);
|
|
232
|
+
const next = createLabel();
|
|
233
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
234
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
235
|
+
compiler.emit(OPCODES.BIN_OP, inIndex);
|
|
236
|
+
compiler.emitJump(OPCODES.JMP_IF_FALSE, next);
|
|
237
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
238
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
239
|
+
compiler.emit(OPCODES.GET_PROP);
|
|
240
|
+
const oldTemp = compiler.createTempName();
|
|
241
|
+
const oldIdx = compiler.addConst(oldTemp);
|
|
242
|
+
compiler.emit(OPCODES.SET_VAR, oldIdx);
|
|
243
|
+
compiler.emit(OPCODES.POP);
|
|
244
|
+
compiler.emit(OPCODES.GET_VAR, oldIdx);
|
|
245
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(delta));
|
|
246
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("+"));
|
|
247
|
+
|
|
248
|
+
const newTemp = compiler.createTempName();
|
|
249
|
+
const newIdx = compiler.addConst(newTemp);
|
|
250
|
+
compiler.emit(OPCODES.SET_VAR, newIdx);
|
|
251
|
+
compiler.emit(OPCODES.POP);
|
|
252
|
+
|
|
253
|
+
compiler.emit(OPCODES.GET_VAR, withIdx);
|
|
254
|
+
compiler.emit(OPCODES.PUSH_CONST, nameIdx);
|
|
255
|
+
compiler.emit(OPCODES.GET_VAR, newIdx);
|
|
256
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
257
|
+
|
|
258
|
+
if (!isPrefix) {
|
|
259
|
+
compiler.emit(OPCODES.POP);
|
|
260
|
+
compiler.emit(OPCODES.GET_VAR, oldIdx);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
compiler.emitJump(OPCODES.JMP, end);
|
|
264
|
+
compiler.mark(next);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (isPrefix) {
|
|
268
|
+
compiler.emit(OPCODES.GET_GLOBAL, nameIdx);
|
|
269
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(delta));
|
|
270
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("+"));
|
|
271
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
272
|
+
} else {
|
|
273
|
+
compiler.emit(OPCODES.GET_GLOBAL, nameIdx);
|
|
274
|
+
compiler.emit(OPCODES.DUP);
|
|
275
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(delta));
|
|
276
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("+"));
|
|
277
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
278
|
+
compiler.emit(OPCODES.POP);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
compiler.mark(end);
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function compileExpression(expr, compiler, state) {
|
|
286
|
+
const { ctx, locals } = state;
|
|
287
|
+
const { t } = ctx;
|
|
288
|
+
|
|
289
|
+
switch (expr.type) {
|
|
290
|
+
case "NumericLiteral":
|
|
291
|
+
case "StringLiteral":
|
|
292
|
+
case "BooleanLiteral":
|
|
293
|
+
case "NullLiteral": {
|
|
294
|
+
const idx = compiler.addConst(literalValue(expr));
|
|
295
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
case "Identifier": {
|
|
299
|
+
if (expr.name === "undefined") {
|
|
300
|
+
const idx = compiler.addConst(undefined);
|
|
301
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
if (locals.has(expr.name)) {
|
|
305
|
+
const idx = compiler.addConst(expr.name);
|
|
306
|
+
compiler.emit(OPCODES.GET_VAR, idx);
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
if (emitWithGet(expr.name, compiler, state)) {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
const idx = compiler.addConst(expr.name);
|
|
313
|
+
compiler.emit(OPCODES.GET_GLOBAL, idx);
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
case "MetaProperty": {
|
|
317
|
+
const { meta, property } = expr;
|
|
318
|
+
if (
|
|
319
|
+
meta &&
|
|
320
|
+
property &&
|
|
321
|
+
meta.type === "Identifier" &&
|
|
322
|
+
property.type === "Identifier" &&
|
|
323
|
+
meta.name === "new" &&
|
|
324
|
+
property.name === "target" &&
|
|
325
|
+
state.newTargetKey
|
|
326
|
+
) {
|
|
327
|
+
const idx = compiler.addConst(state.newTargetKey);
|
|
328
|
+
compiler.emit(OPCODES.GET_VAR, idx);
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
case "ThisExpression":
|
|
334
|
+
compiler.emit(OPCODES.PUSH_THIS);
|
|
335
|
+
return true;
|
|
336
|
+
case "AwaitExpression": {
|
|
337
|
+
if (!state.isAsync) {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
if (!compileExpression(expr.argument, compiler, state)) return false;
|
|
341
|
+
compiler.emit(OPCODES.AWAIT);
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
case "UnaryExpression": {
|
|
345
|
+
if (expr.operator === "delete") {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
const opIndex = UNARY_OPS.indexOf(expr.operator);
|
|
349
|
+
if (opIndex === -1) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
if (!compileExpression(expr.argument, compiler, state)) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
compiler.emit(OPCODES.UNARY_OP, opIndex);
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
case "BinaryExpression": {
|
|
359
|
+
const opIndex = BIN_OPS.indexOf(expr.operator);
|
|
360
|
+
if (opIndex === -1) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
if (!compileExpression(expr.left, compiler, state)) return false;
|
|
364
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
365
|
+
compiler.emit(OPCODES.BIN_OP, opIndex);
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
case "LogicalExpression": {
|
|
369
|
+
const end = createLabel();
|
|
370
|
+
const shortLabel = createLabel();
|
|
371
|
+
if (!compileExpression(expr.left, compiler, state)) return false;
|
|
372
|
+
compiler.emit(OPCODES.DUP);
|
|
373
|
+
if (expr.operator === "&&") {
|
|
374
|
+
compiler.emitJump(OPCODES.JMP_IF_FALSE, shortLabel);
|
|
375
|
+
} else if (expr.operator === "||") {
|
|
376
|
+
compiler.emitJump(OPCODES.JMP_IF_TRUE, shortLabel);
|
|
377
|
+
} else {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
compiler.emit(OPCODES.POP);
|
|
381
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
382
|
+
compiler.emitJump(OPCODES.JMP, end);
|
|
383
|
+
compiler.mark(shortLabel);
|
|
384
|
+
compiler.emitJump(OPCODES.JMP, end);
|
|
385
|
+
compiler.mark(end);
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
case "ConditionalExpression": {
|
|
389
|
+
const elseLabel = createLabel();
|
|
390
|
+
const endLabel = createLabel();
|
|
391
|
+
if (!compileExpression(expr.test, compiler, state)) return false;
|
|
392
|
+
compiler.emitJump(OPCODES.JMP_IF_FALSE, elseLabel);
|
|
393
|
+
if (!compileExpression(expr.consequent, compiler, state)) return false;
|
|
394
|
+
compiler.emitJump(OPCODES.JMP, endLabel);
|
|
395
|
+
compiler.mark(elseLabel);
|
|
396
|
+
if (!compileExpression(expr.alternate, compiler, state)) return false;
|
|
397
|
+
compiler.mark(endLabel);
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
case "AssignmentExpression": {
|
|
401
|
+
if (expr.left.type === "Identifier") {
|
|
402
|
+
const name = expr.left.name;
|
|
403
|
+
const nameIdx = compiler.addConst(name);
|
|
404
|
+
if (locals.has(name)) {
|
|
405
|
+
if (expr.operator === "=") {
|
|
406
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
407
|
+
compiler.emit(OPCODES.SET_VAR, nameIdx);
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
const op = expr.operator.slice(0, -1);
|
|
411
|
+
const opIndex = BIN_OPS.indexOf(op);
|
|
412
|
+
if (opIndex === -1) return false;
|
|
413
|
+
compiler.emit(OPCODES.GET_VAR, nameIdx);
|
|
414
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
415
|
+
compiler.emit(OPCODES.BIN_OP, opIndex);
|
|
416
|
+
compiler.emit(OPCODES.SET_VAR, nameIdx);
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
if (emitWithAssignment(expr, compiler, state)) {
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
if (expr.operator === "=") {
|
|
423
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
424
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
const op = expr.operator.slice(0, -1);
|
|
428
|
+
const opIndex = BIN_OPS.indexOf(op);
|
|
429
|
+
if (opIndex === -1) return false;
|
|
430
|
+
compiler.emit(OPCODES.GET_GLOBAL, nameIdx);
|
|
431
|
+
if (!compileExpression(expr.right, compiler, state)) return false;
|
|
432
|
+
compiler.emit(OPCODES.BIN_OP, opIndex);
|
|
433
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
if (expr.left.type === "MemberExpression") {
|
|
437
|
+
return emitMemberAssignment(expr, compiler, state);
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
case "UpdateExpression": {
|
|
442
|
+
const isPrefix = expr.prefix;
|
|
443
|
+
const delta = expr.operator === "++" ? 1 : -1;
|
|
444
|
+
if (expr.argument.type === "Identifier") {
|
|
445
|
+
const name = expr.argument.name;
|
|
446
|
+
const nameIdx = compiler.addConst(name);
|
|
447
|
+
if (locals.has(name)) {
|
|
448
|
+
compiler.emit(OPCODES.GET_VAR, nameIdx);
|
|
449
|
+
if (!isPrefix) compiler.emit(OPCODES.DUP);
|
|
450
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(delta));
|
|
451
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("+"));
|
|
452
|
+
compiler.emit(OPCODES.SET_VAR, nameIdx);
|
|
453
|
+
if (!isPrefix) compiler.emit(OPCODES.POP);
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
if (emitWithUpdate(expr, compiler, state)) {
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
compiler.emit(OPCODES.GET_GLOBAL, nameIdx);
|
|
460
|
+
if (!isPrefix) compiler.emit(OPCODES.DUP);
|
|
461
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(delta));
|
|
462
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("+"));
|
|
463
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
464
|
+
if (!isPrefix) compiler.emit(OPCODES.POP);
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
if (expr.argument.type === "MemberExpression") {
|
|
468
|
+
return emitMemberUpdate(expr, compiler, state);
|
|
469
|
+
}
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
case "MemberExpression": {
|
|
473
|
+
if (!compileExpression(expr.object, compiler, state)) return false;
|
|
474
|
+
if (expr.computed) {
|
|
475
|
+
if (!compileExpression(expr.property, compiler, state)) return false;
|
|
476
|
+
} else {
|
|
477
|
+
const idx = compiler.addConst(expr.property.name);
|
|
478
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
479
|
+
}
|
|
480
|
+
compiler.emit(OPCODES.GET_PROP);
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
case "CallExpression": {
|
|
484
|
+
const callee = expr.callee;
|
|
485
|
+
if (callee.type === "MemberExpression") {
|
|
486
|
+
if (!compileExpression(callee.object, compiler, state)) return false;
|
|
487
|
+
if (callee.computed) {
|
|
488
|
+
if (!compileExpression(callee.property, compiler, state)) return false;
|
|
489
|
+
} else {
|
|
490
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(callee.property.name));
|
|
491
|
+
}
|
|
492
|
+
for (const arg of expr.arguments) {
|
|
493
|
+
if (arg.type === "SpreadElement") return false;
|
|
494
|
+
if (!compileExpression(arg, compiler, state)) return false;
|
|
495
|
+
}
|
|
496
|
+
compiler.emit(OPCODES.CALL_METHOD, expr.arguments.length);
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
if (!compileExpression(expr.callee, compiler, state)) return false;
|
|
500
|
+
for (const arg of expr.arguments) {
|
|
501
|
+
if (arg.type === "SpreadElement") return false;
|
|
502
|
+
if (!compileExpression(arg, compiler, state)) return false;
|
|
503
|
+
}
|
|
504
|
+
compiler.emit(OPCODES.CALL, expr.arguments.length);
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
case "NewExpression": {
|
|
508
|
+
if (!compileExpression(expr.callee, compiler, state)) return false;
|
|
509
|
+
for (const arg of expr.arguments) {
|
|
510
|
+
if (arg.type === "SpreadElement") return false;
|
|
511
|
+
if (!compileExpression(arg, compiler, state)) return false;
|
|
512
|
+
}
|
|
513
|
+
compiler.emit(OPCODES.NEW, expr.arguments.length);
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
case "ObjectExpression": {
|
|
517
|
+
for (const prop of expr.properties) {
|
|
518
|
+
if (prop.type === "SpreadElement") {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
const hasSuperMethod = expr.properties.some(
|
|
523
|
+
(prop) => prop.type === "ObjectMethod" && containsSuper(prop)
|
|
524
|
+
);
|
|
525
|
+
if (hasSuperMethod) {
|
|
526
|
+
const src = generate(expr).code;
|
|
527
|
+
const idx = compiler.addConst(src);
|
|
528
|
+
compiler.emit(OPCODES.MAKE_FUNC, idx);
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
compiler.emit(OPCODES.MAKE_OBJ);
|
|
532
|
+
for (const prop of expr.properties) {
|
|
533
|
+
if (prop.type === "ObjectProperty") {
|
|
534
|
+
compiler.emit(OPCODES.DUP);
|
|
535
|
+
if (prop.computed) {
|
|
536
|
+
if (!compileExpression(prop.key, compiler, state)) return false;
|
|
537
|
+
} else if (prop.key.type === "Identifier") {
|
|
538
|
+
const idx = compiler.addConst(prop.key.name);
|
|
539
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
540
|
+
} else if (prop.key.type === "StringLiteral" || prop.key.type === "NumericLiteral") {
|
|
541
|
+
const idx = compiler.addConst(String(prop.key.value));
|
|
542
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
543
|
+
} else {
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
if (!compileExpression(prop.value, compiler, state)) return false;
|
|
547
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
548
|
+
compiler.emit(OPCODES.POP);
|
|
549
|
+
} else if (prop.type === "ObjectMethod") {
|
|
550
|
+
compiler.emit(OPCODES.DUP);
|
|
551
|
+
if (prop.computed) {
|
|
552
|
+
if (!compileExpression(prop.key, compiler, state)) return false;
|
|
553
|
+
} else if (prop.key.type === "Identifier") {
|
|
554
|
+
const idx = compiler.addConst(prop.key.name);
|
|
555
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
556
|
+
} else {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
const funcExpr = t.functionExpression(
|
|
560
|
+
null,
|
|
561
|
+
prop.params,
|
|
562
|
+
prop.body,
|
|
563
|
+
prop.generator,
|
|
564
|
+
prop.async
|
|
565
|
+
);
|
|
566
|
+
const src = generate(funcExpr).code;
|
|
567
|
+
const idx = compiler.addConst(src);
|
|
568
|
+
compiler.emit(OPCODES.MAKE_FUNC, idx);
|
|
569
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
570
|
+
compiler.emit(OPCODES.POP);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
case "ArrayExpression": {
|
|
576
|
+
compiler.emit(OPCODES.MAKE_ARR);
|
|
577
|
+
for (let i = 0; i < expr.elements.length; i += 1) {
|
|
578
|
+
const elem = expr.elements[i];
|
|
579
|
+
if (!elem) {
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
if (elem.type === "SpreadElement") return false;
|
|
583
|
+
compiler.emit(OPCODES.DUP);
|
|
584
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(i));
|
|
585
|
+
if (!compileExpression(elem, compiler, state)) return false;
|
|
586
|
+
compiler.emit(OPCODES.SET_PROP);
|
|
587
|
+
compiler.emit(OPCODES.POP);
|
|
588
|
+
}
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
case "TemplateLiteral": {
|
|
592
|
+
if (expr.expressions.length === 0) {
|
|
593
|
+
const idx = compiler.addConst(expr.quasis[0].value.cooked || "");
|
|
594
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
let first = true;
|
|
598
|
+
for (let i = 0; i < expr.quasis.length; i += 1) {
|
|
599
|
+
const quasi = expr.quasis[i];
|
|
600
|
+
const text = quasi.value.cooked || "";
|
|
601
|
+
const idx = compiler.addConst(text);
|
|
602
|
+
if (first) {
|
|
603
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
604
|
+
first = false;
|
|
605
|
+
} else {
|
|
606
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
607
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("+"));
|
|
608
|
+
}
|
|
609
|
+
if (i < expr.expressions.length) {
|
|
610
|
+
if (!compileExpression(expr.expressions[i], compiler, state)) return false;
|
|
611
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("+"));
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
case "FunctionExpression":
|
|
617
|
+
case "ArrowFunctionExpression": {
|
|
618
|
+
const src = generate(expr).code;
|
|
619
|
+
const idx = compiler.addConst(src);
|
|
620
|
+
compiler.emit(OPCODES.MAKE_FUNC, idx);
|
|
621
|
+
return true;
|
|
622
|
+
}
|
|
623
|
+
case "ClassExpression": {
|
|
624
|
+
const src = generate(expr).code;
|
|
625
|
+
const idx = compiler.addConst(src);
|
|
626
|
+
compiler.emit(OPCODES.MAKE_FUNC, idx);
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
case "SequenceExpression": {
|
|
630
|
+
for (let i = 0; i < expr.expressions.length; i += 1) {
|
|
631
|
+
if (!compileExpression(expr.expressions[i], compiler, state)) return false;
|
|
632
|
+
if (i < expr.expressions.length - 1) {
|
|
633
|
+
compiler.emit(OPCODES.POP);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
default:
|
|
639
|
+
if (isSimpleLiteral(expr)) {
|
|
640
|
+
const idx = compiler.addConst(literalValue(expr));
|
|
641
|
+
compiler.emit(OPCODES.PUSH_CONST, idx);
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function compileStatement(stmt, compiler, state) {
|
|
649
|
+
const { ctx } = state;
|
|
650
|
+
const { t } = ctx;
|
|
651
|
+
switch (stmt.type) {
|
|
652
|
+
case "BlockStatement":
|
|
653
|
+
for (const sub of stmt.body) {
|
|
654
|
+
if (!compileStatement(sub, compiler, state)) return false;
|
|
655
|
+
}
|
|
656
|
+
return true;
|
|
657
|
+
case "ExpressionStatement":
|
|
658
|
+
if (!compileExpression(stmt.expression, compiler, state)) return false;
|
|
659
|
+
compiler.emit(OPCODES.POP);
|
|
660
|
+
return true;
|
|
661
|
+
case "WithStatement": {
|
|
662
|
+
const tempName = compiler.createTempName();
|
|
663
|
+
const nameIdx = compiler.addConst(tempName);
|
|
664
|
+
if (!compileExpression(stmt.object, compiler, state)) return false;
|
|
665
|
+
compiler.emit(OPCODES.SET_VAR, nameIdx);
|
|
666
|
+
compiler.emit(OPCODES.POP);
|
|
667
|
+
const withStack = state.withStack ? [...state.withStack, tempName] : [tempName];
|
|
668
|
+
const withState = {
|
|
669
|
+
...state,
|
|
670
|
+
withStack,
|
|
671
|
+
};
|
|
672
|
+
if (!compileStatement(stmt.body, compiler, withState)) return false;
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
675
|
+
case "VariableDeclaration":
|
|
676
|
+
for (const decl of stmt.declarations) {
|
|
677
|
+
if (decl.id.type !== "Identifier") return false;
|
|
678
|
+
if (decl.init) {
|
|
679
|
+
if (!compileExpression(decl.init, compiler, state)) return false;
|
|
680
|
+
} else {
|
|
681
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(undefined));
|
|
682
|
+
}
|
|
683
|
+
const nameIdx = compiler.addConst(decl.id.name);
|
|
684
|
+
if (state.locals.has(decl.id.name)) {
|
|
685
|
+
compiler.emit(OPCODES.SET_VAR, nameIdx);
|
|
686
|
+
} else {
|
|
687
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
688
|
+
}
|
|
689
|
+
compiler.emit(OPCODES.POP);
|
|
690
|
+
}
|
|
691
|
+
return true;
|
|
692
|
+
case "ReturnStatement":
|
|
693
|
+
if (stmt.argument) {
|
|
694
|
+
if (!compileExpression(stmt.argument, compiler, state)) return false;
|
|
695
|
+
} else {
|
|
696
|
+
compiler.emit(OPCODES.PUSH_CONST, compiler.addConst(undefined));
|
|
697
|
+
}
|
|
698
|
+
compiler.emit(OPCODES.RETURN);
|
|
699
|
+
return true;
|
|
700
|
+
case "IfStatement": {
|
|
701
|
+
const elseLabel = createLabel();
|
|
702
|
+
const endLabel = createLabel();
|
|
703
|
+
if (!compileExpression(stmt.test, compiler, state)) return false;
|
|
704
|
+
compiler.emitJump(OPCODES.JMP_IF_FALSE, elseLabel);
|
|
705
|
+
if (!compileStatement(stmt.consequent, compiler, state)) return false;
|
|
706
|
+
compiler.emitJump(OPCODES.JMP, endLabel);
|
|
707
|
+
compiler.mark(elseLabel);
|
|
708
|
+
if (stmt.alternate) {
|
|
709
|
+
if (!compileStatement(stmt.alternate, compiler, state)) return false;
|
|
710
|
+
}
|
|
711
|
+
compiler.mark(endLabel);
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
case "WhileStatement": {
|
|
715
|
+
const start = createLabel();
|
|
716
|
+
const end = createLabel();
|
|
717
|
+
compiler.mark(start);
|
|
718
|
+
if (!compileExpression(stmt.test, compiler, state)) return false;
|
|
719
|
+
compiler.emitJump(OPCODES.JMP_IF_FALSE, end);
|
|
720
|
+
const loopState = {
|
|
721
|
+
...state,
|
|
722
|
+
breakLabel: end,
|
|
723
|
+
continueLabel: start,
|
|
724
|
+
};
|
|
725
|
+
if (!compileStatement(stmt.body, compiler, loopState)) return false;
|
|
726
|
+
compiler.emitJump(OPCODES.JMP, start);
|
|
727
|
+
compiler.mark(end);
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
case "DoWhileStatement": {
|
|
731
|
+
const start = createLabel();
|
|
732
|
+
const end = createLabel();
|
|
733
|
+
compiler.mark(start);
|
|
734
|
+
const loopState = {
|
|
735
|
+
...state,
|
|
736
|
+
breakLabel: end,
|
|
737
|
+
continueLabel: start,
|
|
738
|
+
};
|
|
739
|
+
if (!compileStatement(stmt.body, compiler, loopState)) return false;
|
|
740
|
+
if (!compileExpression(stmt.test, compiler, state)) return false;
|
|
741
|
+
compiler.emitJump(OPCODES.JMP_IF_TRUE, start);
|
|
742
|
+
compiler.mark(end);
|
|
743
|
+
return true;
|
|
744
|
+
}
|
|
745
|
+
case "ForStatement": {
|
|
746
|
+
const start = createLabel();
|
|
747
|
+
const end = createLabel();
|
|
748
|
+
const updateLabel = createLabel();
|
|
749
|
+
if (stmt.init) {
|
|
750
|
+
if (stmt.init.type === "VariableDeclaration") {
|
|
751
|
+
if (!compileStatement(stmt.init, compiler, state)) return false;
|
|
752
|
+
} else if (!compileExpression(stmt.init, compiler, state)) {
|
|
753
|
+
return false;
|
|
754
|
+
} else {
|
|
755
|
+
compiler.emit(OPCODES.POP);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
compiler.mark(start);
|
|
759
|
+
if (stmt.test) {
|
|
760
|
+
if (!compileExpression(stmt.test, compiler, state)) return false;
|
|
761
|
+
compiler.emitJump(OPCODES.JMP_IF_FALSE, end);
|
|
762
|
+
}
|
|
763
|
+
const loopState = {
|
|
764
|
+
...state,
|
|
765
|
+
breakLabel: end,
|
|
766
|
+
continueLabel: updateLabel,
|
|
767
|
+
};
|
|
768
|
+
if (!compileStatement(stmt.body, compiler, loopState)) return false;
|
|
769
|
+
compiler.mark(updateLabel);
|
|
770
|
+
if (stmt.update) {
|
|
771
|
+
if (!compileExpression(stmt.update, compiler, state)) return false;
|
|
772
|
+
compiler.emit(OPCODES.POP);
|
|
773
|
+
}
|
|
774
|
+
compiler.emitJump(OPCODES.JMP, start);
|
|
775
|
+
compiler.mark(end);
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
case "BreakStatement":
|
|
779
|
+
if (!state.breakLabel) return false;
|
|
780
|
+
compiler.emitJump(OPCODES.JMP, state.breakLabel);
|
|
781
|
+
return true;
|
|
782
|
+
case "ContinueStatement":
|
|
783
|
+
if (!state.continueLabel) return false;
|
|
784
|
+
compiler.emitJump(OPCODES.JMP, state.continueLabel);
|
|
785
|
+
return true;
|
|
786
|
+
case "SwitchStatement": {
|
|
787
|
+
const end = createLabel();
|
|
788
|
+
if (!compileExpression(stmt.discriminant, compiler, state)) return false;
|
|
789
|
+
const caseLabels = stmt.cases.map(() => createLabel());
|
|
790
|
+
const entryLabels = stmt.cases.map(() => createLabel());
|
|
791
|
+
let defaultIndex = -1;
|
|
792
|
+
for (let i = 0; i < stmt.cases.length; i += 1) {
|
|
793
|
+
const caseItem = stmt.cases[i];
|
|
794
|
+
if (!caseItem.test) {
|
|
795
|
+
defaultIndex = i;
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
compiler.emit(OPCODES.DUP);
|
|
799
|
+
if (!compileExpression(caseItem.test, compiler, state)) return false;
|
|
800
|
+
compiler.emit(OPCODES.BIN_OP, BIN_OPS.indexOf("==="));
|
|
801
|
+
compiler.emitJump(OPCODES.JMP_IF_TRUE, entryLabels[i]);
|
|
802
|
+
}
|
|
803
|
+
if (defaultIndex >= 0) {
|
|
804
|
+
compiler.emitJump(OPCODES.JMP, entryLabels[defaultIndex]);
|
|
805
|
+
} else {
|
|
806
|
+
compiler.emit(OPCODES.POP);
|
|
807
|
+
compiler.emitJump(OPCODES.JMP, end);
|
|
808
|
+
}
|
|
809
|
+
for (let i = 0; i < stmt.cases.length; i += 1) {
|
|
810
|
+
compiler.mark(entryLabels[i]);
|
|
811
|
+
compiler.emit(OPCODES.POP);
|
|
812
|
+
compiler.emitJump(OPCODES.JMP, caseLabels[i]);
|
|
813
|
+
}
|
|
814
|
+
for (let i = 0; i < stmt.cases.length; i += 1) {
|
|
815
|
+
compiler.mark(caseLabels[i]);
|
|
816
|
+
const caseItem = stmt.cases[i];
|
|
817
|
+
const loopState = {
|
|
818
|
+
...state,
|
|
819
|
+
breakLabel: end,
|
|
820
|
+
};
|
|
821
|
+
for (const caseStmt of caseItem.consequent) {
|
|
822
|
+
if (!compileStatement(caseStmt, compiler, loopState)) return false;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
compiler.mark(end);
|
|
826
|
+
return true;
|
|
827
|
+
}
|
|
828
|
+
case "TryStatement": {
|
|
829
|
+
const catchLabel = stmt.handler ? createLabel() : null;
|
|
830
|
+
const finallyLabel = stmt.finalizer ? createLabel() : null;
|
|
831
|
+
const endLabel = createLabel();
|
|
832
|
+
compiler.emitTry(catchLabel, finallyLabel, endLabel);
|
|
833
|
+
if (!compileStatement(stmt.block, compiler, state)) return false;
|
|
834
|
+
if (catchLabel || finallyLabel) {
|
|
835
|
+
compiler.emitJump(OPCODES.JMP, endLabel);
|
|
836
|
+
}
|
|
837
|
+
if (catchLabel) {
|
|
838
|
+
compiler.mark(catchLabel);
|
|
839
|
+
compiler.emit(OPCODES.ENTER_CATCH);
|
|
840
|
+
if (stmt.handler.param && stmt.handler.param.type === "Identifier") {
|
|
841
|
+
const nameIdx = compiler.addConst(stmt.handler.param.name);
|
|
842
|
+
compiler.emit(OPCODES.SET_VAR, nameIdx);
|
|
843
|
+
compiler.emit(OPCODES.POP);
|
|
844
|
+
}
|
|
845
|
+
if (!compileStatement(stmt.handler.body, compiler, state)) return false;
|
|
846
|
+
compiler.emit(OPCODES.END_TRY);
|
|
847
|
+
if (finallyLabel) {
|
|
848
|
+
compiler.emitJump(OPCODES.JMP, endLabel);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
if (finallyLabel) {
|
|
852
|
+
compiler.mark(finallyLabel);
|
|
853
|
+
compiler.emit(OPCODES.ENTER_FINALLY);
|
|
854
|
+
if (!compileStatement(stmt.finalizer, compiler, state)) return false;
|
|
855
|
+
compiler.emit(OPCODES.END_TRY);
|
|
856
|
+
compiler.emitJump(OPCODES.JMP, endLabel);
|
|
857
|
+
}
|
|
858
|
+
compiler.mark(endLabel);
|
|
859
|
+
if (catchLabel && finallyLabel) {
|
|
860
|
+
compiler.emit(OPCODES.RETHROW);
|
|
861
|
+
} else if (catchLabel) {
|
|
862
|
+
compiler.emit(OPCODES.RETHROW);
|
|
863
|
+
} else if (finallyLabel) {
|
|
864
|
+
compiler.emit(OPCODES.END_TRY);
|
|
865
|
+
}
|
|
866
|
+
return true;
|
|
867
|
+
}
|
|
868
|
+
case "ThrowStatement":
|
|
869
|
+
if (!compileExpression(stmt.argument, compiler, state)) return false;
|
|
870
|
+
compiler.emit(OPCODES.THROW);
|
|
871
|
+
return true;
|
|
872
|
+
case "EmptyStatement":
|
|
873
|
+
return true;
|
|
874
|
+
case "DebuggerStatement":
|
|
875
|
+
return true;
|
|
876
|
+
case "ClassDeclaration": {
|
|
877
|
+
if (!stmt.id) return false;
|
|
878
|
+
const classExpr = t.classExpression(
|
|
879
|
+
stmt.id,
|
|
880
|
+
stmt.superClass,
|
|
881
|
+
stmt.body,
|
|
882
|
+
stmt.decorators || []
|
|
883
|
+
);
|
|
884
|
+
const src = generate(classExpr).code;
|
|
885
|
+
const idx = compiler.addConst(src);
|
|
886
|
+
compiler.emit(OPCODES.MAKE_FUNC, idx);
|
|
887
|
+
const nameIdx = compiler.addConst(stmt.id.name);
|
|
888
|
+
if (state.locals.has(stmt.id.name)) {
|
|
889
|
+
compiler.emit(OPCODES.SET_VAR, nameIdx);
|
|
890
|
+
} else {
|
|
891
|
+
compiler.emit(OPCODES.SET_GLOBAL, nameIdx);
|
|
892
|
+
}
|
|
893
|
+
compiler.emit(OPCODES.POP);
|
|
894
|
+
return true;
|
|
895
|
+
}
|
|
896
|
+
case "FunctionDeclaration": {
|
|
897
|
+
if (!stmt.id) return false;
|
|
898
|
+
const funcExpr = t.functionExpression(
|
|
899
|
+
stmt.id,
|
|
900
|
+
stmt.params,
|
|
901
|
+
stmt.body,
|
|
902
|
+
stmt.generator,
|
|
903
|
+
stmt.async
|
|
904
|
+
);
|
|
905
|
+
if (!compileExpression(funcExpr, compiler, state)) return false;
|
|
906
|
+
const nameIdx = compiler.addConst(stmt.id.name);
|
|
907
|
+
compiler.emit(OPCODES.SET_VAR, nameIdx);
|
|
908
|
+
compiler.emit(OPCODES.POP);
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
default:
|
|
912
|
+
return false;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
module.exports = { compileStatement };
|