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
@@ -15,9 +15,7 @@ var _order = require("../order");
15
15
 
16
16
  var _probability = require("../probability");
17
17
 
18
- var _template = _interopRequireDefault(require("../templates/template"));
19
-
20
- var _traverse = _interopRequireWildcard(require("../traverse"));
18
+ var _traverse = require("../traverse");
21
19
 
22
20
  var _gen = require("../util/gen");
23
21
 
@@ -25,16 +23,12 @@ var _identifiers = require("../util/identifiers");
25
23
 
26
24
  var _insert = require("../util/insert");
27
25
 
28
- var _random = require("../util/random");
29
-
30
26
  var _transform = _interopRequireDefault(require("./transform"));
31
27
 
32
- 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); }
33
-
34
- 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; }
35
-
36
28
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
37
29
 
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; }
31
+
38
32
  /**
39
33
  * Converts function to `new Function("..code..")` syntax as an alternative to `eval`. Eval is disabled in many environments.
40
34
  *
@@ -43,285 +37,184 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
43
37
  * Rigorous checks are in place to only include pure functions.
44
38
  *
45
39
  * `flatten` can attempt to make function reference-less. Recommended to have flatten enabled with RGF.
46
- *
47
- * | Mode | Description |
48
- * | --- | --- |
49
- * | `"all"` | Applies to all scopes |
50
- * | `true` | Applies to the top level only |
51
- * | `false` | Feature disabled |
52
40
  */
53
41
  class RGF extends _transform.default {
42
+ // Array of all the `new Function` calls
43
+ // The name of the array holding all the `new Function` expressions
54
44
  constructor(o) {
55
45
  super(o, _order.ObfuscateOrder.RGF);
46
+
47
+ _defineProperty(this, "arrayExpressionElements", void 0);
48
+
49
+ _defineProperty(this, "arrayExpressionName", void 0);
50
+
51
+ this.arrayExpressionName = this.getPlaceholder() + "_rgf";
52
+ this.arrayExpressionElements = [];
53
+ }
54
+
55
+ apply(tree) {
56
+ super.apply(tree); // Only add the array if there were converted functions
57
+
58
+ if (this.arrayExpressionElements.length > 0) {
59
+ (0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)((0, _gen.Identifier)(this.arrayExpressionName), (0, _gen.ArrayExpression)(this.arrayExpressionElements))));
60
+ }
56
61
  }
57
62
 
58
63
  match(object, parents) {
59
- return (0, _insert.isVarContext)(object) && object.type !== "ArrowFunctionExpression";
64
+ return (object.type === "FunctionDeclaration" || object.type === "FunctionExpression") && // Does not apply to Arrow functions
65
+ !object.async && // Does not apply to async/generator functions
66
+ !object.generator;
60
67
  }
61
68
 
62
- transform(contextObject, contextParents) {
63
- return () => {
64
- var isGlobal = contextObject.type == "Program";
65
- var value = (0, _probability.ComputeProbabilityMap)(this.options.rgf, x => x, isGlobal);
69
+ transform(object, parents) {
70
+ var _this$options$lock, _object$id;
66
71
 
67
- if (value !== "all" && !isGlobal) {
72
+ // Discard getter/setter methods
73
+ if (parents[0].type === "Property" && parents[0].value === object) {
74
+ if (parents[0].method || parents[0].kind === "get" || parents[0].kind === "set") {
68
75
  return;
69
76
  }
77
+ } // Discard class methods
70
78
 
71
- var collect = [];
72
- var queue = [];
73
- var names = new Map();
74
- var referenceSignatures = {};
75
- var definingNodes = new Map();
76
- (0, _traverse.walk)(contextObject, contextParents, (object, parents) => {
77
- if (object !== contextObject && (0, _insert.isFunction)(object) && !object.$requiresEval && !object.async && !object.generator && (0, _insert.getVarContext)(parents[0], parents.slice(1)) === contextObject) {
78
- var _this$options$lock;
79
-
80
- // Discard getter/setter methods
81
- if (parents[0].type === "Property" && parents[0].value === object) {
82
- if (parents[0].method || parents[0].kind === "get" || parents[0].kind === "set") {
83
- return;
84
- }
85
- } // Discard class methods
86
79
 
80
+ if (parents[0].type === "MethodDefinition" && parents[0].value === object) {
81
+ return;
82
+ } // Avoid applying to the countermeasures function
87
83
 
88
- if (parents[0].type === "MethodDefinition" && parents[0].value === object) {
89
- return;
90
- } // Avoid applying to the countermeasures function
91
84
 
85
+ if (typeof ((_this$options$lock = this.options.lock) === null || _this$options$lock === void 0 ? void 0 : _this$options$lock.countermeasures) === "string") {
86
+ // function countermeasures(){...}
87
+ if (object.type === "FunctionDeclaration" && object.id.type === "Identifier" && object.id.name === this.options.lock.countermeasures) {
88
+ return;
89
+ } // var countermeasures = function(){...}
92
90
 
93
- if (typeof ((_this$options$lock = this.options.lock) === null || _this$options$lock === void 0 ? void 0 : _this$options$lock.countermeasures) === "string") {
94
- // function countermeasures(){...}
95
- if (object.type === "FunctionDeclaration" && object.id.type === "Identifier" && object.id.name === this.options.lock.countermeasures) {
96
- return;
97
- } // var countermeasures = function(){...}
98
91
 
92
+ if (parents[0].type === "VariableDeclarator" && parents[0].init === object && parents[0].id.type === "Identifier" && parents[0].id.name === this.options.lock.countermeasures) {
93
+ return;
94
+ }
95
+ } // Check user option
99
96
 
100
- if (parents[0].type === "VariableDeclarator" && parents[0].init === object && parents[0].id.type === "Identifier" && parents[0].id.name === this.options.lock.countermeasures) {
101
- return;
102
- }
97
+
98
+ if (!(0, _probability.ComputeProbabilityMap)(this.options.rgf, x => x, object === null || object === void 0 ? void 0 : (_object$id = object.id) === null || _object$id === void 0 ? void 0 : _object$id.name)) return; // Discard functions that use 'eval' function
99
+
100
+ if (object.$requiresEval) return; // Check for 'this', 'arguments' (not allowed!)
101
+
102
+ var isIllegal = false;
103
+ (0, _traverse.walk)(object, parents, (o, p) => {
104
+ if (o.type === "ThisExpression" || o.type === "Super" || o.type === "Identifier" && o.name === "arguments") {
105
+ isIllegal = true;
106
+ return "EXIT";
107
+ }
108
+ });
109
+ if (isIllegal) return;
110
+ return () => {
111
+ // Make sure function is 'reference-less'
112
+ var definedMap = new Map();
113
+ var isReferenceLess = true;
114
+ var identifierPreventingTransformation;
115
+ (0, _traverse.walk)(object, parents, (o, p) => {
116
+ if (o.type === "Identifier" && !_constants.reservedIdentifiers.has(o.name) && !this.options.globalVariables.has(o.name)) {
117
+ var info = (0, _identifiers.getIdentifierInfo)(o, p);
118
+
119
+ if (!info.spec.isReferenced) {
120
+ return;
103
121
  }
104
122
 
105
- var defined = new Set(),
106
- referenced = new Set();
107
- var isBound = false;
108
- /**
109
- * The fnTraverses serves two important purposes
110
- *
111
- * - Identify all the variables referenced and defined here
112
- * - Identify is the 'this' keyword is used anywhere
113
- *
114
- * @param o
115
- * @param p
116
- * @returns
117
- */
118
-
119
- const fnTraverser = (o, p) => {
120
- if (o.type == "Identifier" && !_constants.reservedIdentifiers.has(o.name) && !this.options.globalVariables.has(o.name)) {
121
- var info = (0, _identifiers.getIdentifierInfo)(o, p);
122
-
123
- if (!info.spec.isReferenced) {
124
- return;
125
- }
123
+ if (info.spec.isDefined) {
124
+ // Add to defined map
125
+ var definingContext = (0, _insert.getDefiningContext)(o, p);
126
126
 
127
- if (info.spec.isDefined && (0, _insert.getDefiningContext)(o, p) === object) {
128
- defined.add(o.name);
129
- } else {
130
- referenced.add(o.name);
127
+ if (!definedMap.has(definingContext)) {
128
+ definedMap.set(definingContext, new Set([o.name]));
129
+ } else {
130
+ definedMap.get(definingContext).add(o.name);
131
+ }
132
+ } else {
133
+ // This approach is dirty and does not account for hoisted FunctionDeclarations
134
+ var isDefinedAbove = false;
135
+
136
+ for (var pNode of p) {
137
+ if (definedMap.has(pNode)) {
138
+ if (definedMap.get(pNode).has(o.name)) {
139
+ isDefinedAbove = true;
140
+ break;
141
+ }
131
142
  }
132
143
  }
133
144
 
134
- if (o.type == "ThisExpression" || o.type == "Super") {
135
- isBound = true;
145
+ if (!isDefinedAbove) {
146
+ isReferenceLess = false;
147
+ identifierPreventingTransformation = o.name;
148
+ return "EXIT";
136
149
  }
137
- };
138
-
139
- (0, _traverse.walk)(object.params, [object, ...parents], fnTraverser);
140
- (0, _traverse.walk)(object.body, [object, ...parents], fnTraverser);
141
-
142
- if (!isBound) {
143
- var _object$id;
144
-
145
- defined.forEach(identifier => {
146
- referenced.delete(identifier);
147
- });
148
- object.params.forEach(param => {
149
- referenced.delete(param.name);
150
- });
151
- collect.push({
152
- location: [object, parents],
153
- references: referenced,
154
- name: (_object$id = object.id) === null || _object$id === void 0 ? void 0 : _object$id.name
155
- });
156
150
  }
157
151
  }
158
- });
152
+ }); // This function is not 'reference-less', cannot be RGF'd
153
+
154
+ if (!isReferenceLess) {
155
+ if (object.id) {
156
+ var _object$id2;
157
+
158
+ this.log("".concat(object === null || object === void 0 ? void 0 : (_object$id2 = object.id) === null || _object$id2 === void 0 ? void 0 : _object$id2.name, "() cannot be transformed because of ").concat(identifierPreventingTransformation));
159
+ }
159
160
 
160
- if (!collect.length) {
161
161
  return;
162
- }
162
+ } // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
163
+ // RGF runs early and needs completed code before converting to a string.
164
+ // (^ the variables haven't been renamed yet)
163
165
 
164
- var miss = 0;
165
- var start = collect.length * 2;
166
-
167
- while (true) {
168
- var hit = false;
169
- collect.forEach(_ref => {
170
- let {
171
- name,
172
- references: references1,
173
- location: location1
174
- } = _ref;
175
-
176
- if (!references1.size && name) {
177
- collect.forEach(o => {
178
- if (o.location[0] !== location1[0] && o.references.size && o.references.delete(name)) {
179
- // console.log(collect);
180
- hit = true;
181
- }
182
- });
183
- }
184
- });
185
166
 
186
- if (hit) {
187
- miss = 0;
188
- } else {
189
- miss++;
190
- }
167
+ var obfuscator = new _obfuscator.default({ ...this.options,
168
+ stringEncoding: false,
169
+ compact: true
170
+ });
191
171
 
192
- if (miss > start) {
193
- break;
194
- }
172
+ if (obfuscator.options.lock) {
173
+ delete obfuscator.options.lock.countermeasures;
195
174
  }
196
175
 
197
- queue = [];
198
- collect.forEach(o => {
199
- if (!o.references.size) {
200
- var [object, parents] = o.location;
201
- queue.push([object, parents]);
202
-
203
- if (object.type == "FunctionDeclaration" && typeof object.id.name === "string") {
204
- var index = names.size;
205
- names.set(object.id.name, index);
206
- referenceSignatures[index] = (0, _random.getRandomString)(10);
207
- definingNodes.set(object.id.name, object.id);
208
- }
209
- }
176
+ var transforms = obfuscator.array.filter(x => x.priority > this.priority);
177
+ var embeddedFunctionName = this.getPlaceholder();
178
+ var embeddedFunction = {
179
+ type: "FunctionDeclaration",
180
+ id: (0, _gen.Identifier)(embeddedFunctionName),
181
+ body: (0, _gen.BlockStatement)([...object.body.body]),
182
+ params: object.params,
183
+ async: false,
184
+ generator: false
185
+ };
186
+ var tree = {
187
+ type: "Program",
188
+ body: [embeddedFunction, (0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(embeddedFunctionName), (0, _gen.Literal)("apply"), true), [(0, _gen.ThisExpression)(), (0, _gen.Identifier)("arguments")]))]
189
+ };
190
+ transforms.forEach(transform => {
191
+ transform.apply(tree);
210
192
  });
193
+ var toString = (0, _compiler.compileJsSync)(tree, obfuscator.options); // new Function(code)
211
194
 
212
- if (!queue.length) {
213
- return;
214
- } // An array containing all the function declarations
215
-
216
-
217
- var referenceArray = "_" + (0, _random.getRandomString)(10);
218
- (0, _traverse.walk)(contextObject, contextParents, (o, p) => {
219
- if (o.type == "Identifier" && !_constants.reservedIdentifiers.has(o.name)) {
220
- var index = names.get(o.name);
221
-
222
- if (typeof index === "number") {
223
- var info = (0, _identifiers.getIdentifierInfo)(o, p);
224
-
225
- if (info.spec.isReferenced && !info.spec.isDefined) {
226
- var location = (0, _identifiers.getDefiningIdentifier)(o, p);
227
-
228
- if (location) {
229
- var pointingTo = location[0];
230
- var shouldBe = definingNodes.get(o.name); // console.log(pointingTo, shouldBe);
231
-
232
- if (pointingTo == shouldBe) {
233
- this.log(o.name, "->", "".concat(referenceArray, "[").concat(index, "]"));
234
- var memberExpression = (0, _gen.MemberExpression)((0, _gen.Identifier)(referenceArray), (0, _gen.Literal)(index), true); // Allow re-assignment to the RGF function
235
-
236
- if (p[0] && p[0].type === "AssignmentExpression" && p[0].left === o) {
237
- // fn = ...
238
- this.replace(o, memberExpression);
239
- } else {
240
- // fn()
241
- // fn
242
- // In most cases the identifier is being used like this (call expression, or referenced to be called later)
243
- // Replace it with a simple wrapper function that will pass on the reference array
244
- var conditionalExpression = (0, _gen.ConditionalExpression)((0, _template.default)("typeof ".concat(referenceArray, "[").concat(index, "] === \"function\" && ").concat(referenceArray, "[").concat(index, "][\"").concat(referenceSignatures[index] || "_", "\"]")).single().expression, (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)( // clone() is required!
245
- (0, _gen.CallExpression)((0, _insert.clone)(memberExpression), [(0, _gen.Identifier)(referenceArray), (0, _gen.SpreadElement)((0, _gen.Identifier)("arguments"))]))]), (0, _insert.clone)(memberExpression));
246
- this.replace(o, conditionalExpression);
247
- }
248
- }
249
- }
250
- }
251
- }
252
- }
253
- });
254
- var arrayExpression = (0, _gen.ArrayExpression)([]);
255
- var variableDeclaration = (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)((0, _gen.Identifier)(referenceArray), arrayExpression)]);
256
- (0, _insert.prepend)(contextObject, variableDeclaration);
257
- queue.forEach(_ref2 => {
258
- var _object$id2;
259
-
260
- let [object, parents] = _ref2;
261
- var name = object === null || object === void 0 ? void 0 : (_object$id2 = object.id) === null || _object$id2 === void 0 ? void 0 : _object$id2.name;
262
- var signature = referenceSignatures[names.get(name)];
263
- var embeddedName = name || this.getPlaceholder(); // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
264
- // RGF runs early and needs completed code before converting to a string.
265
- // (^ the variables haven't been renamed yet)
266
-
267
- var obfuscator = new _obfuscator.default({ ...this.options,
268
- rgf: false,
269
- globalVariables: new Set([...this.options.globalVariables, referenceArray]),
270
- lock: {
271
- integrity: false
272
- },
273
- eval: false,
274
- stringEncoding: false
275
- });
276
- var transforms = obfuscator.array.filter(x => x.priority > this.priority);
277
- var embeddedFunction = { ...object,
278
- type: "FunctionDeclaration",
279
- id: (0, _gen.Identifier)(embeddedName)
280
- };
281
- var tree = {
282
- type: "Program",
283
- body: [embeddedFunction, (0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(embeddedName), (0, _gen.Literal)("call"), true), [(0, _gen.Identifier)("undefined"), (0, _gen.SpreadElement)((0, _template.default)("Array.prototype.slice.call(arguments, 1)").single().expression)]))]
284
- };
285
- tree.__hiddenDeclarations = (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(referenceArray));
286
- tree.__hiddenDeclarations.hidden = true;
287
- tree.__hiddenDeclarations.declarations[0].id.hidden = true;
288
- transforms.forEach(transform => {
289
- transform.apply(tree);
290
- }); // Find eval callbacks
291
-
292
- (0, _traverse.default)(tree, (o, p) => {
293
- if (o.$eval) {
294
- return () => {
295
- o.$eval(o, p);
296
- };
297
- }
298
- });
299
- var toString = (0, _compiler.compileJsSync)(tree, this.options);
300
- var newFunction = (0, _gen.NewExpression)((0, _gen.Identifier)("Function"), [(0, _gen.Literal)(referenceArray), (0, _gen.Literal)(toString)]);
195
+ var newFunctionExpression = (0, _gen.NewExpression)((0, _gen.Identifier)("Function"), [(0, _gen.Literal)(toString)]); // The index where this function is placed in the array
301
196
 
302
- function applySignature(fn) {
303
- if (!signature) {
304
- return fn;
305
- } // This code marks the function object with a unique property
197
+ var newFunctionExpressionIndex = this.arrayExpressionElements.length; // Add it to the array
306
198
 
199
+ this.arrayExpressionElements.push(newFunctionExpression); // The member expression to retrieve this function
307
200
 
308
- return (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)("fn", fn)), (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.MemberExpression)((0, _gen.Identifier)("fn"), (0, _gen.Literal)(signature), true), (0, _gen.Literal)(true))), (0, _gen.ReturnStatement)((0, _gen.Identifier)("fn"))]), []);
309
- }
201
+ var memberExpression = (0, _gen.MemberExpression)((0, _gen.Identifier)(this.arrayExpressionName), (0, _gen.Literal)(newFunctionExpressionIndex), true); // Replace based on type
202
+ // (1) Function Declaration:
203
+ // - Replace body with call to new function
310
204
 
311
- if (object.type === "FunctionDeclaration") {
312
- arrayExpression.elements[names.get(name)] = applySignature(newFunction);
205
+ if (object.type === "FunctionDeclaration") {
206
+ object.body = (0, _gen.BlockStatement)([(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)(memberExpression, (0, _gen.Literal)("apply"), true), [(0, _gen.ThisExpression)(), (0, _gen.Identifier)("arguments")]))]); // The parameters are no longer needed ('arguments' is used to capture them)
313
207
 
314
- if (Array.isArray(parents[0])) {
315
- parents[0].splice(parents[0].indexOf(object), 1);
316
- } else {
317
- this.error(new Error("Error deleting function declaration: " + parents.map(x => x.type).join(",")));
318
- }
319
- } else {
320
- // The wrapper function passes the reference array around
321
- var wrapperFunction = (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)(newFunction, (0, _gen.Literal)("call"), true), [(0, _gen.Identifier)("undefined"), (0, _gen.Identifier)(referenceArray), (0, _gen.SpreadElement)((0, _gen.Identifier)("arguments"))]))]);
322
- this.replace(object, applySignature(wrapperFunction));
323
- }
324
- });
208
+ object.params = [];
209
+ return;
210
+ } // (2) Function Expression:
211
+ // - Replace expression with member expression pointing to new function
212
+
213
+
214
+ if (object.type === "FunctionExpression") {
215
+ this.replace(object, memberExpression);
216
+ return;
217
+ }
325
218
  };
326
219
  }
327
220
 
@@ -25,6 +25,10 @@ var _random = require("../util/random");
25
25
 
26
26
  var _transform = _interopRequireDefault(require("./transform"));
27
27
 
28
+ var _constants = require("../constants");
29
+
30
+ var _functionLength = require("../templates/functionLength");
31
+
28
32
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
29
33
 
30
34
  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; }
@@ -35,6 +39,8 @@ class Stack extends _transform.default {
35
39
 
36
40
  _defineProperty(this, "mangledExpressionsMade", void 0);
37
41
 
42
+ _defineProperty(this, "functionLengthName", void 0);
43
+
38
44
  this.mangledExpressionsMade = 0;
39
45
  }
40
46
 
@@ -103,7 +109,13 @@ class Stack extends _transform.default {
103
109
  setSubscript(param.name, subscripts.size);
104
110
  });
105
111
  var startingSize = subscripts.size;
112
+ var isIllegal = false;
106
113
  (0, _traverse.walk)(object.body, [object, ...parents], (o, p) => {
114
+ if (o.type === "Identifier" && o.name === "arguments") {
115
+ isIllegal = true;
116
+ return "EXIT";
117
+ }
118
+
107
119
  if (o.type == "Identifier") {
108
120
  var info = (0, _identifiers.getIdentifierInfo)(o, p);
109
121
 
@@ -118,6 +130,10 @@ class Stack extends _transform.default {
118
130
  illegal.add(o.name);
119
131
  }
120
132
 
133
+ if (o.name.startsWith(_constants.noRenameVariablePrefix)) {
134
+ illegal.add(o.name);
135
+ }
136
+
121
137
  if (info.isClauseParameter || info.isFunctionParameter || (0, _insert.isForInitialize)(o, p)) {
122
138
  // this.log(
123
139
  // o.name + " is illegal due to clause parameter/function parameter"
@@ -180,6 +196,7 @@ class Stack extends _transform.default {
180
196
  }
181
197
  }
182
198
  });
199
+ if (isIllegal) return;
183
200
  illegal.forEach(name => {
184
201
  defined.delete(name);
185
202
  referenced.delete(name);
@@ -352,11 +369,35 @@ class Stack extends _transform.default {
352
369
 
353
370
  Object.keys(rotateNodes).forEach((index, i) => {
354
371
  object.body.body.splice(parseInt(index) + i, 0, rotateNodes[index]);
355
- }); // Set the params for this function to be the stack array
372
+ }); // Preserve function.length property
373
+
374
+ var originalFunctionLength = (0, _insert.computeFunctionLength)(object.params); // Set the params for this function to be the stack array
356
375
 
357
376
  object.params = [(0, _gen.RestElement)((0, _gen.Identifier)(stackName))]; // Ensure the array is correct length
358
377
 
359
378
  (0, _insert.prepend)(object.body, (0, _template.default)("".concat(stackName, "[\"length\"] = ").concat(startingSize)).single());
379
+
380
+ if (originalFunctionLength !== 0) {
381
+ if (!this.functionLengthName) {
382
+ this.functionLengthName = this.getPlaceholder();
383
+ (0, _insert.prepend)(parents[parents.length - 1] || object, _functionLength.FunctionLengthTemplate.single({
384
+ name: this.functionLengthName
385
+ }));
386
+ }
387
+
388
+ if (object.type === "FunctionDeclaration") {
389
+ var body = parents[0];
390
+
391
+ if (Array.isArray(body)) {
392
+ var index = body.indexOf(object);
393
+ body.splice(index, 0, (0, _gen.ExpressionStatement)((0, _gen.CallExpression)((0, _gen.Identifier)(this.functionLengthName), [(0, _gen.Identifier)(object.id.name), (0, _gen.Literal)(originalFunctionLength)])));
394
+ }
395
+ } else {
396
+ (0, _assert.ok)(object.type === "FunctionExpression" || object.type === "ArrowFunctionExpression");
397
+ this.replace(object, (0, _gen.CallExpression)((0, _gen.Identifier)(this.functionLengthName), [{ ...object
398
+ }, (0, _gen.Literal)(originalFunctionLength)]));
399
+ }
400
+ }
360
401
  };
361
402
  }
362
403
 
@@ -195,7 +195,7 @@ class Transform {
195
195
  getPlaceholder() {
196
196
  const genRanHex = size => [...Array(size)].map(() => Math.floor(Math.random() * 10).toString(10)).join("");
197
197
 
198
- return "__p_" + genRanHex(10);
198
+ return _constants.placeholderVariablePrefix + genRanHex(10);
199
199
  }
200
200
  /**
201
201
  * Returns an independent name generator with it's own counter.
package/dist/util/gen.js CHANGED
@@ -175,6 +175,7 @@ function BreakStatement(label) {
175
175
 
176
176
  function Property(key, value) {
177
177
  let computed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
178
+ let kind = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "init";
178
179
 
179
180
  if (!key) {
180
181
  throw new Error("key is undefined");
@@ -189,7 +190,7 @@ function Property(key, value) {
189
190
  key: key,
190
191
  computed: computed,
191
192
  value: value,
192
- kind: "init",
193
+ kind: kind,
193
194
  method: false,
194
195
  shorthand: false
195
196
  };
@@ -42,6 +42,22 @@ function validateChain(object, parents) {
42
42
  }
43
43
  }
44
44
  }
45
+
46
+ function objectPatternCheck(object, parents) {
47
+ var objectPatternIndex = parents.findIndex(x => x.type === "ObjectPattern");
48
+
49
+ if (objectPatternIndex == -1) {
50
+ return true;
51
+ }
52
+
53
+ var property = parents[objectPatternIndex].properties.find(property => parents[objectPatternIndex - 2] === property);
54
+
55
+ if (property.key === (parents[objectPatternIndex - 3] || object)) {
56
+ return false;
57
+ }
58
+
59
+ return true;
60
+ }
45
61
  /**
46
62
  * Returns detailed information about the given Identifier node.
47
63
  * @param object
@@ -70,11 +86,29 @@ function getIdentifierInfo(object, parents) {
70
86
  }
71
87
 
72
88
  var varIndex = parents.findIndex(x => x.type == "VariableDeclarator");
73
- var isVariableDeclaration = varIndex != -1 && parents[varIndex].id == (parents[varIndex - 1] || object) && parents.find(x => x.type == "VariableDeclaration");
89
+ var isVariableDeclaration = varIndex != -1 && parents[varIndex].id == (parents[varIndex - 1] || object) && parents.find(x => x.type == "VariableDeclaration") && objectPatternCheck(object, parents); // Assignment pattern check!
90
+
91
+ if (isVariableDeclaration) {
92
+ var slicedParents = parents.slice(0, varIndex - 1);
93
+ var i = 0;
94
+
95
+ for (var parent of slicedParents) {
96
+ var childNode = slicedParents[i - 1] || object;
97
+
98
+ if (parent.type === "AssignmentPattern" && parent.right === childNode) {
99
+ isVariableDeclaration = false;
100
+ break;
101
+ }
102
+
103
+ i++;
104
+ }
105
+ }
106
+
74
107
  var forIndex = parents.findIndex(x => x.type == "ForStatement");
75
108
  var isForInitializer = forIndex != -1 && parents[forIndex].init == (parents[forIndex - 1] || object);
76
109
  var functionIndex = parents.findIndex(x => (0, _insert.isFunction)(x));
77
110
  var isFunctionDeclaration = functionIndex != -1 && parents[functionIndex].type == "FunctionDeclaration" && parents[functionIndex].id == object;
111
+ var isNamedFunctionExpression = functionIndex != -1 && parents[functionIndex].type === "FunctionExpression" && parents[functionIndex].id === object;
78
112
  var isAFunctionParameter = isFunctionParameter(object, parents);
79
113
  var isClauseParameter = false; // Special case for Catch clauses
80
114
 
@@ -90,7 +124,7 @@ function getIdentifierInfo(object, parents) {
90
124
  var isFunctionCall = parent.callee == object; // NewExpression and CallExpression
91
125
 
92
126
  var assignmentIndex = parents.findIndex(p => p.type === "AssignmentExpression");
93
- var isAssignmentLeft = assignmentIndex !== -1 && parents[assignmentIndex].left === (parents[assignmentIndex - 1] || object);
127
+ var isAssignmentLeft = assignmentIndex !== -1 && parents[assignmentIndex].left === (parents[assignmentIndex - 1] || object) && objectPatternCheck(object, parents);
94
128
  var isAssignmentValue = assignmentIndex !== -1 && parents[assignmentIndex].right === (parents[assignmentIndex - 1] || object);
95
129
  var isUpdateExpression = parent.type == "UpdateExpression";
96
130
  var isClassDeclaration = (parent.type == "ClassDeclaration" || parent.type == "ClassExpression") && parent.id == object;
@@ -227,7 +261,7 @@ function getIdentifierInfo(object, parents) {
227
261
  /**
228
262
  * Is the Identifier defined, i.e a variable declaration, function declaration, parameter, or class definition
229
263
  */
230
- isDefined: isVariableDeclaration || isFunctionDeclaration || isAFunctionParameter || isClassDeclaration || isClauseParameter || isMethodDefinition || isImportSpecifier,
264
+ isDefined: isVariableDeclaration || isFunctionDeclaration || isNamedFunctionExpression || isAFunctionParameter || isClassDeclaration || isClauseParameter || isMethodDefinition || isImportSpecifier,
231
265
 
232
266
  /**
233
267
  * Is the Identifier modified, either by an `AssignmentExpression` or `UpdateExpression`