js-confuser 1.5.9 → 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 (143) hide show
  1. package/.github/workflows/node.js.yml +2 -2
  2. package/CHANGELOG.md +55 -0
  3. package/README.md +346 -165
  4. package/dist/constants.js +6 -2
  5. package/dist/index.js +9 -21
  6. package/dist/obfuscator.js +19 -31
  7. package/dist/options.js +5 -5
  8. package/dist/order.js +1 -3
  9. package/dist/presets.js +6 -7
  10. package/dist/probability.js +2 -4
  11. package/dist/templates/bufferToString.js +13 -0
  12. package/dist/templates/crash.js +3 -3
  13. package/dist/templates/es5.js +18 -0
  14. package/dist/templates/functionLength.js +16 -0
  15. package/dist/transforms/calculator.js +77 -21
  16. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +980 -367
  17. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +4 -1
  18. package/dist/transforms/controlFlowFlattening/switchCaseObfuscation.js +25 -26
  19. package/dist/transforms/deadCode.js +33 -25
  20. package/dist/transforms/dispatcher.js +8 -4
  21. package/dist/transforms/es5/antiDestructuring.js +2 -0
  22. package/dist/transforms/es5/es5.js +31 -34
  23. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +92 -58
  24. package/dist/transforms/finalizer.js +82 -0
  25. package/dist/transforms/flatten.js +229 -148
  26. package/dist/transforms/identifier/globalAnalysis.js +88 -0
  27. package/dist/transforms/identifier/globalConcealing.js +10 -83
  28. package/dist/transforms/identifier/movedDeclarations.js +35 -88
  29. package/dist/transforms/identifier/renameVariables.js +124 -59
  30. package/dist/transforms/identifier/variableAnalysis.js +58 -62
  31. package/dist/transforms/lock/lock.js +0 -37
  32. package/dist/transforms/minify.js +60 -57
  33. package/dist/transforms/opaquePredicates.js +1 -1
  34. package/dist/transforms/preparation/preparation.js +2 -2
  35. package/dist/transforms/preparation.js +231 -0
  36. package/dist/transforms/renameLabels.js +1 -1
  37. package/dist/transforms/rgf.js +139 -247
  38. package/dist/transforms/stack.js +128 -26
  39. package/dist/transforms/string/encoding.js +150 -179
  40. package/dist/transforms/string/stringCompression.js +14 -15
  41. package/dist/transforms/string/stringConcealing.js +25 -8
  42. package/dist/transforms/string/stringEncoding.js +13 -24
  43. package/dist/transforms/transform.js +12 -19
  44. package/dist/traverse.js +24 -10
  45. package/dist/util/gen.js +17 -1
  46. package/dist/util/identifiers.js +37 -3
  47. package/dist/util/insert.js +35 -4
  48. package/dist/util/random.js +15 -0
  49. package/docs/ControlFlowFlattening.md +595 -0
  50. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  51. package/{Integrity.md → docs/Integrity.md} +2 -2
  52. package/docs/RGF.md +419 -0
  53. package/package.json +5 -5
  54. package/src/constants.ts +3 -0
  55. package/src/index.ts +2 -2
  56. package/src/obfuscator.ts +19 -31
  57. package/src/options.ts +14 -103
  58. package/src/order.ts +1 -5
  59. package/src/presets.ts +6 -7
  60. package/src/probability.ts +2 -3
  61. package/src/templates/bufferToString.ts +68 -0
  62. package/src/templates/crash.ts +15 -19
  63. package/src/templates/es5.ts +131 -0
  64. package/src/templates/functionLength.ts +14 -0
  65. package/src/transforms/calculator.ts +122 -59
  66. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +1583 -571
  67. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +4 -1
  68. package/src/transforms/deadCode.ts +383 -26
  69. package/src/transforms/dispatcher.ts +9 -4
  70. package/src/transforms/es5/antiDestructuring.ts +2 -0
  71. package/src/transforms/es5/es5.ts +32 -77
  72. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +133 -129
  73. package/src/transforms/{hexadecimalNumbers.ts → finalizer.ts} +29 -13
  74. package/src/transforms/flatten.ts +357 -300
  75. package/src/transforms/identifier/globalAnalysis.ts +85 -0
  76. package/src/transforms/identifier/globalConcealing.ts +14 -103
  77. package/src/transforms/identifier/movedDeclarations.ts +49 -102
  78. package/src/transforms/identifier/renameVariables.ts +149 -78
  79. package/src/transforms/identifier/variableAnalysis.ts +66 -73
  80. package/src/transforms/lock/lock.ts +1 -42
  81. package/src/transforms/minify.ts +91 -75
  82. package/src/transforms/opaquePredicates.ts +2 -2
  83. package/src/transforms/preparation.ts +238 -0
  84. package/src/transforms/renameLabels.ts +2 -2
  85. package/src/transforms/rgf.ts +213 -405
  86. package/src/transforms/stack.ts +156 -36
  87. package/src/transforms/string/encoding.ts +115 -212
  88. package/src/transforms/string/stringCompression.ts +27 -18
  89. package/src/transforms/string/stringConcealing.ts +39 -9
  90. package/src/transforms/string/stringEncoding.ts +18 -18
  91. package/src/transforms/transform.ts +21 -23
  92. package/src/traverse.ts +23 -4
  93. package/src/types.ts +2 -1
  94. package/src/util/gen.ts +28 -3
  95. package/src/util/identifiers.ts +43 -2
  96. package/src/util/insert.ts +38 -3
  97. package/src/util/random.ts +13 -0
  98. package/test/code/Cash.test.ts +1 -1
  99. package/test/code/Dynamic.test.ts +12 -10
  100. package/test/code/ES6.src.js +146 -0
  101. package/test/code/ES6.test.ts +28 -2
  102. package/test/index.test.ts +2 -1
  103. package/test/probability.test.ts +44 -0
  104. package/test/templates/template.test.ts +1 -1
  105. package/test/transforms/antiTooling.test.ts +22 -0
  106. package/test/transforms/calculator.test.ts +40 -0
  107. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +702 -160
  108. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +173 -0
  109. package/test/transforms/deadCode.test.ts +66 -15
  110. package/test/transforms/dispatcher.test.ts +20 -1
  111. package/test/transforms/es5/antiDestructuring.test.ts +16 -0
  112. package/test/transforms/flatten.test.ts +399 -86
  113. package/test/transforms/identifier/movedDeclarations.test.ts +63 -8
  114. package/test/transforms/identifier/renameVariables.test.ts +119 -0
  115. package/test/transforms/lock/antiDebug.test.ts +2 -2
  116. package/test/transforms/lock/lock.test.ts +1 -48
  117. package/test/transforms/minify.test.ts +104 -0
  118. package/test/transforms/preparation.test.ts +157 -0
  119. package/test/transforms/rgf.test.ts +261 -381
  120. package/test/transforms/stack.test.ts +143 -21
  121. package/test/transforms/string/stringCompression.test.ts +39 -0
  122. package/test/transforms/string/stringConcealing.test.ts +82 -0
  123. package/test/transforms/string/stringEncoding.test.ts +53 -2
  124. package/test/transforms/transform.test.ts +66 -0
  125. package/test/traverse.test.ts +139 -0
  126. package/test/util/identifiers.test.ts +113 -1
  127. package/test/util/insert.test.ts +57 -3
  128. package/src/transforms/controlFlowFlattening/choiceFlowObfuscation.ts +0 -87
  129. package/src/transforms/controlFlowFlattening/controlFlowObfuscation.ts +0 -203
  130. package/src/transforms/controlFlowFlattening/switchCaseObfuscation.ts +0 -130
  131. package/src/transforms/eval.ts +0 -89
  132. package/src/transforms/hideInitializingCode.ts +0 -432
  133. package/src/transforms/identifier/nameRecycling.ts +0 -280
  134. package/src/transforms/label.ts +0 -64
  135. package/src/transforms/preparation/nameConflicts.ts +0 -102
  136. package/src/transforms/preparation/preparation.ts +0 -176
  137. package/test/transforms/controlFlowFlattening/controlFlowObfuscation.test.ts +0 -101
  138. package/test/transforms/controlFlowFlattening/switchCaseObfuscation.test.ts +0 -120
  139. package/test/transforms/eval.test.ts +0 -131
  140. package/test/transforms/hideInitializingCode.test.ts +0 -336
  141. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
  142. package/test/transforms/preparation/nameConflicts.test.ts +0 -52
  143. package/test/transforms/preparation/preparation.test.ts +0 -62
@@ -13,6 +13,9 @@ var _transform = _interopRequireDefault(require("../transform"));
13
13
 
14
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
15
 
16
+ /**
17
+ * Expression Obfuscation runs before Control Flow Flattening
18
+ */
16
19
  class ExpressionObfuscation extends _transform.default {
17
20
  constructor(o) {
18
21
  super(o);
@@ -27,7 +30,7 @@ class ExpressionObfuscation extends _transform.default {
27
30
  var exprs = [];
28
31
  var deleteExprs = [];
29
32
  object.body.forEach((stmt, i) => {
30
- if (stmt.type == "ExpressionStatement") {
33
+ if (stmt.type == "ExpressionStatement" && !stmt.directive) {
31
34
  var expr = stmt.expression;
32
35
 
33
36
  if (expr.type == "UnaryExpression" && !(expr.operator === "typeof" && expr.argument.type === "Identifier") && exprs.length // typeof is special
@@ -28,22 +28,23 @@ class SwitchCaseObfuscation extends _transform.default {
28
28
  }
29
29
 
30
30
  match(object, parents) {
31
- return object.type == "SwitchStatement" && !object.cases.find(x => !(x.test && typeof x.test === "object" && x.test.type == "Literal" && typeof x.test.value === "number" && Math.abs(x.test.value) < 100000));
31
+ return object.type == "SwitchStatement" && (object.$controlFlowFlattening || !object.cases.find(x => !(x.test && typeof x.test === "object" && x.test.type == "Literal" && typeof x.test.value === "number" && Math.abs(x.test.value) < 100000)));
32
32
  }
33
33
 
34
34
  transform(object, parents) {
35
35
  var types = new Set();
36
36
  (0, _traverse.walk)(object.discriminant, [object, ...parents], (o, p) => {
37
37
  if (o.type) {
38
- if (object.$controlFlowFlattening && o.type == "BinaryExpression" && o.operator === "+") {} else {
39
- types.add(o.type);
40
- }
38
+ types.add(o.type);
41
39
  }
42
40
  });
43
- types.delete("Identifier");
44
41
 
45
- if (types.size) {
46
- return;
42
+ if (!object.$controlFlowFlattening) {
43
+ types.delete("Identifier");
44
+
45
+ if (types.size) {
46
+ return;
47
+ }
47
48
  }
48
49
 
49
50
  var body = parents[0];
@@ -64,33 +65,25 @@ class SwitchCaseObfuscation extends _transform.default {
64
65
  return;
65
66
  }
66
67
 
67
- var factor = (0, _random.getRandomInteger)(-150, 150);
68
-
69
- if (factor == 0) {
70
- factor = 2;
71
- }
72
-
68
+ var factor = (0, _random.getRandomInteger)(2, 100);
73
69
  var offset = (0, _random.getRandomInteger)(-250, 250);
74
70
  var newVar = this.getPlaceholder();
75
71
  var newStates = [];
76
72
  var max;
77
- object.cases.forEach(x => {
78
- var current = x.test.value;
79
- var value = current * factor + offset;
80
- newStates.push(value);
81
-
82
- if (!max || Math.abs(value) > max) {
83
- max = Math.abs(value);
73
+ object.cases.forEach((caseObject, i) => {
74
+ if (caseObject.test && caseObject.test.type === "Literal" && typeof caseObject.test.value === "number") {
75
+ var current = caseObject.test.value;
76
+ var value = current * factor + offset;
77
+ newStates[i] = value;
78
+
79
+ if (!max || Math.abs(value) > max) {
80
+ max = Math.abs(value);
81
+ }
84
82
  }
85
83
  });
86
84
 
87
85
  if (max > 100000) {
88
86
  return;
89
- }
90
-
91
- if (new Set(newStates).size !== newStates.length) {
92
- // not possible because of clashing case test
93
- return;
94
87
  } // State variable declaration
95
88
 
96
89
 
@@ -98,7 +91,13 @@ class SwitchCaseObfuscation extends _transform.default {
98
91
  object.discriminant = (0, _gen.Identifier)(newVar); // possible so override
99
92
 
100
93
  object.cases.forEach((x, i) => {
101
- x.test = (0, _gen.Literal)(newStates[i]);
94
+ if (x.test) {
95
+ if (x.test.type === "Literal" && typeof x.test.value === "number") {
96
+ x.test = (0, _gen.Literal)(newStates[i]);
97
+ } else {
98
+ x.test = (0, _gen.BinaryExpression)("+", (0, _gen.BinaryExpression)("*", x.test, (0, _gen.Literal)(factor)), (0, _gen.Literal)(offset));
99
+ }
100
+ }
102
101
  });
103
102
  }
104
103
 
@@ -25,7 +25,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
25
25
 
26
26
  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; }
27
27
 
28
- const templates = [(0, _template.default)("\n function curCSS( elem, name, computed ) {\n var ret;\n \n computed = computed || getStyles( elem );\n \n if ( computed ) {\n ret = computed.getPropertyValue( name ) || computed[ name ];\n \n if ( ret === \"\" && !isAttached( elem ) ) {\n ret = redacted.style( elem, name );\n }\n }\n \n return ret !== undefined ?\n \n // Support: IE <=9 - 11+\n // IE returns zIndex value as an integer.\n ret + \"\" :\n ret;\n }"), (0, _template.default)("\n function Example() {\n var state = redacted.useState(false);\n return x(\n ErrorBoundary,\n null,\n x(\n DisplayName,\n null,\n )\n );\n }"), (0, _template.default)("\n const path = require('path');\nconst { version } = require('../../package');\nconst { version: dashboardPluginVersion } = require('@redacted/enterprise-plugin/package');\nconst { version: componentsVersion } = require('@redacted/components/package');\nconst { sdkVersion } = require('@redacted/enterprise-plugin');\nconst isStandaloneExecutable = require('../utils/isStandaloneExecutable');\nconst resolveLocalRedactedPath = require('./resolve-local-redacted-path');\n\nconst redactedPath = path.resolve(__dirname, '../redacted.js');"), (0, _template.default)("\nmodule.exports = async (resolveLocalRedactedPath = ()=>{throw new Error(\"No redacted path provided\")}) => {\n const cliParams = new Set(process.argv.slice(2));\n if (!cliParams.has('--version')) {\n if (cliParams.size !== 1) return false;\n if (!cliParams.has('-v')) return false;\n }\n\n const installationModePostfix = await (async (isStandaloneExecutable, redactedPath) => {\n if (isStandaloneExecutable) return ' (standalone)';\n if (redactedPath === (await resolveLocalRedactedPath())) return ' (local)';\n return '';\n })();\n\n return true;\n};"), (0, _template.default)("\nfunction setCookie(cname, cvalue, exdays) {\n var d = new Date();\n d.setTime(d.getTime() + (exdays*24*60*60*1000));\n var expires = \"expires=\"+ d.toUTCString();\n document.cookie = cname + \"=\" + cvalue + \";\" + expires + \";path=/\";\n}"), (0, _template.default)("function getCookie(cname) {\n var name = cname + \"=\";\n var decodedCookie = decodeURIComponent(document.cookie);\n var ca = decodedCookie.split(';');\n for(var i = 0; i <ca.length; i++) {\n var c = ca[i];\n while (c.charAt(0) == ' ') {\n c = c.substring(1);\n }\n if (c.indexOf(name) == 0) {\n return c.substring(name.length, c.length);\n }\n }\n return \"\";\n}"), (0, _template.default)("function getLocalStorageValue(key, cb){\n if ( typeof key !== \"string\" ) {\n throw new Error(\"Invalid data key provided (not type string)\")\n }\n if ( !key ) {\n throw new Error(\"Invalid data key provided (empty string)\")\n }\n var value = window.localStorage.getItem(key)\n try {\n value = JSON.parse(value)\n } catch ( e ) {\n cb(new Error(\"Serialization error for data '\" + key + \"': \" + e.message))\n }\n\n cb(null, value)\n }"), (0, _template.default)("\n \n var __ = \"(c=ak(<~F$VU'9f)~><&85dBPL-module/from\";\n var s = \"q:function(){var ad=ad=>b(ad-29);if(!T.r[(typeof ab==ad(123)?\";\n var g = \"return U[c[c[d(-199)]-b(205)]]||V[ae(b(166))];case T.o[c[c[c[d(-199)]+d(-174)]-(c[b(119)]-(c[d(-199)]-163))]+ae(b(146))](0)==b(167)?d(-130):-d(-144)\";\n\n __.match(s + g);\n "), (0, _template.default)("\n function vec_pack(vec) {\n return vec[1] * 67108864 + (vec[0] < 0 ? 33554432 | vec[0] : vec[0]);\n }\n \n function vec_unpack(number) {\n switch (((number & 33554432) !== 0) * 1 + (number < 0) * 2) {\n case 0:\n return [number % 33554432, Math.trunc(number / 67108864)];\n case 1:\n return [\n (number % 33554432) - 33554432,\n Math.trunc(number / 67108864) + 1,\n ];\n case 2:\n return [\n (((number + 33554432) % 33554432) + 33554432) % 33554432,\n Math.round(number / 67108864),\n ];\n case 3:\n return [number % 33554432, Math.trunc(number / 67108864)];\n }\n }\n \n let a = vec_pack([2, 4]);\n let b = vec_pack([1, 2]);\n \n let c = a + b; // Vector addition\n let d = c - b; // Vector subtraction\n let e = d * 2; // Scalar multiplication\n let f = e / 2; // Scalar division\n \n console.log(vec_unpack(c)); // [3, 6]\n console.log(vec_unpack(d)); // [2, 4]\n console.log(vec_unpack(e)); // [4, 8]\n console.log(vec_unpack(f)); // [2, 4]\n "), (0, _template.default)("\n function buildCharacterMap(str) {\n const characterMap = {};\n \n for (let char of str.replace(/[^w]/g, \"\").toLowerCase())\n characterMap[char] = characterMap[char] + 1 || 1;\n \n return characterMap;\n }\n \n function isAnagrams(stringA, stringB) {\n const stringAMap = buildCharMap(stringA);\n const stringBMap = buildCharMap(stringB);\n \n for (let char in stringAMap) {\n if (stringAMap[char] !== stringBMap[char]) {\n return false;\n }\n }\n \n if (Object.keys(stringAMap).length !== Object.keys(stringBMap).length) {\n return false;\n }\n \n return true;\n }\n \n /**\n * @param {TreeNode} root\n * @return {boolean}\n */\n function isBalanced(root) {\n const height = getHeightBalanced(root);\n return height !== Infinity;\n }\n \n function getHeightBalanced(node) {\n if (!node) {\n return -1;\n }\n \n const leftTreeHeight = getHeightBalanced(node.left);\n const rightTreeHeight = getHeightBalanced(node.right);\n \n const heightDiff = Math.abs(leftTreeHeight - rightTreeHeight);\n \n if (\n leftTreeHeight === Infinity ||\n rightTreeHeight === Infinity ||\n heightDiff > 1\n ) {\n return Infinity;\n }\n \n const currentHeight = Math.max(leftTreeHeight, rightTreeHeight) + 1;\n return currentHeight;\n }\n \n window[\"__GLOBAL__HELPERS__\"] = {\n buildCharacterMap,\n isAnagrams,\n isBalanced,\n getHeightBalanced,\n };\n ")];
28
+ const templates = [(0, _template.default)("\n function curCSS( elem, name, computed ) {\n var ret;\n \n computed = computed || getStyles( elem );\n \n if ( computed ) {\n ret = computed.getPropertyValue( name ) || computed[ name ];\n \n if ( ret === \"\" && !isAttached( elem ) ) {\n ret = redacted.style( elem, name );\n }\n }\n \n return ret !== undefined ?\n \n // Support: IE <=9 - 11+\n // IE returns zIndex value as an integer.\n ret + \"\" :\n ret;\n }"), (0, _template.default)("\n function Example() {\n var state = redacted.useState(false);\n return x(\n ErrorBoundary,\n null,\n x(\n DisplayName,\n null,\n )\n );\n }"), (0, _template.default)("\n const path = require('path');\nconst { version } = require('../../package');\nconst { version: dashboardPluginVersion } = require('@redacted/enterprise-plugin/package');\nconst { version: componentsVersion } = require('@redacted/components/package');\nconst { sdkVersion } = require('@redacted/enterprise-plugin');\nconst isStandaloneExecutable = require('../utils/isStandaloneExecutable');\nconst resolveLocalRedactedPath = require('./resolve-local-redacted-path');\n\nconst redactedPath = path.resolve(__dirname, '../redacted.js');"), (0, _template.default)("\nmodule.exports = async (resolveLocalRedactedPath = ()=>{throw new Error(\"No redacted path provided\")}) => {\n const cliParams = new Set(process.argv.slice(2));\n if (!cliParams.has('--version')) {\n if (cliParams.size !== 1) return false;\n if (!cliParams.has('-v')) return false;\n }\n\n const installationModePostfix = await (async (isStandaloneExecutable, redactedPath) => {\n if (isStandaloneExecutable) return ' (standalone)';\n if (redactedPath === (await resolveLocalRedactedPath())) return ' (local)';\n return '';\n })();\n\n return true;\n};"), (0, _template.default)("\nfunction setCookie(cname, cvalue, exdays) {\n var d = new Date();\n d.setTime(d.getTime() + (exdays*24*60*60*1000));\n var expires = \"expires=\"+ d.toUTCString();\n document.cookie = cname + \"=\" + cvalue + \";\" + expires + \";path=/\";\n}"), (0, _template.default)("function getCookie(cname) {\n var name = cname + \"=\";\n var decodedCookie = decodeURIComponent(document.cookie);\n var ca = decodedCookie.split(';');\n for(var i = 0; i <ca.length; i++) {\n var c = ca[i];\n while (c.charAt(0) == ' ') {\n c = c.substring(1);\n }\n if (c.indexOf(name) == 0) {\n return c.substring(name.length, c.length);\n }\n }\n return \"\";\n}"), (0, _template.default)("function getLocalStorageValue(key, cb){\n if ( typeof key !== \"string\" ) {\n throw new Error(\"Invalid data key provided (not type string)\")\n }\n if ( !key ) {\n throw new Error(\"Invalid data key provided (empty string)\")\n }\n var value = window.localStorage.getItem(key)\n try {\n value = JSON.parse(value)\n } catch ( e ) {\n cb(new Error(\"Serialization error for data '\" + key + \"': \" + e.message))\n }\n\n cb(null, value)\n }"), (0, _template.default)("\n \n var __ = \"(c=ak(<~F$VU'9f)~><&85dBPL-module/from\";\n var s = \"q:function(){var ad=ad=>b(ad-29);if(!T.r[(typeof ab==ad(123)?\";\n var g = \"return U[c[c[d(-199)]-b(205)]]||V[ae(b(166))];case T.o[c[c[c[d(-199)]+d(-174)]-(c[b(119)]-(c[d(-199)]-163))]+ae(b(146))](0)==b(167)?d(-130):-d(-144)\";\n\n __.match(s + g);\n "), (0, _template.default)("\n function vec_pack(vec) {\n return vec[1] * 67108864 + (vec[0] < 0 ? 33554432 | vec[0] : vec[0]);\n }\n \n function vec_unpack(number) {\n switch (((number & 33554432) !== 0) * 1 + (number < 0) * 2) {\n case 0:\n return [number % 33554432, Math.trunc(number / 67108864)];\n case 1:\n return [\n (number % 33554432) - 33554432,\n Math.trunc(number / 67108864) + 1,\n ];\n case 2:\n return [\n (((number + 33554432) % 33554432) + 33554432) % 33554432,\n Math.round(number / 67108864),\n ];\n case 3:\n return [number % 33554432, Math.trunc(number / 67108864)];\n }\n }\n \n let a = vec_pack([2, 4]);\n let b = vec_pack([1, 2]);\n \n let c = a + b; // Vector addition\n let d = c - b; // Vector subtraction\n let e = d * 2; // Scalar multiplication\n let f = e / 2; // Scalar division\n \n console.log(vec_unpack(c)); // [3, 6]\n console.log(vec_unpack(d)); // [2, 4]\n console.log(vec_unpack(e)); // [4, 8]\n console.log(vec_unpack(f)); // [2, 4]\n "), (0, _template.default)("\n function buildCharacterMap(str) {\n const characterMap = {};\n \n for (let char of str.replace(/[^w]/g, \"\").toLowerCase())\n characterMap[char] = characterMap[char] + 1 || 1;\n \n return characterMap;\n }\n \n function isAnagrams(stringA, stringB) {\n const stringAMap = buildCharMap(stringA);\n const stringBMap = buildCharMap(stringB);\n \n for (let char in stringAMap) {\n if (stringAMap[char] !== stringBMap[char]) {\n return false;\n }\n }\n \n if (Object.keys(stringAMap).length !== Object.keys(stringBMap).length) {\n return false;\n }\n \n return true;\n }\n \n /**\n * @param {TreeNode} root\n * @return {boolean}\n */\n function isBalanced(root) {\n const height = getHeightBalanced(root);\n return height !== Infinity;\n }\n \n function getHeightBalanced(node) {\n if (!node) {\n return -1;\n }\n \n const leftTreeHeight = getHeightBalanced(node.left);\n const rightTreeHeight = getHeightBalanced(node.right);\n \n const heightDiff = Math.abs(leftTreeHeight - rightTreeHeight);\n \n if (\n leftTreeHeight === Infinity ||\n rightTreeHeight === Infinity ||\n heightDiff > 1\n ) {\n return Infinity;\n }\n \n const currentHeight = Math.max(leftTreeHeight, rightTreeHeight) + 1;\n return currentHeight;\n }\n \n window[\"__GLOBAL__HELPERS__\"] = {\n buildCharacterMap,\n isAnagrams,\n isBalanced,\n getHeightBalanced,\n };\n "), (0, _template.default)("\n function ListNode(){}\n var addTwoNumbers = function(l1, l2) {\n var carry = 0;\n var sum = 0;\n var head = new ListNode(0);\n var now = head;\n var a = l1;\n var b = l2;\n while (a !== null || b !== null) {\n sum = (a ? a.val : 0) + (b ? b.val : 0) + carry;\n carry = Math.floor(sum / 10);\n now.next = new ListNode(sum % 10);\n now = now.next;\n a = a ? a.next : null;\n b = b ? b.next : null;\n }\n if (carry) now.next = new ListNode(carry);\n return head.next;\n };\n\n console.log(addTwoNumbers)\n "), (0, _template.default)("\n var threeSum = function(nums) {\n var len = nums.length;\n var res = [];\n var l = 0;\n var r = 0;\n nums.sort((a, b) => (a - b));\n for (var i = 0; i < len; i++) {\n if (i > 0 && nums[i] === nums[i - 1]) continue;\n l = i + 1;\n r = len - 1;\n while (l < r) {\n if (nums[i] + nums[l] + nums[r] < 0) {\n l++;\n } else if (nums[i] + nums[l] + nums[r] > 0) {\n r--;\n } else {\n res.push([nums[i], nums[l], nums[r]]);\n while (l < r && nums[l] === nums[l + 1]) l++;\n while (l < r && nums[r] === nums[r - 1]) r--;\n l++;\n r--;\n }\n }\n }\n return res;\n };\n console.log(threeSum)\n "), (0, _template.default)("\n var combinationSum2 = function(candidates, target) {\n var res = [];\n var len = candidates.length;\n candidates.sort((a, b) => (a - b));\n dfs(res, [], 0, len, candidates, target);\n return res;\n };\n\n var dfs = function (res, stack, index, len, candidates, target) {\n var tmp = null;\n if (target < 0) return;\n if (target === 0) return res.push(stack);\n for (var i = index; i < len; i++) {\n if (candidates[i] > target) break;\n if (i > index && candidates[i] === candidates[i - 1]) continue;\n tmp = Array.from(stack);\n tmp.push(candidates[i]);\n dfs(res, tmp, i + 1, len, candidates, target - candidates[i]);\n }\n };\n\n console.log(combinationSum2);\n "), (0, _template.default)("\n var isScramble = function(s1, s2) {\n return helper({}, s1, s2);\n };\n \n var helper = function (dp, s1, s2) {\n var map = {};\n \n if (dp[s1 + s2] !== undefined) return dp[s1 + s2];\n if (s1 === s2) return true;\n \n for (var j = 0; j < s1.length; j++) {\n if (map[s1[j]] === undefined) map[s1[j]] = 0;\n if (map[s2[j]] === undefined) map[s2[j]] = 0;\n map[s1[j]]++;\n map[s2[j]]--;\n }\n \n for (var key in map) {\n if (map[key] !== 0) {\n dp[s1 + s2] = false;\n return false;\n }\n }\n \n for (var i = 1; i < s1.length; i++) {\n if ((helper(dp, s1.substr(0, i), s2.substr(0, i))\n && helper(dp, s1.substr(i), s2.substr(i))) ||\n (helper(dp, s1.substr(0, i), s2.substr(s2.length - i))\n && helper(dp, s1.substr(i), s2.substr(0, s2.length - i)))) {\n dp[s1 + s2] = true;\n return true;\n }\n }\n \n dp[s1 + s2] = false;\n return false;\n };\n\n console.log(isScramble);\n "), (0, _template.default)("\n var candy = function(ratings) {\n var len = ratings.length;\n var res = [];\n var sum = 0;\n for (var i = 0; i < len; i++) {\n res.push((i !== 0 && ratings[i] > ratings[i - 1]) ? (res[i - 1] + 1) : 1);\n }\n for (var j = len - 1; j >= 0; j--) {\n if (j !== len - 1 && ratings[j] > ratings[j + 1]) res[j] = Math.max(res[j], res[j + 1] + 1);\n sum += res[j];\n }\n return sum;\n };\n \n console.log(candy)\n "), (0, _template.default)("\n var maxPoints = function(points) {\n var max = 0;\n var map = {};\n var localMax = 0;\n var samePoint = 0;\n var k = 0;\n var len = points.length;\n for (var i = 0; i < len; i++) {\n map = {};\n localMax = 0;\n samePoint = 1;\n for (var j = i + 1; j < len; j++) {\n if (points[i].x === points[j].x && points[i].y === points[j].y) {\n samePoint++;\n continue;\n }\n if (points[i].y === points[j].y) k = Number.MAX_SAFE_INTEGER;\n else k = (points[i].x - points[j].x) / (points[i].y - points[j].y);\n if (!map[k]) map[k] = 0;\n map[k]++;\n localMax = Math.max(localMax, map[k]);\n }\n localMax += samePoint;\n max = Math.max(max, localMax);\n }\n return max;\n };\n \n console.log(maxPoints)\n "), (0, _template.default)("\n var maximumGap = function(nums) {\n var len = nums.length;\n if (len < 2) return 0;\n \n var max = Math.max(...nums);\n var min = Math.min(...nums);\n if (max === min) return 0;\n \n var minBuckets = Array(len - 1).fill(Number.MAX_SAFE_INTEGER);\n var maxBuckets = Array(len - 1).fill(Number.MIN_SAFE_INTEGER);\n var gap = Math.ceil((max - min) / (len - 1));\n var index = 0;\n for (var i = 0; i < len; i++) {\n if (nums[i] === min || nums[i] === max) continue;\n index = Math.floor((nums[i] - min) / gap);\n minBuckets[index] = Math.min(minBuckets[index], nums[i]);\n maxBuckets[index] = Math.max(maxBuckets[index], nums[i]);\n }\n \n var maxGap = Number.MIN_SAFE_INTEGER;\n var preVal = min;\n for (var j = 0; j < len - 1; j++) {\n if (minBuckets[j] === Number.MAX_SAFE_INTEGER && maxBuckets[j] === Number.MIN_SAFE_INTEGER) continue;\n maxGap = Math.max(maxGap, minBuckets[j] - preVal);\n preVal = maxBuckets[j];\n }\n maxGap = Math.max(maxGap, max - preVal);\n \n return maxGap;\n };\n\n console.log(maximumGap);\n "), (0, _template.default)("\n /**\n * @param {number} capacity\n */\n var LRUCache = function(capacity) {\n this.capacity = capacity;\n this.length = 0;\n this.map = {};\n this.head = null;\n this.tail = null;\n };\n \n /** \n * @param {number} key\n * @return {number}\n */\n LRUCache.prototype.get = function(key) {\n var node = this.map[key];\n if (node) {\n this.remove(node);\n this.insert(node.key, node.val);\n return node.val;\n } else {\n return -1;\n }\n };\n \n /** \n * @param {number} key \n * @param {number} value\n * @return {void}\n */\n LRUCache.prototype.put = function(key, value) {\n if (this.map[key]) {\n this.remove(this.map[key]);\n this.insert(key, value);\n } else {\n if (this.length === this.capacity) {\n this.remove(this.head);\n this.insert(key, value);\n } else {\n this.insert(key, value);\n this.length++;\n }\n }\n };\n \n /** \n * Your LRUCache object will be instantiated and called as such:\n * var obj = Object.create(LRUCache).createNew(capacity)\n * var param_1 = obj.get(key)\n * obj.put(key,value)\n */\n \n LRUCache.prototype.remove = function (node) {\n var prev = node.prev;\n var next = node.next;\n if (next) next.prev = prev;\n if (prev) prev.next = next;\n if (this.head === node) this.head = next;\n if (this.tail === node) this.tail = prev;\n delete this.map[node.key];\n };\n \n LRUCache.prototype.insert = function (key, val) {\n var node = new List(key, val);\n if (!this.tail) {\n this.tail = node;\n this.head = node;\n } else {\n this.tail.next = node;\n node.prev = this.tail;\n this.tail = node;\n }\n this.map[key] = node;\n };\n\n console.log(LRUCache);\n "), (0, _template.default)("\n var isInterleave = function(s1, s2, s3) {\n var dp = {};\n if (s3.length !== s1.length + s2.length) return false;\n return helper(s1, s2, s3, 0, 0, 0, dp);\n };\n \n var helper = function (s1, s2, s3, i, j, k, dp) {\n var res = false;\n \n if (k >= s3.length) return true;\n if (dp['' + i + j + k] !== undefined) return dp['' + i + j + k];\n \n if (s3[k] === s1[i] && s3[k] === s2[j]) {\n res = helper(s1, s2, s3, i + 1, j, k + 1, dp) || helper(s1, s2, s3, i, j + 1, k + 1, dp);\n } else if (s3[k] === s1[i]) {\n res = helper(s1, s2, s3, i + 1, j, k + 1, dp);\n } else if (s3[k] === s2[j]) {\n res = helper(s1, s2, s3, i, j + 1, k + 1, dp);\n }\n \n dp['' + i + j + k] = res;\n \n return res;\n };\n\n console.log(isInterleave);\n "), (0, _template.default)("\n var solveNQueens = function(n) {\n var res = [];\n if (n === 1 || n >= 4) dfs(res, [], n, 0);\n return res;\n };\n \n var dfs = function (res, points, n, index) {\n for (var i = index; i < n; i++) {\n if (points.length !== i) return;\n for (var j = 0; j < n; j++) {\n if (isValid(points, [i, j])) {\n points.push([i, j]);\n dfs(res, points, n, i + 1);\n if (points.length === n) res.push(buildRes(points));\n points.pop();\n }\n }\n }\n };\n \n var buildRes = function (points) {\n var res = [];\n var n = points.length;\n for (var i = 0; i < n; i++) {\n res[i] = '';\n for (var j = 0; j < n; j++) {\n res[i] += (points[i][1] === j ? 'Q' : '.');\n }\n }\n return res;\n };\n \n var isValid = function (oldPoints, newPoint) {\n var len = oldPoints.length;\n for (var i = 0; i < len; i++) {\n if (oldPoints[i][0] === newPoint[0] || oldPoints[i][1] === newPoint[1]) return false;\n if (Math.abs((oldPoints[i][0] - newPoint[0]) / (oldPoints[i][1] - newPoint[1])) === 1) return false;\n }\n return true;\n };\n\n console.log(solveNQueens);\n ")];
29
29
  /**
30
30
  * Adds dead code to blocks.
31
31
  *
@@ -37,8 +37,6 @@ class DeadCode extends _transform.default {
37
37
  constructor(o) {
38
38
  super(o, _order.ObfuscateOrder.DeadCode);
39
39
 
40
- _defineProperty(this, "usedNames", void 0);
41
-
42
40
  _defineProperty(this, "made", void 0);
43
41
 
44
42
  this.made = 0;
@@ -49,29 +47,39 @@ class DeadCode extends _transform.default {
49
47
  }
50
48
 
51
49
  transform(object, parents) {
52
- if ((0, _probability.ComputeProbabilityMap)(this.options.deadCode)) {
53
- return () => {
54
- this.made++;
55
-
56
- if (this.made > 100) {
57
- return;
58
- }
59
-
60
- var name = this.getPlaceholder();
61
- var variableDeclaration = (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(name, (0, _gen.Literal)(false)));
62
- var body = (0, _insert.getBlockBody)(object);
63
- var index = (0, _random.getRandomInteger)(0, body.length);
64
- var template;
65
-
66
- do {
67
- template = (0, _random.choice)(templates);
68
- } while (this.options.es5 && template.source.includes("async"));
69
-
70
- var ifStatement = (0, _gen.IfStatement)((0, _gen.Identifier)(name), template.compile(), null);
71
- body.splice(index, 0, ifStatement);
72
- (0, _insert.prepend)(object, variableDeclaration);
73
- };
50
+ if (!(0, _probability.ComputeProbabilityMap)(this.options.deadCode)) {
51
+ return;
52
+ } // Hard-coded limit of 100 Dead Code insertions
53
+
54
+
55
+ this.made++;
56
+
57
+ if (this.made > 100) {
58
+ return;
74
59
  }
60
+
61
+ return () => {
62
+ var body = (0, _insert.getBlockBody)(object); // Do not place code before Import statements or 'use strict' directives
63
+
64
+ var safeOffset = 0;
65
+
66
+ for (var node of body) {
67
+ if (node.type === "ImportDeclaration" || node.directive) safeOffset++;else break;
68
+ }
69
+
70
+ var index = (0, _random.getRandomInteger)(safeOffset, body.length);
71
+ var name = this.getPlaceholder();
72
+ var variableDeclaration = (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(name, (0, _gen.Literal)(false)));
73
+ var template;
74
+
75
+ do {
76
+ template = (0, _random.choice)(templates);
77
+ } while (this.options.es5 && template.source.includes("async"));
78
+
79
+ var ifStatement = (0, _gen.IfStatement)((0, _gen.Identifier)(name), template.compile(), null);
80
+ body.splice(index, 0, ifStatement);
81
+ (0, _insert.prepend)(object, variableDeclaration);
82
+ };
75
83
  }
76
84
 
77
85
  }
@@ -56,9 +56,12 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
56
56
  * 3. using `this`
57
57
  */
58
58
  class Dispatcher extends _transform.default {
59
+ // Debug mode preserves function names
59
60
  constructor(o) {
60
61
  super(o, _order.ObfuscateOrder.Dispatcher);
61
62
 
63
+ _defineProperty(this, "isDebug", false);
64
+
62
65
  _defineProperty(this, "count", void 0);
63
66
 
64
67
  this.count = 0;
@@ -102,11 +105,12 @@ class Dispatcher extends _transform.default {
102
105
 
103
106
  if (context === c) {
104
107
  if (o.type == "FunctionDeclaration" && o.id.name) {
108
+ var name = o.id.name;
109
+
105
110
  if (o.$requiresEval || o.async || o.generator || p.find(x => x.$dispatcherSkip || x.type == "MethodDefinition") || o.body.type != "BlockStatement") {
106
111
  illegalFnNames.add(name);
107
- }
112
+ } // If dupe, no routing
108
113
 
109
- var name = o.id.name; // If dupe, no routing
110
114
 
111
115
  if (functionDeclarations[name]) {
112
116
  illegalFnNames.add(name);
@@ -157,13 +161,13 @@ class Dispatcher extends _transform.default {
157
161
 
158
162
  var gen = this.getGenerator();
159
163
  Object.keys(functionDeclarations).forEach(name => {
160
- newFnNames[name] = gen.generate();
164
+ newFnNames[name] = this.isDebug ? "_dispatcher_" + this.count + "_" + name : gen.generate();
161
165
  }); // set containing new name
162
166
 
163
167
  var set = new Set(Object.keys(newFnNames)); // Only make a dispatcher function if it caught any functions
164
168
 
165
169
  if (set.size > 0) {
166
- var payloadArg = "$jsc_d".concat(this.count, "_payload");
170
+ var payloadArg = this.getPlaceholder() + "_dispatcher_" + this.count + "_payload";
167
171
  var dispatcherFnName = this.getPlaceholder() + "_dispatcher_" + this.count;
168
172
  this.log(dispatcherFnName, set);
169
173
  this.count++;
@@ -158,6 +158,8 @@ class AntiDestructuring extends _transform.default {
158
158
  if (x.type == "Identifier") {
159
159
  exprs.push((0, _gen.AssignmentExpression)(operator, (0, _insert.clone)(x), realm));
160
160
  names.add(x.name);
161
+ } else if (x.type == "MemberExpression") {
162
+ exprs.push((0, _gen.AssignmentExpression)(operator, (0, _insert.clone)(x), realm));
161
163
  } else if (x.type == "ObjectPattern") {
162
164
  x.properties.forEach(property => {
163
165
  recursive(property.value, (0, _gen.MemberExpression)(realm, property.key, property.computed));
@@ -13,8 +13,6 @@ var _insert = require("../../util/insert");
13
13
 
14
14
  var _traverse = require("../../traverse");
15
15
 
16
- var _template = _interopRequireDefault(require("../../templates/template"));
17
-
18
16
  var _order = require("../../order");
19
17
 
20
18
  var _assert = require("assert");
@@ -31,6 +29,8 @@ var _antiES6Object = _interopRequireDefault(require("./antiES6Object"));
31
29
 
32
30
  var _antiSpreadOperator = _interopRequireDefault(require("./antiSpreadOperator"));
33
31
 
32
+ var _es = require("../../templates/es5");
33
+
34
34
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
35
35
 
36
36
  /**
@@ -93,24 +93,44 @@ class AntiArrowFunction extends _transform.default {
93
93
  }
94
94
 
95
95
  }
96
+ /**
97
+ * The ES5 options aims to convert ES6 and up features down to ES5-compatible code.
98
+ *
99
+ * The obfuscator regularly adds ES6 code (variable destructuring, spread element, etc.)
100
+ * This transformations goal is undo only these things.
101
+ */
102
+
96
103
 
97
104
  exports.AntiArrowFunction = AntiArrowFunction;
98
105
 
99
- class FixedExpressions extends _transform.default {
106
+ class ES5 extends _transform.default {
100
107
  constructor(o) {
101
- super(o);
108
+ super(o, _order.ObfuscateOrder.ES5);
109
+ this.before.push(new _antiClass.default(o));
110
+ this.before.push(new _antiTemplate.default(o));
111
+ this.before.push(new _antiSpreadOperator.default(o));
112
+ this.before.push(new _antiES6Object.default(o));
113
+ this.before.push(new AntiArrowFunction(o));
114
+ this.before.push(new _antiDestructuring.default(o));
115
+ this.before.push(new AntiConstLet(o));
102
116
  }
103
117
 
118
+ apply(tree) {
119
+ super.apply(tree);
120
+
121
+ var nodesToAdd = _es.ES5Template.compile();
122
+
123
+ (0, _insert.prepend)(tree, ...nodesToAdd);
124
+ } // FixedExpressions
125
+
126
+
104
127
  match(object, parents) {
105
- return true;
128
+ return !!object.type;
106
129
  }
107
130
 
108
131
  transform(object, parents) {
109
132
  return () => {
110
- if (object.type == "ForStatement" && object.init.type == "ExpressionStatement") {
111
- object.init = object.init.expression;
112
- }
113
-
133
+ // Object.keyword -> Object["keyword"]
114
134
  if (object.type == "MemberExpression") {
115
135
  if (!object.computed && object.property.type == "Identifier") {
116
136
  if (_constants.reservedKeywords.has(object.property.name)) {
@@ -118,7 +138,8 @@ class FixedExpressions extends _transform.default {
118
138
  object.computed = true;
119
139
  }
120
140
  }
121
- }
141
+ } // { keyword: ... } -> { "keyword": ... }
142
+
122
143
 
123
144
  if (object.type == "Property") {
124
145
  if (!object.computed && object.key.type == "Identifier") {
@@ -132,28 +153,4 @@ class FixedExpressions extends _transform.default {
132
153
 
133
154
  }
134
155
 
135
- class ES5 extends _transform.default {
136
- constructor(o) {
137
- super(o, _order.ObfuscateOrder.ES5);
138
- this.before.push(new _antiClass.default(o));
139
- this.before.push(new _antiTemplate.default(o));
140
- this.before.push(new _antiSpreadOperator.default(o));
141
- this.before.push(new _antiES6Object.default(o));
142
- this.before.push(new AntiArrowFunction(o));
143
- this.before.push(new _antiDestructuring.default(o));
144
- this.before.push(new AntiConstLet(o));
145
- this.concurrent.push(new FixedExpressions(o));
146
- }
147
-
148
- match(object, parents) {
149
- return object.type == "Program";
150
- }
151
-
152
- transform(object, parents) {
153
- var block = (0, _traverse.getBlock)(object, parents);
154
- (0, _insert.getBlockBody)(block).splice(0, 0, ...(0, _template.default)("\n !Array.prototype.forEach ? Array.prototype.forEach = function (callback, thisArg) {\n thisArg = thisArg;\n for (var i = 0; i < this.length; i++) {\n callback.call(thisArg, this[i], i, this);\n }\n } : 0;\n \n !Array.prototype.map ? Array.prototype.map = function (callback, thisArg) {\n thisArg = thisArg;\n var array=[];\n for (var i = 0; i < this.length; i++) {\n array.push( callback.call(thisArg, this[i], i, this) );\n }\n return array;\n } : 0;\n\n !Array.prototype.reduce ? Array.prototype.reduce = function(fn, initial) {\n var values = this;\n if ( typeof initial === \"undefined\" ) {\n initial = 0;\n }\n\n values.forEach(function(item, index){\n initial = fn(initial, item, index, this);\n });\n\n return initial;\n } : 0;\n ").compile());
155
- }
156
-
157
- }
158
-
159
156
  exports.default = ES5;
@@ -23,6 +23,8 @@ var _assert = require("assert");
23
23
 
24
24
  var _random = require("../../util/random");
25
25
 
26
+ var _traverse = require("../../traverse");
27
+
26
28
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
29
 
28
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; }
@@ -46,12 +48,19 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
46
48
  * ```
47
49
  */
48
50
  class DuplicateLiteralsRemoval extends _transform.default {
51
+ // The array holding all the duplicate literals
52
+ // The array expression node to be inserted into the program
53
+
54
+ /**
55
+ * Literals in the array
56
+ */
57
+
49
58
  /**
50
- * getter fn name -> accumulative shift
59
+ * Literals are saved here the first time they are seen.
51
60
  */
52
61
 
53
62
  /**
54
- * lex context -> getter fn name
63
+ * Block -> { functionName, indexShift }
55
64
  */
56
65
  constructor(o) {
57
66
  super(o, _order.ObfuscateOrder.DuplicateLiteralsRemoval);
@@ -64,23 +73,45 @@ class DuplicateLiteralsRemoval extends _transform.default {
64
73
 
65
74
  _defineProperty(this, "first", void 0);
66
75
 
67
- _defineProperty(this, "fnShifts", void 0);
68
-
69
- _defineProperty(this, "fnGetters", void 0);
76
+ _defineProperty(this, "functions", void 0);
70
77
 
71
78
  this.map = new Map();
72
79
  this.first = new Map();
73
- this.fnShifts = new Map();
74
- this.fnGetters = new Map();
80
+ this.functions = new Map();
75
81
  }
76
82
 
77
83
  apply(tree) {
78
84
  super.apply(tree);
79
85
 
80
- if (this.arrayName && this.arrayExpression.elements.length) {
86
+ if (this.arrayName && this.arrayExpression.elements.length > 0) {
87
+ // This function simply returns the array
81
88
  var getArrayFn = this.getPlaceholder();
82
- (0, _insert.append)(tree, (0, _gen.FunctionDeclaration)(getArrayFn, [], [(0, _gen.ReturnStatement)(this.arrayExpression)]));
83
- (0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(getArrayFn), (0, _gen.Identifier)("call"), false), [(0, _gen.ThisExpression)()]))));
89
+ (0, _insert.append)(tree, (0, _gen.FunctionDeclaration)(getArrayFn, [], [(0, _gen.ReturnStatement)(this.arrayExpression)])); // This variable holds the array
90
+
91
+ (0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(getArrayFn), (0, _gen.Literal)("call"), true), [(0, _gen.ThisExpression)()])))); // Create all the functions needed
92
+
93
+ for (var blockNode of this.functions.keys()) {
94
+ var {
95
+ functionName,
96
+ indexShift
97
+ } = this.functions.get(blockNode);
98
+ var propertyNode = (0, _gen.BinaryExpression)("-", (0, _gen.Identifier)("index_param"), (0, _gen.Literal)(indexShift));
99
+ var indexRangeInclusive = [0 + indexShift - 1, this.map.size + indexShift]; // The function uses mangling to hide the index being accessed
100
+
101
+ var mangleCount = (0, _random.getRandomInteger)(1, 10);
102
+
103
+ for (var i = 0; i < mangleCount; i++) {
104
+ var operator = (0, _random.choice)([">", "<"]);
105
+ var compareValue = (0, _random.choice)(indexRangeInclusive);
106
+ var test = (0, _gen.BinaryExpression)(operator, (0, _gen.Identifier)("index_param"), (0, _gen.Literal)(compareValue));
107
+ var alternate = (0, _gen.BinaryExpression)("-", (0, _gen.Identifier)("index_param"), (0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100)));
108
+ var testValue = operator === ">" && compareValue === indexRangeInclusive[0] || operator === "<" && compareValue === indexRangeInclusive[1];
109
+ propertyNode = (0, _gen.ConditionalExpression)(test, testValue ? propertyNode : alternate, !testValue ? propertyNode : alternate);
110
+ }
111
+
112
+ var returnArgument = (0, _gen.MemberExpression)((0, _gen.Identifier)(this.arrayName), propertyNode, true);
113
+ (0, _insert.prepend)(blockNode, (0, _gen.FunctionDeclaration)(functionName, [(0, _gen.Identifier)("index_param")], [(0, _gen.ReturnStatement)(returnArgument)]));
114
+ }
84
115
  }
85
116
  }
86
117
 
@@ -95,43 +126,39 @@ class DuplicateLiteralsRemoval extends _transform.default {
95
126
  */
96
127
 
97
128
 
98
- toCaller(object, parents, index) {
99
- // get all the getters defined here or higher
100
- var getterNames = [object, ...parents].map(x => this.fnGetters.get(x)).filter(x => x); // use random getter function
129
+ transformLiteral(object, parents, index) {
130
+ var blockNode = (0, _random.choice)(parents.filter(x => this.functions.has(x))); // Create initial function if none exist
101
131
 
102
- var getterName = (0, _random.choice)(getterNames); // get this literals context
132
+ if (this.functions.size === 0) {
133
+ var root = parents[parents.length - 1];
134
+ var rootFunctionName = this.getPlaceholder() + "_dLR_0";
135
+ this.functions.set(root, {
136
+ functionName: rootFunctionName,
137
+ indexShift: (0, _random.getRandomInteger)(-100, 100)
138
+ });
139
+ blockNode = root;
140
+ } // If no function here exist, possibly create new chained function
103
141
 
104
- var lexContext = (0, _insert.getLexContext)(object, parents);
105
- var hasGetterHere = this.fnGetters.has(lexContext); // create one if none are available (or by random chance if none are here locally)
106
142
 
107
- var shouldCreateNew = !getterName || !hasGetterHere && Math.random() > 0.9;
143
+ var block = (0, _traverse.getBlock)(object, parents);
108
144
 
109
- if (shouldCreateNew) {
110
- (0, _assert.ok)(!this.fnGetters.has(lexContext));
111
- var lexContextIndex = parents.findIndex(x => x !== lexContext && (0, _insert.isLexContext)(x));
112
- var basedOn = lexContextIndex !== -1 ? (0, _random.choice)(parents.slice(lexContextIndex + 1).map(x => this.fnGetters.get(x)).filter(x => x)) : null;
113
- var body = [];
114
- var thisShift = (0, _random.getRandomInteger)(-250, 250); // the name of the getter
145
+ if (!this.functions.has(block) && (0, _random.chance)(50 - this.functions.size)) {
146
+ var newFunctionName = this.getPlaceholder() + "_dLR_" + this.functions.size;
147
+ this.functions.set(block, {
148
+ functionName: newFunctionName,
149
+ indexShift: (0, _random.getRandomInteger)(-100, 100)
150
+ });
151
+ blockNode = block;
152
+ } // Derive the function to call from the selected blockNode
115
153
 
116
- getterName = this.getPlaceholder() + "_dLR_" + this.fnGetters.size;
117
154
 
118
- if (basedOn) {
119
- var shift = this.fnShifts.get(basedOn);
120
- (0, _assert.ok)(typeof shift === "number");
121
- body = [(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.Identifier)(basedOn), [(0, _gen.BinaryExpression)("+", (0, _gen.Identifier)("index"), (0, _gen.Literal)(thisShift))]))];
122
- this.fnShifts.set(getterName, shift + thisShift);
123
- } else {
124
- // from scratch
125
- body = [(0, _gen.ReturnStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(this.arrayName), (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)("index"), (0, _gen.Literal)(thisShift)), true))];
126
- this.fnShifts.set(getterName, thisShift);
127
- }
155
+ var {
156
+ functionName,
157
+ indexShift
158
+ } = this.functions.get(blockNode); // Call the function given it's indexShift
128
159
 
129
- this.fnGetters.set(lexContext, getterName);
130
- (0, _insert.prepend)(lexContext, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(getterName, (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.FunctionExpression)([(0, _gen.Identifier)("index")], body))]), []))));
131
- }
132
-
133
- var theShift = this.fnShifts.get(getterName);
134
- this.replaceIdentifierOrLiteral(object, (0, _gen.CallExpression)((0, _gen.Identifier)(getterName), [(0, _gen.Literal)(index - theShift)]), parents);
160
+ var callExpression = (0, _gen.CallExpression)((0, _gen.Identifier)(functionName), [(0, _gen.Literal)(index + indexShift)]);
161
+ this.replaceIdentifierOrLiteral(object, callExpression, parents);
135
162
  }
136
163
 
137
164
  transform(object, parents) {
@@ -144,19 +171,22 @@ class DuplicateLiteralsRemoval extends _transform.default {
144
171
 
145
172
  if (!(0, _probability.ComputeProbabilityMap)(this.options.duplicateLiteralsRemoval)) {
146
173
  return;
147
- }
174
+ } // HARD CODED LIMIT of 10,000 (after 1,000 elements)
175
+
176
+
177
+ if (this.map.size > 1000 && (0, _random.chance)(this.map.size / 100)) return;
148
178
 
149
179
  if (this.arrayName && parents[0].object && parents[0].object.name == this.arrayName) {
150
180
  return;
151
181
  }
152
182
 
153
- var value;
183
+ var stringValue;
154
184
 
155
185
  if (object.type == "Literal") {
156
- value = typeof object.value + ":" + object.value;
186
+ stringValue = typeof object.value + ":" + object.value;
157
187
 
158
188
  if (object.value === null) {
159
- value = "null:null";
189
+ stringValue = "null:null";
160
190
  } else {
161
191
  // Skip empty strings
162
192
  if (typeof object.value === "string" && !object.value) {
@@ -164,38 +194,42 @@ class DuplicateLiteralsRemoval extends _transform.default {
164
194
  }
165
195
  }
166
196
  } else if (object.type == "Identifier") {
167
- value = "identifier:" + object.name;
197
+ stringValue = "identifier:" + object.name;
168
198
  } else {
169
199
  throw new Error("Unsupported primitive type: " + object.type);
170
200
  }
171
201
 
172
- (0, _assert.ok)(value);
202
+ (0, _assert.ok)(stringValue);
173
203
 
174
- if (!this.first.has(value) && !this.map.has(value)) {
175
- this.first.set(value, [object, parents]);
176
- } else {
204
+ if (this.map.has(stringValue) || this.first.has(stringValue)) {
205
+ // Create the array if not already made
177
206
  if (!this.arrayName) {
178
207
  this.arrayName = this.getPlaceholder();
179
208
  this.arrayExpression = (0, _gen.ArrayExpression)([]);
180
- }
209
+ } // Delete with first location
181
210
 
182
- var firstLocation = this.first.get(value);
211
+
212
+ var firstLocation = this.first.get(stringValue);
183
213
 
184
214
  if (firstLocation) {
185
- this.first.set(value, null);
186
215
  var index = this.map.size;
187
- (0, _assert.ok)(!this.map.has(value));
188
- this.map.set(value, index);
216
+ (0, _assert.ok)(!this.map.has(stringValue));
217
+ this.map.set(stringValue, index);
218
+ this.first.delete(stringValue);
189
219
  var pushing = (0, _insert.clone)(object);
190
220
  this.arrayExpression.elements.push(pushing);
191
221
  (0, _assert.ok)(this.arrayExpression.elements[index] === pushing);
192
- this.toCaller(firstLocation[0], firstLocation[1], index);
222
+ this.transformLiteral(firstLocation[0], firstLocation[1], index);
193
223
  }
194
224
 
195
- var index = this.map.get(value);
225
+ var index = this.map.get(stringValue);
196
226
  (0, _assert.ok)(typeof index === "number");
197
- this.toCaller(object, parents, index);
198
- }
227
+ this.transformLiteral(object, parents, index);
228
+ return;
229
+ } // Save this, maybe a duplicate will be found.
230
+
231
+
232
+ this.first.set(stringValue, [object, parents]);
199
233
  };
200
234
  }
201
235