js-confuser 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +215 -170
  3. package/dist/constants.js +6 -2
  4. package/dist/obfuscator.js +0 -6
  5. package/dist/options.js +4 -4
  6. package/dist/presets.js +6 -7
  7. package/dist/templates/crash.js +2 -2
  8. package/dist/templates/functionLength.js +16 -0
  9. package/dist/transforms/dispatcher.js +4 -1
  10. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +89 -58
  11. package/dist/transforms/flatten.js +224 -147
  12. package/dist/transforms/identifier/movedDeclarations.js +38 -85
  13. package/dist/transforms/identifier/renameVariables.js +94 -41
  14. package/dist/transforms/lock/lock.js +0 -37
  15. package/dist/transforms/minify.js +2 -2
  16. package/dist/transforms/rgf.js +139 -246
  17. package/dist/transforms/stack.js +42 -1
  18. package/dist/transforms/transform.js +1 -1
  19. package/dist/util/gen.js +2 -1
  20. package/dist/util/identifiers.js +37 -3
  21. package/dist/util/insert.js +24 -3
  22. package/docs/ControlFlowFlattening.md +595 -0
  23. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  24. package/{Integrity.md → docs/Integrity.md} +2 -2
  25. package/docs/RGF.md +419 -0
  26. package/package.json +1 -1
  27. package/src/constants.ts +3 -0
  28. package/src/obfuscator.ts +0 -4
  29. package/src/options.ts +9 -86
  30. package/src/presets.ts +6 -7
  31. package/src/templates/crash.ts +10 -10
  32. package/src/templates/functionLength.ts +14 -0
  33. package/src/transforms/dispatcher.ts +5 -1
  34. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +130 -129
  35. package/src/transforms/flatten.ts +357 -290
  36. package/src/transforms/identifier/movedDeclarations.ts +50 -96
  37. package/src/transforms/identifier/renameVariables.ts +120 -56
  38. package/src/transforms/lock/lock.ts +1 -42
  39. package/src/transforms/minify.ts +11 -2
  40. package/src/transforms/rgf.ts +214 -404
  41. package/src/transforms/stack.ts +62 -0
  42. package/src/transforms/transform.ts +6 -2
  43. package/src/util/gen.ts +7 -2
  44. package/src/util/identifiers.ts +43 -2
  45. package/src/util/insert.ts +26 -2
  46. package/test/code/ES6.src.js +24 -0
  47. package/test/transforms/flatten.test.ts +352 -88
  48. package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
  49. package/test/transforms/identifier/renameVariables.test.ts +37 -0
  50. package/test/transforms/lock/lock.test.ts +1 -48
  51. package/test/transforms/minify.test.ts +19 -0
  52. package/test/transforms/rgf.test.ts +262 -353
  53. package/test/transforms/stack.test.ts +52 -0
  54. package/test/util/identifiers.test.ts +113 -1
  55. package/test/util/insert.test.ts +57 -3
  56. package/src/transforms/eval.ts +0 -89
  57. package/src/transforms/identifier/nameRecycling.ts +0 -280
  58. package/test/transforms/eval.test.ts +0 -131
  59. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
@@ -11,7 +11,7 @@ var _constants = require("../constants");
11
11
 
12
12
  var _order = require("../order");
13
13
 
14
- var _traverse = _interopRequireWildcard(require("../traverse"));
14
+ var _traverse = require("../traverse");
15
15
 
16
16
  var _gen = require("../util/gen");
17
17
 
@@ -23,71 +23,87 @@ var _random = require("../util/random");
23
23
 
24
24
  var _transform = _interopRequireDefault(require("./transform"));
25
25
 
26
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
-
28
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
26
+ var _functionLength = require("../templates/functionLength");
29
27
 
30
- function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
28
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
31
29
 
32
30
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
33
31
 
34
32
  /**
35
- * Brings every function to the global level.
33
+ * Flatten takes functions and isolates them from their original scope, and brings it to the top level of the program.
36
34
  *
37
- * Functions take parameters, input, have a return value and return modified changes to the scoped variables.
35
+ * An additional `flatObject` parameter is passed in, giving access to the original scoped variables.
36
+ *
37
+ * The `flatObject` uses `get` and `set` properties to allow easy an AST transformation:
38
38
  *
39
39
  * ```js
40
- * function topLevel(ref1, ref2, refN, param1, param2, paramN){
41
- * return [ref1, ref2, refN, returnValue];
40
+ * // Input
41
+ * function myFunction(myParam){
42
+ * modified = true;
43
+ * if(reference) {
44
+ *
45
+ * }
46
+ * ...
47
+ * console.log(myParam);
48
+ * }
49
+ *
50
+ * // Output
51
+ * function myFunction_flat([myParam], flatObject){
52
+ * flatObject["set_modified"] = true;
53
+ * if(flatObject["get_reference"]) {
54
+ *
55
+ * }
56
+ * ...
57
+ * console.log(myParam)
58
+ * }
59
+ *
60
+ * function myFunction(){
61
+ * var flatObject = {
62
+ * set set_modified(v) { modified = v }
63
+ * get get_reference() { return reference }
64
+ * }
65
+ * return myFunction_flat([...arguments], flatObject)
42
66
  * }
43
67
  * ```
44
68
  *
45
69
  * Flatten is used to make functions eligible for the RGF transformation.
70
+ *
71
+ * - `myFunction_flat` is now eligible because it does not rely on outside scoped variables
46
72
  */
47
73
  class Flatten extends _transform.default {
74
+ // Array of FunctionDeclaration nodes
48
75
  constructor(o) {
49
76
  super(o, _order.ObfuscateOrder.Flatten);
50
77
 
78
+ _defineProperty(this, "isDebug", false);
79
+
51
80
  _defineProperty(this, "definedNames", void 0);
52
81
 
53
82
  _defineProperty(this, "flattenedFns", void 0);
54
83
 
55
84
  _defineProperty(this, "gen", void 0);
56
85
 
86
+ _defineProperty(this, "functionLengthName", void 0);
87
+
57
88
  this.definedNames = new Map();
58
89
  this.flattenedFns = [];
59
- this.gen = this.getGenerator();
90
+ this.gen = this.getGenerator("mangled");
91
+
92
+ if (this.isDebug) {
93
+ console.warn("Flatten debug mode");
94
+ }
60
95
  }
61
96
 
62
97
  apply(tree) {
63
- (0, _traverse.default)(tree, (o, p) => {
64
- if (o.type == "Identifier" && !_constants.reservedIdentifiers.has(o.name) && !this.options.globalVariables.has(o.name)) {
65
- var info = (0, _identifiers.getIdentifierInfo)(o, p);
66
-
67
- if (info.spec.isReferenced) {
68
- if (info.spec.isDefined) {
69
- var c = (0, _insert.getVarContext)(o, p);
70
-
71
- if (c) {
72
- if (!this.definedNames.has(c)) {
73
- this.definedNames.set(c, new Set([o.name]));
74
- } else {
75
- this.definedNames.get(c).add(o.name);
76
- }
77
- }
78
- }
79
- }
80
- }
81
- });
82
98
  super.apply(tree);
83
99
 
84
100
  if (this.flattenedFns.length) {
85
- (0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)(this.flattenedFns));
101
+ (0, _insert.prepend)(tree, ...this.flattenedFns);
86
102
  }
87
103
  }
88
104
 
89
105
  match(object, parents) {
90
- return (object.type == "FunctionDeclaration" || object.type === "FunctionExpression") && object.body.type == "BlockStatement" && !object.generator && !object.params.find(x => x.type !== "Identifier");
106
+ return (object.type == "FunctionDeclaration" || object.type === "FunctionExpression") && object.body.type == "BlockStatement" && !object.$requiresEval && !object.generator && !object.params.find(x => x.type !== "Identifier");
91
107
  }
92
108
 
93
109
  transform(object, parents) {
@@ -101,7 +117,7 @@ class Flatten extends _transform.default {
101
117
  } // Don't change getter/setter methods
102
118
 
103
119
 
104
- if (parents[0].type === "Property" && parents[0].value === object && parents[0].kind !== "init") {
120
+ if (parents[0].type === "Property" && parents[0].value === object && (parents[0].kind !== "init" || parents[0].method)) {
105
121
  return;
106
122
  }
107
123
  }
@@ -111,76 +127,54 @@ class Flatten extends _transform.default {
111
127
  var currentFnName = object.type === "FunctionDeclaration" ? (_object$id = object.id) === null || _object$id === void 0 ? void 0 : _object$id.name : ((_parents$ = parents[0]) === null || _parents$ === void 0 ? void 0 : _parents$.type) === "VariableDeclarator" && ((_parents$0$id = parents[0].id) === null || _parents$0$id === void 0 ? void 0 : _parents$0$id.type) === "Identifier" && ((_parents$0$id2 = parents[0].id) === null || _parents$0$id2 === void 0 ? void 0 : _parents$0$id2.name);
112
128
 
113
129
  if (((_parents$2 = parents[0]) === null || _parents$2 === void 0 ? void 0 : _parents$2.type) === "Property" && (_parents$3 = parents[0]) !== null && _parents$3 !== void 0 && _parents$3.key) {
114
- var _parents$4, _parents$4$key, _parents$5, _parents$5$key;
130
+ var _parents$4, _parents$4$key;
115
131
 
116
- currentFnName = currentFnName || String(((_parents$4 = parents[0]) === null || _parents$4 === void 0 ? void 0 : (_parents$4$key = _parents$4.key) === null || _parents$4$key === void 0 ? void 0 : _parents$4$key.name) || ((_parents$5 = parents[0]) === null || _parents$5 === void 0 ? void 0 : (_parents$5$key = _parents$5.key) === null || _parents$5$key === void 0 ? void 0 : _parents$5$key.value));
132
+ currentFnName = currentFnName || String((_parents$4 = parents[0]) === null || _parents$4 === void 0 ? void 0 : (_parents$4$key = _parents$4.key) === null || _parents$4$key === void 0 ? void 0 : _parents$4$key.name);
117
133
  }
118
134
 
119
135
  if (!currentFnName) currentFnName = "unnamed";
120
- var defined = new Set();
121
- var references = new Set();
122
- var modified = new Set();
136
+ var definedMap = new Map();
123
137
  var illegal = new Set();
124
138
  var isIllegal = false;
125
- var definedAbove = new Set(this.options.globalVariables);
126
- parents.forEach(x => {
127
- var set = this.definedNames.get(x);
128
-
129
- if (set) {
130
- set.forEach(name => definedAbove.add(name));
131
- }
132
- });
139
+ var identifierNodes = [];
133
140
  (0, _traverse.walk)(object, parents, (o, p) => {
134
- if (object.id && o === object.id) {
135
- return;
141
+ if (o.type === "Identifier" && o.name === "arguments" || o.type === "UnaryExpression" && o.operator === "delete" || o.type == "ThisExpression" || o.type == "Super" || o.type == "MetaProperty") {
142
+ isIllegal = true;
143
+ return "EXIT";
136
144
  }
137
145
 
138
- if (o.type == "Identifier" && !this.options.globalVariables.has(o.name) && !_constants.reservedIdentifiers.has(o.name)) {
146
+ if (o.type == "Identifier" && o !== object.id && !this.options.globalVariables.has(o.name) && !_constants.reservedIdentifiers.has(o.name)) {
139
147
  var info = (0, _identifiers.getIdentifierInfo)(o, p);
140
148
 
141
149
  if (!info.spec.isReferenced) {
142
150
  return;
143
151
  }
144
152
 
145
- if (o.hidden) {
153
+ if (info.spec.isExported || o.name.startsWith(_constants.noRenameVariablePrefix)) {
146
154
  illegal.add(o.name);
147
- } else if (info.spec.isDefined) {
148
- defined.add(o.name);
149
- } else if (info.spec.isModified) {
150
- modified.add(o.name);
151
- } else {
152
- references.add(o.name);
155
+ return;
153
156
  }
154
- }
155
157
 
156
- if (o.type == "TryStatement") {
157
- isIllegal = true;
158
- return "EXIT";
159
- }
158
+ if (info.spec.isDefined) {
159
+ var definingContext = (0, _insert.getDefiningContext)(o, p);
160
160
 
161
- if (o.type == "Identifier") {
162
- if (o.name == "arguments") {
163
- isIllegal = true;
164
- return "EXIT";
165
- }
166
- }
161
+ if (!definedMap.has(definingContext)) {
162
+ definedMap.set(definingContext, new Set([o.name]));
163
+ } else {
164
+ definedMap.get(definingContext).add(o.name);
165
+ }
167
166
 
168
- if (o.type == "ThisExpression") {
169
- isIllegal = true;
170
- return "EXIT";
171
- }
167
+ return;
168
+ }
172
169
 
173
- if (o.type == "Super") {
174
- isIllegal = true;
175
- return "EXIT";
176
- }
170
+ var isDefined = p.find(x => definedMap.has(x) && definedMap.get(x).has(o.name));
177
171
 
178
- if (o.type == "MetaProperty") {
179
- isIllegal = true;
180
- return "EXIT";
172
+ if (!isDefined) {
173
+ identifierNodes.push([o, p, info]);
174
+ }
181
175
  }
182
176
 
183
- if (o.type == "VariableDeclaration" && o.kind !== "var") {
177
+ if (o.type == "TryStatement") {
184
178
  isIllegal = true;
185
179
  return "EXIT";
186
180
  }
@@ -194,86 +188,169 @@ class Flatten extends _transform.default {
194
188
  return;
195
189
  }
196
190
 
197
- defined.forEach(name => {
198
- references.delete(name);
199
- modified.delete(name);
200
- }); // console.log(object.id.name, illegal, references);
191
+ var newFnName = this.getPlaceholder() + "_flat_" + currentFnName;
192
+ var flatObjectName = this.getPlaceholder() + "_flat_object";
201
193
 
202
- var input = Array.from(new Set([...modified, ...references]));
194
+ const getFlatObjectMember = propertyName => {
195
+ return (0, _gen.MemberExpression)((0, _gen.Identifier)(flatObjectName), (0, _gen.Literal)(propertyName), true);
196
+ };
203
197
 
204
- if (Array.from(input).find(x => !definedAbove.has(x))) {
205
- return;
206
- }
198
+ var getterPropNames = Object.create(null);
199
+ var setterPropNames = Object.create(null);
200
+ var typeofPropNames = Object.create(null);
201
+ var callPropNames = Object.create(null);
207
202
 
208
- var output = Array.from(modified);
209
- var newName = this.getPlaceholder() + "_flat_" + currentFnName;
210
- var resultName = this.getPlaceholder();
211
- var propName = this.gen.generate();
212
- var newOutputNames = Object.create(null);
213
- output.forEach(name => {
214
- newOutputNames[name] = this.gen.generate();
215
- });
216
- var returnOutputName = this.gen.generate();
217
- (0, _insert.getBlockBody)(object.body).push((0, _gen.ReturnStatement)());
218
- (0, _traverse.walk)(object.body, [object, ...parents], (o, p) => {
219
- // Change return statements from
220
- // return (argument)
221
- // to
222
- // return [ [modifiedRefs], ]
223
- if (o.type == "ReturnStatement" && (0, _insert.getVarContext)(o, p) === object) {
224
- return () => {
225
- var returnObject = (0, _gen.ObjectExpression)(output.map(outputName => (0, _gen.Property)((0, _gen.Literal)(newOutputNames[outputName]), (0, _gen.Identifier)(outputName), true)));
226
-
227
- if (o.argument && !(o.argument.type == "Identifier" && o.argument.name == "undefined")) {
228
- // FIX: The return argument must be executed first so it must use 'unshift'
229
- returnObject.properties.unshift((0, _gen.Property)((0, _gen.Literal)(returnOutputName), (0, _insert.clone)(o.argument), true));
230
- }
203
+ for (var [o, p, info] of identifierNodes) {
204
+ var identifierName = o.name;
205
+ if (p.find(x => definedMap.has(x) && definedMap.get(x).has(identifierName))) continue;
206
+ (0, _assert.ok)(!info.spec.isDefined);
207
+ var type = info.spec.isModified ? "setter" : "getter";
208
+
209
+ switch (type) {
210
+ case "setter":
211
+ var setterPropName = setterPropNames[identifierName];
212
+
213
+ if (typeof setterPropName === "undefined") {
214
+ // No getter function made yet, make it (Try to re-use getter name if available)
215
+ setterPropName = getterPropNames[identifierName] || (this.isDebug ? "set_" + identifierName : this.gen.generate());
216
+ setterPropNames[identifierName] = setterPropName;
217
+ } // If an update expression, ensure a getter function is also available. Ex: a++
218
+
219
+
220
+ if (p[0].type === "UpdateExpression") {
221
+ getterPropNames[identifierName] = setterPropName;
222
+ } else {
223
+ // If assignment on member expression, ensure a getter function is also available: Ex. myObject.property = ...
224
+ var assignmentIndex = p.findIndex(x => x.type === "AssignmentExpression");
225
+
226
+ if (assignmentIndex !== -1 && p[assignmentIndex].left.type !== "Identifier") {
227
+ getterPropNames[identifierName] = setterPropName;
228
+ }
229
+ } // calls flatObject.set_identifier_value(newValue)
230
+
231
+
232
+ this.replace(o, getFlatObjectMember(setterPropName));
233
+ break;
234
+
235
+ case "getter":
236
+ var getterPropName = getterPropNames[identifierName];
237
+
238
+ if (typeof getterPropName === "undefined") {
239
+ // No getter function made yet, make it (Try to re-use setter name if available)
240
+ getterPropName = setterPropNames[identifierName] || (this.isDebug ? "get_" + identifierName : this.gen.generate());
241
+ getterPropNames[identifierName] = getterPropName;
242
+ } // Typeof expression check
243
+
244
+
245
+ if (p[0].type === "UnaryExpression" && p[0].operator === "typeof" && p[0].argument === o) {
246
+ var typeofPropName = typeofPropNames[identifierName];
247
+
248
+ if (typeof typeofPropName === "undefined") {
249
+ // No typeof getter function made yet, make it (Don't re-use getter/setter names)
250
+ typeofPropName = this.isDebug ? "get_typeof_" + identifierName : this.gen.generate();
251
+ typeofPropNames[identifierName] = typeofPropName;
252
+ } // Replace the entire unary expression not just the identifier node
253
+ // calls flatObject.get_typeof_identifier()
254
+
255
+
256
+ this.replace(p[0], getFlatObjectMember(typeofPropName));
257
+ break;
258
+ } // Bound call-expression check
259
+
260
+
261
+ if (p[0].type === "CallExpression" && p[0].callee === o) {
262
+ var callPropName = callPropNames[identifierName];
231
263
 
232
- o.argument = (0, _gen.AssignmentExpression)("=", (0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Identifier)(propName), false), returnObject);
233
- };
264
+ if (typeof callPropName === "undefined") {
265
+ callPropName = this.isDebug ? "call_" + identifierName : this.gen.generate();
266
+ callPropNames[identifierName] = callPropName;
267
+ } // Replace the entire call expression not just the identifier node
268
+ // calls flatObject.call_identifier(...arguments)
269
+
270
+
271
+ this.replace(p[0], (0, _gen.CallExpression)(getFlatObjectMember(callPropName), p[0].arguments));
272
+ break;
273
+ } // calls flatObject.get_identifier_value()
274
+
275
+
276
+ this.replace(o, getFlatObjectMember(getterPropName));
277
+ break;
234
278
  }
235
- });
279
+ } // Create the getter and setter functions
280
+
281
+
282
+ var flatObjectProperties = []; // Getter functions
283
+
284
+ for (var identifierName in getterPropNames) {
285
+ var getterPropName = getterPropNames[identifierName];
286
+ flatObjectProperties.push((0, _gen.Property)((0, _gen.Literal)(getterPropName), (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.Identifier)(identifierName))]), true, "get"));
287
+ } // Get typeof functions
288
+
289
+
290
+ for (var identifierName in typeofPropNames) {
291
+ var typeofPropName = typeofPropNames[identifierName];
292
+ flatObjectProperties.push((0, _gen.Property)((0, _gen.Literal)(typeofPropName), (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.UnaryExpression)("typeof", (0, _gen.Identifier)(identifierName)))]), true, "get"));
293
+ } // Call functions
294
+
295
+
296
+ for (var identifierName in callPropNames) {
297
+ var callPropName = callPropNames[identifierName];
298
+ var argumentsName = this.getPlaceholder();
299
+ flatObjectProperties.push((0, _gen.Property)((0, _gen.Literal)(callPropName), (0, _gen.FunctionExpression)([(0, _gen.RestElement)((0, _gen.Identifier)(argumentsName))], [(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.Identifier)(identifierName), [(0, _gen.SpreadElement)((0, _gen.Identifier)(argumentsName))]))]), true));
300
+ } // Setter functions
301
+
302
+
303
+ for (var identifierName in setterPropNames) {
304
+ var setterPropName = setterPropNames[identifierName];
305
+ var newValueParameterName = this.getPlaceholder();
306
+ flatObjectProperties.push((0, _gen.Property)((0, _gen.Literal)(setterPropName), (0, _gen.FunctionExpression)([(0, _gen.Identifier)(newValueParameterName)], [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(identifierName), (0, _gen.Identifier)(newValueParameterName)))]), true, "set"));
307
+ }
308
+
309
+ if (!this.isDebug) {
310
+ (0, _random.shuffle)(flatObjectProperties);
311
+ }
312
+
236
313
  var newBody = (0, _insert.getBlockBody)(object.body); // Remove 'use strict' directive
237
314
 
238
315
  if (newBody.length > 0 && newBody[0].directive) {
239
316
  newBody.shift();
240
317
  }
241
318
 
242
- var newFunctionExpression = (0, _gen.FunctionExpression)([(0, _gen.ArrayPattern)(input.map(name => (0, _gen.Identifier)(name))), (0, _gen.ArrayPattern)((0, _insert.clone)(object.params)), (0, _gen.Identifier)(resultName)], newBody);
243
- newFunctionExpression.async = !!object.async;
244
- newFunctionExpression.generator = !!object.generator;
245
- this.flattenedFns.push((0, _gen.VariableDeclarator)(newName, newFunctionExpression));
246
- var newParamNames = object.params.map(() => this.getPlaceholder()); // result.pop()
247
-
248
- var getOutputMemberExpression = outputName => (0, _gen.MemberExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(propName), true), (0, _gen.Literal)(outputName), true); // newFn.call([...refs], ...arguments, resultObject)
249
-
250
-
251
- var callExpression = (0, _gen.CallExpression)((0, _gen.Identifier)(newName), [(0, _gen.ArrayExpression)(input.map(name => (0, _gen.Identifier)(name))), (0, _gen.ArrayExpression)(newParamNames.map(name => (0, _gen.Identifier)(name))), (0, _gen.Identifier)(resultName)]);
252
- var newObjectBody = [// var resultObject = {};
253
- (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(resultName, (0, _gen.ObjectExpression)([]))]), (0, _gen.ExpressionStatement)(newFunctionExpression.async ? (0, _gen.AwaitExpression)(callExpression) : callExpression)];
254
- var outputReversed = [...output].reverse(); // realVar
255
-
256
- outputReversed.forEach(outputName => {
257
- newObjectBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(outputName), getOutputMemberExpression(newOutputNames[outputName]))));
258
- }); // DECOY STATEMENTS
259
-
260
- var decoyKey = this.gen.generate();
261
- var decoyNodes = [// if (result.random) throw result.prop.random
262
- (0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ThrowStatement)((0, _gen.NewExpression)((0, _gen.Identifier)("Error"), [getOutputMemberExpression(this.gen.generate())]))]), // if (result.random) return true;
263
- (0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ReturnStatement)((0, _gen.Literal)(true))]), // if (result.random) return result;
264
- (0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ReturnStatement)((0, _gen.Identifier)(resultName))]), // if (result.random) return result.random;
265
- (0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(decoyKey), true), [(0, _gen.ReturnStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(decoyKey), true))]), // if(result.random1) return result.random2;
266
- (0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ReturnStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true))]), // if(result.random) return flatFn;
267
- (0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ReturnStatement)((0, _gen.Identifier)(newName))]), // if(result.random) flatFn = undefined;
268
- (0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(newName), (0, _gen.Identifier)("undefined")))]), // if(!result) return;
269
- (0, _gen.IfStatement)((0, _gen.UnaryExpression)("!", (0, _gen.Identifier)(resultName)), [(0, _gen.ReturnStatement)()])].filter(() => (0, _random.chance)(25)); // if (result.output) return result.output.returnValue;
270
- // this is the real return statement, it is always added
271
-
272
- decoyNodes.push((0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(propName), true), [(0, _gen.ReturnStatement)(getOutputMemberExpression(returnOutputName))]));
273
- (0, _random.shuffle)(decoyNodes);
274
- newObjectBody.push(...decoyNodes);
275
- object.body = (0, _gen.BlockStatement)(newObjectBody);
276
- object.params = newParamNames.map(name => (0, _gen.Identifier)(name));
319
+ var newFunctionDeclaration = (0, _gen.FunctionDeclaration)(newFnName, [(0, _gen.ArrayPattern)((0, _insert.clone)(object.params)), (0, _gen.Identifier)(flatObjectName)], newBody);
320
+ newFunctionDeclaration.async = !!object.async;
321
+ newFunctionDeclaration.generator = false;
322
+ this.flattenedFns.push(newFunctionDeclaration);
323
+ var argumentsName = this.getPlaceholder(); // newFn.call([...arguments], flatObject)
324
+
325
+ var callExpression = (0, _gen.CallExpression)((0, _gen.Identifier)(newFnName), [(0, _gen.Identifier)(argumentsName), (0, _gen.Identifier)(flatObjectName)]);
326
+ var newObjectBody = [// var flatObject = { get(), set() };
327
+ (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(flatObjectName, (0, _gen.ObjectExpression)(flatObjectProperties))]), (0, _gen.ReturnStatement)(newFunctionDeclaration.async ? (0, _gen.AwaitExpression)(callExpression) : callExpression)];
328
+ object.body = (0, _gen.BlockStatement)(newObjectBody); // Preserve function.length property
329
+
330
+ var originalFunctionLength = (0, _insert.computeFunctionLength)(object.params);
331
+ object.params = [(0, _gen.SpreadElement)((0, _gen.Identifier)(argumentsName))];
332
+
333
+ if (originalFunctionLength !== 0) {
334
+ if (!this.functionLengthName) {
335
+ this.functionLengthName = this.getPlaceholder();
336
+ (0, _insert.prepend)(parents[parents.length - 1] || object, _functionLength.FunctionLengthTemplate.single({
337
+ name: this.functionLengthName
338
+ }));
339
+ }
340
+
341
+ if (object.type === "FunctionDeclaration") {
342
+ var body = parents[0];
343
+
344
+ if (Array.isArray(body)) {
345
+ var index = body.indexOf(object);
346
+ body.splice(index + 1, 0, (0, _gen.ExpressionStatement)((0, _gen.CallExpression)((0, _gen.Identifier)(this.functionLengthName), [(0, _gen.Identifier)(object.id.name), (0, _gen.Literal)(originalFunctionLength)])));
347
+ }
348
+ } else {
349
+ (0, _assert.ok)(object.type === "FunctionExpression");
350
+ this.replace(object, (0, _gen.CallExpression)((0, _gen.Identifier)(this.functionLengthName), [{ ...object
351
+ }, (0, _gen.Literal)(originalFunctionLength)]));
352
+ }
353
+ }
277
354
  };
278
355
  }
279
356
 
@@ -17,10 +17,6 @@ var _assert = require("assert");
17
17
 
18
18
  var _order = require("../../order");
19
19
 
20
- var _identifiers = require("../../util/identifiers");
21
-
22
- var _scope = require("../../util/scope");
23
-
24
20
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
21
 
26
22
  /**
@@ -32,94 +28,51 @@ class MovedDeclarations extends _transform.default {
32
28
  }
33
29
 
34
30
  match(object, parents) {
35
- return (0, _scope.isLexicalScope)(object);
31
+ return object.type === "VariableDeclaration" && object.kind === "var" && object.declarations.length === 1 && object.declarations[0].id.type === "Identifier";
36
32
  }
37
33
 
38
34
  transform(object, parents) {
39
35
  return () => {
40
- var body = (0, _traverse.isBlock)(object) ? object.body : object.consequent;
41
- (0, _assert.ok)(Array.isArray(body));
42
- var illegal = new Set();
43
- var defined = new Set();
44
- var variableDeclarations = Object.create(null);
45
- (0, _traverse.walk)(object, parents, (o, p) => {
46
- if (o.type == "Identifier") {
47
- if (o.hidden || (0, _scope.getLexicalScope)(o, p) !== object) {
48
- illegal.add(o.name);
49
- } else {
50
- var info = (0, _identifiers.getIdentifierInfo)(o, p);
51
-
52
- if (!info.spec.isReferenced) {
53
- return;
54
- }
55
-
56
- if (info.spec.isDefined) {
57
- if (info.isFunctionDeclaration || info.isClassDeclaration) {
58
- illegal.add(o.name);
59
- } else {
60
- if (defined.has(o.name)) {
61
- illegal.add(o.name);
62
- } else {
63
- defined.add(o.name);
64
- }
65
- }
66
- }
67
- }
68
- }
36
+ var forInitializeType = (0, _insert.isForInitialize)(object, parents); // Get the block statement or Program node
37
+
38
+ var blockIndex = parents.findIndex(x => (0, _traverse.isBlock)(x));
39
+ var block = parents[blockIndex];
40
+ var body = block.body;
41
+ var bodyObject = parents[blockIndex - 2] || object; // Make sure in the block statement, and not already at the top of it
42
+
43
+ var index = body.indexOf(bodyObject);
44
+ if (index === -1 || index === 0) return;
45
+ var topVariableDeclaration;
46
+
47
+ if (body[0].type === "VariableDeclaration" && body[0].kind === "var") {
48
+ topVariableDeclaration = body[0];
49
+ } else {
50
+ topVariableDeclaration = {
51
+ type: "VariableDeclaration",
52
+ declarations: [],
53
+ kind: "var"
54
+ };
55
+ (0, _insert.prepend)(block, topVariableDeclaration);
56
+ }
69
57
 
70
- if (o.type == "VariableDeclaration") {
71
- return () => {
72
- if (o.declarations.length === 1 && o.declarations[0].id.type === "Identifier") {
73
- var name = o.declarations[0].id.name; // Check if duplicate
74
-
75
- if (variableDeclarations[name] || o.kind !== "var") {
76
- illegal.add(name);
77
- return;
78
- } // Check if already at top
79
-
80
-
81
- if (body[0] === o) {
82
- illegal.add(name);
83
- return;
84
- }
85
-
86
- var replace = (0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(name), o.declarations[0].init || (0, _gen.Identifier)("undefined"));
87
- var forType = (0, _insert.isForInitialize)(o, p);
88
-
89
- if (forType === "left-hand") {
90
- replace = (0, _gen.Identifier)(name);
91
- } else if (!forType) {
92
- replace = (0, _gen.ExpressionStatement)(replace);
93
- }
94
-
95
- variableDeclarations[name] = {
96
- location: [o, p],
97
- replace: replace
98
- };
99
- }
100
- };
58
+ var varName = object.declarations[0].id.name;
59
+ (0, _assert.ok)(typeof varName === "string"); // Add `var x` at the top of the block
60
+
61
+ topVariableDeclaration.declarations.push((0, _gen.VariableDeclarator)((0, _gen.Identifier)(varName)));
62
+ var assignmentExpression = (0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(varName), object.declarations[0].init || (0, _gen.Identifier)(varName));
63
+
64
+ if (forInitializeType) {
65
+ if (forInitializeType === "initializer") {
66
+ // Replace `for (var i = 0...)` to `for (i = 0...)`
67
+ this.replace(object, assignmentExpression);
68
+ } else if (forInitializeType === "left-hand") {
69
+ // Replace `for (var k in...)` to `for (k in ...)`
70
+ this.replace(object, (0, _gen.Identifier)(varName));
101
71
  }
102
- });
103
- illegal.forEach(name => {
104
- delete variableDeclarations[name];
105
- });
106
- var movingNames = Object.keys(variableDeclarations);
107
-
108
- if (movingNames.length === 0) {
109
- return;
72
+ } else {
73
+ // Replace `var x = value` to `x = value`
74
+ this.replace(object, (0, _gen.ExpressionStatement)(assignmentExpression));
110
75
  }
111
-
112
- var variableDeclaration = (0, _gen.VariableDeclaration)(movingNames.map(name => {
113
- return (0, _gen.VariableDeclarator)(name);
114
- }));
115
- (0, _insert.prepend)(object, variableDeclaration);
116
- movingNames.forEach(name => {
117
- var {
118
- location,
119
- replace
120
- } = variableDeclarations[name];
121
- this.replace(location[0], replace);
122
- });
123
76
  };
124
77
  }
125
78