@dacely/toildefender 0.1.0 → 0.1.2
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 -661
- package/NOTICE.md +16 -16
- package/README.md +467 -380
- package/cli.js +168 -168
- package/defendjs.js +6 -6
- package/docs/all-modes-output.demo.js +673 -673
- package/estest.js +49 -49
- package/esutils.js +107 -107
- package/logger.js +28 -28
- package/obfuscator.js +534 -534
- package/package.json +108 -108
- package/processors/deadCode.js +62 -62
- package/processors/flattener.js +808 -808
- package/processors/health.js +55 -55
- package/processors/identifiers.js +256 -256
- package/processors/literalObfuscator.js +40 -40
- package/processors/literals.js +233 -233
- package/processors/methods.js +332 -332
- package/processors/modules.js +231 -231
- package/processors/normalizer.js +490 -490
- package/processors/numericVm.js +950 -950
- package/processors/postprocessing.js +69 -69
- package/processors/preprocessing.js +248 -248
- package/processors/scopes.js +177 -177
- package/processors/uglifier.js +25 -25
- package/processors/variables.js +185 -185
- package/toildefender.js +7 -7
- package/traverser.js +115 -115
- package/utils.js +135 -135
package/processors/methods.js
CHANGED
|
@@ -1,332 +1,332 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const METHODS_INJECT = `
|
|
4
|
-
function veilmark$mergeArguments(a, b) {
|
|
5
|
-
return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function veilmark$bind() {
|
|
9
|
-
var fn = arguments[0], prepend = Array.prototype.slice.call(arguments, 1);
|
|
10
|
-
var wrapper = function() {
|
|
11
|
-
return fn.apply(this, prepend.concat(Array.prototype.slice.call(arguments)));
|
|
12
|
-
};
|
|
13
|
-
wrapper.prototype = fn.prototype;
|
|
14
|
-
return wrapper;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function veilmark$sliceArguments(args, num) {
|
|
18
|
-
return Array.prototype.slice.call(args, num);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function veilmark$toObject(schema, values) {
|
|
22
|
-
var obj = {};
|
|
23
|
-
if (values === undefined) {
|
|
24
|
-
for (var legacy = 0; legacy < schema.length; legacy += 2) {
|
|
25
|
-
obj[schema[legacy]] = schema[legacy + 1];
|
|
26
|
-
}
|
|
27
|
-
return obj;
|
|
28
|
-
}
|
|
29
|
-
var cursor = 2;
|
|
30
|
-
var salt = schema[0];
|
|
31
|
-
var count = schema[1];
|
|
32
|
-
for (var i = 0; i < count; i += 1) {
|
|
33
|
-
var len = schema[cursor++] ^ ((salt + i * 131) & 65535);
|
|
34
|
-
var key = "";
|
|
35
|
-
for (var j = 0; j < len; j += 1) {
|
|
36
|
-
key += String.fromCharCode(schema[cursor++] ^ ((salt + i * 257 + j * 17) & 65535));
|
|
37
|
-
}
|
|
38
|
-
obj[key] = values[i];
|
|
39
|
-
}
|
|
40
|
-
return obj;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function veilmark$decodeString(arr) {
|
|
44
|
-
return arr.map(function(x) { return String.fromCharCode(x & ~0 >>> 16) + String.fromCharCode(x >> 16); }).join("");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function veilmark$fromCharCodes() {
|
|
48
|
-
return String.fromCharCode.apply(null, arguments);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
`;
|
|
52
|
-
|
|
53
|
-
var assert = require("assert");
|
|
54
|
-
var fs = require("fs");
|
|
55
|
-
|
|
56
|
-
var _ = require("lodash");
|
|
57
|
-
var escope = require("escope");
|
|
58
|
-
var esprima = require("esprima");
|
|
59
|
-
|
|
60
|
-
var estest = require("../estest");
|
|
61
|
-
var traverser = require("../traverser");
|
|
62
|
-
var utils = require("../utils");
|
|
63
|
-
|
|
64
|
-
const ANON_METHOD_ID = "veilmark$anonymousMethodId";
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Wrap function with veilmark$bind.
|
|
68
|
-
* @param {Identifier} Function identifier
|
|
69
|
-
* @returns {Node} Wrapped function
|
|
70
|
-
*/
|
|
71
|
-
function createMethodStub(id) {
|
|
72
|
-
assert.equal(id.type, "Identifier");
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
type: "CallExpression",
|
|
76
|
-
callee: { type: "Identifier", name: "veilmark$bind" },
|
|
77
|
-
arguments: [
|
|
78
|
-
id
|
|
79
|
-
]
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function anonymousMethodName(node) {
|
|
84
|
-
assert.equal(node.type, "FunctionExpression");
|
|
85
|
-
|
|
86
|
-
if (!node[ANON_METHOD_ID]) {
|
|
87
|
-
Object.defineProperty(node, ANON_METHOD_ID, {
|
|
88
|
-
configurable: false,
|
|
89
|
-
enumerable: false,
|
|
90
|
-
value: `veilmark$anon$${utils.hash(node)}`
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return node[ANON_METHOD_ID];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Get index of argument in function.
|
|
99
|
-
* @param {Function} method Function
|
|
100
|
-
* @param {Identifier} identifier} Argument identifier
|
|
101
|
-
* @returns {number} Index of argument
|
|
102
|
-
*/
|
|
103
|
-
function getArgumentIndex(method, identifier) {
|
|
104
|
-
assert.ok(estest.isFunction(method));
|
|
105
|
-
assert.equal(identifier.type, "Identifier");
|
|
106
|
-
|
|
107
|
-
return _.findIndex(method.params, x => x.name == identifier.name);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function rawArgumentsIdentifier() {
|
|
111
|
-
return {
|
|
112
|
-
type: "Identifier",
|
|
113
|
-
name: "arguments",
|
|
114
|
-
veilmark$rawArguments: true
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
module.exports = class Methods {
|
|
119
|
-
|
|
120
|
-
constructor (logger) {
|
|
121
|
-
this.logger = logger;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Adds helper methods to the beginning of the app.
|
|
126
|
-
* @param {Node} Root node
|
|
127
|
-
*/
|
|
128
|
-
addCustomBind (ast) {
|
|
129
|
-
assert.ok(estest.isNode(ast));
|
|
130
|
-
|
|
131
|
-
var code = esprima.parse(METHODS_INJECT);
|
|
132
|
-
code.type = "BlockStatement";
|
|
133
|
-
ast.body.splice(0, 0, code);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Checks whether a method refers to the "arguments" array.
|
|
138
|
-
* @param {Function} method
|
|
139
|
-
* @param {ScopeManager} scopeManager
|
|
140
|
-
* @returns {boolean}
|
|
141
|
-
*/
|
|
142
|
-
methodRefersToArguments (method, scopeManager) {
|
|
143
|
-
assert.ok(estest.isFunction(method));
|
|
144
|
-
assert.ok(scopeManager);
|
|
145
|
-
|
|
146
|
-
return scopeManager
|
|
147
|
-
.acquire(method)
|
|
148
|
-
.references
|
|
149
|
-
.some(reference => !utils.isResolvedReference(reference) && reference.identifier.name == "arguments");
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Inserts code to copy/slice arguments from the arguments array like
|
|
154
|
-
* function () { ... }
|
|
155
|
-
* to
|
|
156
|
-
* function () { var veilmark$arguments = veilmark$sliceArguments(arguments, 1); ... }
|
|
157
|
-
* @param {Function} method
|
|
158
|
-
* @param {number} num Number of arguments to be sliced off. 0 if none.
|
|
159
|
-
*/
|
|
160
|
-
removeFirstArguments (method, num) {
|
|
161
|
-
assert.ok(estest.isFunction(method));
|
|
162
|
-
assert.equal(typeof num, "number");
|
|
163
|
-
|
|
164
|
-
method.body.body.splice(0, 0, {
|
|
165
|
-
type: "VariableDeclaration",
|
|
166
|
-
kind: "var",
|
|
167
|
-
declarations: [
|
|
168
|
-
{
|
|
169
|
-
type: "VariableDeclarator",
|
|
170
|
-
id: { type: "Identifier", name: "veilmark$arguments" },
|
|
171
|
-
init: rawArgumentsIdentifier()
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
type: "VariableDeclarator",
|
|
175
|
-
id: { type: "Identifier", name: "veilmark$bareArguments" },
|
|
176
|
-
init: num > 0 ? {
|
|
177
|
-
type: "CallExpression",
|
|
178
|
-
callee: { type: "Identifier", name: "veilmark$sliceArguments" },
|
|
179
|
-
arguments: [
|
|
180
|
-
rawArgumentsIdentifier(),
|
|
181
|
-
{ type: "Literal", value: num, veilmark$removeFirstArguments: true }
|
|
182
|
-
]
|
|
183
|
-
} : rawArgumentsIdentifier()
|
|
184
|
-
}
|
|
185
|
-
],
|
|
186
|
-
veilmark$reassigningArguments: true,
|
|
187
|
-
veilmark$followsSlicingArguments: num > 0
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Lists all methods.
|
|
193
|
-
* @param {Node} ast Root node
|
|
194
|
-
* @returns {string[]} Method names
|
|
195
|
-
*/
|
|
196
|
-
listMethods (ast) {
|
|
197
|
-
assert.ok(estest.isNode(ast));
|
|
198
|
-
|
|
199
|
-
var methods = [];
|
|
200
|
-
|
|
201
|
-
traverser.traverse(ast, [], (node, stack) => {
|
|
202
|
-
if (node.type == "FunctionDeclaration") { // Statement
|
|
203
|
-
methods.push(node.id.name);
|
|
204
|
-
} else if (node.type == "FunctionExpression") { // Expression
|
|
205
|
-
methods.push(anonymousMethodName(node));
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return node;
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
return methods;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Extracts all methods from the AST.
|
|
216
|
-
* @param {Node} ast Root node
|
|
217
|
-
* @returns {Function[]}
|
|
218
|
-
*/
|
|
219
|
-
extractMethods (ast) {
|
|
220
|
-
assert.ok(estest.isNode(ast));
|
|
221
|
-
|
|
222
|
-
var methods = [];
|
|
223
|
-
|
|
224
|
-
traverser.traverse(ast, [], (node, stack) => {
|
|
225
|
-
if (node.type == "FunctionDeclaration") { // Statement
|
|
226
|
-
methods.push(node);
|
|
227
|
-
return { type: "ExpressionStatement", expression: createMethodStub(node.id) }; // This is not ideal
|
|
228
|
-
} else if (node.type == "FunctionExpression") { // Expression
|
|
229
|
-
var id = anonymousMethodName(node);
|
|
230
|
-
// Merge into old object instead of creating a new one to preserve object references
|
|
231
|
-
methods.push(_.assign(node, {
|
|
232
|
-
type: "FunctionDeclaration",
|
|
233
|
-
id: { type: "Identifier", name: id }
|
|
234
|
-
}));
|
|
235
|
-
return createMethodStub({ type: "Identifier", name: id });
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return node;
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
return methods;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Replaces direct argument references with arguments references like
|
|
246
|
-
* function (a) { return a; }
|
|
247
|
-
* to
|
|
248
|
-
* function (a) { return veilmark$arguments[0]; }
|
|
249
|
-
* @param {Function} method Function whose body will be transformed
|
|
250
|
-
* @param {boolean} useReassignedVariable Use veilmark$arguments instead of arguments
|
|
251
|
-
* @returns {Function} Function from method parameter
|
|
252
|
-
*/
|
|
253
|
-
replaceArgumentReferences (method, useReassignedVariable) {
|
|
254
|
-
assert.ok(estest.isFunction(method));
|
|
255
|
-
|
|
256
|
-
traverser.traverse(method.body, [], (node, stack) => {
|
|
257
|
-
if (node.type == "Identifier") {
|
|
258
|
-
var nestedFunction = stack.some(frame => estest.isFunction(frame.node));
|
|
259
|
-
if (useReassignedVariable && node.name == "arguments" && !node.veilmark$rawArguments && !nestedFunction) {
|
|
260
|
-
return { type: "Identifier", name: "veilmark$bareArguments" };
|
|
261
|
-
}
|
|
262
|
-
var index = getArgumentIndex(method, node);
|
|
263
|
-
if (index != -1) {
|
|
264
|
-
return {
|
|
265
|
-
type: "MemberExpression",
|
|
266
|
-
object: { type: "Identifier", name: useReassignedVariable ? "veilmark$arguments" : "arguments" },
|
|
267
|
-
property: { type: "Literal", value: index },
|
|
268
|
-
computed: true
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return node;
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
method.params = [];
|
|
277
|
-
|
|
278
|
-
return method;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Replaces function calls with main calls like
|
|
283
|
-
* test()
|
|
284
|
-
* to
|
|
285
|
-
* veilmark$bind(main, 1234)()
|
|
286
|
-
* @param {Node} ast Root node
|
|
287
|
-
* @param {Object[]} methodEntryExitPoints Method entry point table
|
|
288
|
-
* @param {number} methodEntryExitPoints[].entry Entry point
|
|
289
|
-
*/
|
|
290
|
-
replaceFunctionCalls (ast, methodEntryExitPoints) {
|
|
291
|
-
assert.ok(estest.isNode(ast));
|
|
292
|
-
assert.equal(typeof methodEntryExitPoints, "object");
|
|
293
|
-
|
|
294
|
-
traverser.traverse(ast, [], (node, stack) => {
|
|
295
|
-
if (node.type == "Identifier" && methodEntryExitPoints[node.name] && methodEntryExitPoints[node.name].entry) {
|
|
296
|
-
return {
|
|
297
|
-
type: "CallExpression",
|
|
298
|
-
callee: { type: "Identifier", name: "veilmark$bind" },
|
|
299
|
-
arguments: [
|
|
300
|
-
{ type: "Identifier", name: "main" },
|
|
301
|
-
{ type: "Identifier", name: methodEntryExitPoints[node.name].entry }
|
|
302
|
-
]
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
return node;
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Bumps all arguments indices like
|
|
311
|
-
* veilmark$arguments[0]
|
|
312
|
-
* to
|
|
313
|
-
* veilmark$arguments[1]
|
|
314
|
-
* @param {Function} method Function whose body will be transformed
|
|
315
|
-
* @param {number} inc Number to be added to all argument indices
|
|
316
|
-
*/
|
|
317
|
-
bumpArgumentsIndices (method, inc) {
|
|
318
|
-
assert.ok(estest.isFunction(method));
|
|
319
|
-
assert.equal(typeof inc, "number");
|
|
320
|
-
|
|
321
|
-
traverser.traverse(method.body, [], (node, stack) => {
|
|
322
|
-
if (node.type == "MemberExpression" && node.object.type == "Identifier" && node.object.name == "veilmark$arguments") {
|
|
323
|
-
node.property.value += inc;
|
|
324
|
-
}
|
|
325
|
-
if (node.veilmark$removeFirstArguments) {
|
|
326
|
-
node.value += inc;
|
|
327
|
-
}
|
|
328
|
-
return node;
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const METHODS_INJECT = `
|
|
4
|
+
function veilmark$mergeArguments(a, b) {
|
|
5
|
+
return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function veilmark$bind() {
|
|
9
|
+
var fn = arguments[0], prepend = Array.prototype.slice.call(arguments, 1);
|
|
10
|
+
var wrapper = function() {
|
|
11
|
+
return fn.apply(this, prepend.concat(Array.prototype.slice.call(arguments)));
|
|
12
|
+
};
|
|
13
|
+
wrapper.prototype = fn.prototype;
|
|
14
|
+
return wrapper;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function veilmark$sliceArguments(args, num) {
|
|
18
|
+
return Array.prototype.slice.call(args, num);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function veilmark$toObject(schema, values) {
|
|
22
|
+
var obj = {};
|
|
23
|
+
if (values === undefined) {
|
|
24
|
+
for (var legacy = 0; legacy < schema.length; legacy += 2) {
|
|
25
|
+
obj[schema[legacy]] = schema[legacy + 1];
|
|
26
|
+
}
|
|
27
|
+
return obj;
|
|
28
|
+
}
|
|
29
|
+
var cursor = 2;
|
|
30
|
+
var salt = schema[0];
|
|
31
|
+
var count = schema[1];
|
|
32
|
+
for (var i = 0; i < count; i += 1) {
|
|
33
|
+
var len = schema[cursor++] ^ ((salt + i * 131) & 65535);
|
|
34
|
+
var key = "";
|
|
35
|
+
for (var j = 0; j < len; j += 1) {
|
|
36
|
+
key += String.fromCharCode(schema[cursor++] ^ ((salt + i * 257 + j * 17) & 65535));
|
|
37
|
+
}
|
|
38
|
+
obj[key] = values[i];
|
|
39
|
+
}
|
|
40
|
+
return obj;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function veilmark$decodeString(arr) {
|
|
44
|
+
return arr.map(function(x) { return String.fromCharCode(x & ~0 >>> 16) + String.fromCharCode(x >> 16); }).join("");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function veilmark$fromCharCodes() {
|
|
48
|
+
return String.fromCharCode.apply(null, arguments);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
var assert = require("assert");
|
|
54
|
+
var fs = require("fs");
|
|
55
|
+
|
|
56
|
+
var _ = require("lodash");
|
|
57
|
+
var escope = require("escope");
|
|
58
|
+
var esprima = require("esprima");
|
|
59
|
+
|
|
60
|
+
var estest = require("../estest");
|
|
61
|
+
var traverser = require("../traverser");
|
|
62
|
+
var utils = require("../utils");
|
|
63
|
+
|
|
64
|
+
const ANON_METHOD_ID = "veilmark$anonymousMethodId";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Wrap function with veilmark$bind.
|
|
68
|
+
* @param {Identifier} Function identifier
|
|
69
|
+
* @returns {Node} Wrapped function
|
|
70
|
+
*/
|
|
71
|
+
function createMethodStub(id) {
|
|
72
|
+
assert.equal(id.type, "Identifier");
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
type: "CallExpression",
|
|
76
|
+
callee: { type: "Identifier", name: "veilmark$bind" },
|
|
77
|
+
arguments: [
|
|
78
|
+
id
|
|
79
|
+
]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function anonymousMethodName(node) {
|
|
84
|
+
assert.equal(node.type, "FunctionExpression");
|
|
85
|
+
|
|
86
|
+
if (!node[ANON_METHOD_ID]) {
|
|
87
|
+
Object.defineProperty(node, ANON_METHOD_ID, {
|
|
88
|
+
configurable: false,
|
|
89
|
+
enumerable: false,
|
|
90
|
+
value: `veilmark$anon$${utils.hash(node)}`
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return node[ANON_METHOD_ID];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get index of argument in function.
|
|
99
|
+
* @param {Function} method Function
|
|
100
|
+
* @param {Identifier} identifier} Argument identifier
|
|
101
|
+
* @returns {number} Index of argument
|
|
102
|
+
*/
|
|
103
|
+
function getArgumentIndex(method, identifier) {
|
|
104
|
+
assert.ok(estest.isFunction(method));
|
|
105
|
+
assert.equal(identifier.type, "Identifier");
|
|
106
|
+
|
|
107
|
+
return _.findIndex(method.params, x => x.name == identifier.name);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function rawArgumentsIdentifier() {
|
|
111
|
+
return {
|
|
112
|
+
type: "Identifier",
|
|
113
|
+
name: "arguments",
|
|
114
|
+
veilmark$rawArguments: true
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = class Methods {
|
|
119
|
+
|
|
120
|
+
constructor (logger) {
|
|
121
|
+
this.logger = logger;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Adds helper methods to the beginning of the app.
|
|
126
|
+
* @param {Node} Root node
|
|
127
|
+
*/
|
|
128
|
+
addCustomBind (ast) {
|
|
129
|
+
assert.ok(estest.isNode(ast));
|
|
130
|
+
|
|
131
|
+
var code = esprima.parse(METHODS_INJECT);
|
|
132
|
+
code.type = "BlockStatement";
|
|
133
|
+
ast.body.splice(0, 0, code);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Checks whether a method refers to the "arguments" array.
|
|
138
|
+
* @param {Function} method
|
|
139
|
+
* @param {ScopeManager} scopeManager
|
|
140
|
+
* @returns {boolean}
|
|
141
|
+
*/
|
|
142
|
+
methodRefersToArguments (method, scopeManager) {
|
|
143
|
+
assert.ok(estest.isFunction(method));
|
|
144
|
+
assert.ok(scopeManager);
|
|
145
|
+
|
|
146
|
+
return scopeManager
|
|
147
|
+
.acquire(method)
|
|
148
|
+
.references
|
|
149
|
+
.some(reference => !utils.isResolvedReference(reference) && reference.identifier.name == "arguments");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Inserts code to copy/slice arguments from the arguments array like
|
|
154
|
+
* function () { ... }
|
|
155
|
+
* to
|
|
156
|
+
* function () { var veilmark$arguments = veilmark$sliceArguments(arguments, 1); ... }
|
|
157
|
+
* @param {Function} method
|
|
158
|
+
* @param {number} num Number of arguments to be sliced off. 0 if none.
|
|
159
|
+
*/
|
|
160
|
+
removeFirstArguments (method, num) {
|
|
161
|
+
assert.ok(estest.isFunction(method));
|
|
162
|
+
assert.equal(typeof num, "number");
|
|
163
|
+
|
|
164
|
+
method.body.body.splice(0, 0, {
|
|
165
|
+
type: "VariableDeclaration",
|
|
166
|
+
kind: "var",
|
|
167
|
+
declarations: [
|
|
168
|
+
{
|
|
169
|
+
type: "VariableDeclarator",
|
|
170
|
+
id: { type: "Identifier", name: "veilmark$arguments" },
|
|
171
|
+
init: rawArgumentsIdentifier()
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: "VariableDeclarator",
|
|
175
|
+
id: { type: "Identifier", name: "veilmark$bareArguments" },
|
|
176
|
+
init: num > 0 ? {
|
|
177
|
+
type: "CallExpression",
|
|
178
|
+
callee: { type: "Identifier", name: "veilmark$sliceArguments" },
|
|
179
|
+
arguments: [
|
|
180
|
+
rawArgumentsIdentifier(),
|
|
181
|
+
{ type: "Literal", value: num, veilmark$removeFirstArguments: true }
|
|
182
|
+
]
|
|
183
|
+
} : rawArgumentsIdentifier()
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
veilmark$reassigningArguments: true,
|
|
187
|
+
veilmark$followsSlicingArguments: num > 0
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Lists all methods.
|
|
193
|
+
* @param {Node} ast Root node
|
|
194
|
+
* @returns {string[]} Method names
|
|
195
|
+
*/
|
|
196
|
+
listMethods (ast) {
|
|
197
|
+
assert.ok(estest.isNode(ast));
|
|
198
|
+
|
|
199
|
+
var methods = [];
|
|
200
|
+
|
|
201
|
+
traverser.traverse(ast, [], (node, stack) => {
|
|
202
|
+
if (node.type == "FunctionDeclaration") { // Statement
|
|
203
|
+
methods.push(node.id.name);
|
|
204
|
+
} else if (node.type == "FunctionExpression") { // Expression
|
|
205
|
+
methods.push(anonymousMethodName(node));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return node;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return methods;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Extracts all methods from the AST.
|
|
216
|
+
* @param {Node} ast Root node
|
|
217
|
+
* @returns {Function[]}
|
|
218
|
+
*/
|
|
219
|
+
extractMethods (ast) {
|
|
220
|
+
assert.ok(estest.isNode(ast));
|
|
221
|
+
|
|
222
|
+
var methods = [];
|
|
223
|
+
|
|
224
|
+
traverser.traverse(ast, [], (node, stack) => {
|
|
225
|
+
if (node.type == "FunctionDeclaration") { // Statement
|
|
226
|
+
methods.push(node);
|
|
227
|
+
return { type: "ExpressionStatement", expression: createMethodStub(node.id) }; // This is not ideal
|
|
228
|
+
} else if (node.type == "FunctionExpression") { // Expression
|
|
229
|
+
var id = anonymousMethodName(node);
|
|
230
|
+
// Merge into old object instead of creating a new one to preserve object references
|
|
231
|
+
methods.push(_.assign(node, {
|
|
232
|
+
type: "FunctionDeclaration",
|
|
233
|
+
id: { type: "Identifier", name: id }
|
|
234
|
+
}));
|
|
235
|
+
return createMethodStub({ type: "Identifier", name: id });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return node;
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return methods;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Replaces direct argument references with arguments references like
|
|
246
|
+
* function (a) { return a; }
|
|
247
|
+
* to
|
|
248
|
+
* function (a) { return veilmark$arguments[0]; }
|
|
249
|
+
* @param {Function} method Function whose body will be transformed
|
|
250
|
+
* @param {boolean} useReassignedVariable Use veilmark$arguments instead of arguments
|
|
251
|
+
* @returns {Function} Function from method parameter
|
|
252
|
+
*/
|
|
253
|
+
replaceArgumentReferences (method, useReassignedVariable) {
|
|
254
|
+
assert.ok(estest.isFunction(method));
|
|
255
|
+
|
|
256
|
+
traverser.traverse(method.body, [], (node, stack) => {
|
|
257
|
+
if (node.type == "Identifier") {
|
|
258
|
+
var nestedFunction = stack.some(frame => estest.isFunction(frame.node));
|
|
259
|
+
if (useReassignedVariable && node.name == "arguments" && !node.veilmark$rawArguments && !nestedFunction) {
|
|
260
|
+
return { type: "Identifier", name: "veilmark$bareArguments" };
|
|
261
|
+
}
|
|
262
|
+
var index = getArgumentIndex(method, node);
|
|
263
|
+
if (index != -1) {
|
|
264
|
+
return {
|
|
265
|
+
type: "MemberExpression",
|
|
266
|
+
object: { type: "Identifier", name: useReassignedVariable ? "veilmark$arguments" : "arguments" },
|
|
267
|
+
property: { type: "Literal", value: index },
|
|
268
|
+
computed: true
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return node;
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
method.params = [];
|
|
277
|
+
|
|
278
|
+
return method;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Replaces function calls with main calls like
|
|
283
|
+
* test()
|
|
284
|
+
* to
|
|
285
|
+
* veilmark$bind(main, 1234)()
|
|
286
|
+
* @param {Node} ast Root node
|
|
287
|
+
* @param {Object[]} methodEntryExitPoints Method entry point table
|
|
288
|
+
* @param {number} methodEntryExitPoints[].entry Entry point
|
|
289
|
+
*/
|
|
290
|
+
replaceFunctionCalls (ast, methodEntryExitPoints) {
|
|
291
|
+
assert.ok(estest.isNode(ast));
|
|
292
|
+
assert.equal(typeof methodEntryExitPoints, "object");
|
|
293
|
+
|
|
294
|
+
traverser.traverse(ast, [], (node, stack) => {
|
|
295
|
+
if (node.type == "Identifier" && methodEntryExitPoints[node.name] && methodEntryExitPoints[node.name].entry) {
|
|
296
|
+
return {
|
|
297
|
+
type: "CallExpression",
|
|
298
|
+
callee: { type: "Identifier", name: "veilmark$bind" },
|
|
299
|
+
arguments: [
|
|
300
|
+
{ type: "Identifier", name: "main" },
|
|
301
|
+
{ type: "Identifier", name: methodEntryExitPoints[node.name].entry }
|
|
302
|
+
]
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return node;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Bumps all arguments indices like
|
|
311
|
+
* veilmark$arguments[0]
|
|
312
|
+
* to
|
|
313
|
+
* veilmark$arguments[1]
|
|
314
|
+
* @param {Function} method Function whose body will be transformed
|
|
315
|
+
* @param {number} inc Number to be added to all argument indices
|
|
316
|
+
*/
|
|
317
|
+
bumpArgumentsIndices (method, inc) {
|
|
318
|
+
assert.ok(estest.isFunction(method));
|
|
319
|
+
assert.equal(typeof inc, "number");
|
|
320
|
+
|
|
321
|
+
traverser.traverse(method.body, [], (node, stack) => {
|
|
322
|
+
if (node.type == "MemberExpression" && node.object.type == "Identifier" && node.object.name == "veilmark$arguments") {
|
|
323
|
+
node.property.value += inc;
|
|
324
|
+
}
|
|
325
|
+
if (node.veilmark$removeFirstArguments) {
|
|
326
|
+
node.value += inc;
|
|
327
|
+
}
|
|
328
|
+
return node;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
};
|