@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/health.js
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var assert = require("assert");
|
|
4
|
-
|
|
5
|
-
var _ = require("lodash");
|
|
6
|
-
var escodegen = require("escodegen");
|
|
7
|
-
|
|
8
|
-
var estest = require("../estest");
|
|
9
|
-
var traverser = require("../traverser");
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
module.exports = class Health {
|
|
13
|
-
|
|
14
|
-
constructor (logger) {
|
|
15
|
-
this.logger = logger;
|
|
16
|
-
this.strict = false;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
throwError (msg) {
|
|
20
|
-
if (this.strict) {
|
|
21
|
-
throw new Error(msg);
|
|
22
|
-
} else {
|
|
23
|
-
this.logger.warn(msg);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Perform various health checks on the AST without modifying it.
|
|
29
|
-
* @param {Node} ast Root node
|
|
30
|
-
* @returns {Node} Root node
|
|
31
|
-
*/
|
|
32
|
-
check (ast) {
|
|
33
|
-
var visited = [];
|
|
34
|
-
|
|
35
|
-
traverser.traverse(ast, [], (node, stack) => {
|
|
36
|
-
if (_.includes(visited, node)) {
|
|
37
|
-
this.throwError("Node has multiple parents: " + JSON.stringify(node));
|
|
38
|
-
} else {
|
|
39
|
-
visited.push(node);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (node.type == "BlockStatement") {
|
|
43
|
-
node.body.forEach(stmt => {
|
|
44
|
-
if (!estest.isStatement(stmt)) {
|
|
45
|
-
this.throwError(JSON.stringify(stack[1], null, 2));
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return node;
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
return ast;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var assert = require("assert");
|
|
4
|
+
|
|
5
|
+
var _ = require("lodash");
|
|
6
|
+
var escodegen = require("escodegen");
|
|
7
|
+
|
|
8
|
+
var estest = require("../estest");
|
|
9
|
+
var traverser = require("../traverser");
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
module.exports = class Health {
|
|
13
|
+
|
|
14
|
+
constructor (logger) {
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
this.strict = false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
throwError (msg) {
|
|
20
|
+
if (this.strict) {
|
|
21
|
+
throw new Error(msg);
|
|
22
|
+
} else {
|
|
23
|
+
this.logger.warn(msg);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Perform various health checks on the AST without modifying it.
|
|
29
|
+
* @param {Node} ast Root node
|
|
30
|
+
* @returns {Node} Root node
|
|
31
|
+
*/
|
|
32
|
+
check (ast) {
|
|
33
|
+
var visited = [];
|
|
34
|
+
|
|
35
|
+
traverser.traverse(ast, [], (node, stack) => {
|
|
36
|
+
if (_.includes(visited, node)) {
|
|
37
|
+
this.throwError("Node has multiple parents: " + JSON.stringify(node));
|
|
38
|
+
} else {
|
|
39
|
+
visited.push(node);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (node.type == "BlockStatement") {
|
|
43
|
+
node.body.forEach(stmt => {
|
|
44
|
+
if (!estest.isStatement(stmt)) {
|
|
45
|
+
this.throwError(JSON.stringify(stack[1], null, 2));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return node;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return ast;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var assert = require("assert");
|
|
4
|
-
|
|
5
|
-
var _ = require("lodash");
|
|
6
|
-
|
|
7
|
-
var estest = require("../estest");
|
|
8
|
-
var ESUtils = require("../esutils");
|
|
9
|
-
var traverser = require("../traverser");
|
|
10
|
-
var utils = require("../utils");
|
|
11
|
-
|
|
12
|
-
function literal(value) {
|
|
13
|
-
return { type: "Literal", value: value };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function encodeObjectKey(key, salt, index) {
|
|
17
|
-
var encoded = [ key.length ^ ((salt + index * 131) & 65535) ];
|
|
18
|
-
for (var i = 0; i < key.length; i += 1) {
|
|
19
|
-
encoded.push(key.charCodeAt(i) ^ ((salt + index * 257 + i * 17) & 65535));
|
|
20
|
-
}
|
|
21
|
-
return encoded;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function objectKey(prop) {
|
|
25
|
-
return prop.key.name || prop.key.value;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function isBigIntLiteral(node) {
|
|
29
|
-
return node.type == "Literal" && typeof node.value == "bigint";
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
module.exports = class Identifiers {
|
|
33
|
-
|
|
34
|
-
constructor (logger) {
|
|
35
|
-
this.logger = logger;
|
|
36
|
-
this.esutils = new ESUtils(logger);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* This checks whether the given node has a parent that
|
|
41
|
-
* accepts undefined children without throwing errors.
|
|
42
|
-
* Those cannot be moved to separate variables without
|
|
43
|
-
* causing errors by assigning undefined variables
|
|
44
|
-
* to new variables.
|
|
45
|
-
* @param {Node} node
|
|
46
|
-
* @returns {boolean}
|
|
47
|
-
*/
|
|
48
|
-
hasParentAcceptingUndefined (node) {
|
|
49
|
-
var parent = this.esutils.getParent(node);
|
|
50
|
-
return parent
|
|
51
|
-
&& parent.type == "UnaryExpression"
|
|
52
|
-
&& _.includes([ "typeof", "delete" ], parent.operator);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Replace property references like obj.prop with obj["prop"].
|
|
57
|
-
* @param {Node} ast Root node
|
|
58
|
-
* @returns {Node} Root node
|
|
59
|
-
*/
|
|
60
|
-
computeProperties (ast) {
|
|
61
|
-
assert.ok(estest.isNode(ast));
|
|
62
|
-
|
|
63
|
-
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
64
|
-
if (node.type == "MemberExpression"
|
|
65
|
-
&& !node.computed) {
|
|
66
|
-
assert(node.property.type == "Identifier");
|
|
67
|
-
node.property = { type: "Literal", value: node.property.name };
|
|
68
|
-
node.computed = true;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return node;
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
return ast;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Replace objects with an array via veilmark$toObject.
|
|
79
|
-
* @param {Node} ast Root node
|
|
80
|
-
* @returns {Node} Root node
|
|
81
|
-
*/
|
|
82
|
-
arrayizeObjects (ast, options) {
|
|
83
|
-
assert.ok(estest.isNode(ast));
|
|
84
|
-
options = options || {};
|
|
85
|
-
|
|
86
|
-
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
87
|
-
if (node.type == "ObjectExpression") {
|
|
88
|
-
if (options.objectPacking === false) {
|
|
89
|
-
var arr = [];
|
|
90
|
-
node.properties.forEach(prop => {
|
|
91
|
-
arr.push(literal(objectKey(prop)));
|
|
92
|
-
arr.push(prop.value);
|
|
93
|
-
});
|
|
94
|
-
return {
|
|
95
|
-
type: "CallExpression",
|
|
96
|
-
callee: { type: "Identifier", name: "veilmark$toObject" },
|
|
97
|
-
arguments: [
|
|
98
|
-
{
|
|
99
|
-
type: "ArrayExpression",
|
|
100
|
-
elements: arr
|
|
101
|
-
}
|
|
102
|
-
]
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
var salt = utils.random(1, 65535);
|
|
107
|
-
var schema = [ salt, node.properties.length ];
|
|
108
|
-
var values = [];
|
|
109
|
-
|
|
110
|
-
node.properties.forEach(prop => {
|
|
111
|
-
var key = objectKey(prop);
|
|
112
|
-
encodeObjectKey(String(key), salt, values.length).forEach(value => schema.push(value));
|
|
113
|
-
values.push(prop.value);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
type: "CallExpression",
|
|
118
|
-
callee: { type: "Identifier", name: "veilmark$toObject" },
|
|
119
|
-
arguments: [
|
|
120
|
-
{
|
|
121
|
-
type: "ArrayExpression",
|
|
122
|
-
elements: schema.map(literal)
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
type: "ArrayExpression",
|
|
126
|
-
elements: values
|
|
127
|
-
}
|
|
128
|
-
]
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return node;
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
return ast;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// This seems to be ununsed.
|
|
139
|
-
// TODO: Figure this out
|
|
140
|
-
moveIdentifiers (ast, scopeManager) {
|
|
141
|
-
assert.ok(estest.isNode(ast));
|
|
142
|
-
|
|
143
|
-
var rng = new utils.UniqueRandomAlpha(3);
|
|
144
|
-
|
|
145
|
-
this.esutils.setParentsRecursive(ast);
|
|
146
|
-
|
|
147
|
-
scopeManager.scopes.forEach(scope => {
|
|
148
|
-
/**
|
|
149
|
-
* That could cause problems if there are multiple unresolved
|
|
150
|
-
* references with the same name. (is that even possible?)
|
|
151
|
-
*/
|
|
152
|
-
|
|
153
|
-
var replaced = new utils.HashMap();
|
|
154
|
-
|
|
155
|
-
scope.references
|
|
156
|
-
.filter(reference => !utils.isResolvedReference(reference))
|
|
157
|
-
.forEach(reference => {
|
|
158
|
-
if (replaced.exists(reference.identifier.name)) {
|
|
159
|
-
reference.identifier.name = replaced.get(reference.identifier.name);
|
|
160
|
-
} else if (!this.hasParentAcceptingUndefined(reference.identifier)) {
|
|
161
|
-
var name = "$$ident$" + rng.get();
|
|
162
|
-
replaced.set(reference.identifier.name, name);
|
|
163
|
-
|
|
164
|
-
var init;
|
|
165
|
-
if (reference.identifier.name == "undefined") {
|
|
166
|
-
init = { type: "Identifier", name: "undefined" };
|
|
167
|
-
} else {
|
|
168
|
-
init = {
|
|
169
|
-
type: "ConditionalExpression",
|
|
170
|
-
test: {
|
|
171
|
-
type: "BinaryExpression",
|
|
172
|
-
operator: "!==",
|
|
173
|
-
left: {
|
|
174
|
-
type: "UnaryExpression",
|
|
175
|
-
operator: "typeof",
|
|
176
|
-
prefix: true,
|
|
177
|
-
argument: { type: "Identifier", name: reference.identifier.name }
|
|
178
|
-
},
|
|
179
|
-
right: { type: "Literal", value: "undefined" }
|
|
180
|
-
},
|
|
181
|
-
consequent: { type: "Identifier", name: reference.identifier.name },
|
|
182
|
-
alternate: { type: "Identifier", name: "undefined" }
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
this.esutils.insertIntoScope(scope, {
|
|
187
|
-
type: "VariableDeclaration",
|
|
188
|
-
kind: "var",
|
|
189
|
-
declarations: [
|
|
190
|
-
{
|
|
191
|
-
type: "VariableDeclarator",
|
|
192
|
-
id: { type: "Identifier", name: name },
|
|
193
|
-
init: init
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
reference.identifier.name = name;
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return ast;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Move all literals into the veilmark$literals array.
|
|
208
|
-
* @param {Node} ast Root node
|
|
209
|
-
* @param {ScopeManager} scopeManager Scope manager
|
|
210
|
-
* @returns {Node} Root node
|
|
211
|
-
*/
|
|
212
|
-
moveLiterals (ast, scopeManager) {
|
|
213
|
-
assert.ok(estest.isNode(ast));
|
|
214
|
-
|
|
215
|
-
var rng = new utils.UniqueRandomAlpha(3);
|
|
216
|
-
|
|
217
|
-
var vars = [];
|
|
218
|
-
|
|
219
|
-
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
220
|
-
if (node.type == "Literal" && !isBigIntLiteral(node) && stack.length > 0 && stack[1].node.type != "Property") {
|
|
221
|
-
var idx = vars.indexOf(node.value);
|
|
222
|
-
if (idx == -1) {
|
|
223
|
-
idx = vars.length;
|
|
224
|
-
vars.push(node.value);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
type: "MemberExpression",
|
|
229
|
-
object: { type: "Identifier", name: "veilmark$literals" },
|
|
230
|
-
property: { type: "Literal", value: idx },
|
|
231
|
-
computed: true
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return node;
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
ast.body.splice(0, 0, {
|
|
239
|
-
type: "VariableDeclaration",
|
|
240
|
-
kind: "var",
|
|
241
|
-
declarations: [
|
|
242
|
-
{
|
|
243
|
-
type: "VariableDeclarator",
|
|
244
|
-
id: { type: "Identifier", name: "veilmark$literals" },
|
|
245
|
-
init: {
|
|
246
|
-
type: "ArrayExpression",
|
|
247
|
-
elements: vars.map(x => ({ type: "Literal", value: x }))
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
]
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
return ast;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var assert = require("assert");
|
|
4
|
+
|
|
5
|
+
var _ = require("lodash");
|
|
6
|
+
|
|
7
|
+
var estest = require("../estest");
|
|
8
|
+
var ESUtils = require("../esutils");
|
|
9
|
+
var traverser = require("../traverser");
|
|
10
|
+
var utils = require("../utils");
|
|
11
|
+
|
|
12
|
+
function literal(value) {
|
|
13
|
+
return { type: "Literal", value: value };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function encodeObjectKey(key, salt, index) {
|
|
17
|
+
var encoded = [ key.length ^ ((salt + index * 131) & 65535) ];
|
|
18
|
+
for (var i = 0; i < key.length; i += 1) {
|
|
19
|
+
encoded.push(key.charCodeAt(i) ^ ((salt + index * 257 + i * 17) & 65535));
|
|
20
|
+
}
|
|
21
|
+
return encoded;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function objectKey(prop) {
|
|
25
|
+
return prop.key.name || prop.key.value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isBigIntLiteral(node) {
|
|
29
|
+
return node.type == "Literal" && typeof node.value == "bigint";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = class Identifiers {
|
|
33
|
+
|
|
34
|
+
constructor (logger) {
|
|
35
|
+
this.logger = logger;
|
|
36
|
+
this.esutils = new ESUtils(logger);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* This checks whether the given node has a parent that
|
|
41
|
+
* accepts undefined children without throwing errors.
|
|
42
|
+
* Those cannot be moved to separate variables without
|
|
43
|
+
* causing errors by assigning undefined variables
|
|
44
|
+
* to new variables.
|
|
45
|
+
* @param {Node} node
|
|
46
|
+
* @returns {boolean}
|
|
47
|
+
*/
|
|
48
|
+
hasParentAcceptingUndefined (node) {
|
|
49
|
+
var parent = this.esutils.getParent(node);
|
|
50
|
+
return parent
|
|
51
|
+
&& parent.type == "UnaryExpression"
|
|
52
|
+
&& _.includes([ "typeof", "delete" ], parent.operator);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Replace property references like obj.prop with obj["prop"].
|
|
57
|
+
* @param {Node} ast Root node
|
|
58
|
+
* @returns {Node} Root node
|
|
59
|
+
*/
|
|
60
|
+
computeProperties (ast) {
|
|
61
|
+
assert.ok(estest.isNode(ast));
|
|
62
|
+
|
|
63
|
+
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
64
|
+
if (node.type == "MemberExpression"
|
|
65
|
+
&& !node.computed) {
|
|
66
|
+
assert(node.property.type == "Identifier");
|
|
67
|
+
node.property = { type: "Literal", value: node.property.name };
|
|
68
|
+
node.computed = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return node;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return ast;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Replace objects with an array via veilmark$toObject.
|
|
79
|
+
* @param {Node} ast Root node
|
|
80
|
+
* @returns {Node} Root node
|
|
81
|
+
*/
|
|
82
|
+
arrayizeObjects (ast, options) {
|
|
83
|
+
assert.ok(estest.isNode(ast));
|
|
84
|
+
options = options || {};
|
|
85
|
+
|
|
86
|
+
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
87
|
+
if (node.type == "ObjectExpression") {
|
|
88
|
+
if (options.objectPacking === false) {
|
|
89
|
+
var arr = [];
|
|
90
|
+
node.properties.forEach(prop => {
|
|
91
|
+
arr.push(literal(objectKey(prop)));
|
|
92
|
+
arr.push(prop.value);
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
type: "CallExpression",
|
|
96
|
+
callee: { type: "Identifier", name: "veilmark$toObject" },
|
|
97
|
+
arguments: [
|
|
98
|
+
{
|
|
99
|
+
type: "ArrayExpression",
|
|
100
|
+
elements: arr
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
var salt = utils.random(1, 65535);
|
|
107
|
+
var schema = [ salt, node.properties.length ];
|
|
108
|
+
var values = [];
|
|
109
|
+
|
|
110
|
+
node.properties.forEach(prop => {
|
|
111
|
+
var key = objectKey(prop);
|
|
112
|
+
encodeObjectKey(String(key), salt, values.length).forEach(value => schema.push(value));
|
|
113
|
+
values.push(prop.value);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
type: "CallExpression",
|
|
118
|
+
callee: { type: "Identifier", name: "veilmark$toObject" },
|
|
119
|
+
arguments: [
|
|
120
|
+
{
|
|
121
|
+
type: "ArrayExpression",
|
|
122
|
+
elements: schema.map(literal)
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
type: "ArrayExpression",
|
|
126
|
+
elements: values
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return node;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return ast;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// This seems to be ununsed.
|
|
139
|
+
// TODO: Figure this out
|
|
140
|
+
moveIdentifiers (ast, scopeManager) {
|
|
141
|
+
assert.ok(estest.isNode(ast));
|
|
142
|
+
|
|
143
|
+
var rng = new utils.UniqueRandomAlpha(3);
|
|
144
|
+
|
|
145
|
+
this.esutils.setParentsRecursive(ast);
|
|
146
|
+
|
|
147
|
+
scopeManager.scopes.forEach(scope => {
|
|
148
|
+
/**
|
|
149
|
+
* That could cause problems if there are multiple unresolved
|
|
150
|
+
* references with the same name. (is that even possible?)
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
var replaced = new utils.HashMap();
|
|
154
|
+
|
|
155
|
+
scope.references
|
|
156
|
+
.filter(reference => !utils.isResolvedReference(reference))
|
|
157
|
+
.forEach(reference => {
|
|
158
|
+
if (replaced.exists(reference.identifier.name)) {
|
|
159
|
+
reference.identifier.name = replaced.get(reference.identifier.name);
|
|
160
|
+
} else if (!this.hasParentAcceptingUndefined(reference.identifier)) {
|
|
161
|
+
var name = "$$ident$" + rng.get();
|
|
162
|
+
replaced.set(reference.identifier.name, name);
|
|
163
|
+
|
|
164
|
+
var init;
|
|
165
|
+
if (reference.identifier.name == "undefined") {
|
|
166
|
+
init = { type: "Identifier", name: "undefined" };
|
|
167
|
+
} else {
|
|
168
|
+
init = {
|
|
169
|
+
type: "ConditionalExpression",
|
|
170
|
+
test: {
|
|
171
|
+
type: "BinaryExpression",
|
|
172
|
+
operator: "!==",
|
|
173
|
+
left: {
|
|
174
|
+
type: "UnaryExpression",
|
|
175
|
+
operator: "typeof",
|
|
176
|
+
prefix: true,
|
|
177
|
+
argument: { type: "Identifier", name: reference.identifier.name }
|
|
178
|
+
},
|
|
179
|
+
right: { type: "Literal", value: "undefined" }
|
|
180
|
+
},
|
|
181
|
+
consequent: { type: "Identifier", name: reference.identifier.name },
|
|
182
|
+
alternate: { type: "Identifier", name: "undefined" }
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.esutils.insertIntoScope(scope, {
|
|
187
|
+
type: "VariableDeclaration",
|
|
188
|
+
kind: "var",
|
|
189
|
+
declarations: [
|
|
190
|
+
{
|
|
191
|
+
type: "VariableDeclarator",
|
|
192
|
+
id: { type: "Identifier", name: name },
|
|
193
|
+
init: init
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
reference.identifier.name = name;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return ast;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Move all literals into the veilmark$literals array.
|
|
208
|
+
* @param {Node} ast Root node
|
|
209
|
+
* @param {ScopeManager} scopeManager Scope manager
|
|
210
|
+
* @returns {Node} Root node
|
|
211
|
+
*/
|
|
212
|
+
moveLiterals (ast, scopeManager) {
|
|
213
|
+
assert.ok(estest.isNode(ast));
|
|
214
|
+
|
|
215
|
+
var rng = new utils.UniqueRandomAlpha(3);
|
|
216
|
+
|
|
217
|
+
var vars = [];
|
|
218
|
+
|
|
219
|
+
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
220
|
+
if (node.type == "Literal" && !isBigIntLiteral(node) && stack.length > 0 && stack[1].node.type != "Property") {
|
|
221
|
+
var idx = vars.indexOf(node.value);
|
|
222
|
+
if (idx == -1) {
|
|
223
|
+
idx = vars.length;
|
|
224
|
+
vars.push(node.value);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
type: "MemberExpression",
|
|
229
|
+
object: { type: "Identifier", name: "veilmark$literals" },
|
|
230
|
+
property: { type: "Literal", value: idx },
|
|
231
|
+
computed: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return node;
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
ast.body.splice(0, 0, {
|
|
239
|
+
type: "VariableDeclaration",
|
|
240
|
+
kind: "var",
|
|
241
|
+
declarations: [
|
|
242
|
+
{
|
|
243
|
+
type: "VariableDeclarator",
|
|
244
|
+
id: { type: "Identifier", name: "veilmark$literals" },
|
|
245
|
+
init: {
|
|
246
|
+
type: "ArrayExpression",
|
|
247
|
+
elements: vars.map(x => ({ type: "Literal", value: x }))
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return ast;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
};
|