@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.
@@ -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.type == "ReturnStatement") {
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
  };