@dacely/toildefender 0.1.3 → 0.1.5
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/README.md +86 -57
- package/docs/all-modes-output.demo.js +1016 -670
- package/esutils.js +11 -1
- package/obfuscator.js +194 -23
- package/package.json +3 -26
- package/processors/deadCode.js +5 -1
- package/processors/flattener.js +56 -32
- package/processors/identifiers.js +9 -15
- package/processors/methods.js +109 -7
- package/processors/normalizer.js +946 -22
- package/processors/numericVm.js +179 -77
- package/processors/scopes.js +25 -0
- package/processors/variables.js +86 -2
- package/traverser.js +8 -2
package/processors/normalizer.js
CHANGED
|
@@ -51,6 +51,331 @@ function blockToArray (node) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function hasSpreadElement(nodes) {
|
|
55
|
+
return nodes.some(node => node && node.type == "SpreadElement");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isSimpleThisReceiver(node) {
|
|
59
|
+
return node.type == "Identifier" || node.type == "ThisExpression";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildArrayConcat(parts) {
|
|
63
|
+
if (parts.length == 0) {
|
|
64
|
+
return { type: "ArrayExpression", elements: [] };
|
|
65
|
+
}
|
|
66
|
+
if (parts.length == 1) {
|
|
67
|
+
return parts[0];
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
type: "CallExpression",
|
|
71
|
+
callee: {
|
|
72
|
+
type: "MemberExpression",
|
|
73
|
+
object: parts[0],
|
|
74
|
+
property: { type: "Identifier", name: "concat" },
|
|
75
|
+
computed: false
|
|
76
|
+
},
|
|
77
|
+
arguments: parts.slice(1)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function spreadArgumentsToArray(args) {
|
|
82
|
+
var parts = [];
|
|
83
|
+
var pending = [];
|
|
84
|
+
|
|
85
|
+
function flushPending() {
|
|
86
|
+
if (pending.length > 0) {
|
|
87
|
+
parts.push({ type: "ArrayExpression", elements: pending });
|
|
88
|
+
pending = [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
args.forEach(arg => {
|
|
93
|
+
if (arg.type == "SpreadElement") {
|
|
94
|
+
flushPending();
|
|
95
|
+
parts.push(arg.argument);
|
|
96
|
+
} else {
|
|
97
|
+
pending.push(arg);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
flushPending();
|
|
101
|
+
|
|
102
|
+
return buildArrayConcat(parts);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function isLoopOrSwitch(node) {
|
|
106
|
+
return node.type == "WhileStatement"
|
|
107
|
+
|| node.type == "DoWhileStatement"
|
|
108
|
+
|| node.type == "ForStatement"
|
|
109
|
+
|| node.type == "ForInStatement"
|
|
110
|
+
|| node.type == "ForOfStatement"
|
|
111
|
+
|| node.type == "SwitchStatement";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function exitsCurrentTry(node, stack) {
|
|
115
|
+
if (node.type == "ReturnStatement") {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (node.type == "BreakStatement" || node.type == "ContinueStatement") {
|
|
120
|
+
return !stack.some(frame => isLoopOrSwitch(frame.node));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function withFinalizerBefore(node, finalizer) {
|
|
127
|
+
var body = [];
|
|
128
|
+
|
|
129
|
+
if (node.type == "ReturnStatement") {
|
|
130
|
+
body.push({
|
|
131
|
+
type: "VariableDeclaration",
|
|
132
|
+
kind: "var",
|
|
133
|
+
declarations: [
|
|
134
|
+
{
|
|
135
|
+
type: "VariableDeclarator",
|
|
136
|
+
id: { type: "Identifier", name: "veilmark$return" },
|
|
137
|
+
init: node.argument
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
});
|
|
141
|
+
body.push(utils.cloneISwearIKnowWhatImDoing(finalizer));
|
|
142
|
+
body.push({
|
|
143
|
+
type: "ReturnStatement",
|
|
144
|
+
argument: { type: "Identifier", name: "veilmark$return" }
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
body.push(utils.cloneISwearIKnowWhatImDoing(finalizer));
|
|
148
|
+
body.push(node);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
type: "BlockStatement",
|
|
153
|
+
body: body
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function methodDefinitionName(method) {
|
|
158
|
+
if (!method || !method.key) {
|
|
159
|
+
return "";
|
|
160
|
+
}
|
|
161
|
+
if (method.key.type == "Identifier") {
|
|
162
|
+
return method.key.name;
|
|
163
|
+
}
|
|
164
|
+
if (method.key.type == "Literal") {
|
|
165
|
+
return String(method.key.value);
|
|
166
|
+
}
|
|
167
|
+
return "";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function isConstructorMethod(method) {
|
|
171
|
+
return method.type == "MethodDefinition" && method.kind == "constructor" && methodDefinitionName(method) == "constructor";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function privateStoreName(className, privateName) {
|
|
175
|
+
return `$$private$${className}$${privateName}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function classFieldKey(field) {
|
|
179
|
+
if (field.key.type == "Identifier") {
|
|
180
|
+
return {
|
|
181
|
+
type: "Identifier",
|
|
182
|
+
name: field.key.name
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (field.key.type == "PrivateIdentifier") {
|
|
186
|
+
return {
|
|
187
|
+
type: "Literal",
|
|
188
|
+
value: field.key.name
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return field.key;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function assignmentStatement(left, right) {
|
|
195
|
+
return {
|
|
196
|
+
type: "ExpressionStatement",
|
|
197
|
+
expression: {
|
|
198
|
+
type: "AssignmentExpression",
|
|
199
|
+
operator: "=",
|
|
200
|
+
left: left,
|
|
201
|
+
right: right || { type: "Identifier", name: "undefined" }
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function weakMapSetStatement(storeName, object, value) {
|
|
207
|
+
return {
|
|
208
|
+
type: "ExpressionStatement",
|
|
209
|
+
expression: {
|
|
210
|
+
type: "CallExpression",
|
|
211
|
+
callee: {
|
|
212
|
+
type: "MemberExpression",
|
|
213
|
+
object: { type: "Identifier", name: storeName },
|
|
214
|
+
property: { type: "Identifier", name: "set" },
|
|
215
|
+
computed: false
|
|
216
|
+
},
|
|
217
|
+
arguments: [
|
|
218
|
+
object,
|
|
219
|
+
value || { type: "Identifier", name: "undefined" }
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function weakMapGetExpression(storeName, object) {
|
|
226
|
+
return {
|
|
227
|
+
type: "CallExpression",
|
|
228
|
+
callee: {
|
|
229
|
+
type: "MemberExpression",
|
|
230
|
+
object: { type: "Identifier", name: storeName },
|
|
231
|
+
property: { type: "Identifier", name: "get" },
|
|
232
|
+
computed: false
|
|
233
|
+
},
|
|
234
|
+
arguments: [ object ]
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function undefinedExpression() {
|
|
239
|
+
return { type: "Identifier", name: "undefined" };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function nullishTest(expression) {
|
|
243
|
+
return {
|
|
244
|
+
type: "BinaryExpression",
|
|
245
|
+
operator: "==",
|
|
246
|
+
left: expression,
|
|
247
|
+
right: { type: "Literal", value: null }
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function notNullishTest(expression) {
|
|
252
|
+
return {
|
|
253
|
+
type: "BinaryExpression",
|
|
254
|
+
operator: "!=",
|
|
255
|
+
left: expression,
|
|
256
|
+
right: { type: "Literal", value: null }
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function propertyKeyValue(property) {
|
|
261
|
+
if (property.key.type == "Identifier") {
|
|
262
|
+
return property.key.name;
|
|
263
|
+
}
|
|
264
|
+
if (property.key.type == "Literal") {
|
|
265
|
+
return property.key.value;
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function propertyMemberExpression(object, property) {
|
|
271
|
+
return {
|
|
272
|
+
type: "MemberExpression",
|
|
273
|
+
object: object,
|
|
274
|
+
property: property.key.type == "Identifier"
|
|
275
|
+
? { type: "Identifier", name: property.key.name }
|
|
276
|
+
: utils.cloneISwearIKnowWhatImDoing(property.key),
|
|
277
|
+
computed: property.computed === true || property.key.type == "Literal"
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function hasObjectRest(pattern) {
|
|
282
|
+
return pattern.type == "ObjectPattern" && pattern.properties.some(prop => prop.type == "RestElement");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function canLowerObjectRest(pattern) {
|
|
286
|
+
return pattern.type == "ObjectPattern" && pattern.properties.every(prop => {
|
|
287
|
+
if (prop.type == "RestElement") {
|
|
288
|
+
return prop.argument.type == "Identifier";
|
|
289
|
+
}
|
|
290
|
+
if (prop.type != "Property" || prop.computed === true || propertyKeyValue(prop) == null) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
if (prop.value.type == "Identifier") {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
return prop.value.type == "AssignmentPattern" && prop.value.left.type == "Identifier";
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function hasObjectSpread(node) {
|
|
301
|
+
return node.properties.some(prop => prop.type == "SpreadElement");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function objectAssignCall(parts) {
|
|
305
|
+
return {
|
|
306
|
+
type: "CallExpression",
|
|
307
|
+
callee: {
|
|
308
|
+
type: "MemberExpression",
|
|
309
|
+
object: { type: "Identifier", name: "Object" },
|
|
310
|
+
property: { type: "Identifier", name: "assign" },
|
|
311
|
+
computed: false
|
|
312
|
+
},
|
|
313
|
+
arguments: parts
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function objectWithoutKeysCall(source, keys) {
|
|
318
|
+
return {
|
|
319
|
+
type: "CallExpression",
|
|
320
|
+
callee: { type: "Identifier", name: "veilmark$objectWithoutKeys" },
|
|
321
|
+
arguments: [
|
|
322
|
+
source,
|
|
323
|
+
{
|
|
324
|
+
type: "ArrayExpression",
|
|
325
|
+
elements: keys.map(key => ({ type: "Literal", value: key }))
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function objectPatternPropertyDeclaration(kind, sourceName, prop) {
|
|
332
|
+
var member = propertyMemberExpression(
|
|
333
|
+
{ type: "Identifier", name: sourceName },
|
|
334
|
+
prop
|
|
335
|
+
);
|
|
336
|
+
var id = prop.value;
|
|
337
|
+
var init = member;
|
|
338
|
+
|
|
339
|
+
if (prop.value.type == "AssignmentPattern") {
|
|
340
|
+
id = prop.value.left;
|
|
341
|
+
init = {
|
|
342
|
+
type: "ConditionalExpression",
|
|
343
|
+
test: {
|
|
344
|
+
type: "BinaryExpression",
|
|
345
|
+
operator: "===",
|
|
346
|
+
left: utils.cloneISwearIKnowWhatImDoing(member),
|
|
347
|
+
right: undefinedExpression()
|
|
348
|
+
},
|
|
349
|
+
consequent: prop.value.right,
|
|
350
|
+
alternate: member
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
type: "VariableDeclaration",
|
|
356
|
+
kind: kind,
|
|
357
|
+
declarations: [
|
|
358
|
+
{
|
|
359
|
+
type: "VariableDeclarator",
|
|
360
|
+
id: id,
|
|
361
|
+
init: init
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function containsThisExpression(node) {
|
|
368
|
+
var found = false;
|
|
369
|
+
traverser.traverseEx(node, [], function (child) {
|
|
370
|
+
if (child.type == "ThisExpression") {
|
|
371
|
+
found = true;
|
|
372
|
+
this.abort();
|
|
373
|
+
}
|
|
374
|
+
return child;
|
|
375
|
+
});
|
|
376
|
+
return found;
|
|
377
|
+
}
|
|
378
|
+
|
|
54
379
|
module.exports = class Normalizer {
|
|
55
380
|
|
|
56
381
|
constructor (logger) {
|
|
@@ -79,10 +404,26 @@ module.exports = class Normalizer {
|
|
|
79
404
|
return this.simplifyForStatement(node);
|
|
80
405
|
case "ForInStatement":
|
|
81
406
|
return this.simplifyForStatement(this.simplifyForInStatement(node));
|
|
407
|
+
case "ForOfStatement":
|
|
408
|
+
return this.simplifyForOfStatement(node);
|
|
82
409
|
/*case "SwitchStatement":
|
|
83
410
|
return this.simplifySwitchStatement(node);*/
|
|
84
411
|
case "TryStatement":
|
|
85
412
|
return this.simplifyTryStatement(node);
|
|
413
|
+
case "CallExpression":
|
|
414
|
+
return this.simplifyCallExpression(node);
|
|
415
|
+
case "ChainExpression":
|
|
416
|
+
return this.simplifyChainExpression(node);
|
|
417
|
+
case "LogicalExpression":
|
|
418
|
+
return this.simplifyLogicalExpression(node);
|
|
419
|
+
case "ObjectExpression":
|
|
420
|
+
return this.simplifyObjectExpression(node);
|
|
421
|
+
case "VariableDeclaration":
|
|
422
|
+
return this.simplifyVariableDeclaration(node, stack);
|
|
423
|
+
case "ArrowFunctionExpression":
|
|
424
|
+
return this.simplifyArrowFunctionExpression(node);
|
|
425
|
+
case "ClassDeclaration":
|
|
426
|
+
return this.simplifyClassDeclaration(node);
|
|
86
427
|
default:
|
|
87
428
|
return node;
|
|
88
429
|
}
|
|
@@ -299,6 +640,93 @@ module.exports = class Normalizer {
|
|
|
299
640
|
return forStmt;
|
|
300
641
|
}
|
|
301
642
|
|
|
643
|
+
/**
|
|
644
|
+
* Simplify ForOfStatement to an index-based ForStatement.
|
|
645
|
+
* @param {ForOfStatement} node
|
|
646
|
+
* @return {Node}
|
|
647
|
+
*/
|
|
648
|
+
simplifyForOfStatement (node) {
|
|
649
|
+
assert.ok(estest.isNode(node));
|
|
650
|
+
|
|
651
|
+
var valuesName = `$$forof$values$${this.rngAlpha.get()}`, iterName = `$$forof$iter$${this.rngAlpha.get()}`;
|
|
652
|
+
var valueAtIndex = {
|
|
653
|
+
type: "MemberExpression",
|
|
654
|
+
object: { type: "Identifier", name: valuesName },
|
|
655
|
+
property: { type: "Identifier", name: iterName },
|
|
656
|
+
computed: true
|
|
657
|
+
};
|
|
658
|
+
var assignValue = node.left.type == "VariableDeclaration"
|
|
659
|
+
?
|
|
660
|
+
{
|
|
661
|
+
type: "VariableDeclaration",
|
|
662
|
+
kind: node.left.kind == "const" ? "let" : node.left.kind,
|
|
663
|
+
declarations: [
|
|
664
|
+
{
|
|
665
|
+
type: "VariableDeclarator",
|
|
666
|
+
id: node.left.declarations[0].id,
|
|
667
|
+
init: valueAtIndex
|
|
668
|
+
}
|
|
669
|
+
]
|
|
670
|
+
}
|
|
671
|
+
:
|
|
672
|
+
{
|
|
673
|
+
type: "ExpressionStatement",
|
|
674
|
+
expression: {
|
|
675
|
+
type: "AssignmentExpression",
|
|
676
|
+
operator: "=",
|
|
677
|
+
left: node.left,
|
|
678
|
+
right: valueAtIndex
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
return {
|
|
683
|
+
type: "BlockStatement",
|
|
684
|
+
body: [
|
|
685
|
+
{
|
|
686
|
+
type: "VariableDeclaration",
|
|
687
|
+
kind: "var",
|
|
688
|
+
declarations: [
|
|
689
|
+
{
|
|
690
|
+
type: "VariableDeclarator",
|
|
691
|
+
id: { type: "Identifier", name: valuesName },
|
|
692
|
+
init: node.right
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
type: "VariableDeclarator",
|
|
696
|
+
id: { type: "Identifier", name: iterName },
|
|
697
|
+
init: { type: "Literal", value: 0 }
|
|
698
|
+
}
|
|
699
|
+
]
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
type: "ForStatement",
|
|
703
|
+
init: null,
|
|
704
|
+
test: {
|
|
705
|
+
type: "BinaryExpression",
|
|
706
|
+
operator: "<",
|
|
707
|
+
left: { type: "Identifier", name: iterName },
|
|
708
|
+
right: {
|
|
709
|
+
type: "MemberExpression",
|
|
710
|
+
object: { type: "Identifier", name: valuesName },
|
|
711
|
+
property: { type: "Identifier", name: "length" },
|
|
712
|
+
computed: false
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
update: {
|
|
716
|
+
type: "UpdateExpression",
|
|
717
|
+
operator: "++",
|
|
718
|
+
argument: { type: "Identifier", name: iterName },
|
|
719
|
+
prefix: false
|
|
720
|
+
},
|
|
721
|
+
body: {
|
|
722
|
+
type: "BlockStatement",
|
|
723
|
+
body: [assignValue].concat(blockToArray(node.body))
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
]
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
|
|
302
730
|
/**
|
|
303
731
|
* Simplify SwitchStatement.
|
|
304
732
|
* @param {SwitchStatement} node
|
|
@@ -416,28 +844,8 @@ module.exports = class Normalizer {
|
|
|
416
844
|
if (stack.some(x => estest.isFunction(x.node))) {
|
|
417
845
|
this.abort();
|
|
418
846
|
return node;
|
|
419
|
-
} else if (node
|
|
420
|
-
return
|
|
421
|
-
type: "BlockStatement",
|
|
422
|
-
body: [
|
|
423
|
-
{
|
|
424
|
-
type: "VariableDeclaration",
|
|
425
|
-
kind: "var",
|
|
426
|
-
declarations: [
|
|
427
|
-
{
|
|
428
|
-
type: "VariableDeclarator",
|
|
429
|
-
id: { type: "Identifier", name: "veilmark$return" },
|
|
430
|
-
init: node.argument
|
|
431
|
-
}
|
|
432
|
-
]
|
|
433
|
-
},
|
|
434
|
-
utils.cloneISwearIKnowWhatImDoing(finalizer),
|
|
435
|
-
{
|
|
436
|
-
type: "ReturnStatement",
|
|
437
|
-
argument: { type: "Identifier", name: "veilmark$return" }
|
|
438
|
-
}
|
|
439
|
-
]
|
|
440
|
-
};
|
|
847
|
+
} else if (exitsCurrentTry(node, stack)) {
|
|
848
|
+
return withFinalizerBefore(node, finalizer);
|
|
441
849
|
} else {
|
|
442
850
|
return node;
|
|
443
851
|
}
|
|
@@ -487,4 +895,520 @@ module.exports = class Normalizer {
|
|
|
487
895
|
}
|
|
488
896
|
}
|
|
489
897
|
|
|
898
|
+
/**
|
|
899
|
+
* Lower simple spread calls like target.push(...items) to
|
|
900
|
+
* target.push.apply(target, items). This keeps append-style calls stable
|
|
901
|
+
* even when Babel is disabled.
|
|
902
|
+
* @param {CallExpression} node
|
|
903
|
+
* @return {Node}
|
|
904
|
+
*/
|
|
905
|
+
simplifyCallExpression (node) {
|
|
906
|
+
assert.ok(estest.isNode(node));
|
|
907
|
+
|
|
908
|
+
if (!hasSpreadElement(node.arguments)) {
|
|
909
|
+
return node;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
var thisArg = { type: "Literal", value: null };
|
|
913
|
+
if (node.callee.type == "MemberExpression") {
|
|
914
|
+
if (!isSimpleThisReceiver(node.callee.object)) {
|
|
915
|
+
return node;
|
|
916
|
+
}
|
|
917
|
+
thisArg = utils.cloneISwearIKnowWhatImDoing(node.callee.object);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
return {
|
|
921
|
+
type: "CallExpression",
|
|
922
|
+
callee: {
|
|
923
|
+
type: "MemberExpression",
|
|
924
|
+
object: node.callee,
|
|
925
|
+
property: { type: "Identifier", name: "apply" },
|
|
926
|
+
computed: false
|
|
927
|
+
},
|
|
928
|
+
arguments: [
|
|
929
|
+
thisArg,
|
|
930
|
+
spreadArgumentsToArray(node.arguments)
|
|
931
|
+
]
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Lower optional chains to conditional expressions before legacy passes.
|
|
937
|
+
* This intentionally targets deterministic AST compatibility rather than
|
|
938
|
+
* Babel-perfect single-evaluation semantics for every exotic receiver.
|
|
939
|
+
* @param {ChainExpression} node
|
|
940
|
+
* @return {Node}
|
|
941
|
+
*/
|
|
942
|
+
simplifyChainExpression (node) {
|
|
943
|
+
assert.ok(estest.isNode(node));
|
|
944
|
+
|
|
945
|
+
return this.lowerOptionalChain(node.expression);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
lowerOptionalChain (node) {
|
|
949
|
+
if (node.type == "MemberExpression") {
|
|
950
|
+
var object = this.lowerOptionalChain(node.object);
|
|
951
|
+
var member = {
|
|
952
|
+
type: "MemberExpression",
|
|
953
|
+
object: utils.cloneISwearIKnowWhatImDoing(object),
|
|
954
|
+
property: node.property,
|
|
955
|
+
computed: node.computed === true
|
|
956
|
+
};
|
|
957
|
+
if (node.optional === true) {
|
|
958
|
+
return {
|
|
959
|
+
type: "ConditionalExpression",
|
|
960
|
+
test: nullishTest(utils.cloneISwearIKnowWhatImDoing(object)),
|
|
961
|
+
consequent: undefinedExpression(),
|
|
962
|
+
alternate: member
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
return member;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
if (node.type == "CallExpression") {
|
|
969
|
+
if (node.callee.type == "MemberExpression") {
|
|
970
|
+
return this.lowerOptionalMemberCall(node);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
var callee = this.lowerOptionalChain(node.callee);
|
|
974
|
+
var call = {
|
|
975
|
+
type: "CallExpression",
|
|
976
|
+
callee: utils.cloneISwearIKnowWhatImDoing(callee),
|
|
977
|
+
arguments: node.arguments,
|
|
978
|
+
optional: false
|
|
979
|
+
};
|
|
980
|
+
if (node.optional === true) {
|
|
981
|
+
return {
|
|
982
|
+
type: "ConditionalExpression",
|
|
983
|
+
test: nullishTest(utils.cloneISwearIKnowWhatImDoing(callee)),
|
|
984
|
+
consequent: undefinedExpression(),
|
|
985
|
+
alternate: call
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
return call;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
return node;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
lowerOptionalMemberCall (node) {
|
|
995
|
+
var member = node.callee;
|
|
996
|
+
var object = this.lowerOptionalChain(member.object);
|
|
997
|
+
var directMember = {
|
|
998
|
+
type: "MemberExpression",
|
|
999
|
+
object: utils.cloneISwearIKnowWhatImDoing(object),
|
|
1000
|
+
property: member.property,
|
|
1001
|
+
computed: member.computed === true
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
var alternate;
|
|
1005
|
+
if (node.optional === true) {
|
|
1006
|
+
alternate = {
|
|
1007
|
+
type: "CallExpression",
|
|
1008
|
+
callee: {
|
|
1009
|
+
type: "MemberExpression",
|
|
1010
|
+
object: utils.cloneISwearIKnowWhatImDoing(directMember),
|
|
1011
|
+
property: { type: "Identifier", name: "call" },
|
|
1012
|
+
computed: false
|
|
1013
|
+
},
|
|
1014
|
+
arguments: [ utils.cloneISwearIKnowWhatImDoing(object) ].concat(node.arguments),
|
|
1015
|
+
optional: false
|
|
1016
|
+
};
|
|
1017
|
+
alternate = {
|
|
1018
|
+
type: "ConditionalExpression",
|
|
1019
|
+
test: nullishTest(utils.cloneISwearIKnowWhatImDoing(directMember)),
|
|
1020
|
+
consequent: undefinedExpression(),
|
|
1021
|
+
alternate: alternate
|
|
1022
|
+
};
|
|
1023
|
+
} else {
|
|
1024
|
+
alternate = {
|
|
1025
|
+
type: "CallExpression",
|
|
1026
|
+
callee: directMember,
|
|
1027
|
+
arguments: node.arguments,
|
|
1028
|
+
optional: false
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
if (member.optional === true) {
|
|
1033
|
+
return {
|
|
1034
|
+
type: "ConditionalExpression",
|
|
1035
|
+
test: nullishTest(utils.cloneISwearIKnowWhatImDoing(object)),
|
|
1036
|
+
consequent: undefinedExpression(),
|
|
1037
|
+
alternate: alternate
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
return alternate;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Lower nullish coalescing to an ES5-compatible conditional expression.
|
|
1046
|
+
* @param {LogicalExpression} node
|
|
1047
|
+
* @return {Node}
|
|
1048
|
+
*/
|
|
1049
|
+
simplifyLogicalExpression (node) {
|
|
1050
|
+
assert.ok(estest.isNode(node));
|
|
1051
|
+
|
|
1052
|
+
if (node.operator != "??") {
|
|
1053
|
+
return node;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
return {
|
|
1057
|
+
type: "ConditionalExpression",
|
|
1058
|
+
test: notNullishTest(utils.cloneISwearIKnowWhatImDoing(node.left)),
|
|
1059
|
+
consequent: node.left,
|
|
1060
|
+
alternate: node.right
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Lower object spread to Object.assign({}, ...parts).
|
|
1066
|
+
* @param {ObjectExpression} node
|
|
1067
|
+
* @return {Node}
|
|
1068
|
+
*/
|
|
1069
|
+
simplifyObjectExpression (node) {
|
|
1070
|
+
assert.ok(estest.isNode(node));
|
|
1071
|
+
|
|
1072
|
+
if (!hasObjectSpread(node)) {
|
|
1073
|
+
return node;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
var parts = [
|
|
1077
|
+
{
|
|
1078
|
+
type: "ObjectExpression",
|
|
1079
|
+
properties: []
|
|
1080
|
+
}
|
|
1081
|
+
];
|
|
1082
|
+
var pending = [];
|
|
1083
|
+
|
|
1084
|
+
function flushPending() {
|
|
1085
|
+
if (pending.length > 0) {
|
|
1086
|
+
parts.push({
|
|
1087
|
+
type: "ObjectExpression",
|
|
1088
|
+
properties: pending
|
|
1089
|
+
});
|
|
1090
|
+
pending = [];
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
node.properties.forEach(prop => {
|
|
1095
|
+
if (prop.type == "SpreadElement") {
|
|
1096
|
+
flushPending();
|
|
1097
|
+
parts.push(prop.argument);
|
|
1098
|
+
} else {
|
|
1099
|
+
pending.push(prop);
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
flushPending();
|
|
1103
|
+
|
|
1104
|
+
return objectAssignCall(parts);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Lower simple object rest declarations:
|
|
1109
|
+
* const { a, ...rest } = source
|
|
1110
|
+
* becomes:
|
|
1111
|
+
* var tmp = source; var a = tmp.a; var rest = withoutKeys(tmp, ["a"])
|
|
1112
|
+
* @param {VariableDeclaration} node
|
|
1113
|
+
* @param {Node[]} stack
|
|
1114
|
+
* @return {Node}
|
|
1115
|
+
*/
|
|
1116
|
+
simplifyVariableDeclaration (node, stack) {
|
|
1117
|
+
assert.ok(estest.isNode(node));
|
|
1118
|
+
|
|
1119
|
+
if (!node.declarations.some(decl => hasObjectRest(decl.id))) {
|
|
1120
|
+
return node;
|
|
1121
|
+
}
|
|
1122
|
+
if (node.declarations.some(decl => hasObjectRest(decl.id) && !canLowerObjectRest(decl.id))) {
|
|
1123
|
+
return node;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
var parentFrame = stack[1];
|
|
1127
|
+
if (parentFrame && parentFrame.node.type == "ForStatement" && parentFrame.key == "init") {
|
|
1128
|
+
return node;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
var statements = [];
|
|
1132
|
+
var normalDeclarations = [];
|
|
1133
|
+
var declarationKind = node.kind == "const" ? "var" : node.kind;
|
|
1134
|
+
|
|
1135
|
+
function flushNormalDeclarations() {
|
|
1136
|
+
if (normalDeclarations.length > 0) {
|
|
1137
|
+
statements.push({
|
|
1138
|
+
type: "VariableDeclaration",
|
|
1139
|
+
kind: declarationKind,
|
|
1140
|
+
declarations: normalDeclarations
|
|
1141
|
+
});
|
|
1142
|
+
normalDeclarations = [];
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
node.declarations.forEach(decl => {
|
|
1147
|
+
if (!hasObjectRest(decl.id)) {
|
|
1148
|
+
normalDeclarations.push(decl);
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
flushNormalDeclarations();
|
|
1153
|
+
|
|
1154
|
+
var sourceName = `$$destructure$obj$${this.rngAlpha.get()}`;
|
|
1155
|
+
statements.push({
|
|
1156
|
+
type: "VariableDeclaration",
|
|
1157
|
+
kind: "var",
|
|
1158
|
+
declarations: [
|
|
1159
|
+
{
|
|
1160
|
+
type: "VariableDeclarator",
|
|
1161
|
+
id: { type: "Identifier", name: sourceName },
|
|
1162
|
+
init: decl.init || { type: "ObjectExpression", properties: [] }
|
|
1163
|
+
}
|
|
1164
|
+
]
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
var excluded = [];
|
|
1168
|
+
decl.id.properties.forEach(prop => {
|
|
1169
|
+
if (prop.type == "RestElement") {
|
|
1170
|
+
statements.push({
|
|
1171
|
+
type: "VariableDeclaration",
|
|
1172
|
+
kind: declarationKind,
|
|
1173
|
+
declarations: [
|
|
1174
|
+
{
|
|
1175
|
+
type: "VariableDeclarator",
|
|
1176
|
+
id: prop.argument,
|
|
1177
|
+
init: objectWithoutKeysCall(
|
|
1178
|
+
{ type: "Identifier", name: sourceName },
|
|
1179
|
+
excluded
|
|
1180
|
+
)
|
|
1181
|
+
}
|
|
1182
|
+
]
|
|
1183
|
+
});
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
var key = propertyKeyValue(prop);
|
|
1188
|
+
excluded.push(String(key));
|
|
1189
|
+
statements.push(objectPatternPropertyDeclaration(declarationKind, sourceName, prop));
|
|
1190
|
+
});
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
flushNormalDeclarations();
|
|
1194
|
+
|
|
1195
|
+
return {
|
|
1196
|
+
type: "BlockStatement",
|
|
1197
|
+
body: statements
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Lower arrows so scope/control-flow passes do not leave callback bodies
|
|
1203
|
+
* inside an outer flattened frame. Bind lexical this only when needed.
|
|
1204
|
+
* @param {ArrowFunctionExpression} node
|
|
1205
|
+
* @return {Node}
|
|
1206
|
+
*/
|
|
1207
|
+
simplifyArrowFunctionExpression (node) {
|
|
1208
|
+
assert.ok(estest.isNode(node));
|
|
1209
|
+
|
|
1210
|
+
var fn = {
|
|
1211
|
+
type: "FunctionExpression",
|
|
1212
|
+
id: null,
|
|
1213
|
+
params: node.params,
|
|
1214
|
+
body: node.body.type == "BlockStatement" ? node.body : {
|
|
1215
|
+
type: "BlockStatement",
|
|
1216
|
+
body: [
|
|
1217
|
+
{
|
|
1218
|
+
type: "ReturnStatement",
|
|
1219
|
+
argument: node.body
|
|
1220
|
+
}
|
|
1221
|
+
]
|
|
1222
|
+
},
|
|
1223
|
+
generator: false,
|
|
1224
|
+
expression: false,
|
|
1225
|
+
async: node.async === true
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
if (!containsThisExpression(fn.body)) {
|
|
1229
|
+
return fn;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
return {
|
|
1233
|
+
type: "CallExpression",
|
|
1234
|
+
callee: {
|
|
1235
|
+
type: "MemberExpression",
|
|
1236
|
+
object: fn,
|
|
1237
|
+
property: { type: "Identifier", name: "bind" },
|
|
1238
|
+
computed: false
|
|
1239
|
+
},
|
|
1240
|
+
arguments: [
|
|
1241
|
+
{ type: "ThisExpression" }
|
|
1242
|
+
]
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* Lower class fields/private fields to older ESTree nodes that escodegen
|
|
1248
|
+
* and the classic passes can handle.
|
|
1249
|
+
* @param {ClassDeclaration} node
|
|
1250
|
+
* @return {Node}
|
|
1251
|
+
*/
|
|
1252
|
+
simplifyClassDeclaration (node) {
|
|
1253
|
+
assert.ok(estest.isNode(node));
|
|
1254
|
+
|
|
1255
|
+
var className = node.id && node.id.name || `$$class$${this.rngAlpha.get()}`;
|
|
1256
|
+
var privateStores = {};
|
|
1257
|
+
var instanceInitializers = [];
|
|
1258
|
+
var staticAssignments = [];
|
|
1259
|
+
var methods = [];
|
|
1260
|
+
|
|
1261
|
+
node.body.body.forEach(element => {
|
|
1262
|
+
if (element.type != "PropertyDefinition" && element.type != "FieldDefinition") {
|
|
1263
|
+
methods.push(element);
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
if (element.key.type == "PrivateIdentifier") {
|
|
1268
|
+
var storeName = privateStoreName(className, element.key.name);
|
|
1269
|
+
privateStores[element.key.name] = storeName;
|
|
1270
|
+
if (element.static) {
|
|
1271
|
+
staticAssignments.push(weakMapSetStatement(
|
|
1272
|
+
storeName,
|
|
1273
|
+
{ type: "Identifier", name: className },
|
|
1274
|
+
element.value
|
|
1275
|
+
));
|
|
1276
|
+
} else {
|
|
1277
|
+
instanceInitializers.push(weakMapSetStatement(
|
|
1278
|
+
storeName,
|
|
1279
|
+
{ type: "ThisExpression" },
|
|
1280
|
+
element.value
|
|
1281
|
+
));
|
|
1282
|
+
}
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
var target = {
|
|
1287
|
+
type: "MemberExpression",
|
|
1288
|
+
object: element.static ? { type: "Identifier", name: className } : { type: "ThisExpression" },
|
|
1289
|
+
property: classFieldKey(element),
|
|
1290
|
+
computed: element.computed === true || element.key.type == "Literal"
|
|
1291
|
+
};
|
|
1292
|
+
if (element.static) {
|
|
1293
|
+
staticAssignments.push(assignmentStatement(target, element.value));
|
|
1294
|
+
} else {
|
|
1295
|
+
instanceInitializers.push(assignmentStatement(target, element.value));
|
|
1296
|
+
}
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
methods.forEach(method => {
|
|
1300
|
+
this.lowerPrivateMembers(method, privateStores);
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
if (instanceInitializers.length > 0) {
|
|
1304
|
+
var constructor = methods.find(isConstructorMethod);
|
|
1305
|
+
if (!constructor) {
|
|
1306
|
+
constructor = {
|
|
1307
|
+
type: "MethodDefinition",
|
|
1308
|
+
key: { type: "Identifier", name: "constructor" },
|
|
1309
|
+
computed: false,
|
|
1310
|
+
value: {
|
|
1311
|
+
type: "FunctionExpression",
|
|
1312
|
+
id: null,
|
|
1313
|
+
params: [],
|
|
1314
|
+
body: {
|
|
1315
|
+
type: "BlockStatement",
|
|
1316
|
+
body: node.superClass ? [
|
|
1317
|
+
{
|
|
1318
|
+
type: "ExpressionStatement",
|
|
1319
|
+
expression: {
|
|
1320
|
+
type: "CallExpression",
|
|
1321
|
+
callee: { type: "Super" },
|
|
1322
|
+
arguments: []
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
] : []
|
|
1326
|
+
},
|
|
1327
|
+
generator: false,
|
|
1328
|
+
expression: false,
|
|
1329
|
+
async: false
|
|
1330
|
+
},
|
|
1331
|
+
kind: "constructor",
|
|
1332
|
+
static: false
|
|
1333
|
+
};
|
|
1334
|
+
methods.unshift(constructor);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
var body = constructor.value.body.body;
|
|
1338
|
+
var insertAt = 0;
|
|
1339
|
+
if (node.superClass) {
|
|
1340
|
+
var superIndex = body.findIndex(stmt => stmt.type == "ExpressionStatement"
|
|
1341
|
+
&& stmt.expression.type == "CallExpression"
|
|
1342
|
+
&& stmt.expression.callee.type == "Super");
|
|
1343
|
+
insertAt = superIndex == -1 ? 0 : superIndex + 1;
|
|
1344
|
+
}
|
|
1345
|
+
body.splice.apply(body, [insertAt, 0].concat(instanceInitializers));
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
node.body.body = methods;
|
|
1349
|
+
|
|
1350
|
+
var privateDeclarations = Object.keys(privateStores).map(name => {
|
|
1351
|
+
return {
|
|
1352
|
+
type: "VariableDeclaration",
|
|
1353
|
+
kind: "var",
|
|
1354
|
+
declarations: [
|
|
1355
|
+
{
|
|
1356
|
+
type: "VariableDeclarator",
|
|
1357
|
+
id: { type: "Identifier", name: privateStores[name] },
|
|
1358
|
+
init: {
|
|
1359
|
+
type: "NewExpression",
|
|
1360
|
+
callee: { type: "Identifier", name: "WeakMap" },
|
|
1361
|
+
arguments: []
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
]
|
|
1365
|
+
};
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
if (privateDeclarations.length == 0 && staticAssignments.length == 0) {
|
|
1369
|
+
return node;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
return {
|
|
1373
|
+
type: "BlockStatement",
|
|
1374
|
+
body: privateDeclarations.concat([node]).concat(staticAssignments)
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
lowerPrivateMembers (node, privateStores) {
|
|
1379
|
+
traverser.traverse(node, [], (child, stack) => {
|
|
1380
|
+
var parentFrame = stack[1];
|
|
1381
|
+
if (child.type == "MemberExpression"
|
|
1382
|
+
&& parentFrame
|
|
1383
|
+
&& parentFrame.node.type == "AssignmentExpression"
|
|
1384
|
+
&& parentFrame.key == "left") {
|
|
1385
|
+
return child;
|
|
1386
|
+
}
|
|
1387
|
+
if (child.type == "AssignmentExpression"
|
|
1388
|
+
&& child.left.type == "MemberExpression"
|
|
1389
|
+
&& child.left.property.type == "PrivateIdentifier"
|
|
1390
|
+
&& privateStores[child.left.property.name]) {
|
|
1391
|
+
return {
|
|
1392
|
+
type: "CallExpression",
|
|
1393
|
+
callee: {
|
|
1394
|
+
type: "MemberExpression",
|
|
1395
|
+
object: { type: "Identifier", name: privateStores[child.left.property.name] },
|
|
1396
|
+
property: { type: "Identifier", name: "set" },
|
|
1397
|
+
computed: false
|
|
1398
|
+
},
|
|
1399
|
+
arguments: [
|
|
1400
|
+
child.left.object,
|
|
1401
|
+
child.right
|
|
1402
|
+
]
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
if (child.type == "MemberExpression"
|
|
1406
|
+
&& child.property.type == "PrivateIdentifier"
|
|
1407
|
+
&& privateStores[child.property.name]) {
|
|
1408
|
+
return weakMapGetExpression(privateStores[child.property.name], child.object);
|
|
1409
|
+
}
|
|
1410
|
+
return child;
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
|
|
490
1414
|
};
|