@dacely/toildefender 0.1.0
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/LICENSE +661 -0
- package/NOTICE.md +16 -0
- package/README.md +380 -0
- package/cli.js +168 -0
- package/defendjs.js +7 -0
- package/docs/all-modes-output.demo.js +673 -0
- package/estest.js +49 -0
- package/esutils.js +107 -0
- package/logger.js +28 -0
- package/obfuscator.js +534 -0
- package/package.json +108 -0
- package/processors/deadCode.js +62 -0
- package/processors/flattener.js +808 -0
- package/processors/health.js +55 -0
- package/processors/identifiers.js +256 -0
- package/processors/literalObfuscator.js +40 -0
- package/processors/literals.js +233 -0
- package/processors/methods.js +332 -0
- package/processors/modules.js +231 -0
- package/processors/normalizer.js +490 -0
- package/processors/numericVm.js +950 -0
- package/processors/postprocessing.js +69 -0
- package/processors/preprocessing.js +248 -0
- package/processors/scopes.js +177 -0
- package/processors/uglifier.js +26 -0
- package/processors/variables.js +185 -0
- package/toildefender.js +7 -0
- package/traverser.js +115 -0
- package/utils.js +135 -0
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var assert = require("assert");
|
|
4
|
+
var events = require("events");
|
|
5
|
+
|
|
6
|
+
var _ = require("lodash");
|
|
7
|
+
|
|
8
|
+
var estest = require("../estest");
|
|
9
|
+
var traverser = require("../traverser");
|
|
10
|
+
var utils = require("../utils");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Push a SwitchCase onto an array while removing all identical SwitchCases
|
|
14
|
+
* @param {SwitchCase[]} arr
|
|
15
|
+
* @param {SwitchCase} elem
|
|
16
|
+
*/
|
|
17
|
+
function pushUniqSwitchCase(arr, elem) {
|
|
18
|
+
_.remove(arr, x => x.test.value == elem.test.value);
|
|
19
|
+
arr.push(elem);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Shuffle SwitchCase statements while respecting fall troughs.
|
|
24
|
+
* @param entries {SwitchCase[]} Array of the unshuffled cases
|
|
25
|
+
* @returns {SwitchCase[]} New array of the shuffled cases
|
|
26
|
+
*/
|
|
27
|
+
function shuffleSwitchCases(entries) {
|
|
28
|
+
var groups = [], stack = [];
|
|
29
|
+
function clearStack() {
|
|
30
|
+
if (stack.length > 0) {
|
|
31
|
+
groups.push(stack);
|
|
32
|
+
stack = [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
entries.forEach(entry => {
|
|
36
|
+
var breaks = entry.consequent.some(x => x.type == "BreakStatement");
|
|
37
|
+
if (breaks) {
|
|
38
|
+
clearStack();
|
|
39
|
+
groups.push([ entry ]);
|
|
40
|
+
} else {
|
|
41
|
+
stack.push(entry);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
clearStack();
|
|
45
|
+
return Array.prototype.concat.apply([], _.shuffle(groups));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Merge nested BlockStatements (BlockStatements containing other BlockStatements)
|
|
50
|
+
* @param {BlockStatement} node Root BlockStatement
|
|
51
|
+
* @returns {BlockStatement} Merged BlockStatement
|
|
52
|
+
*/
|
|
53
|
+
function mergeNestedBlocks(node) {
|
|
54
|
+
assert(estest.isNode(node));
|
|
55
|
+
|
|
56
|
+
function getBlockBodys(node) {
|
|
57
|
+
if (node.type == "Program" || node.type == "BlockStatement") {
|
|
58
|
+
var stmts = [];
|
|
59
|
+
node.body.forEach(stmt => utils.push(stmts, getBlockBodys(stmt)));
|
|
60
|
+
return stmts;
|
|
61
|
+
} else {
|
|
62
|
+
return [ node ];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
type: node.type,
|
|
68
|
+
body: getBlockBodys(node)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Split array of statements into array of compound statements and BlockStatements containing an array of non-compound statements
|
|
74
|
+
* @param {Node[]} nodes Array of statements
|
|
75
|
+
* @returns {Statement[]} Array of Statements
|
|
76
|
+
*/
|
|
77
|
+
function splitBlocks(nodes) {
|
|
78
|
+
var stack = [], output = [];
|
|
79
|
+
for (var i = 0; i < nodes.length; ++i) {
|
|
80
|
+
if (estest.isCompoundStatement(nodes[i])) {
|
|
81
|
+
if (stack.length > 0) {
|
|
82
|
+
output.push({
|
|
83
|
+
type: "BlockStatement",
|
|
84
|
+
body: stack
|
|
85
|
+
});
|
|
86
|
+
stack = [];
|
|
87
|
+
}
|
|
88
|
+
output.push(nodes[i]);
|
|
89
|
+
} else if (estest.isStatement(nodes[i])) {
|
|
90
|
+
stack.push(nodes[i]);
|
|
91
|
+
} else if (estest.isExpression(nodes[i])) {
|
|
92
|
+
this.logger.warn("Unexpected expression " + nodes[i].type);
|
|
93
|
+
stack.push(nodes[i]);
|
|
94
|
+
} else {
|
|
95
|
+
throw new Error("Illegal statement type " + nodes[i].type);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (stack.length > 0) {
|
|
99
|
+
output.push({
|
|
100
|
+
type: "BlockStatement",
|
|
101
|
+
body: stack
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = class Flattener {
|
|
108
|
+
|
|
109
|
+
constructor (logger, rng) {
|
|
110
|
+
this.logger = logger;
|
|
111
|
+
this.rng = rng;
|
|
112
|
+
this.emitter = new events.EventEmitter();
|
|
113
|
+
this.output = [];
|
|
114
|
+
this.handlers = [];
|
|
115
|
+
this.breaks = [];
|
|
116
|
+
this.continues = [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Transform method
|
|
121
|
+
* @param {Statement} input Method body
|
|
122
|
+
* @param {number} entry Entry point
|
|
123
|
+
* @param {number} exit Exit point
|
|
124
|
+
*/
|
|
125
|
+
addMethod (input, entry, exit) {
|
|
126
|
+
assert.ok(estest.isStatement(input));
|
|
127
|
+
assert.equal(typeof entry, "number");
|
|
128
|
+
assert.equal(typeof exit, "number");
|
|
129
|
+
|
|
130
|
+
this.transformStatement(input, entry, exit);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get output switch construct
|
|
135
|
+
* @param {number} entry Entry point
|
|
136
|
+
* @param {number} exit Exit point
|
|
137
|
+
* @returns {Statement} Switch construct
|
|
138
|
+
*/
|
|
139
|
+
getCases (entry, exit) {
|
|
140
|
+
assert.equal(typeof entry, "number");
|
|
141
|
+
assert.equal(typeof exit, "number");
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
type: "TryStatement",
|
|
145
|
+
block: {
|
|
146
|
+
type: "BlockStatement",
|
|
147
|
+
body: [
|
|
148
|
+
{
|
|
149
|
+
type: "SwitchStatement",
|
|
150
|
+
discriminant: { type: "Identifier", name: "state" },
|
|
151
|
+
cases: shuffleSwitchCases(this.output.concat([
|
|
152
|
+
{
|
|
153
|
+
type: "SwitchCase",
|
|
154
|
+
test: { type: "Literal", value: exit },
|
|
155
|
+
consequent: [
|
|
156
|
+
{
|
|
157
|
+
type: "ReturnStatement"
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
]))
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
handler: {
|
|
166
|
+
type: "CatchClause",
|
|
167
|
+
param: { type: "Identifier", name: "e" },
|
|
168
|
+
body: {
|
|
169
|
+
type: "BlockStatement",
|
|
170
|
+
body: [
|
|
171
|
+
{
|
|
172
|
+
type: "ExpressionStatement",
|
|
173
|
+
expression: {
|
|
174
|
+
type: "AssignmentExpression",
|
|
175
|
+
operator: "=",
|
|
176
|
+
left: { type: "Identifier", name: "veilmark$tobethrown" },
|
|
177
|
+
right: { type: "Literal", value: null }
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: "SwitchStatement",
|
|
182
|
+
discriminant: { type: "Identifier", name: "state" },
|
|
183
|
+
cases: this.handlers.concat({
|
|
184
|
+
type: "SwitchCase",
|
|
185
|
+
test: null,
|
|
186
|
+
consequent: [
|
|
187
|
+
{
|
|
188
|
+
type: "ThrowStatement",
|
|
189
|
+
argument: { type: "Identifier", name: "e" }
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get output switch construct program
|
|
202
|
+
* @param {number} entry Entry point
|
|
203
|
+
* @param {number} exit Exit point
|
|
204
|
+
* @returns {Program} Switch construct program
|
|
205
|
+
*/
|
|
206
|
+
getProgram (entry, exit) {
|
|
207
|
+
assert.equal(typeof entry, "number");
|
|
208
|
+
assert.equal(typeof exit, "number");
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
type: "Program",
|
|
212
|
+
body: [
|
|
213
|
+
{
|
|
214
|
+
type: "FunctionDeclaration",
|
|
215
|
+
id: { type: "Identifier", name: "main" },
|
|
216
|
+
params: [
|
|
217
|
+
{ type: "Identifier", name: "state" },
|
|
218
|
+
{ type: "Identifier", name: "scope" }
|
|
219
|
+
],
|
|
220
|
+
body: {
|
|
221
|
+
type: "BlockStatement",
|
|
222
|
+
body: [
|
|
223
|
+
{
|
|
224
|
+
type: "WhileStatement",
|
|
225
|
+
test: { type: "Literal", value: true },
|
|
226
|
+
body: this.getCases(entry, exit)
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
type: "ExpressionStatement",
|
|
233
|
+
expression: {
|
|
234
|
+
type: "CallExpression",
|
|
235
|
+
callee: { type: "Identifier", name: "main" },
|
|
236
|
+
arguments: [
|
|
237
|
+
{ type: "Literal", value: entry },
|
|
238
|
+
{ type: "ObjectExpression", properties: [] }
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Import statement into control flow table
|
|
248
|
+
* @param {Statement} node
|
|
249
|
+
* @param {number} entry Entry point
|
|
250
|
+
* @param {number} exit Exit point
|
|
251
|
+
*/
|
|
252
|
+
transformStatement (node, entry, exit) {
|
|
253
|
+
assert(estest.isStatement(node));
|
|
254
|
+
assert.equal(typeof entry, "number");
|
|
255
|
+
assert.equal(typeof exit, "number");
|
|
256
|
+
|
|
257
|
+
switch (node.type) {
|
|
258
|
+
case "Program":
|
|
259
|
+
case "BlockStatement": {
|
|
260
|
+
this.transformBlock(node, entry, exit);
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
case "IfStatement": {
|
|
264
|
+
this.transformIf(node, entry, exit);
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
case "WhileStatement": {
|
|
268
|
+
this.transformWhile(node, entry, exit);
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case "DoWhileStatement": {
|
|
272
|
+
this.transformDoWhile(node, entry, exit);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case "SwitchStatement": {
|
|
276
|
+
this.transformSwitch(node, entry, exit);
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
case "TryStatement": {
|
|
280
|
+
if (node.handler && !node.finalizer) {
|
|
281
|
+
this.transformTryCatch(node, entry, exit);
|
|
282
|
+
} else {
|
|
283
|
+
throw new Error("Not normalized");
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
case "EmptyStatement": {
|
|
288
|
+
// Empty
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
default: {
|
|
292
|
+
this.logger.warn("Unsupported type " + node.type);
|
|
293
|
+
// This might be not the most elegant solution (TODO?)
|
|
294
|
+
// This caused an infinite loop when SwitchStatement was not handled separately
|
|
295
|
+
this.transformBlock({ type: "BlockStatement", body: [ node ] }, entry, exit);
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Import BlockStatement into control flow table
|
|
303
|
+
* @param {BlockStatement} node
|
|
304
|
+
* @param {number} entry Entry point
|
|
305
|
+
* @param {number} exit Exit point
|
|
306
|
+
*/
|
|
307
|
+
transformBlock (node, entry, exit) {
|
|
308
|
+
assert.ok(node.type == "Program" || node.type == "BlockStatement");
|
|
309
|
+
assert.equal(typeof entry, "number");
|
|
310
|
+
assert.equal(typeof exit, "number");
|
|
311
|
+
|
|
312
|
+
assert(node.type == "Program" || node.type == "BlockStatement");
|
|
313
|
+
|
|
314
|
+
node = mergeNestedBlocks(node);
|
|
315
|
+
var blocks = splitBlocks(node.body);
|
|
316
|
+
|
|
317
|
+
for (var i = 0; i < blocks.length; ++i) {
|
|
318
|
+
if (blocks[i].type == "LabeledStatement") {
|
|
319
|
+
blocks[i].body.label = blocks.label;
|
|
320
|
+
blocks[i] = blocks[i].body;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (!estest.isStatement(blocks[i])) {
|
|
324
|
+
console.warn(blocks[i].type + " is not a statement");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
var partExit = i != blocks.length - 1 ? this.rng.get() : exit;
|
|
328
|
+
if (blocks[i].type == "BlockStatement") {
|
|
329
|
+
this.transformSequence(blocks[i], entry, partExit);
|
|
330
|
+
} else {
|
|
331
|
+
this.transformStatement(blocks[i], entry, partExit);
|
|
332
|
+
}
|
|
333
|
+
entry = partExit;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Import sequence from splitBlocks into control flow table
|
|
339
|
+
* @param {BlockStatement} node
|
|
340
|
+
* @param {number} entry Entry point
|
|
341
|
+
* @param {number} exit Exit point
|
|
342
|
+
*/
|
|
343
|
+
transformSequence (node, entry, exit) {
|
|
344
|
+
assert.equal(node.type, "BlockStatement");
|
|
345
|
+
assert.equal(typeof entry, "number");
|
|
346
|
+
assert.equal(typeof exit, "number");
|
|
347
|
+
|
|
348
|
+
var stmts = [];
|
|
349
|
+
|
|
350
|
+
var aborted = !node.body.every(stmt => {
|
|
351
|
+
assert(estest.isStatement(stmt), stmt.type + " is not a statement");
|
|
352
|
+
|
|
353
|
+
switch (stmt.type) {
|
|
354
|
+
case "BreakStatement": {
|
|
355
|
+
var break_;
|
|
356
|
+
if (stmt.label) {
|
|
357
|
+
break_ = _.find(this.breaks, x => x.label.name == stmt.label.name);
|
|
358
|
+
} else {
|
|
359
|
+
break_ = _.last(this.breaks);
|
|
360
|
+
}
|
|
361
|
+
assert(break_ && break_.id, "No break target");
|
|
362
|
+
|
|
363
|
+
stmts.push({
|
|
364
|
+
type: "ExpressionStatement",
|
|
365
|
+
expression: {
|
|
366
|
+
type: "AssignmentExpression",
|
|
367
|
+
operator: "=",
|
|
368
|
+
left: { type: "Identifier", name: "state" },
|
|
369
|
+
right: { type: "Literal", value: break_.id }
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
stmts.push({
|
|
373
|
+
type: "BreakStatement"
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
case "ContinueStatement": {
|
|
379
|
+
var continue_;
|
|
380
|
+
if (stmt.label) {
|
|
381
|
+
continue_ = _.find(this.continues, x => x.label.name == stmt.label.name);
|
|
382
|
+
} else {
|
|
383
|
+
continue_ = _.last(this.continues);
|
|
384
|
+
}
|
|
385
|
+
assert(continue_ && continue_.id, "No continue target");
|
|
386
|
+
|
|
387
|
+
stmts.push({
|
|
388
|
+
type: "ExpressionStatement",
|
|
389
|
+
expression: {
|
|
390
|
+
type: "AssignmentExpression",
|
|
391
|
+
operator: "=",
|
|
392
|
+
left: { type: "Identifier", name: "state" },
|
|
393
|
+
right: { type: "Literal", value: continue_.id }
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
stmts.push({
|
|
397
|
+
type: "BreakStatement"
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
case "ReturnStatement": {
|
|
403
|
+
stmts.push(stmt);
|
|
404
|
+
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
case "EmptyStatement": {
|
|
408
|
+
// Empty
|
|
409
|
+
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
default: {
|
|
413
|
+
stmts.push(stmt);
|
|
414
|
+
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
if (!aborted) {
|
|
421
|
+
stmts.push({
|
|
422
|
+
type: "ExpressionStatement",
|
|
423
|
+
expression: {
|
|
424
|
+
type: "AssignmentExpression",
|
|
425
|
+
operator: "=",
|
|
426
|
+
left: { type: "Identifier", name: "state" },
|
|
427
|
+
right: { type: "Literal", value: exit }
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
stmts.push({
|
|
431
|
+
type: "BreakStatement"
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
this.output.push({
|
|
436
|
+
type: "SwitchCase",
|
|
437
|
+
test: { type: "Literal", value: entry },
|
|
438
|
+
consequent: stmts
|
|
439
|
+
});
|
|
440
|
+
this.emitter.emit("branch", entry);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Import IfStatement into control flow table
|
|
445
|
+
* @param {IfStatement} node
|
|
446
|
+
* @param {number} entry Entry point
|
|
447
|
+
* @param {number} exit Exit point
|
|
448
|
+
*/
|
|
449
|
+
transformIf (node, entry, exit) {
|
|
450
|
+
assert.equal(node.type, "IfStatement");
|
|
451
|
+
assert.equal(typeof entry, "number");
|
|
452
|
+
assert.equal(typeof exit, "number");
|
|
453
|
+
|
|
454
|
+
var thenEntry = this.rng.get();
|
|
455
|
+
var elseEntry = node.alternate ? this.rng.get() : exit;
|
|
456
|
+
this.output.push({
|
|
457
|
+
type: "SwitchCase",
|
|
458
|
+
test: { type: "Literal", value: entry },
|
|
459
|
+
consequent: [
|
|
460
|
+
{
|
|
461
|
+
type: "ExpressionStatement",
|
|
462
|
+
expression: {
|
|
463
|
+
type: "AssignmentExpression",
|
|
464
|
+
operator: "=",
|
|
465
|
+
left: { type: "Identifier", name: "state" },
|
|
466
|
+
right: {
|
|
467
|
+
type: "ConditionalExpression",
|
|
468
|
+
test: node.test,
|
|
469
|
+
consequent: { type: "Literal", value: thenEntry },
|
|
470
|
+
alternate: { type: "Literal", value: elseEntry }
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
type: "BreakStatement"
|
|
476
|
+
}
|
|
477
|
+
]
|
|
478
|
+
});
|
|
479
|
+
this.emitter.emit("branch", entry);
|
|
480
|
+
this.transformStatement(node.consequent, thenEntry, exit);
|
|
481
|
+
if (node.alternate) {
|
|
482
|
+
this.transformStatement(node.alternate, elseEntry, exit);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Import WhileStatement into control flow table
|
|
488
|
+
* @param {WhileStatement} node
|
|
489
|
+
* @param {number} entry Entry point
|
|
490
|
+
* @param {number} exit Exit point
|
|
491
|
+
*/
|
|
492
|
+
transformWhile (node, entry, exit) {
|
|
493
|
+
assert.equal(node.type, "WhileStatement");
|
|
494
|
+
assert.equal(typeof entry, "number");
|
|
495
|
+
assert.equal(typeof exit, "number");
|
|
496
|
+
|
|
497
|
+
var bodyEntry = this.rng.get();
|
|
498
|
+
this.output.push({
|
|
499
|
+
type: "SwitchCase",
|
|
500
|
+
test: { type: "Literal", value: entry },
|
|
501
|
+
consequent: [
|
|
502
|
+
{
|
|
503
|
+
type: "ExpressionStatement",
|
|
504
|
+
expression: {
|
|
505
|
+
type: "AssignmentExpression",
|
|
506
|
+
operator: "=",
|
|
507
|
+
left: { type: "Identifier", name: "state" },
|
|
508
|
+
right: {
|
|
509
|
+
type: "ConditionalExpression",
|
|
510
|
+
test: node.test,
|
|
511
|
+
consequent: { type: "Literal", value: bodyEntry },
|
|
512
|
+
alternate: { type: "Literal", value: exit }
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
type: "BreakStatement"
|
|
518
|
+
}
|
|
519
|
+
]
|
|
520
|
+
});
|
|
521
|
+
this.emitter.emit("branch", entry);
|
|
522
|
+
|
|
523
|
+
this.breaks.push({
|
|
524
|
+
label: node.label && node.label.name,
|
|
525
|
+
id: exit
|
|
526
|
+
});
|
|
527
|
+
this.continues.push({
|
|
528
|
+
label: node.label && node.label.name,
|
|
529
|
+
id: entry
|
|
530
|
+
});
|
|
531
|
+
this.transformBlock(node.body, bodyEntry, entry);
|
|
532
|
+
this.breaks.pop();
|
|
533
|
+
this.continues.pop();
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Import DoWhileStatement into control flow table
|
|
538
|
+
* @param {DoWhileStatement} node
|
|
539
|
+
* @param {number} entry Entry point
|
|
540
|
+
* @param {number} exit Exit point
|
|
541
|
+
*/
|
|
542
|
+
transformDoWhile (node, entry, exit) {
|
|
543
|
+
assert.equal(node.type, "DoWhileStatement");
|
|
544
|
+
assert.equal(typeof entry, "number");
|
|
545
|
+
assert.equal(typeof exit, "number");
|
|
546
|
+
|
|
547
|
+
var testEntry = this.rng.get();
|
|
548
|
+
this.output.push({
|
|
549
|
+
type: "SwitchCase",
|
|
550
|
+
test: { type: "Literal", value: testEntry },
|
|
551
|
+
consequent: [
|
|
552
|
+
{
|
|
553
|
+
type: "ExpressionStatement",
|
|
554
|
+
expression: {
|
|
555
|
+
type: "AssignmentExpression",
|
|
556
|
+
operator: "=",
|
|
557
|
+
left: { type: "Identifier", name: "state" },
|
|
558
|
+
right: {
|
|
559
|
+
type: "ConditionalExpression",
|
|
560
|
+
test: node.test,
|
|
561
|
+
consequent: { type: "Literal", value: entry },
|
|
562
|
+
alternate: { type: "Literal", value: exit }
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
type: "BreakStatement"
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
});
|
|
571
|
+
this.emitter.emit("branch", testEntry);
|
|
572
|
+
|
|
573
|
+
this.breaks.push({
|
|
574
|
+
label: node.label && node.label.name,
|
|
575
|
+
id: exit
|
|
576
|
+
});
|
|
577
|
+
this.continues.push({
|
|
578
|
+
label: node.label && node.label.name,
|
|
579
|
+
id: entry
|
|
580
|
+
});
|
|
581
|
+
this.transformBlock(node.body, entry, testEntry);
|
|
582
|
+
this.breaks.pop();
|
|
583
|
+
this.continues.pop();
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Import SwitchStatement into control flow table
|
|
588
|
+
* @param {SwitchStatement} node
|
|
589
|
+
* @param {number} entry Entry point
|
|
590
|
+
* @param {number} exit Exit point
|
|
591
|
+
*/
|
|
592
|
+
transformSwitch (node, entry, exit) {
|
|
593
|
+
assert.equal(node.type, "SwitchStatement");
|
|
594
|
+
assert.equal(typeof entry, "number");
|
|
595
|
+
assert.equal(typeof exit, "number");
|
|
596
|
+
|
|
597
|
+
var comps = [];
|
|
598
|
+
|
|
599
|
+
this.breaks.push({
|
|
600
|
+
label: null,
|
|
601
|
+
id: exit
|
|
602
|
+
});
|
|
603
|
+
var nextCaseEntry = this.rng.get();
|
|
604
|
+
node.cases.forEach(switchCase => {
|
|
605
|
+
var isLast = switchCase == _.last(node.cases);
|
|
606
|
+
|
|
607
|
+
var caseEntry = nextCaseEntry;
|
|
608
|
+
nextCaseEntry = this.rng.get();
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* What happens if there are empty BlockStatements elsewhere? Does it hang?
|
|
612
|
+
*/
|
|
613
|
+
|
|
614
|
+
if (switchCase.consequent.length > 0) {
|
|
615
|
+
this.transformBlock({
|
|
616
|
+
type: "BlockStatement",
|
|
617
|
+
body: switchCase.consequent
|
|
618
|
+
}, caseEntry, isLast ? exit : nextCaseEntry);
|
|
619
|
+
} else {
|
|
620
|
+
nextCaseEntry = caseEntry;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (switchCase.test) {
|
|
624
|
+
comps.push({
|
|
625
|
+
type: "IfStatement",
|
|
626
|
+
test: {
|
|
627
|
+
type: "BinaryExpression",
|
|
628
|
+
operator: "==",
|
|
629
|
+
left: utils.cloneISwearIKnowWhatImDoing(node.discriminant),
|
|
630
|
+
right: switchCase.test
|
|
631
|
+
},
|
|
632
|
+
consequent: {
|
|
633
|
+
type: "BlockStatement",
|
|
634
|
+
body: [
|
|
635
|
+
{
|
|
636
|
+
type: "ExpressionStatement",
|
|
637
|
+
expression: {
|
|
638
|
+
type: "AssignmentExpression",
|
|
639
|
+
operator: "=",
|
|
640
|
+
left: { type: "Identifier", name: "state" },
|
|
641
|
+
right: { type: "Literal", value: caseEntry }
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
type: "BreakStatement"
|
|
646
|
+
}
|
|
647
|
+
]
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
} else {
|
|
651
|
+
comps.push({
|
|
652
|
+
type: "BlockStatement",
|
|
653
|
+
body: [
|
|
654
|
+
{
|
|
655
|
+
type: "ExpressionStatement",
|
|
656
|
+
expression: {
|
|
657
|
+
type: "AssignmentExpression",
|
|
658
|
+
operator: "=",
|
|
659
|
+
left: { type: "Identifier", name: "state" },
|
|
660
|
+
right: { type: "Literal", value: caseEntry }
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
type: "BreakStatement"
|
|
665
|
+
}
|
|
666
|
+
]
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
this.breaks.pop();
|
|
671
|
+
|
|
672
|
+
this.output.push({
|
|
673
|
+
type: "SwitchCase",
|
|
674
|
+
test: { type: "Literal", value: entry },
|
|
675
|
+
consequent: comps
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Import TryStatement into control flow table
|
|
681
|
+
* @param {TryStatement} node
|
|
682
|
+
* @param {number} entry Entry point
|
|
683
|
+
* @param {number} exit Exit point
|
|
684
|
+
*/
|
|
685
|
+
transformTryCatch (node, entry, exit) {
|
|
686
|
+
assert.equal(node.type, "TryStatement");
|
|
687
|
+
assert.equal(typeof entry, "number");
|
|
688
|
+
assert.equal(typeof exit, "number");
|
|
689
|
+
assert.ok(node.handler);
|
|
690
|
+
assert.ok(!node.finalizer);
|
|
691
|
+
|
|
692
|
+
var catchEntry = this.rng.get();
|
|
693
|
+
|
|
694
|
+
if (node.handler) {
|
|
695
|
+
var scopeDef = node.handler.body.body.splice(0, 2);
|
|
696
|
+
assert(
|
|
697
|
+
scopeDef[0].type == "VariableDeclaration" &&
|
|
698
|
+
scopeDef[0].declarations.length == 1 &&
|
|
699
|
+
scopeDef[0].declarations[0].id.name.indexOf("$$scope") == 0,
|
|
700
|
+
"First element of node.handler.body isn't a VariableDeclaration of a scope object");
|
|
701
|
+
assert(
|
|
702
|
+
scopeDef[1].type == "ExpressionStatement" &&
|
|
703
|
+
scopeDef[1].expression.type == "AssignmentExpression" &&
|
|
704
|
+
scopeDef[1].expression.left.type == "MemberExpression" &&
|
|
705
|
+
scopeDef[1].expression.left.object.name.indexOf("$$scope") == 0 &&
|
|
706
|
+
scopeDef[1].expression.right.name.indexOf("$$var") == 0,
|
|
707
|
+
"Second element of node.handler.body is not a e assignment");
|
|
708
|
+
}
|
|
709
|
+
function createHandler(entry) {
|
|
710
|
+
if (node.handler) {
|
|
711
|
+
pushUniqSwitchCase(this.handlers, {
|
|
712
|
+
type: "SwitchCase",
|
|
713
|
+
test: { type: "Literal", value: entry },
|
|
714
|
+
consequent: [
|
|
715
|
+
scopeDef[0],
|
|
716
|
+
{
|
|
717
|
+
type: "ExpressionStatement",
|
|
718
|
+
expression: {
|
|
719
|
+
type: "AssignmentExpression",
|
|
720
|
+
operator: "=",
|
|
721
|
+
left: node.handler.veilmark$exception,
|
|
722
|
+
right: { type: "Identifier", name: "e" }
|
|
723
|
+
}
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
type: "ExpressionStatement",
|
|
727
|
+
expression: {
|
|
728
|
+
type: "AssignmentExpression",
|
|
729
|
+
operator: "=",
|
|
730
|
+
left: { type: "Identifier", name: "state" },
|
|
731
|
+
right: { type: "Literal", value: catchEntry }
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
type: "BreakStatement"
|
|
736
|
+
}
|
|
737
|
+
]
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
this.emitter.on("branch", createHandler);
|
|
742
|
+
this.transformBlock(node.block, entry, exit);
|
|
743
|
+
this.emitter.removeListener("branch", createHandler);
|
|
744
|
+
|
|
745
|
+
if (node.handler) {
|
|
746
|
+
this.transformBlock(node.handler.body, catchEntry, exit);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Transform duplicate scope and arguments into single unified declarations
|
|
752
|
+
* @params {Node} ast Root node
|
|
753
|
+
* @returns {Node}
|
|
754
|
+
*/
|
|
755
|
+
unifyPrefixStatements (ast) {
|
|
756
|
+
var maximumScopeIndex = 0;
|
|
757
|
+
|
|
758
|
+
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
759
|
+
if (node.veilmark$reassigningArguments && !node.veilmark$followsSlicingArguments) {
|
|
760
|
+
node = { type: "EmptyStatement" };
|
|
761
|
+
} else if (node.veilmark$scopeObject) {
|
|
762
|
+
node = { type: "EmptyStatement" };
|
|
763
|
+
} else if (node.veilmark$scopeObjectReference) {
|
|
764
|
+
maximumScopeIndex = Math.max(maximumScopeIndex, node.property.value);
|
|
765
|
+
} else if (node.type == "Identifier" && _.startsWith(node.name, "$$scope")) {
|
|
766
|
+
node.name = "$$unifiedScope";
|
|
767
|
+
}
|
|
768
|
+
return node;
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
ast.body[0].body.body.splice(0, 0,
|
|
772
|
+
{
|
|
773
|
+
type: "ExpressionStatement",
|
|
774
|
+
expression: {
|
|
775
|
+
type: "VariableDeclaration",
|
|
776
|
+
kind: "var",
|
|
777
|
+
declarations: [
|
|
778
|
+
{
|
|
779
|
+
type: "VariableDeclarator",
|
|
780
|
+
id: { type: "Identifier", name: "$$unifiedScope" },
|
|
781
|
+
init: {
|
|
782
|
+
type: "NewExpression",
|
|
783
|
+
callee: { type: "Identifier", name: "Array" },
|
|
784
|
+
arguments: [
|
|
785
|
+
{ type: "Literal", value: maximumScopeIndex }
|
|
786
|
+
]
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
]
|
|
790
|
+
}
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
type: "VariableDeclaration",
|
|
794
|
+
kind: "var",
|
|
795
|
+
declarations: [
|
|
796
|
+
{
|
|
797
|
+
type: "VariableDeclarator",
|
|
798
|
+
id: { type: "Identifier", name: "veilmark$arguments" },
|
|
799
|
+
init: { type: "Identifier", name: "arguments" }
|
|
800
|
+
}
|
|
801
|
+
]
|
|
802
|
+
}
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
return ast;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
};
|