js-confuser 1.7.1 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +12 -27
  3. package/dist/compiler.js +2 -8
  4. package/dist/constants.js +17 -10
  5. package/dist/index.js +7 -30
  6. package/dist/obfuscator.js +15 -62
  7. package/dist/options.js +21 -38
  8. package/dist/order.js +4 -7
  9. package/dist/parser.js +5 -13
  10. package/dist/precedence.js +6 -8
  11. package/dist/presets.js +4 -6
  12. package/dist/probability.js +13 -24
  13. package/dist/templates/bufferToString.js +100 -5
  14. package/dist/templates/crash.js +51 -9
  15. package/dist/templates/es5.js +125 -6
  16. package/dist/templates/functionLength.js +24 -6
  17. package/dist/templates/globals.js +9 -0
  18. package/dist/templates/template.js +71 -30
  19. package/dist/transforms/antiTooling.js +26 -22
  20. package/dist/transforms/calculator.js +18 -54
  21. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +236 -333
  22. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +46 -25
  23. package/dist/transforms/deadCode.js +528 -27
  24. package/dist/transforms/dispatcher.js +106 -110
  25. package/dist/transforms/es5/antiClass.js +70 -44
  26. package/dist/transforms/es5/antiDestructuring.js +14 -38
  27. package/dist/transforms/es5/antiES6Object.js +39 -48
  28. package/dist/transforms/es5/antiSpreadOperator.js +5 -14
  29. package/dist/transforms/es5/antiTemplate.js +10 -19
  30. package/dist/transforms/es5/es5.js +7 -40
  31. package/dist/transforms/extraction/classExtraction.js +83 -0
  32. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +41 -80
  33. package/dist/transforms/extraction/objectExtraction.js +24 -56
  34. package/dist/transforms/finalizer.js +6 -20
  35. package/dist/transforms/flatten.js +51 -99
  36. package/dist/transforms/identifier/globalAnalysis.js +8 -26
  37. package/dist/transforms/identifier/globalConcealing.js +35 -54
  38. package/dist/transforms/identifier/movedDeclarations.js +66 -38
  39. package/dist/transforms/identifier/renameVariables.js +29 -68
  40. package/dist/transforms/identifier/variableAnalysis.js +21 -48
  41. package/dist/transforms/lock/antiDebug.js +20 -25
  42. package/dist/transforms/lock/integrity.js +48 -47
  43. package/dist/transforms/lock/lock.js +62 -113
  44. package/dist/transforms/minify.js +77 -108
  45. package/dist/transforms/opaquePredicates.js +11 -48
  46. package/dist/transforms/preparation.js +17 -50
  47. package/dist/transforms/renameLabels.js +5 -22
  48. package/dist/transforms/rgf.js +93 -69
  49. package/dist/transforms/shuffle.js +41 -46
  50. package/dist/transforms/stack.js +35 -98
  51. package/dist/transforms/string/encoding.js +73 -27
  52. package/dist/transforms/string/stringCompression.js +44 -68
  53. package/dist/transforms/string/stringConcealing.js +125 -134
  54. package/dist/transforms/string/stringEncoding.js +6 -26
  55. package/dist/transforms/string/stringSplitting.js +5 -30
  56. package/dist/transforms/transform.js +50 -100
  57. package/dist/traverse.js +11 -18
  58. package/dist/util/compare.js +27 -29
  59. package/dist/util/gen.js +32 -86
  60. package/dist/util/guard.js +0 -1
  61. package/dist/util/identifiers.js +9 -72
  62. package/dist/util/insert.js +27 -77
  63. package/dist/util/math.js +0 -3
  64. package/dist/util/object.js +3 -7
  65. package/dist/util/random.js +5 -36
  66. package/dist/util/scope.js +6 -3
  67. package/package.json +3 -3
  68. package/src/constants.ts +12 -0
  69. package/src/options.ts +13 -0
  70. package/src/order.ts +2 -2
  71. package/src/templates/bufferToString.ts +49 -11
  72. package/src/templates/functionLength.ts +21 -3
  73. package/src/templates/globals.ts +3 -0
  74. package/src/templates/template.ts +85 -25
  75. package/src/transforms/antiTooling.ts +33 -11
  76. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +2 -2
  77. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +46 -10
  78. package/src/transforms/deadCode.ts +0 -16
  79. package/src/transforms/dispatcher.ts +91 -69
  80. package/src/transforms/es5/antiClass.ts +10 -1
  81. package/src/transforms/extraction/classExtraction.ts +168 -0
  82. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +9 -10
  83. package/src/transforms/extraction/objectExtraction.ts +4 -15
  84. package/src/transforms/flatten.ts +20 -5
  85. package/src/transforms/identifier/globalConcealing.ts +29 -65
  86. package/src/transforms/identifier/movedDeclarations.ts +90 -24
  87. package/src/transforms/minify.ts +27 -12
  88. package/src/transforms/rgf.ts +94 -5
  89. package/src/transforms/stack.ts +12 -3
  90. package/src/transforms/string/encoding.ts +85 -51
  91. package/src/transforms/string/stringCompression.ts +5 -8
  92. package/src/transforms/string/stringConcealing.ts +139 -113
  93. package/src/transforms/string/stringEncoding.ts +1 -2
  94. package/src/transforms/string/stringSplitting.ts +1 -2
  95. package/src/transforms/transform.ts +30 -1
  96. package/src/util/compare.ts +39 -5
  97. package/src/util/gen.ts +10 -3
  98. package/src/util/insert.ts +17 -0
  99. package/src/util/scope.ts +14 -2
  100. package/test/code/Cash.test.ts +10 -4
  101. package/test/code/StrictMode.src.js +65 -0
  102. package/test/code/StrictMode.test.js +37 -0
  103. package/test/compare.test.ts +62 -2
  104. package/test/options.test.ts +111 -55
  105. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +37 -18
  106. package/test/transforms/dispatcher.test.ts +55 -0
  107. package/test/transforms/extraction/classExtraction.test.ts +86 -0
  108. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +8 -0
  109. package/test/transforms/extraction/objectExtraction.test.ts +2 -0
  110. package/test/transforms/identifier/globalConcealing.test.ts +19 -0
  111. package/test/transforms/identifier/movedDeclarations.test.ts +61 -0
  112. package/test/transforms/minify.test.ts +37 -0
  113. package/test/transforms/rgf.test.ts +50 -0
  114. package/dist/transforms/controlFlowFlattening/choiceFlowObfuscation.js +0 -62
  115. package/dist/transforms/controlFlowFlattening/controlFlowObfuscation.js +0 -159
  116. package/dist/transforms/controlFlowFlattening/switchCaseObfuscation.js +0 -106
  117. package/dist/transforms/eval.js +0 -84
  118. package/dist/transforms/hexadecimalNumbers.js +0 -63
  119. package/dist/transforms/hideInitializingCode.js +0 -270
  120. package/dist/transforms/identifier/nameRecycling.js +0 -218
  121. package/dist/transforms/label.js +0 -67
  122. package/dist/transforms/preparation/nameConflicts.js +0 -116
  123. package/dist/transforms/preparation/preparation.js +0 -188
@@ -34,14 +34,19 @@ import {
34
34
  isVarContext,
35
35
  prepend,
36
36
  append,
37
+ computeFunctionLength,
38
+ isFunction,
37
39
  } from "../util/insert";
38
40
  import Transform from "./transform";
39
41
  import { isInsideType } from "../util/compare";
40
42
  import { choice, shuffle } from "../util/random";
41
43
  import { ComputeProbabilityMap } from "../probability";
42
- import { reservedIdentifiers } from "../constants";
44
+ import { predictableFunctionTag, reservedIdentifiers } from "../constants";
43
45
  import { ObfuscateOrder } from "../order";
44
46
  import Template from "../templates/template";
47
+ import { FunctionLengthTemplate } from "../templates/functionLength";
48
+ import { ObjectDefineProperty } from "../templates/globals";
49
+ import { getLexicalScope } from "../util/scope";
45
50
 
46
51
  /**
47
52
  * A Dispatcher processes function calls. All the function declarations are brought into a dictionary.
@@ -72,12 +77,30 @@ export default class Dispatcher extends Transform {
72
77
  isDebug = false;
73
78
  count: number;
74
79
 
80
+ functionLengthName: string;
81
+
75
82
  constructor(o) {
76
83
  super(o, ObfuscateOrder.Dispatcher);
77
84
 
78
85
  this.count = 0;
79
86
  }
80
87
 
88
+ apply(tree: Node): void {
89
+ super.apply(tree);
90
+
91
+ if (this.options.preserveFunctionLength && this.functionLengthName) {
92
+ prepend(
93
+ tree,
94
+ FunctionLengthTemplate.single({
95
+ name: this.functionLengthName,
96
+ ObjectDefineProperty: this.createInitVariable(ObjectDefineProperty, [
97
+ tree,
98
+ ]),
99
+ })
100
+ );
101
+ }
102
+ }
103
+
81
104
  match(object: Node, parents: Node[]) {
82
105
  if (isInsideType("AwaitExpression", object, parents)) {
83
106
  return false;
@@ -113,6 +136,8 @@ export default class Dispatcher extends Transform {
113
136
  ? object
114
137
  : getVarContext(object, parents);
115
138
 
139
+ var lexicalScope = isFunction(context) ? context.body : context;
140
+
116
141
  walk(object, parents, (o: Node, p: Node[]) => {
117
142
  if (object == o) {
118
143
  // Fix 1
@@ -138,6 +163,15 @@ export default class Dispatcher extends Transform {
138
163
  o.body.type != "BlockStatement"
139
164
  ) {
140
165
  illegalFnNames.add(name);
166
+ return;
167
+ }
168
+
169
+ // Must defined in the same block as the current function being scanned
170
+ // Solves 'let' and 'class' declaration issue
171
+ var ls = getLexicalScope(o, p);
172
+ if (ls !== lexicalScope) {
173
+ illegalFnNames.add(name);
174
+ return;
141
175
  }
142
176
 
143
177
  // If dupe, no routing
@@ -217,11 +251,18 @@ export default class Dispatcher extends Transform {
217
251
 
218
252
  // Only make a dispatcher function if it caught any functions
219
253
  if (set.size > 0) {
254
+ if (!this.functionLengthName) {
255
+ this.functionLengthName = this.getPlaceholder();
256
+ }
257
+
220
258
  var payloadArg =
221
259
  this.getPlaceholder() + "_dispatcher_" + this.count + "_payload";
222
260
 
223
261
  var dispatcherFnName =
224
- this.getPlaceholder() + "_dispatcher_" + this.count;
262
+ this.getPlaceholder() +
263
+ "_dispatcher_" +
264
+ this.count +
265
+ predictableFunctionTag;
225
266
 
226
267
  this.log(dispatcherFnName, set);
227
268
  this.count++;
@@ -253,6 +294,8 @@ export default class Dispatcher extends Transform {
253
294
  expression: false,
254
295
  type: "FunctionExpression",
255
296
  id: null,
297
+ params: [],
298
+ [predictableFunctionTag]: true,
256
299
  };
257
300
  this.addComment(functionExpression, name);
258
301
 
@@ -272,48 +315,6 @@ export default class Dispatcher extends Transform {
272
315
  );
273
316
 
274
317
  prepend(def.body, variableDeclaration);
275
-
276
- // replace params with random identifiers
277
- var args = [0, 1, 2].map((x) => this.getPlaceholder());
278
- functionExpression.params = args.map((x) => Identifier(x));
279
-
280
- var deadCode = choice(["fakeReturn", "ifStatement"]);
281
-
282
- switch (deadCode) {
283
- case "fakeReturn":
284
- // Dead code...
285
- var ifStatement = IfStatement(
286
- UnaryExpression("!", Identifier(args[0])),
287
- [
288
- ReturnStatement(
289
- CallExpression(Identifier(args[1]), [
290
- ThisExpression(),
291
- Identifier(args[2]),
292
- ])
293
- ),
294
- ],
295
- null
296
- );
297
-
298
- body.unshift(ifStatement);
299
- break;
300
-
301
- case "ifStatement":
302
- var test = LogicalExpression(
303
- "||",
304
- Identifier(args[0]),
305
- AssignmentExpression(
306
- "=",
307
- Identifier(args[1]),
308
- CallExpression(Identifier(args[2]), [])
309
- )
310
- );
311
- def.body = BlockStatement([
312
- IfStatement(test, [...body], null),
313
- ReturnStatement(Identifier(args[1])),
314
- ]);
315
- break;
316
- }
317
318
  }
318
319
 
319
320
  // For logging purposes
@@ -380,6 +381,48 @@ export default class Dispatcher extends Transform {
380
381
  null
381
382
  ),
382
383
 
384
+ VariableDeclaration(
385
+ VariableDeclarator(
386
+ Identifier("lengths"),
387
+ ObjectExpression(
388
+ !this.options.preserveFunctionLength
389
+ ? []
390
+ : shuffledKeys
391
+ .map((name) => {
392
+ var [def, defParents] = functionDeclarations[name];
393
+
394
+ return {
395
+ key: newFnNames[name],
396
+ value: computeFunctionLength(def.params),
397
+ };
398
+ })
399
+ .filter((item) => item.value !== 0)
400
+ .map((item) =>
401
+ Property(Literal(item.key), Literal(item.value))
402
+ )
403
+ )
404
+ )
405
+ ),
406
+
407
+ Template(`
408
+ function makeFn${predictableFunctionTag}(){
409
+ var fn = function(...args){
410
+ ${payloadArg} = args;
411
+ return ${mapName}[${x}].call(this)
412
+ }, a = lengths[${x}]
413
+
414
+ ${
415
+ this.options.preserveFunctionLength
416
+ ? `if(a){
417
+ return ${this.functionLengthName}(fn, a)
418
+ }`
419
+ : ""
420
+ }
421
+
422
+ return fn
423
+ }
424
+ `).single(),
425
+
383
426
  // Arg to get a function reference
384
427
  IfStatement(
385
428
  BinaryExpression("==", Identifier(y), Literal(expectedGet)),
@@ -403,30 +446,9 @@ export default class Dispatcher extends Transform {
403
446
  Identifier(x),
404
447
  true
405
448
  ),
406
- FunctionExpression(
407
- [RestElement(Identifier(getterArgName))],
408
- [
409
- // Arg setter
410
- ExpressionStatement(
411
- AssignmentExpression(
412
- "=",
413
- Identifier(payloadArg),
414
- Identifier(getterArgName)
415
- )
416
- ),
417
-
418
- // Call fn & return
419
- ReturnStatement(
420
- CallExpression(
421
- MemberExpression(
422
- getAccessor(),
423
- Identifier("call"),
424
- false
425
- ),
426
- [ThisExpression(), Literal(gen.generate())]
427
- )
428
- ),
429
- ]
449
+ CallExpression(
450
+ Identifier(`makeFn${predictableFunctionTag}`),
451
+ []
430
452
  )
431
453
  )
432
454
  )
@@ -439,7 +461,7 @@ export default class Dispatcher extends Transform {
439
461
  AssignmentExpression(
440
462
  "=",
441
463
  Identifier(returnProp),
442
- CallExpression(getAccessor(), [Literal(gen.generate())])
464
+ CallExpression(getAccessor(), [])
443
465
  )
444
466
  ),
445
467
  ]
@@ -94,8 +94,17 @@ export default class AntiClass extends Transform {
94
94
  first.expression.type == "CallExpression"
95
95
  ) {
96
96
  if (first.expression.callee.type == "Super") {
97
+ /// super(...args)
97
98
  superArguments = first.expression.arguments;
98
99
  value.body.body.shift();
100
+ } else if (
101
+ // F(super(...args))
102
+ first.expression.arguments[0] &&
103
+ first.expression.arguments[0].type === "CallExpression" &&
104
+ first.expression.arguments[0].callee.type === "Super"
105
+ ) {
106
+ superArguments = first.expression.arguments[0].arguments;
107
+ first.expression.arguments[0] = Identifier("undefined");
99
108
  }
100
109
  }
101
110
 
@@ -198,7 +207,7 @@ export default class AntiClass extends Transform {
198
207
  );
199
208
 
200
209
  if (superName) {
201
- ok(superArguments, "Super class with no super arguments");
210
+ ok(superArguments, "Failed to find super() arguments");
202
211
 
203
212
  // save the super state
204
213
  virtualBody.unshift(
@@ -0,0 +1,168 @@
1
+ import { ok } from "assert";
2
+ import { ExitCallback, getBlock, walk } from "../../traverse";
3
+ import {
4
+ CallExpression,
5
+ FunctionDeclaration,
6
+ FunctionExpression,
7
+ Identifier,
8
+ Literal,
9
+ MemberExpression,
10
+ MethodDefinition,
11
+ Node,
12
+ ReturnStatement,
13
+ Super,
14
+ ThisExpression,
15
+ } from "../../util/gen";
16
+ import { isStringLiteral } from "../../util/guard";
17
+ import { isClass, prepend } from "../../util/insert";
18
+ import { getLexicalScope } from "../../util/scope";
19
+ import Transform from "../transform";
20
+
21
+ export default class ClassExtraction extends Transform {
22
+ constructor(o) {
23
+ super(o);
24
+ }
25
+
26
+ match(object: Node, parents: Node[]): boolean {
27
+ return (
28
+ object.type === "ClassDeclaration" || object.type === "ClassExpression"
29
+ );
30
+ }
31
+
32
+ extractKeyString(property: Node): string | null {
33
+ if (property.key.type === "Identifier" && !property.key.computed) {
34
+ return property.key.name;
35
+ }
36
+
37
+ if (isStringLiteral(property.key)) {
38
+ return property.key.value;
39
+ }
40
+
41
+ return null;
42
+ }
43
+
44
+ transform(object: Node, parents: Node[]): void | ExitCallback {
45
+ return () => {
46
+ var classBody = object.body;
47
+ var className = object.id?.type === "Identifier" && object.id?.name;
48
+
49
+ if (!className) className = this.getPlaceholder();
50
+
51
+ var lexicalScope = getLexicalScope(object, parents);
52
+
53
+ var superMethodName: string;
54
+
55
+ for (var methodDefinition of classBody.body) {
56
+ if (
57
+ methodDefinition.type === "MethodDefinition" &&
58
+ methodDefinition.value.type === "FunctionExpression"
59
+ ) {
60
+ // Don't change constructors calling super()
61
+ if (methodDefinition.kind === "constructor" && object.superClass)
62
+ continue;
63
+
64
+ var functionExpression: Node = methodDefinition.value;
65
+
66
+ var fnName =
67
+ className +
68
+ "_" +
69
+ methodDefinition.kind +
70
+ "_" +
71
+ this.extractKeyString(methodDefinition) || this.getPlaceholder();
72
+
73
+ walk(
74
+ functionExpression,
75
+ [methodDefinition, object, ...parents],
76
+ (o, p) => {
77
+ if (o.type === "Super") {
78
+ var classContext = p.find((node) => isClass(node));
79
+ if (classContext !== object) return;
80
+
81
+ return () => {
82
+ if (!superMethodName) {
83
+ superMethodName =
84
+ this.getGenerator("randomized").generate();
85
+ }
86
+
87
+ var memberExpression = p[0];
88
+ if (memberExpression.type === "CallExpression") {
89
+ throw new Error("Failed to detect super() usage");
90
+ }
91
+ ok(memberExpression.type === "MemberExpression");
92
+
93
+ var propertyArg = memberExpression.computed
94
+ ? memberExpression.property
95
+ : (ok(memberExpression.property.type === "Identifier"),
96
+ Literal(memberExpression.property.name));
97
+
98
+ var getSuperExpression = CallExpression(
99
+ MemberExpression(
100
+ ThisExpression(),
101
+ Literal(superMethodName),
102
+ true
103
+ ),
104
+ [propertyArg]
105
+ );
106
+
107
+ if (p[1].type === "CallExpression" && p[1].callee === p[0]) {
108
+ getSuperExpression = CallExpression(
109
+ MemberExpression(
110
+ getSuperExpression,
111
+ Literal("bind"),
112
+ true
113
+ ),
114
+ [ThisExpression()]
115
+ );
116
+ }
117
+
118
+ this.replace(p[0], getSuperExpression);
119
+ };
120
+ }
121
+ }
122
+ );
123
+
124
+ var originalParams = functionExpression.params;
125
+ var originalBody = functionExpression.body.body;
126
+
127
+ functionExpression.body.body = [
128
+ ReturnStatement(
129
+ CallExpression(
130
+ MemberExpression(Identifier(fnName), Literal("apply"), true),
131
+ [ThisExpression(), Identifier("arguments")]
132
+ )
133
+ ),
134
+ ];
135
+
136
+ functionExpression.params = [];
137
+ if (methodDefinition.kind === "set") {
138
+ functionExpression.params = [Identifier(this.getPlaceholder())];
139
+ }
140
+
141
+ prepend(
142
+ lexicalScope,
143
+ FunctionDeclaration(fnName, [...originalParams], [...originalBody])
144
+ );
145
+ }
146
+ }
147
+
148
+ if (superMethodName) {
149
+ classBody.body.push(
150
+ MethodDefinition(
151
+ Literal(superMethodName),
152
+ FunctionExpression(
153
+ [Identifier("key")],
154
+ [
155
+ ReturnStatement(
156
+ MemberExpression(Super(), Identifier("key"), true)
157
+ ),
158
+ ]
159
+ ),
160
+ "method",
161
+ false,
162
+ true
163
+ )
164
+ );
165
+ }
166
+ };
167
+ }
168
+ }
@@ -16,15 +16,14 @@ import {
16
16
  ConditionalExpression,
17
17
  } from "../../util/gen";
18
18
  import { append, clone, prepend } from "../../util/insert";
19
- import { isDirective, isPrimitive } from "../../util/compare";
20
-
19
+ import { isDirective, isModuleSource, isPrimitive } from "../../util/compare";
21
20
  import { ObfuscateOrder } from "../../order";
22
- import { isModuleSource } from "../string/stringConcealing";
23
21
  import { ComputeProbabilityMap } from "../../probability";
24
22
  import { ok } from "assert";
25
23
  import { chance, choice, getRandomInteger } from "../../util/random";
26
24
  import { getBlock } from "../../traverse";
27
25
  import { getIdentifierInfo } from "../../util/identifiers";
26
+ import { predictableFunctionTag } from "../../constants";
28
27
 
29
28
  /**
30
29
  * [Duplicate Literals Removal](https://docs.jscrambler.com/code-integrity/documentation/transformations/duplicate-literals-removal) replaces duplicate literals with a variable name.
@@ -119,7 +118,7 @@ export default class DuplicateLiteralsRemoval extends Transform {
119
118
  ];
120
119
 
121
120
  // The function uses mangling to hide the index being accessed
122
- var mangleCount = getRandomInteger(1, 10);
121
+ var mangleCount = getRandomInteger(1, 5);
123
122
  for (var i = 0; i < mangleCount; i++) {
124
123
  var operator = choice([">", "<"]);
125
124
  var compareValue = choice(indexRangeInclusive);
@@ -188,7 +187,7 @@ export default class DuplicateLiteralsRemoval extends Transform {
188
187
  var root = parents[parents.length - 1];
189
188
  var rootFunctionName = this.getPlaceholder() + "_dLR_0";
190
189
  this.functions.set(root, {
191
- functionName: rootFunctionName,
190
+ functionName: rootFunctionName + predictableFunctionTag,
192
191
  indexShift: getRandomInteger(-100, 100),
193
192
  });
194
193
 
@@ -199,7 +198,10 @@ export default class DuplicateLiteralsRemoval extends Transform {
199
198
  var block = getBlock(object, parents);
200
199
  if (!this.functions.has(block) && chance(50 - this.functions.size)) {
201
200
  var newFunctionName =
202
- this.getPlaceholder() + "_dLR_" + this.functions.size;
201
+ this.getPlaceholder() +
202
+ "_dLR_" +
203
+ this.functions.size +
204
+ predictableFunctionTag;
203
205
 
204
206
  this.functions.set(block, {
205
207
  functionName: newFunctionName,
@@ -224,7 +226,7 @@ export default class DuplicateLiteralsRemoval extends Transform {
224
226
  return () => {
225
227
  if (object.type === "Identifier") {
226
228
  var info = getIdentifierInfo(object, parents);
227
- if (info.isLabel || info.spec.isDefined) return;
229
+ if (info.isLabel || info.spec.isDefined || info.spec.isModified) return;
228
230
  }
229
231
  if (object.regex) {
230
232
  return;
@@ -234,9 +236,6 @@ export default class DuplicateLiteralsRemoval extends Transform {
234
236
  return;
235
237
  }
236
238
 
237
- // HARD CODED LIMIT of 10,000 (after 1,000 elements)
238
- if (this.map.size > 1000 && chance(this.map.size / 100)) return;
239
-
240
239
  if (
241
240
  this.arrayName &&
242
241
  parents[0].object &&
@@ -1,19 +1,7 @@
1
1
  import Transform from "../transform";
2
2
  import { walk } from "../../traverse";
3
- import {
4
- Node,
5
- Location,
6
- Identifier,
7
- VariableDeclaration,
8
- VariableDeclarator,
9
- } from "../../util/gen";
10
- import {
11
- clone,
12
- deleteDeclaration,
13
- getVarContext,
14
- isVarContext,
15
- prepend,
16
- } from "../../util/insert";
3
+ import { Node, Location, Identifier, VariableDeclarator } from "../../util/gen";
4
+ import { getVarContext, isVarContext } from "../../util/insert";
17
5
  import { ObfuscateOrder } from "../../order";
18
6
  import { getIdentifierInfo } from "../../util/identifiers";
19
7
  import { isValidIdentifier } from "../../util/compare";
@@ -316,8 +304,9 @@ export default class ObjectExtraction extends Transform {
316
304
  ...variableDeclarators
317
305
  );
318
306
 
307
+ // const can only be safely changed to let
319
308
  if (declaration.kind === "const") {
320
- declaration.kind = "var";
309
+ declaration.kind = "let";
321
310
  }
322
311
 
323
312
  // update all identifiers that pointed to the old object
@@ -1,5 +1,9 @@
1
1
  import { ok } from "assert";
2
- import { noRenameVariablePrefix, reservedIdentifiers } from "../constants";
2
+ import {
3
+ noRenameVariablePrefix,
4
+ predictableFunctionTag,
5
+ reservedIdentifiers,
6
+ } from "../constants";
3
7
  import { ObfuscateOrder } from "../order";
4
8
  import { walk } from "../traverse";
5
9
  import {
@@ -35,6 +39,7 @@ import {
35
39
  import { shuffle } from "../util/random";
36
40
  import Transform from "./transform";
37
41
  import { FunctionLengthTemplate } from "../templates/functionLength";
42
+ import { ObjectDefineProperty } from "../templates/globals";
38
43
 
39
44
  /**
40
45
  * Flatten takes functions and isolates them from their original scope, and brings it to the top level of the program.
@@ -235,7 +240,11 @@ export default class Flatten extends Transform {
235
240
  return;
236
241
  }
237
242
 
238
- var newFnName = this.getPlaceholder() + "_flat_" + currentFnName;
243
+ var newFnName =
244
+ this.getPlaceholder() +
245
+ "_flat_" +
246
+ currentFnName +
247
+ predictableFunctionTag;
239
248
  var flatObjectName = this.getPlaceholder() + "_flat_object";
240
249
 
241
250
  const getFlatObjectMember = (propertyName: string) => {
@@ -498,15 +507,21 @@ export default class Flatten extends Transform {
498
507
  // Preserve function.length property
499
508
  var originalFunctionLength = computeFunctionLength(object.params);
500
509
 
501
- object.params = [SpreadElement(Identifier(argumentsName))];
510
+ object.params = [RestElement(Identifier(argumentsName))];
502
511
 
503
- if (originalFunctionLength !== 0) {
512
+ if (this.options.preserveFunctionLength && originalFunctionLength !== 0) {
504
513
  if (!this.functionLengthName) {
505
514
  this.functionLengthName = this.getPlaceholder();
506
515
 
507
516
  prepend(
508
517
  parents[parents.length - 1] || object,
509
- FunctionLengthTemplate.single({ name: this.functionLengthName })
518
+ FunctionLengthTemplate.single({
519
+ name: this.functionLengthName,
520
+ ObjectDefineProperty: this.createInitVariable(
521
+ ObjectDefineProperty,
522
+ parents
523
+ ),
524
+ })
510
525
  );
511
526
  }
512
527