js-confuser 1.2.1 → 1.4.1

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 (94) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/README.md +7 -6
  3. package/dist/options.js +5 -1
  4. package/dist/parser.js +1 -2
  5. package/dist/presets.js +2 -2
  6. package/dist/transforms/calculator.js +48 -60
  7. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +482 -95
  8. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +4 -0
  9. package/dist/transforms/controlFlowFlattening/{switchCaseObfucation.js → switchCaseObfuscation.js} +2 -2
  10. package/dist/transforms/deadCode.js +1 -1
  11. package/dist/transforms/dispatcher.js +14 -13
  12. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +5 -10
  13. package/dist/transforms/flatten.js +5 -1
  14. package/dist/transforms/hideInitializingCode.js +17 -2
  15. package/dist/transforms/identifier/globalConcealing.js +46 -25
  16. package/dist/transforms/identifier/movedDeclarations.js +69 -68
  17. package/dist/transforms/identifier/renameVariables.js +22 -98
  18. package/dist/transforms/identifier/variableAnalysis.js +133 -0
  19. package/dist/transforms/label.js +11 -2
  20. package/dist/transforms/lock/antiDebug.js +32 -13
  21. package/dist/transforms/lock/lock.js +13 -2
  22. package/dist/transforms/minify.js +117 -120
  23. package/dist/transforms/opaquePredicates.js +4 -2
  24. package/dist/transforms/preparation/preparation.js +8 -0
  25. package/dist/transforms/renameLabels.js +17 -3
  26. package/dist/transforms/rgf.js +8 -3
  27. package/dist/transforms/shuffle.js +25 -9
  28. package/dist/transforms/stack.js +5 -9
  29. package/dist/transforms/string/encoding.js +209 -0
  30. package/dist/transforms/string/stringCompression.js +10 -10
  31. package/dist/transforms/string/stringConcealing.js +94 -65
  32. package/dist/transforms/string/stringSplitting.js +7 -7
  33. package/dist/transforms/transform.js +10 -0
  34. package/dist/traverse.js +1 -35
  35. package/dist/util/gen.js +3 -1
  36. package/dist/util/identifiers.js +9 -19
  37. package/dist/util/insert.js +6 -40
  38. package/dist/util/scope.js +17 -0
  39. package/package.json +2 -2
  40. package/src/options.ts +19 -3
  41. package/src/parser.ts +1 -2
  42. package/src/presets.ts +2 -2
  43. package/src/transforms/calculator.ts +87 -91
  44. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +742 -142
  45. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +6 -0
  46. package/src/transforms/controlFlowFlattening/{switchCaseObfucation.ts → switchCaseObfuscation.ts} +6 -2
  47. package/src/transforms/deadCode.ts +8 -0
  48. package/src/transforms/dispatcher.ts +29 -14
  49. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +43 -19
  50. package/src/transforms/flatten.ts +15 -2
  51. package/src/transforms/hideInitializingCode.ts +432 -406
  52. package/src/transforms/identifier/globalConcealing.ts +148 -46
  53. package/src/transforms/identifier/movedDeclarations.ts +78 -101
  54. package/src/transforms/identifier/renameVariables.ts +21 -96
  55. package/src/transforms/identifier/variableAnalysis.ts +124 -0
  56. package/src/transforms/label.ts +20 -2
  57. package/src/transforms/lock/antiDebug.ts +69 -26
  58. package/src/transforms/lock/lock.ts +37 -3
  59. package/src/transforms/minify.ts +154 -130
  60. package/src/transforms/opaquePredicates.ts +25 -3
  61. package/src/transforms/preparation/preparation.ts +8 -1
  62. package/src/transforms/renameLabels.ts +26 -3
  63. package/src/transforms/rgf.ts +6 -1
  64. package/src/transforms/shuffle.ts +87 -29
  65. package/src/transforms/stack.ts +6 -8
  66. package/src/transforms/string/encoding.ts +310 -0
  67. package/src/transforms/string/stringCompression.ts +37 -24
  68. package/src/transforms/string/stringConcealing.ts +157 -160
  69. package/src/transforms/string/stringSplitting.ts +12 -8
  70. package/src/transforms/transform.ts +15 -2
  71. package/src/traverse.ts +1 -31
  72. package/src/util/gen.ts +5 -3
  73. package/src/util/identifiers.ts +20 -20
  74. package/src/util/insert.ts +12 -78
  75. package/src/util/scope.ts +9 -0
  76. package/test/{transforms/compare.test.ts → compare.test.ts} +2 -2
  77. package/test/index.test.ts +109 -1
  78. package/test/templates/template.test.ts +14 -0
  79. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +392 -10
  80. package/test/transforms/dispatcher.test.ts +30 -0
  81. package/test/transforms/flatten.test.ts +28 -0
  82. package/test/transforms/hideInitializingCode.test.ts +336 -336
  83. package/test/transforms/identifier/globalConcealing.test.ts +1 -2
  84. package/test/transforms/identifier/movedDeclarations.test.ts +137 -112
  85. package/test/transforms/identifier/renameVariables.test.ts +124 -13
  86. package/test/transforms/lock/antiDebug.test.ts +43 -0
  87. package/test/transforms/lock/selfDefending.test.ts +68 -0
  88. package/test/transforms/minify.test.ts +137 -0
  89. package/test/transforms/renameLabels.test.ts +33 -0
  90. package/test/transforms/rgf.test.ts +29 -0
  91. package/test/transforms/string/stringSplitting.test.ts +33 -0
  92. package/test/util/identifiers.test.ts +105 -17
  93. package/dist/util/expr.js +0 -60
  94. package/src/util/expr.ts +0 -56
@@ -25,159 +25,146 @@ import {
25
25
  import { isValidIdentifier, isEquivalent } from "../util/compare";
26
26
  import { walk, isBlock } from "../traverse";
27
27
  import { ok } from "assert";
28
+ import { isLexicalScope } from "../util/scope";
29
+
30
+ /**
31
+ * Basic transformations to reduce code size.
32
+ *
33
+ * Examples:
34
+ * - `if(a) { b() }` **->** `a && b()`
35
+ * - `if(a){b()}else{c()}` **->** `a?b():c()`
36
+ * - `x['y']` **->** `x.y`
37
+ */
38
+ export default class Minify extends Transform {
39
+ variables: Map<Node, Location[]>;
28
40
 
29
- class MinifyBlock extends Transform {
30
41
  constructor(o) {
31
- super(o);
42
+ super(o, ObfuscateOrder.Minify);
43
+
44
+ this.variables = new Map();
32
45
  }
33
46
 
34
47
  match(object: Node, parents: Node[]) {
35
- return isBlock(object) || object.type == "SwitchCase";
48
+ return object.hasOwnProperty("type");
36
49
  }
37
50
 
38
51
  transform(object: Node, parents: Node[]) {
39
- return () => {
40
- var body =
41
- object.type == "SwitchCase" ? object.consequent : getBlockBody(object);
42
- var earlyReturn = body.length;
43
- var fnDecs: [Node, number][] = [];
52
+ if (isLexicalScope(object)) {
53
+ return () => {
54
+ var body =
55
+ object.type == "SwitchCase"
56
+ ? object.consequent
57
+ : getBlockBody(object);
58
+ var earlyReturn = body.length;
59
+ var fnDecs: [Node, number][] = [];
44
60
 
45
- body.forEach((stmt, i) => {
46
- if (
47
- stmt.type == "ReturnStatement" ||
48
- stmt.type == "BreakStatement" ||
49
- stmt.type == "ContinueStatement"
50
- ) {
51
- if (earlyReturn > i + 1) {
52
- earlyReturn = i + 1;
61
+ body.forEach((stmt, i) => {
62
+ if (
63
+ stmt.type == "ReturnStatement" ||
64
+ stmt.type == "BreakStatement" ||
65
+ stmt.type == "ContinueStatement"
66
+ ) {
67
+ if (earlyReturn > i + 1) {
68
+ earlyReturn = i + 1;
69
+ }
53
70
  }
54
- }
55
71
 
56
- if (stmt.type == "FunctionDeclaration") {
57
- fnDecs.push([stmt, i]);
58
- }
59
- });
72
+ if (stmt.type == "FunctionDeclaration") {
73
+ fnDecs.push([stmt, i]);
74
+ }
75
+ });
60
76
 
61
- if (earlyReturn < body.length) {
62
- body.length = earlyReturn;
63
- body.push(
64
- ...fnDecs.filter((x) => x[1] >= earlyReturn).map((x) => x[0])
65
- );
66
- }
77
+ if (earlyReturn < body.length) {
78
+ body.length = earlyReturn;
79
+ body.push(
80
+ ...fnDecs.filter((x) => x[1] >= earlyReturn).map((x) => x[0])
81
+ );
82
+ }
67
83
 
68
- // Now combine ExpressionStatements
84
+ // Now combine ExpressionStatements
69
85
 
70
- if (body.length > 1) {
71
- var exprs = [];
72
- var startIndex = -1;
86
+ if (body.length > 1) {
87
+ var exprs = [];
88
+ var startIndex = -1;
73
89
 
74
- var sequences: { index: number; exprs: Node[] }[] = [];
90
+ var sequences: { index: number; exprs: Node[] }[] = [];
75
91
 
76
- body.forEach((stmt, i) => {
77
- if (stmt.type == "ExpressionStatement") {
78
- exprs.push(stmt.expression);
79
- if (startIndex == -1) {
80
- startIndex = i;
81
- }
82
- } else {
83
- if (exprs.length) {
84
- sequences.push({ exprs: exprs, index: startIndex });
92
+ body.forEach((stmt, i) => {
93
+ if (stmt.type == "ExpressionStatement") {
94
+ exprs.push(stmt.expression);
95
+ if (startIndex == -1) {
96
+ startIndex = i;
97
+ }
98
+ } else {
99
+ if (exprs.length) {
100
+ sequences.push({ exprs: exprs, index: startIndex });
101
+ }
102
+ exprs = [];
103
+ startIndex = -1;
85
104
  }
86
- exprs = [];
87
- startIndex = -1;
105
+ });
106
+
107
+ if (exprs.length) {
108
+ sequences.push({ exprs: exprs, index: startIndex });
88
109
  }
89
- });
90
110
 
91
- if (exprs.length) {
92
- sequences.push({ exprs: exprs, index: startIndex });
111
+ sequences.reverse().forEach((seq) => {
112
+ ok(seq.index != -1);
113
+ body.splice(
114
+ seq.index,
115
+ seq.exprs.length,
116
+ ExpressionStatement(
117
+ seq.exprs.length == 1
118
+ ? seq.exprs[0]
119
+ : SequenceExpression(seq.exprs)
120
+ )
121
+ );
122
+ });
93
123
  }
94
124
 
95
- sequences.reverse().forEach((seq) => {
96
- ok(seq.index != -1);
97
- body.splice(
98
- seq.index,
99
- seq.exprs.length,
100
- ExpressionStatement(
101
- seq.exprs.length == 1
102
- ? seq.exprs[0]
103
- : SequenceExpression(seq.exprs)
104
- )
105
- );
106
- });
107
- }
108
-
109
- if (object.type != "SwitchCase") {
110
- // Unnecessary return
111
- if (body.length && body[body.length - 1]) {
112
- var last = body[body.length - 1];
113
- var isUndefined = last.argument == null;
114
- if (last.type == "ReturnStatement" && isUndefined) {
115
- body.pop();
125
+ if (object.type != "SwitchCase") {
126
+ // Unnecessary return
127
+ if (body.length && body[body.length - 1]) {
128
+ var last = body[body.length - 1];
129
+ var isUndefined = last.argument == null;
130
+ if (last.type == "ReturnStatement" && isUndefined) {
131
+ body.pop();
132
+ }
116
133
  }
117
- }
118
134
 
119
- // Variable declaration grouping
120
- // var a = 1;
121
- // var b = 1;
122
- // var c = 1;
123
- //
124
- // var a=1,b=1,c=1;
125
- var lastDec = null;
126
-
127
- var remove = [];
128
- body.forEach((x, i) => {
129
- if (x.type === "VariableDeclaration") {
130
- if (
131
- !lastDec ||
132
- lastDec.kind !== x.kind ||
133
- !lastDec.declarations.length
134
- ) {
135
- lastDec = x;
135
+ // Variable declaration grouping
136
+ // var a = 1;
137
+ // var b = 1;
138
+ // var c = 1;
139
+ //
140
+ // var a=1,b=1,c=1;
141
+ var lastDec = null;
142
+
143
+ var remove = [];
144
+ body.forEach((x, i) => {
145
+ if (x.type === "VariableDeclaration") {
146
+ if (
147
+ !lastDec ||
148
+ lastDec.kind !== x.kind ||
149
+ !lastDec.declarations.length
150
+ ) {
151
+ lastDec = x;
152
+ } else {
153
+ lastDec.declarations.push(...x.declarations);
154
+ remove.unshift(i);
155
+ }
136
156
  } else {
137
- lastDec.declarations.push(...x.declarations);
138
- remove.unshift(i);
157
+ lastDec = null;
139
158
  }
140
- } else {
141
- lastDec = null;
142
- }
143
- });
144
-
145
- remove.forEach((x) => {
146
- body.splice(x, 1);
147
- });
148
- }
149
- };
150
- }
151
- }
152
-
153
- /**
154
- * Basic transformations to reduce code size.
155
- *
156
- * Examples:
157
- * - `if(a) { b() }` **->** `a && b()`
158
- * - `if(a){b()}else{c()}` **->** `a?b():c()`
159
- * - `x['y']` **->** `x.y`
160
- */
161
- export default class Minify extends Transform {
162
- variables: Map<Node, Location[]>;
163
-
164
- constructor(o) {
165
- super(o, ObfuscateOrder.Minify);
166
-
167
- this.variables = new Map();
159
+ });
168
160
 
169
- /**
170
- * Minify runs at every Node, making Expression-based minification.
171
- * MinifyBlock runs only on Blocks, making Statement-based minification.
172
- */
173
- this.after.push(new MinifyBlock(o));
174
- }
175
-
176
- match(object: Node, parents: Node[]) {
177
- return object.hasOwnProperty("type");
178
- }
161
+ remove.forEach((x) => {
162
+ body.splice(x, 1);
163
+ });
164
+ }
165
+ };
166
+ }
179
167
 
180
- transform(object: Node, parents: Node[]) {
181
168
  /**
182
169
  * ES6 and higher only
183
170
  * - `function(){}` -> `()=>{}`
@@ -378,6 +365,7 @@ export default class Minify extends Transform {
378
365
  if (last) {
379
366
  var lastStatement = last.consequent[last.consequent.length - 1];
380
367
  if (
368
+ lastStatement &&
381
369
  lastStatement.type == "BreakStatement" &&
382
370
  lastStatement.label == null
383
371
  ) {
@@ -538,7 +526,7 @@ export default class Minify extends Transform {
538
526
  if (key == "toString" && object.arguments.length == 0) {
539
527
  this.replace(
540
528
  object,
541
- BinaryExpression("+", clone(object.callee.object), Literal(""))
529
+ BinaryExpression("+", Literal(""), clone(object.callee.object))
542
530
  );
543
531
  }
544
532
  }
@@ -574,6 +562,30 @@ export default class Minify extends Transform {
574
562
  }
575
563
  }
576
564
 
565
+ if (
566
+ object.id.type == "ObjectPattern" &&
567
+ object.init.type == "ObjectExpression"
568
+ ) {
569
+ if (
570
+ object.id.properties.length === 1 &&
571
+ object.init.properties.length === 1
572
+ ) {
573
+ var key1 = object.id.properties[0].computed
574
+ ? object.id.properties[0].key.value
575
+ : object.id.properties[0].key.name;
576
+ var key2 = object.init.properties[0].computed
577
+ ? object.init.properties[0].key.value
578
+ : object.init.properties[0].key.name;
579
+
580
+ // console.log(key1, key2);
581
+
582
+ if (key1 && key2 && key1 === key2) {
583
+ object.id = object.id.properties[0].value;
584
+ object.init = object.init.properties[0].value;
585
+ }
586
+ }
587
+ }
588
+
577
589
  // check for redundant patterns
578
590
  if (
579
591
  object.id.type == "ArrayPattern" &&
@@ -593,7 +605,11 @@ export default class Minify extends Transform {
593
605
  return () => {
594
606
  switch (typeof object.value) {
595
607
  case "boolean":
596
- // this.replace(object, UnaryExpression("!", Literal(object.value ? 0 : 1)));
608
+ this.replaceIdentifierOrLiteral(
609
+ object,
610
+ UnaryExpression("!", Literal(object.value ? 0 : 1)),
611
+ parents
612
+ );
597
613
  break;
598
614
  }
599
615
  };
@@ -601,9 +617,17 @@ export default class Minify extends Transform {
601
617
  if (object.type == "Identifier") {
602
618
  return () => {
603
619
  if (object.name == "undefined" && !isForInitialize(object, parents)) {
604
- this.replace(object, UnaryExpression("void", Literal(0)));
620
+ this.replaceIdentifierOrLiteral(
621
+ object,
622
+ UnaryExpression("void", Literal(0)),
623
+ parents
624
+ );
605
625
  } else if (object.name == "Infinity") {
606
- this.replace(object, BinaryExpression("/", Literal(1), Literal(0)));
626
+ this.replaceIdentifierOrLiteral(
627
+ object,
628
+ BinaryExpression("/", Literal(1), Literal(0)),
629
+ parents
630
+ );
607
631
  }
608
632
  };
609
633
  }
@@ -15,6 +15,8 @@ import {
15
15
  VariableDeclarator,
16
16
  ConditionalExpression,
17
17
  UnaryExpression,
18
+ ReturnStatement,
19
+ AssignmentPattern,
18
20
  } from "../util/gen";
19
21
  import {
20
22
  choice,
@@ -93,10 +95,27 @@ export default class OpaquePredicates extends Transform {
93
95
  if (!this.predicate) {
94
96
  this.predicateName = this.getPlaceholder();
95
97
  this.predicate = ObjectExpression([]);
98
+
99
+ var tempName = this.getPlaceholder();
100
+
96
101
  prepend(
97
102
  parents[parents.length - 1] || object,
98
103
  VariableDeclaration(
99
- VariableDeclarator(this.predicateName, this.predicate)
104
+ VariableDeclarator(
105
+ this.predicateName,
106
+ CallExpression(
107
+ FunctionExpression(
108
+ [],
109
+ [
110
+ VariableDeclaration(
111
+ VariableDeclarator(tempName, this.predicate)
112
+ ),
113
+ ReturnStatement(Identifier(tempName)),
114
+ ]
115
+ ),
116
+ []
117
+ )
118
+ )
100
119
  )
101
120
  );
102
121
  }
@@ -119,11 +138,14 @@ export default class OpaquePredicates extends Transform {
119
138
  this.predicate.properties.push(
120
139
  Property(Identifier(arrayProp), ArrayExpression([]))
121
140
  );
141
+
142
+ var paramName = this.getPlaceholder();
143
+
122
144
  this.predicate.properties.push(
123
145
  Property(
124
146
  Identifier(prop),
125
147
  FunctionExpression(
126
- [],
148
+ [AssignmentPattern(Identifier(paramName), Literal("length"))],
127
149
  Template(`
128
150
  if ( !${this.predicateName}.${arrayProp}[0] ) {
129
151
  ${this.predicateName}.${arrayProp}.push(${getRandomInteger(
@@ -131,7 +153,7 @@ export default class OpaquePredicates extends Transform {
131
153
  100
132
154
  )});
133
155
  }
134
- return ${this.predicateName}.${arrayProp}.length;
156
+ return ${this.predicateName}.${arrayProp}[${paramName}];
135
157
  `).compile()
136
158
  )
137
159
  )
@@ -13,7 +13,7 @@ import {
13
13
  ReturnStatement,
14
14
  } from "../../util/gen";
15
15
  import { ObfuscateOrder } from "../../order";
16
- import { getIndexDirect, clone } from "../../util/insert";
16
+ import { getIndexDirect, clone, getFunction } from "../../util/insert";
17
17
  import { ok } from "assert";
18
18
  import { getIdentifierInfo } from "../../util/identifiers";
19
19
  import { walk } from "../../traverse";
@@ -79,6 +79,13 @@ class ExplicitIdentifiers extends Transform {
79
79
  }
80
80
 
81
81
  transform(object, parents) {
82
+ if (object.name === "eval") {
83
+ var fn = getFunction(object, parents);
84
+ if (fn) {
85
+ fn.$requiresEval = true;
86
+ }
87
+ }
88
+
82
89
  var info = getIdentifierInfo(object, parents);
83
90
  if (info.isPropertyKey || info.isAccessor) {
84
91
  var propIndex = parents.findIndex(
@@ -24,13 +24,35 @@ export default class RenameLabels extends Transform {
24
24
  transform(object, parents) {
25
25
  return () => {
26
26
  var newName = null;
27
+ var isRemovable = object.body.type !== "BlockStatement";
28
+ var labelNeverUsed = true;
27
29
 
28
30
  walk(object, parents, (o, p) => {
29
31
  if (o.type == "BreakStatement" || o.type == "ContinueStatement") {
30
- var labelStatement = p.find((x) => isLoop(x));
32
+ function isContinuableStatement(x, stmtParents) {
33
+ return isLoop(x) && x.type !== "SwitchStatement";
34
+ }
35
+ function isBreakableStatement(x, stmtParents) {
36
+ return (
37
+ isLoop(x) ||
38
+ (x.type == "BlockStatement" &&
39
+ o.label &&
40
+ stmtParents[0] &&
41
+ stmtParents[0].type == "LabeledStatement")
42
+ );
43
+ }
44
+
45
+ var fn =
46
+ o.type == "ContinueStatement"
47
+ ? isContinuableStatement
48
+ : isBreakableStatement;
49
+
50
+ var labelStatement = p.find((node, i) => {
51
+ return fn(node, p.slice(i + 1));
52
+ });
31
53
 
32
54
  if (o.label && o.label.name == object.label.name) {
33
- if (object.body == labelStatement) {
55
+ if (object.body == labelStatement && isRemovable) {
34
56
  // In same loop
35
57
 
36
58
  o.label = null;
@@ -39,6 +61,7 @@ export default class RenameLabels extends Transform {
39
61
  newName = this.gen.generate();
40
62
  }
41
63
  o.label = Identifier(newName);
64
+ labelNeverUsed = false;
42
65
  }
43
66
  }
44
67
  }
@@ -46,7 +69,7 @@ export default class RenameLabels extends Transform {
46
69
 
47
70
  if (newName) {
48
71
  object.label = Identifier(newName);
49
- } else {
72
+ } else if (isRemovable || labelNeverUsed) {
50
73
  this.replace(object, clone(object.body));
51
74
  }
52
75
  };
@@ -76,6 +76,7 @@ export default class RGF extends Transform {
76
76
  if (
77
77
  object !== contextObject &&
78
78
  isFunction(object) &&
79
+ !object.$requiresEval &&
79
80
  !object.async &&
80
81
  !object.generator &&
81
82
  getVarContext(parents[0], parents.slice(1)) === contextObject
@@ -92,9 +93,12 @@ export default class RGF extends Transform {
92
93
  !this.options.globalVariables.has(o.name)
93
94
  ) {
94
95
  var info = getIdentifierInfo(o, p);
96
+ if (!info.spec.isReferenced) {
97
+ return;
98
+ }
95
99
  if (info.spec.isDefined) {
96
100
  defined.add(o.name);
97
- } else if (info.spec.isReferenced || info.spec.isModified) {
101
+ } else {
98
102
  referenced.add(o.name);
99
103
  }
100
104
  }
@@ -256,6 +260,7 @@ export default class RGF extends Transform {
256
260
  integrity: false,
257
261
  },
258
262
  eval: false,
263
+ hideInitializingCode: false,
259
264
  });
260
265
  var transforms = Object.values(obfuscator.transforms).filter(
261
266
  (x) => x.priority > this.priority
@@ -5,10 +5,12 @@ import Template from "../templates/template";
5
5
  import {
6
6
  BinaryExpression,
7
7
  CallExpression,
8
+ ExpressionStatement,
8
9
  ForStatement,
9
10
  FunctionExpression,
10
11
  Identifier,
11
12
  Literal,
13
+ MemberExpression,
12
14
  ReturnStatement,
13
15
  UpdateExpression,
14
16
  VariableDeclaration,
@@ -91,22 +93,20 @@ export default class Shuffle extends Transform {
91
93
  return;
92
94
  }
93
95
 
94
- var mode = ComputeProbabilityMap(
95
- this.options.shuffle,
96
- (x) => x,
97
- object.elements.map((x) => x.value)
98
- );
96
+ var mapped = object.elements.map((x) => x.value);
97
+
98
+ var mode = ComputeProbabilityMap(this.options.shuffle, (x) => x, mapped);
99
99
  if (mode) {
100
100
  var shift = getRandomInteger(
101
101
  1,
102
- Math.min(100, object.elements.length * 6)
102
+ Math.min(60, object.elements.length * 6)
103
103
  );
104
104
 
105
105
  var expr = Literal(shift);
106
106
  var name = this.getPlaceholder();
107
107
 
108
108
  if (mode == "hash") {
109
- var str = object.elements.map((x) => x.value + "").join("");
109
+ var str = mapped.join("");
110
110
  shift = Hash(str);
111
111
 
112
112
  if (!this.hashName) {
@@ -140,40 +140,98 @@ export default class Shuffle extends Transform {
140
140
  var code = [];
141
141
 
142
142
  var iName = this.getPlaceholder();
143
- code.push(
144
- ForStatement(
145
- VariableDeclaration(VariableDeclarator(iName, expr)),
146
- Identifier(iName),
147
- UpdateExpression("--", Identifier(iName), false),
148
- [Template(`${name}.unshift(${name}.pop())`).single()]
149
- )
150
- );
151
143
 
152
144
  var inPlace = false;
145
+ var inPlaceName;
146
+ var inPlaceBody;
147
+ var inPlaceIndex;
153
148
 
154
149
  var varDeclarator = parents[0];
155
150
  if (varDeclarator.type == "VariableDeclarator") {
156
151
  var varDec = parents[2];
157
152
  if (varDec.type == "VariableDeclaration") {
158
153
  var body = parents[3];
159
- if (varDec.declarations.length == 1 && Array.isArray(body)) {
160
- inPlace = true;
161
-
162
- var i = body.indexOf(varDec);
163
- ok(i != -1);
164
-
165
- body.splice(
166
- i + 1,
167
- 0,
168
- VariableDeclaration(
169
- VariableDeclarator(name, Identifier(varDeclarator.id.name))
170
- ),
171
- ...code
172
- );
154
+ if (
155
+ varDec.declarations.length == 1 &&
156
+ Array.isArray(body) &&
157
+ varDeclarator.id.type === "Identifier" &&
158
+ varDeclarator.init === object
159
+ ) {
160
+ inPlaceIndex = body.indexOf(varDec);
161
+ inPlaceBody = body;
162
+ inPlace = inPlaceIndex !== -1;
163
+ inPlaceName = varDeclarator.id.name;
173
164
  }
174
165
  }
175
166
  }
176
167
 
168
+ if (mode !== "hash") {
169
+ code.push(
170
+ Template(`
171
+ for ( var x = 16; x%4 === 0; x++) {
172
+ var z = 0;
173
+ ${
174
+ inPlace ? `${inPlaceName} = ${name}` : name
175
+ } = ${name}.concat((function(){
176
+ z++;
177
+ if(z === 1){
178
+ return [];
179
+ }
180
+
181
+ for( var i = ${getRandomInteger(5, 105)}; i; i-- ){
182
+ ${name}.unshift(${name}.pop());
183
+ }
184
+ return [];
185
+ })());
186
+ }
187
+ `).single()
188
+ );
189
+ }
190
+
191
+ code.push(
192
+ ForStatement(
193
+ VariableDeclaration(VariableDeclarator(iName, expr)),
194
+ Identifier(iName),
195
+ UpdateExpression("--", Identifier(iName), false),
196
+ [
197
+ // ${name}.unshift(${name}.pop());
198
+ ExpressionStatement(
199
+ CallExpression(
200
+ MemberExpression(
201
+ Identifier(name),
202
+ Identifier("unshift"),
203
+ false
204
+ ),
205
+ [
206
+ CallExpression(
207
+ MemberExpression(
208
+ Identifier(name),
209
+ Identifier("pop"),
210
+ false
211
+ ),
212
+ []
213
+ ),
214
+ ]
215
+ )
216
+ ),
217
+ ]
218
+ )
219
+ );
220
+
221
+ if (inPlace) {
222
+ var varDeclarator = parents[0];
223
+ ok(i != -1);
224
+
225
+ inPlaceBody.splice(
226
+ inPlaceIndex + 1,
227
+ 0,
228
+ VariableDeclaration(
229
+ VariableDeclarator(name, Identifier(varDeclarator.id.name))
230
+ ),
231
+ ...code
232
+ );
233
+ }
234
+
177
235
  if (!inPlace) {
178
236
  this.replace(
179
237
  object,