瀏覽代碼

Merge branch 'dev'

# Conflicts:
#	dist/index.js
sanex3339 8 年之前
父節點
當前提交
b2615f16bb
共有 100 個文件被更改,包括 1431 次插入862 次删除
  1. 8 1
      .babelrc
  2. 23 14
      README.md
  3. 417 336
      dist/index.js
  4. 16 12
      package.json
  5. 12 1
      src/JavaScriptObfuscator.ts
  6. 1 7
      src/JavaScriptObfuscatorInternal.ts
  7. 2 2
      src/Obfuscator.ts
  8. 1 3
      src/cli/CLIUtils.ts
  9. 8 4
      src/cli/JavaScriptObfuscatorCLI.ts
  10. 4 3
      src/container/ServiceIdentifiers.ts
  11. 1 1
      src/container/modules/node-transformers/NodeControlFlowTransformersModule.ts
  12. 11 11
      src/container/modules/node-transformers/NodeObfuscatorsModule.ts
  13. 1 1
      src/container/modules/node-transformers/NodeTransformersModule.ts
  14. 1 1
      src/container/modules/stack-trace-analyzer/StackTraceAnalyzerModule.ts
  15. 5 0
      src/container/modules/storages/StoragesModule.ts
  16. 9 2
      src/custom-nodes/AbstractCustomNode.ts
  17. 1 1
      src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts
  18. 2 2
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionNode.ts
  19. 1 1
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode.ts
  20. 3 11
      src/custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts
  21. 1 1
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts
  22. 1 1
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts
  23. 1 1
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts
  24. 1 1
      src/custom-nodes/domain-lock-nodes/DomainLockNode.ts
  25. 2 2
      src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts
  26. 2 2
      src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts
  27. 27 35
      src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts
  28. 15 28
      src/custom-nodes/string-array-nodes/StringArrayNode.ts
  29. 6 6
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  30. 13 11
      src/custom-nodes/string-array-nodes/group/StringArrayCustomNodeGroup.ts
  31. 1 1
      src/declarations/ESTree.d.ts
  32. 10 0
      src/declarations/escodegen.d.ts
  33. 26 30
      src/decorators/Initializable.ts
  34. 2 0
      src/enums/NodeType.ts
  35. 0 5
      src/interfaces/IGenerator.d.ts
  36. 4 0
      src/interfaces/IJavaScriptObfsucator.d.ts
  37. 4 0
      src/interfaces/IObfuscator.d.ts
  38. 5 0
      src/interfaces/ISourceMapCorrector.d.ts
  39. 0 8
      src/interfaces/custom-nodes/ICustomNodeWithData.d.ts
  40. 0 5
      src/interfaces/custom-nodes/ICustomNodeWithIdentifier.d.ts
  41. 11 0
      src/interfaces/event-emitters/IObfuscationEventEmitter.d.ts
  42. 8 3
      src/interfaces/node-transformers/IControlFlowReplacer.d.ts
  43. 4 0
      src/interfaces/node-transformers/INodeTransformer.d.ts
  44. 7 0
      src/interfaces/node-transformers/IObfuscatorReplacer.d.ts
  45. 9 0
      src/interfaces/node-transformers/IObfuscatorReplacerWithStorage.d.ts
  46. 0 3
      src/interfaces/node-transformers/IReplacer.d.ts
  47. 1 0
      src/interfaces/options/IOptions.d.ts
  48. 5 0
      src/interfaces/stack-trace-analyzer/ICalleeDataExtractor.d.ts
  49. 4 0
      src/interfaces/stack-trace-analyzer/IStackTraceAnalyzer.d.ts
  50. 41 0
      src/interfaces/storages/IStorage.d.ts
  51. 97 22
      src/node-transformers/node-control-flow-transformers/FunctionControlFlowTransformer.ts
  52. 3 17
      src/node-transformers/node-control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts
  53. 73 24
      src/node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts
  54. 15 16
      src/node-transformers/node-obfuscators/CatchClauseObfuscator.ts
  55. 16 18
      src/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.ts
  56. 15 16
      src/node-transformers/node-obfuscators/FunctionObfuscator.ts
  57. 15 16
      src/node-transformers/node-obfuscators/LabeledStatementObfuscator.ts
  58. 4 4
      src/node-transformers/node-obfuscators/LiteralObfuscator.ts
  59. 4 4
      src/node-transformers/node-obfuscators/MemberExpressionObfuscator.ts
  60. 5 6
      src/node-transformers/node-obfuscators/MethodDefinitionObfuscator.ts
  61. 16 18
      src/node-transformers/node-obfuscators/VariableDeclarationObfuscator.ts
  62. 4 3
      src/node-transformers/node-obfuscators/replacers/AbstractReplacer.ts
  63. 9 22
      src/node-transformers/node-obfuscators/replacers/IdentifierReplacer.ts
  64. 1 1
      src/node-transformers/node-obfuscators/replacers/NumberLiteralReplacer.ts
  65. 15 19
      src/node-transformers/node-obfuscators/replacers/StringLiteralReplacer.ts
  66. 8 0
      src/node/Node.ts
  67. 1 1
      src/node/NodeAppender.ts
  68. 31 14
      src/node/NodeUtils.ts
  69. 9 1
      src/options/Options.ts
  70. 29 5
      src/options/OptionsNormalizer.ts
  71. 3 2
      src/options/presets/Default.ts
  72. 3 2
      src/options/presets/NoCustomNodes.ts
  73. 1 1
      src/stack-trace-analyzer/StackTraceAnalyzer.ts
  74. 1 1
      src/stack-trace-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor.ts
  75. 1 1
      src/stack-trace-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor.ts
  76. 1 1
      src/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts
  77. 31 0
      src/storages/ArrayStorage.ts
  78. 28 1
      src/storages/MapStorage.ts
  79. 2 0
      src/storages/custom-node-group/CustomNodeGroupStorage.ts
  80. 14 1
      src/storages/string-array/StringArrayStorage.ts
  81. 2 2
      src/utils/CryptUtils.ts
  82. 21 10
      src/utils/RandomGeneratorUtils.ts
  83. 19 19
      src/utils/Utils.ts
  84. 66 3
      test/dev/dev.ts
  85. 18 18
      test/fixtures/compile-performance.js
  86. 3 0
      test/fixtures/node-transformers/node-control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer-1.js
  87. 4 0
      test/fixtures/node-transformers/node-control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer-2.js
  88. 13 1
      test/functional-tests/JavaScriptObfuscator.spec.ts
  89. 1 1
      test/functional-tests/JavaScriptObfuscatorInternal.spec.ts
  90. 1 1
      test/functional-tests/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.spec.ts
  91. 1 1
      test/functional-tests/custom-nodes/domain-lock-nodes/DomainLockNode.spec.ts
  92. 1 1
      test/functional-tests/custom-nodes/string-array-nodes/StringArrayCallsWrapper.spec.ts
  93. 1 1
      test/functional-tests/custom-nodes/string-array-nodes/StringArrayNode.spec.ts
  94. 1 1
      test/functional-tests/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.spec.ts
  95. 77 0
      test/functional-tests/node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.spec.ts
  96. 5 5
      test/functional-tests/node-transformers/node-obfuscators/CatchClauseObfuscator.spec.ts
  97. 1 1
      test/functional-tests/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.spec.ts
  98. 1 1
      test/functional-tests/node-transformers/node-obfuscators/FunctionObfuscator.spec.ts
  99. 1 1
      test/functional-tests/node-transformers/node-obfuscators/LabeledStatementObfuscator.spec.ts
  100. 1 1
      test/functional-tests/node-transformers/node-obfuscators/LiteralObfuscator.spec.ts

+ 8 - 1
.babelrc

@@ -2,5 +2,12 @@
   "generatorOpts": {
   "generatorOpts": {
     "retainFunctionParens": true
     "retainFunctionParens": true
   },
   },
-  "presets": ["es2015"]
+  "presets": ["es2015"],
+  "plugins": [
+    ["transform-runtime", {
+      "helpers": true,
+      "polyfill": true,
+      "regenerator": false
+    }]
+  ]
 }
 }

+ 23 - 14
README.md

@@ -78,7 +78,7 @@ var _0xabf1 = [
 Returns `ObfuscationResult` object which contains two public methods:
 Returns `ObfuscationResult` object which contains two public methods:
 
 
 * `getObfuscatedCode()` - returns `string` with obfuscated code;
 * `getObfuscatedCode()` - returns `string` with obfuscated code;
-* `getSourceMap()` - if `sourceMap` options is enable - returns `string` with source map or an empty string if `sourceMapMode` option is set as `inline`.
+* `getSourceMap()` - if [`sourceMap`](#sourcemap) option is enabled - returns `string` with source map or an empty string if [`sourceMapMode`](#sourcemapmode) option is set as `inline`.
 
 
 Calling `toString()` for `ObfuscationResult` object will return `string` with obfuscated code.
 Calling `toString()` for `ObfuscationResult` object will return `string` with obfuscated code.
 
 
@@ -171,7 +171,7 @@ Compact code output on one line.
 ### `controlFlowFlattening`
 ### `controlFlowFlattening`
 Type: `boolean` Default: `false`
 Type: `boolean` Default: `false`
 
 
-Enables code control flow flattening. Control flow flattening is a structure transformation of the source code that hindering the comprehension of the program.
+Enables code control flow flattening. Control flow flattening is a structure transformation of the source code that hinders program comprehension.
 
 
 Example:
 Example:
 ```ts
 ```ts
@@ -210,6 +210,15 @@ Example:
 }());
 }());
 ```
 ```
 
 
+### `controlFlowFlatteningThreshold`
+Type: `number` Default: `0.75` Min: `0` Max: `1`
+
+The probability that the [`controlFlowFlattening`](#controlflowflattening) transformation will be applied to the node.
+
+This setting is especially useful for large code size because large amount of control flow transformations can slow down your code and increase code size.
+
+`controlFlowFlatteningThreshold: 0` equals to `controlFlowFlattening: false`.
+
 ### `debugProtection`
 ### `debugProtection`
 Type: `boolean` Default: `false`
 Type: `boolean` Default: `false`
 
 
@@ -225,7 +234,7 @@ Type: `boolean` Default: `false`
 
 
 ##### :warning: Can freeze your browser! Use at own risk.
 ##### :warning: Can freeze your browser! Use at own risk.
 
 
-If checked, an interval is used to force the debug mode on the Console tab, making it harder to use other features of the Developer Tools. Works if `debugProtection` is enabled.
+If checked, an interval is used to force the debug mode on the Console tab, making it harder to use other features of the Developer Tools. Works if [`debugProtection`](#debugprotection) is enabled.
 
 
 ### `disableConsoleOutput`
 ### `disableConsoleOutput`
 Type: `boolean` Default: `true`
 Type: `boolean` Default: `true`
@@ -258,7 +267,7 @@ Example:
 ### `rotateStringArray`
 ### `rotateStringArray`
 Type: `boolean` Default: `true`
 Type: `boolean` Default: `true`
 
 
-##### :warning: `stringArray` must be enabled
+##### :warning: [`stringArray`](#stringarray) must be enabled
 
 
 Shift the `stringArray` array by a fixed and random (generated at the code obfuscation) places. This makes it harder to match the order of the removed strings to their original place.
 Shift the `stringArray` array by a fixed and random (generated at the code obfuscation) places. This makes it harder to match the order of the removed strings to their original place.
 
 
@@ -275,7 +284,7 @@ If seed is `0` - random generator will work without seed.
 Type: `boolean` Default: `false`
 Type: `boolean` Default: `false`
 
 
 ##### :warning: Don't change obfuscated code in any way after obfuscation with this option, because any change like uglifying of code can trigger self defending and code wont work anymore!
 ##### :warning: Don't change obfuscated code in any way after obfuscation with this option, because any change like uglifying of code can trigger self defending and code wont work anymore!
-##### :warning: this option forcibly set `compact` value to `true`
+##### :warning: This option forcibly sets `compact` value to `true`
 
 
 This option makes the output code resilient against formatting and variable renaming. If one tries to use a JavaScript beautifier on the obfuscated code, the code won't work anymore, making it harder to understand and modify it.
 This option makes the output code resilient against formatting and variable renaming. If one tries to use a JavaScript beautifier on the obfuscated code, the code won't work anymore, making it harder to understand and modify it.
 
 
@@ -284,12 +293,12 @@ Type: `boolean` Default: `false`
 
 
 Enables source map generation for obfuscated code.
 Enables source map generation for obfuscated code.
 
 
-Source maps can be useful to help you debug your obfuscated Java Script source code. If you want or need to debug in production, you can upload the separate source map file to a secret location and then point your browser there. 
+Source maps can be useful to help you debug your obfuscated JavaScript source code. If you want or need to debug in production, you can upload the separate source map file to a secret location and then point your browser there. 
 
 
 ### `sourceMapBaseUrl`
 ### `sourceMapBaseUrl`
 Type: `string` Default: ``
 Type: `string` Default: ``
 
 
-Sets base url to the source map import url when `sourceMapMode: 'separate'`.
+Sets base url to the source map import url when [`sourceMapMode: 'separate'`](#sourcemapmode).
  
  
 CLI example:
 CLI example:
 ```
 ```
@@ -321,12 +330,12 @@ Type: `string` Default: `separate`
 
 
 Specifies source map generation mode:
 Specifies source map generation mode:
 * `inline` - emit a single file with source maps instead of having a separate file;
 * `inline` - emit a single file with source maps instead of having a separate file;
-* `separate` - generates corresponding '.map' file with source map. If obfuscator run through CLI - adds link to source map file to the end of file with obfuscated code `//# sourceMappingUrl=file.js.map`.
+* `separate` - generates corresponding '.map' file with source map. In case you run obfuscator through CLI - adds link to source map file to the end of file with obfuscated code `//# sourceMappingUrl=file.js.map`.
 
 
 ### `stringArray`
 ### `stringArray`
 Type: `boolean` Default: `true`
 Type: `boolean` Default: `true`
 
 
-Removes string literals and place them in a special array. For instance the string `"Hello World"` in `var m = "Hello World";` will be replaced with something like `var m = _0x12c456[0x1];`
+Removes string literals and place them in a special array. For instance, the string `"Hello World"` in `var m = "Hello World";` will be replaced with something like `var m = _0x12c456[0x1];`
     
     
 ### `stringArrayEncoding`
 ### `stringArrayEncoding`
 Type: `boolean|string` Default: `false`
 Type: `boolean|string` Default: `false`
@@ -335,7 +344,7 @@ Type: `boolean|string` Default: `false`
 
 
 This option can slightly slow down your script.
 This option can slightly slow down your script.
 
 
-Encode all string literals of the `stringArray` using `base64` or `rc4` and inserts a special code that used to decode it back at runtime.
+Encode all string literals of the [`stringArray`](#stringarray) using `base64` or `rc4` and inserts a special code that used to decode it back at runtime.
 
 
 Available values:
 Available values:
 * `true` (`boolean`): encode `stringArray` values using `base64`
 * `true` (`boolean`): encode `stringArray` values using `base64`
@@ -346,11 +355,11 @@ Available values:
 ### `stringArrayThreshold`
 ### `stringArrayThreshold`
 Type: `number` Default: `0.8` Min: `0` Max: `1`
 Type: `number` Default: `0.8` Min: `0` Max: `1`
 
 
-##### :warning: `stringArray` option must be enabled
+##### :warning: [`stringArray`](#stringarray) option must be enabled
 
 
 You can use this setting to adjust the probability (from 0 to 1) that a string literal will be inserted into the `stringArray`.
 You can use this setting to adjust the probability (from 0 to 1) that a string literal will be inserted into the `stringArray`.
 
 
-This setting is useful with large code size because repeatdely calls to the `stringArray` array can slightly slow down your code.
+This setting is especially useful for large code size because it repeatedly calls to the `string array` and can slow down your code.
 
 
 `stringArrayThreshold: 0` equals to `stringArray: false`.
 `stringArrayThreshold: 0` equals to `stringArray: false`.
 
 
@@ -359,7 +368,7 @@ Type: `boolean` Default: `true`
 
 
 Allows to enable/disable string conversion to unicode escape sequence.
 Allows to enable/disable string conversion to unicode escape sequence.
 
 
-Unicode escape sequence greatly increases code size. Recommend to disable this option when using `stringArrayEncoding` (especially with `rc4` encoding).
+Unicode escape sequence increases code size greatly. It is recommended to disable this option when using [`stringArrayEncoding`](#stringarrayencoding) (especially with `rc4` encoding).
 
 
 ## License
 ## License
 Copyright (C) 2016 [Timofey Kachalov](http://github.com/sanex3339).
 Copyright (C) 2016 [Timofey Kachalov](http://github.com/sanex3339).
@@ -382,4 +391,4 @@ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

文件差異過大導致無法顯示
+ 417 - 336
dist/index.js


+ 16 - 12
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "javascript-obfuscator",
   "name": "javascript-obfuscator",
-  "version": "0.9.0-dev.1",
+  "version": "0.9.0-dev.2",
   "description": "JavaScript obfuscator",
   "description": "JavaScript obfuscator",
   "keywords": [
   "keywords": [
     "obfuscator",
     "obfuscator",
@@ -20,19 +20,21 @@
     "javascript-obfuscator": "./bin/javascript-obfuscator.js"
     "javascript-obfuscator": "./bin/javascript-obfuscator.js"
   },
   },
   "dependencies": {
   "dependencies": {
-    "babel-polyfill": "6.20.0",
+    "babel-polyfill": "^6.20.0",
+    "babel-runtime": "^6.20.0",
     "chance": "1.0.4",
     "chance": "1.0.4",
     "class-validator": "0.6.6",
     "class-validator": "0.6.6",
     "commander": "2.9.0",
     "commander": "2.9.0",
     "escodegen": "1.8.1",
     "escodegen": "1.8.1",
     "esprima": "3.1.2",
     "esprima": "3.1.2",
     "estraverse": "4.2.0",
     "estraverse": "4.2.0",
-    "inversify": "^3.0.0-rc.1",
-    "is-equal": "^1.5.3",
+    "inversify": "^3.0.0-rc.2",
+    "lodash": "^4.17.2",
     "mkdirp": "0.5.1",
     "mkdirp": "0.5.1",
     "reflect-metadata": "^0.1.8",
     "reflect-metadata": "^0.1.8",
     "source-map-support": "0.4.6",
     "source-map-support": "0.4.6",
-    "string-template": "^1.0.0"
+    "string-template": "^1.0.0",
+    "tslib": "^1.2.0"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@types/chai": "3.4.34",
     "@types/chai": "3.4.34",
@@ -43,22 +45,24 @@
     "@types/estraverse": "0.0.6",
     "@types/estraverse": "0.0.6",
     "@types/estree": "0.0.34",
     "@types/estree": "0.0.34",
     "@types/joi": "9.0.33",
     "@types/joi": "9.0.33",
+    "@types/lodash": "^4.14.43",
     "@types/mkdirp": "0.3.29",
     "@types/mkdirp": "0.3.29",
     "@types/mocha": "2.2.33",
     "@types/mocha": "2.2.33",
-    "@types/node": "6.0.51",
-    "@types/sinon": "1.16.32",
-    "@types/string-template": "^1.0.2",
-    "awesome-typescript-loader": "^3.0.0-beta.9",
+    "@types/node": "6.0.52",
+    "@types/sinon": "1.16.33",
+    "@types/string-template": "1.0.2",
+    "awesome-typescript-loader": "3.0.0-beta.17",
     "babel-cli": "6.18.0",
     "babel-cli": "6.18.0",
-    "babel-loader": "6.2.9",
+    "babel-loader": "6.2.10",
+    "babel-plugin-transform-runtime": "^6.15.0",
     "babel-preset-es2015": "6.18.0",
     "babel-preset-es2015": "6.18.0",
     "chai": "3.5.0",
     "chai": "3.5.0",
     "coveralls": "2.11.15",
     "coveralls": "2.11.15",
     "istanbul": "1.1.0-alpha.1",
     "istanbul": "1.1.0-alpha.1",
     "mocha": "3.2.0",
     "mocha": "3.2.0",
     "sinon": "2.0.0-pre.3",
     "sinon": "2.0.0-pre.3",
-    "ts-node": "1.7.0",
-    "tslint": "4.0.2",
+    "ts-node": "1.7.2",
+    "tslint": "4.1.1",
     "typescript": "2.1.4",
     "typescript": "2.1.4",
     "webpack": "2.1.0-beta.27",
     "webpack": "2.1.0-beta.27",
     "webpack-node-externals": "1.5.4"
     "webpack-node-externals": "1.5.4"

+ 12 - 1
src/JavaScriptObfuscator.ts

@@ -4,15 +4,20 @@ if (!(<any>global)._babelPolyfill) {
     require('babel-polyfill');
     require('babel-polyfill');
 }
 }
 
 
+import { ServiceIdentifiers } from './container/ServiceIdentifiers';
+
+import { Chance } from 'chance';
+
 import { TInputOptions } from './types/options/TInputOptions';
 import { TInputOptions } from './types/options/TInputOptions';
 
 
 import { IInversifyContainerFacade } from './interfaces/container/IInversifyContainerFacade';
 import { IInversifyContainerFacade } from './interfaces/container/IInversifyContainerFacade';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
+import { IOptions } from './interfaces/options/IOptions';
 
 
 import { InversifyContainerFacade } from './container/InversifyContainerFacade';
 import { InversifyContainerFacade } from './container/InversifyContainerFacade';
 import { JavaScriptObfuscatorCLI } from './cli/JavaScriptObfuscatorCLI';
 import { JavaScriptObfuscatorCLI } from './cli/JavaScriptObfuscatorCLI';
-import { ServiceIdentifiers } from './container/ServiceIdentifiers';
+import { RandomGeneratorUtils } from './utils/RandomGeneratorUtils';
 
 
 export class JavaScriptObfuscator {
 export class JavaScriptObfuscator {
     /**
     /**
@@ -22,6 +27,12 @@ export class JavaScriptObfuscator {
      */
      */
     public static obfuscate (sourceCode: string, inputOptions: TInputOptions = {}): IObfuscationResult {
     public static obfuscate (sourceCode: string, inputOptions: TInputOptions = {}): IObfuscationResult {
         const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade(inputOptions);
         const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade(inputOptions);
+        const options: IOptions = inversifyContainerFacade.get<IOptions>(ServiceIdentifiers.IOptions);
+
+        if (options.seed !== 0) {
+            RandomGeneratorUtils.setRandomGenerator(new Chance(options.seed));
+        }
+
         const javaScriptObfuscator: IJavaScriptObfuscator = inversifyContainerFacade
         const javaScriptObfuscator: IJavaScriptObfuscator = inversifyContainerFacade
             .get<IJavaScriptObfuscator>(ServiceIdentifiers.IJavaScriptObfuscator);
             .get<IJavaScriptObfuscator>(ServiceIdentifiers.IJavaScriptObfuscator);
 
 

+ 1 - 7
src/JavaScriptObfuscatorInternal.ts

@@ -5,15 +5,13 @@ import * as esprima from 'esprima';
 import * as escodegen from 'escodegen';
 import * as escodegen from 'escodegen';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
+import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
 import { IObfuscator } from './interfaces/IObfuscator';
 import { IObfuscator } from './interfaces/IObfuscator';
-import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
 import { IOptions } from './interfaces/options/IOptions';
 import { IOptions } from './interfaces/options/IOptions';
 import { ISourceMapCorrector } from './interfaces/ISourceMapCorrector';
 import { ISourceMapCorrector } from './interfaces/ISourceMapCorrector';
 
 
-import { RandomGeneratorUtils } from './utils/RandomGeneratorUtils';
-
 @injectable()
 @injectable()
 export class JavaScriptObfuscatorInternal implements IJavaScriptObfuscator {
 export class JavaScriptObfuscatorInternal implements IJavaScriptObfuscator {
     /**
     /**
@@ -102,10 +100,6 @@ export class JavaScriptObfuscatorInternal implements IJavaScriptObfuscator {
      * @returns {IObfuscationResult}
      * @returns {IObfuscationResult}
      */
      */
     public obfuscate (sourceCode: string): IObfuscationResult {
     public obfuscate (sourceCode: string): IObfuscationResult {
-        if (this.options.seed !== 0) {
-            RandomGeneratorUtils.setRandomGeneratorSeed(this.options.seed);
-        }
-
         // parse AST tree
         // parse AST tree
         const astTree: ESTree.Program = esprima.parse(sourceCode, JavaScriptObfuscatorInternal.esprimaParams);
         const astTree: ESTree.Program = esprima.parse(sourceCode, JavaScriptObfuscatorInternal.esprimaParams);
 
 

+ 2 - 2
src/Obfuscator.ts

@@ -29,7 +29,7 @@ export class Obfuscator implements IObfuscator {
     /**
     /**
      * @type {Map<string, NodeTransformers[]>}
      * @type {Map<string, NodeTransformers[]>}
      */
      */
-    private static readonly nodeControlFlowTransformersMap: Map <string, NodeTransformers[]> = new Map <string, NodeTransformers[]> ([
+    private static readonly nodeControlFlowTransformersMap: Map <string, NodeTransformers[]> = new Map([
         [NodeType.FunctionDeclaration, [NodeTransformers.FunctionControlFlowTransformer]],
         [NodeType.FunctionDeclaration, [NodeTransformers.FunctionControlFlowTransformer]],
         [NodeType.FunctionExpression, [NodeTransformers.FunctionControlFlowTransformer]]
         [NodeType.FunctionExpression, [NodeTransformers.FunctionControlFlowTransformer]]
     ]);
     ]);
@@ -37,7 +37,7 @@ export class Obfuscator implements IObfuscator {
     /**
     /**
      * @type {Map<string, NodeTransformers[]>}
      * @type {Map<string, NodeTransformers[]>}
      */
      */
-    private static readonly nodeObfuscatorsMap: Map <string, NodeTransformers[]> = new Map <string, NodeTransformers[]> ([
+    private static readonly nodeObfuscatorsMap: Map <string, NodeTransformers[]> = new Map([
         [NodeType.ArrowFunctionExpression, [NodeTransformers.FunctionObfuscator]],
         [NodeType.ArrowFunctionExpression, [NodeTransformers.FunctionObfuscator]],
         [NodeType.ClassDeclaration, [NodeTransformers.FunctionDeclarationObfuscator]],
         [NodeType.ClassDeclaration, [NodeTransformers.FunctionDeclarationObfuscator]],
         [NodeType.CatchClause, [NodeTransformers.CatchClauseObfuscator]],
         [NodeType.CatchClause, [NodeTransformers.CatchClauseObfuscator]],

+ 1 - 3
src/cli/CLIUtils.ts

@@ -4,8 +4,6 @@ import * as path from 'path';
 
 
 import { IPackageConfig } from '../interfaces/IPackageConfig';
 import { IPackageConfig } from '../interfaces/IPackageConfig';
 
 
-import { Utils } from '../utils/Utils';
-
 export class CLIUtils {
 export class CLIUtils {
     /**
     /**
      * @type {string[]}
      * @type {string[]}
@@ -102,7 +100,7 @@ export class CLIUtils {
             throw new ReferenceError(`Given input path must be a valid file path`);
             throw new ReferenceError(`Given input path must be a valid file path`);
         }
         }
 
 
-        if (!Utils.arrayContains(CLIUtils.availableInputExtensions, path.extname(inputPath))) {
+        if (!CLIUtils.availableInputExtensions.includes(path.extname(inputPath))) {
             throw new ReferenceError(`Input file must have .js extension`);
             throw new ReferenceError(`Input file must have .js extension`);
         }
         }
     }
     }

+ 8 - 4
src/cli/JavaScriptObfuscatorCLI.ts

@@ -9,11 +9,10 @@ import { IObfuscationResult } from '../interfaces/IObfuscationResult';
 import { SourceMapMode } from '../enums/SourceMapMode';
 import { SourceMapMode } from '../enums/SourceMapMode';
 import { StringArrayEncoding } from '../enums/StringArrayEncoding';
 import { StringArrayEncoding } from '../enums/StringArrayEncoding';
 
 
-import { DEFAULT_PRESET } from '../preset-options/DefaultPreset';
+import { DEFAULT_PRESET } from '../options/presets/Default';
 
 
 import { CLIUtils } from './CLIUtils';
 import { CLIUtils } from './CLIUtils';
 import { JavaScriptObfuscator } from '../JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../JavaScriptObfuscator';
-import { Utils } from '../utils/Utils';
 
 
 export class JavaScriptObfuscatorCLI {
 export class JavaScriptObfuscatorCLI {
     /**
     /**
@@ -104,7 +103,7 @@ export class JavaScriptObfuscatorCLI {
     public run (): void {
     public run (): void {
         this.configureCommands();
         this.configureCommands();
 
 
-        if (!this.arguments.length || Utils.arrayContains(this.arguments, '--help')) {
+        if (!this.arguments.length || this.arguments.includes('--help')) {
             this.commands.outputHelp();
             this.commands.outputHelp();
 
 
             return;
             return;
@@ -129,7 +128,7 @@ export class JavaScriptObfuscatorCLI {
                 continue;
                 continue;
             }
             }
 
 
-            if (!Utils.arrayContains(availableOptions, option)) {
+            if (!availableOptions.includes(option)) {
                 continue;
                 continue;
             }
             }
 
 
@@ -160,6 +159,11 @@ export class JavaScriptObfuscatorCLI {
                 'Enables control flow flattening',
                 'Enables control flow flattening',
                 JavaScriptObfuscatorCLI.parseBoolean
                 JavaScriptObfuscatorCLI.parseBoolean
             )
             )
+            .option(
+                '--controlFlowFlatteningThreshold <number>',
+                'The probability that the control flow flattening transformation will be applied to the node',
+                parseFloat
+            )
             .option(
             .option(
                 '--debugProtection <boolean>',
                 '--debugProtection <boolean>',
                 'Disable browser Debug panel (can cause DevTools enabled browser freeze)',
                 'Disable browser Debug panel (can cause DevTools enabled browser freeze)',

+ 4 - 3
src/container/ServiceIdentifiers.ts

@@ -5,7 +5,7 @@ export const ServiceIdentifiers: any = {
     'Factory<ICustomNodeGroup>': Symbol('Factory<ICustomNodeGroup>'),
     'Factory<ICustomNodeGroup>': Symbol('Factory<ICustomNodeGroup>'),
     'Factory<INodeTransformer[]>': Symbol('Factory<INodeTransformer[]>'),
     'Factory<INodeTransformer[]>': Symbol('Factory<INodeTransformer[]>'),
     'Factory<IObfuscationResult>': Symbol('Factory<IObfuscationResult>'),
     'Factory<IObfuscationResult>': Symbol('Factory<IObfuscationResult>'),
-    'Factory<IReplacer>': Symbol('Factory<IReplacer>'),
+    'Factory<IObfuscatorReplacer>': Symbol('Factory<IObfuscatorReplacer>'),
     'Factory<IStorage<ICustomNode>>': Symbol('Factory<IStorage<ICustomNode>>'),
     'Factory<IStorage<ICustomNode>>': Symbol('Factory<IStorage<ICustomNode>>'),
     ICalleeDataExtractor: Symbol('ICalleeDataExtractor'),
     ICalleeDataExtractor: Symbol('ICalleeDataExtractor'),
     ICustomNode: Symbol('ICustomNode'),
     ICustomNode: Symbol('ICustomNode'),
@@ -17,9 +17,10 @@ export const ServiceIdentifiers: any = {
     IObfuscationResult: Symbol('IObfuscationResult'),
     IObfuscationResult: Symbol('IObfuscationResult'),
     IObfuscator: Symbol('IObfuscator'),
     IObfuscator: Symbol('IObfuscator'),
     IOptions: Symbol('IOptions'),
     IOptions: Symbol('IOptions'),
-    IReplacer: Symbol('IReplacer'),
+    IObfuscatorReplacer: Symbol('IObfuscatorReplacer'),
     ISourceMapCorrector: Symbol('ISourceMapCorrector'),
     ISourceMapCorrector: Symbol('ISourceMapCorrector'),
     IStackTraceAnalyzer: Symbol('IStackTraceAnalyzer'),
     IStackTraceAnalyzer: Symbol('IStackTraceAnalyzer'),
     'IStorage<ICustomNode>': Symbol('IStorage<ICustomNode>'),
     'IStorage<ICustomNode>': Symbol('IStorage<ICustomNode>'),
-    'IStorage<ICustomNodeGroup>': Symbol('IStorage<ICustomNodeGroup>')
+    'IStorage<ICustomNodeGroup>': Symbol('IStorage<ICustomNodeGroup>'),
+    'IStorage<string>': Symbol('IStorage<string>')
 };
 };

+ 1 - 1
src/container/modules/node-transformers/NodeControlFlowTransformersModule.ts

@@ -14,7 +14,7 @@ export const nodeControlFlowTransformersModule: interfaces.ContainerModule = new
 
 
     bind<IControlFlowReplacer>(ServiceIdentifiers['Factory<IControlFlowReplacer>'])
     bind<IControlFlowReplacer>(ServiceIdentifiers['Factory<IControlFlowReplacer>'])
         .toFactory<IControlFlowReplacer>((context: interfaces.Context) => {
         .toFactory<IControlFlowReplacer>((context: interfaces.Context) => {
-            const cache: Map <NodeControlFlowReplacers, IControlFlowReplacer> = new Map <NodeControlFlowReplacers, IControlFlowReplacer> ();
+            const cache: Map <NodeControlFlowReplacers, IControlFlowReplacer> = new Map();
 
 
             return (replacerName: NodeControlFlowReplacers) => {
             return (replacerName: NodeControlFlowReplacers) => {
                 if (cache.has(replacerName)) {
                 if (cache.has(replacerName)) {

+ 11 - 11
src/container/modules/node-transformers/NodeObfuscatorsModule.ts

@@ -1,7 +1,7 @@
 import { ContainerModule, interfaces } from 'inversify';
 import { ContainerModule, interfaces } from 'inversify';
 import { ServiceIdentifiers } from '../../ServiceIdentifiers';
 import { ServiceIdentifiers } from '../../ServiceIdentifiers';
 
 
-import { IReplacer } from '../../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../../interfaces/node-transformers/IObfuscatorReplacer';
 
 
 import { NodeObfuscatorsReplacers } from '../../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../../enums/container/NodeObfuscatorsReplacers';
 
 
@@ -11,33 +11,33 @@ import { NumberLiteralReplacer } from '../../../node-transformers/node-obfuscato
 import { StringLiteralReplacer } from '../../../node-transformers/node-obfuscators/replacers/StringLiteralReplacer';
 import { StringLiteralReplacer } from '../../../node-transformers/node-obfuscators/replacers/StringLiteralReplacer';
 
 
 export const nodeObfuscatorsModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
 export const nodeObfuscatorsModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
-    bind<IReplacer>(ServiceIdentifiers.IReplacer)
+    bind<IObfuscatorReplacer>(ServiceIdentifiers.IObfuscatorReplacer)
         .to(BooleanLiteralReplacer)
         .to(BooleanLiteralReplacer)
         .whenTargetNamed(NodeObfuscatorsReplacers.BooleanReplacer);
         .whenTargetNamed(NodeObfuscatorsReplacers.BooleanReplacer);
 
 
-    bind<IReplacer>(ServiceIdentifiers.IReplacer)
+    bind<IObfuscatorReplacer>(ServiceIdentifiers.IObfuscatorReplacer)
         .to(IdentifierReplacer)
         .to(IdentifierReplacer)
         .whenTargetNamed(NodeObfuscatorsReplacers.IdentifierReplacer);
         .whenTargetNamed(NodeObfuscatorsReplacers.IdentifierReplacer);
 
 
-    bind<IReplacer>(ServiceIdentifiers.IReplacer)
+    bind<IObfuscatorReplacer>(ServiceIdentifiers.IObfuscatorReplacer)
         .to(NumberLiteralReplacer)
         .to(NumberLiteralReplacer)
         .whenTargetNamed(NodeObfuscatorsReplacers.NumberLiteralReplacer);
         .whenTargetNamed(NodeObfuscatorsReplacers.NumberLiteralReplacer);
 
 
-    bind<IReplacer>(ServiceIdentifiers.IReplacer)
+    bind<IObfuscatorReplacer>(ServiceIdentifiers.IObfuscatorReplacer)
         .to(StringLiteralReplacer)
         .to(StringLiteralReplacer)
         .whenTargetNamed(NodeObfuscatorsReplacers.StringLiteralReplacer);
         .whenTargetNamed(NodeObfuscatorsReplacers.StringLiteralReplacer);
 
 
-    bind<IReplacer>(ServiceIdentifiers['Factory<IReplacer>'])
-        .toFactory<IReplacer>((context: interfaces.Context) => {
-            const cache: Map <NodeObfuscatorsReplacers, IReplacer> = new Map <NodeObfuscatorsReplacers, IReplacer> ();
+    bind<IObfuscatorReplacer>(ServiceIdentifiers['Factory<IObfuscatorReplacer>'])
+        .toFactory<IObfuscatorReplacer>((context: interfaces.Context) => {
+            const cache: Map <NodeObfuscatorsReplacers, IObfuscatorReplacer> = new Map();
 
 
             return (replacerName: NodeObfuscatorsReplacers) => {
             return (replacerName: NodeObfuscatorsReplacers) => {
                 if (cache.has(replacerName)) {
                 if (cache.has(replacerName)) {
-                    return <IReplacer>cache.get(replacerName);
+                    return <IObfuscatorReplacer>cache.get(replacerName);
                 }
                 }
 
 
-                const obfuscationReplacer: IReplacer = context.container.getNamed<IReplacer>(
-                    ServiceIdentifiers.IReplacer,
+                const obfuscationReplacer: IObfuscatorReplacer = context.container.getNamed<IObfuscatorReplacer>(
+                    ServiceIdentifiers.IObfuscatorReplacer,
                     replacerName
                     replacerName
                 );
                 );
 
 

+ 1 - 1
src/container/modules/node-transformers/NodeTransformersModule.ts

@@ -63,7 +63,7 @@ export const nodeTransformersModule: interfaces.ContainerModule = new ContainerM
     // node transformers factory
     // node transformers factory
     bind<INodeTransformer[]>(ServiceIdentifiers['Factory<INodeTransformer[]>'])
     bind<INodeTransformer[]>(ServiceIdentifiers['Factory<INodeTransformer[]>'])
         .toFactory<INodeTransformer[]>((context: interfaces.Context) => {
         .toFactory<INodeTransformer[]>((context: interfaces.Context) => {
-            const cache: Map <NodeTransformers, INodeTransformer> = new Map <NodeTransformers, INodeTransformer> ();
+            const cache: Map <NodeTransformers, INodeTransformer> = new Map();
 
 
             return (nodeTransformersMap: Map<string, NodeTransformers[]>) => (nodeType: string) => {
             return (nodeTransformersMap: Map<string, NodeTransformers[]>) => (nodeType: string) => {
                 const nodeTransformers: NodeTransformers[] = nodeTransformersMap.get(nodeType) || [];
                 const nodeTransformers: NodeTransformers[] = nodeTransformersMap.get(nodeType) || [];

+ 1 - 1
src/container/modules/stack-trace-analyzer/StackTraceAnalyzerModule.ts

@@ -32,7 +32,7 @@ export const stackTraceAnalyzerModule: interfaces.ContainerModule = new Containe
     // node transformers factory
     // node transformers factory
     bind<ICalleeDataExtractor>(ServiceIdentifiers['Factory<ICalleeDataExtractor>'])
     bind<ICalleeDataExtractor>(ServiceIdentifiers['Factory<ICalleeDataExtractor>'])
         .toFactory<ICalleeDataExtractor>((context: interfaces.Context) => {
         .toFactory<ICalleeDataExtractor>((context: interfaces.Context) => {
-            const cache: Map <CalleeDataExtractors, ICalleeDataExtractor> = new Map <CalleeDataExtractors, ICalleeDataExtractor> ();
+            const cache: Map <CalleeDataExtractors, ICalleeDataExtractor> = new Map();
 
 
             return (calleeDataExtractorName: CalleeDataExtractors) => {
             return (calleeDataExtractorName: CalleeDataExtractors) => {
                 if (cache.has(calleeDataExtractorName)) {
                 if (cache.has(calleeDataExtractorName)) {

+ 5 - 0
src/container/modules/storages/StoragesModule.ts

@@ -7,6 +7,7 @@ import { IStorage } from '../../../interfaces/storages/IStorage';
 
 
 import { ControlFlowStorage } from '../../../storages/control-flow/ControlFlowStorage';
 import { ControlFlowStorage } from '../../../storages/control-flow/ControlFlowStorage';
 import { CustomNodeGroupStorage } from '../../../storages/custom-node-group/CustomNodeGroupStorage';
 import { CustomNodeGroupStorage } from '../../../storages/custom-node-group/CustomNodeGroupStorage';
+import { StringArrayStorage } from '../../../storages/string-array/StringArrayStorage';
 
 
 export const storagesModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
 export const storagesModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
     // storages
     // storages
@@ -17,6 +18,10 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
     bind<IStorage<ICustomNode>>(ServiceIdentifiers['IStorage<ICustomNode>'])
     bind<IStorage<ICustomNode>>(ServiceIdentifiers['IStorage<ICustomNode>'])
         .to(ControlFlowStorage);
         .to(ControlFlowStorage);
 
 
+    bind<IStorage<string>>(ServiceIdentifiers['IStorage<string>'])
+        .to(StringArrayStorage)
+        .inSingletonScope();
+
     // controlFlowStorage factory
     // controlFlowStorage factory
     bind<IStorage<ICustomNode>>(ServiceIdentifiers['Factory<IStorage<ICustomNode>>'])
     bind<IStorage<ICustomNode>>(ServiceIdentifiers['Factory<IStorage<ICustomNode>>'])
         .toFactory<IStorage<ICustomNode>>((context: interfaces.Context) => {
         .toFactory<IStorage<ICustomNode>>((context: interfaces.Context) => {

+ 9 - 2
src/custom-nodes/AbstractCustomNode.ts

@@ -31,7 +31,9 @@ export abstract class AbstractCustomNode implements ICustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public abstract getCode (): string;
+    public getCode (): string {
+        return NodeUtils.convertStructureToCode(this.getNode());
+    }
 
 
     /**
     /**
      * @returns {TStatement[]}
      * @returns {TStatement[]}
@@ -44,6 +46,11 @@ export abstract class AbstractCustomNode implements ICustomNode {
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
     protected getNodeStructure (): TStatement[] {
     protected getNodeStructure (): TStatement[] {
-        return NodeUtils.convertCodeToStructure(this.getCode());
+        return NodeUtils.convertCodeToStructure(this.getTemplate());
     }
     }
+
+    /**
+     * @returns {string}
+     */
+    protected abstract getTemplate (): string;
 }
 }

+ 1 - 1
src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts

@@ -39,7 +39,7 @@ export class ConsoleOutputDisableExpressionNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         return format(ConsoleOutputDisableExpressionTemplate(), {
         return format(ConsoleOutputDisableExpressionTemplate(), {
             consoleLogDisableFunctionName: RandomGeneratorUtils.getRandomVariableName(),
             consoleLogDisableFunctionName: RandomGeneratorUtils.getRandomVariableName(),
             singleNodeCallControllerFunctionName: this.callsControllerFunctionName
             singleNodeCallControllerFunctionName: this.callsControllerFunctionName

+ 2 - 2
src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionNode.ts

@@ -32,14 +32,14 @@ export class BinaryExpressionFunctionNode extends AbstractCustomNode {
     /**
     /**
      * @param operator
      * @param operator
      */
      */
-    initialize (operator: string): void {
+    public initialize (operator: string): void {
         this.operator = operator;
         this.operator = operator;
     }
     }
 
 
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         return format(BinaryExpressionFunctionTemplate(), {
         return format(BinaryExpressionFunctionTemplate(), {
             functionName: RandomGeneratorUtils.getRandomVariableName(1),
             functionName: RandomGeneratorUtils.getRandomVariableName(1),
             operator: this.operator
             operator: this.operator

+ 1 - 1
src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode.ts

@@ -67,7 +67,7 @@ export class ControlFlowStorageCallNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         return format(ControlFlowStorageCallTemplate(), {
         return format(ControlFlowStorageCallTemplate(), {
             controlFlowStorageKey: this.controlFlowStorageKey,
             controlFlowStorageKey: this.controlFlowStorageKey,
             controlFlowStorageName: this.controlFlowStorageName,
             controlFlowStorageName: this.controlFlowStorageName,

+ 3 - 11
src/custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts

@@ -21,12 +21,6 @@ export class ControlFlowStorageNode extends AbstractCustomNode {
     @initializable()
     @initializable()
     private controlFlowStorage: IStorage <ICustomNode>;
     private controlFlowStorage: IStorage <ICustomNode>;
 
 
-    /**
-     * @type {string}
-     */
-    @initializable()
-    private controlFlowStorageName: string;
-
     /**
     /**
      * @param options
      * @param options
      */
      */
@@ -38,20 +32,18 @@ export class ControlFlowStorageNode extends AbstractCustomNode {
 
 
     /**
     /**
      * @param controlFlowStorage
      * @param controlFlowStorage
-     * @param controlFlowStorageName
      */
      */
-    public initialize (controlFlowStorage: IStorage <ICustomNode>, controlFlowStorageName: string): void {
+    public initialize (controlFlowStorage: IStorage <ICustomNode>): void {
         this.controlFlowStorage = controlFlowStorage;
         this.controlFlowStorage = controlFlowStorage;
-        this.controlFlowStorageName = controlFlowStorageName;
     }
     }
 
 
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         return format(ControlFlowStorageTemplate(), {
         return format(ControlFlowStorageTemplate(), {
             controlFlowStorage: this.controlFlowStorage.toString(),
             controlFlowStorage: this.controlFlowStorage.toString(),
-            controlFlowStorageName: this.controlFlowStorageName
+            controlFlowStorageName: this.controlFlowStorage.getStorageId()
         });
         });
     }
     }
 }
 }

+ 1 - 1
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts

@@ -38,7 +38,7 @@ export class DebugProtectionFunctionCallNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         return format(DebugProtectionFunctionCallTemplate(), {
         return format(DebugProtectionFunctionCallTemplate(), {
             debugProtectionFunctionName: this.debugProtectionFunctionName
             debugProtectionFunctionName: this.debugProtectionFunctionName
         });
         });

+ 1 - 1
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts

@@ -38,7 +38,7 @@ export class DebugProtectionFunctionIntervalNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         return format(DebugProtectionFunctionIntervalTemplate(), {
         return format(DebugProtectionFunctionIntervalTemplate(), {
             debugProtectionFunctionName: this.debugProtectionFunctionName
             debugProtectionFunctionName: this.debugProtectionFunctionName
         });
         });

+ 1 - 1
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts

@@ -38,7 +38,7 @@ export class DebugProtectionFunctionNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         return format(DebugProtectionFunctionTemplate(), {
         return format(DebugProtectionFunctionTemplate(), {
             debugProtectionFunctionName: this.debugProtectionFunctionName
             debugProtectionFunctionName: this.debugProtectionFunctionName
         });
         });

+ 1 - 1
src/custom-nodes/domain-lock-nodes/DomainLockNode.ts

@@ -40,7 +40,7 @@ export class DomainLockNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         let domainsString: string = this.options.domainLock.join(';'),
         let domainsString: string = this.options.domainLock.join(';'),
             [hiddenDomainsString, diff]: string[] = CryptUtils.hideString(domainsString, domainsString.length * 3);
             [hiddenDomainsString, diff]: string[] = CryptUtils.hideString(domainsString, domainsString.length * 3);
 
 

+ 2 - 2
src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts

@@ -13,7 +13,7 @@ import { initializable } from '../../decorators/Initializable';
 
 
 import { SingleNodeCallControllerTemplate } from '../../templates/custom-nodes/SingleNodeCallControllerTemplate';
 import { SingleNodeCallControllerTemplate } from '../../templates/custom-nodes/SingleNodeCallControllerTemplate';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../options/presets/NoCustomNodes';
 
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
@@ -53,7 +53,7 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         if (this.appendEvent === ObfuscationEvents.AfterObfuscation) {
         if (this.appendEvent === ObfuscationEvents.AfterObfuscation) {
             return JavaScriptObfuscator.obfuscate(
             return JavaScriptObfuscator.obfuscate(
                 format(SingleNodeCallControllerTemplate(), {
                 format(SingleNodeCallControllerTemplate(), {

+ 2 - 2
src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts

@@ -7,7 +7,7 @@ import { IOptions } from '../../interfaces/options/IOptions';
 
 
 import { initializable } from '../../decorators/Initializable';
 import { initializable } from '../../decorators/Initializable';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../options/presets/NoCustomNodes';
 
 
 import { SelfDefendingTemplate } from '../../templates/custom-nodes/self-defending-nodes/self-defending-unicode-node/SelfDefendingTemplate';
 import { SelfDefendingTemplate } from '../../templates/custom-nodes/self-defending-nodes/self-defending-unicode-node/SelfDefendingTemplate';
 
 
@@ -42,7 +42,7 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         return JavaScriptObfuscator.obfuscate(
         return JavaScriptObfuscator.obfuscate(
             format(SelfDefendingTemplate(), {
             format(SelfDefendingTemplate(), {
                 selfDefendingFunctionName: RandomGeneratorUtils.getRandomVariableName(),
                 selfDefendingFunctionName: RandomGeneratorUtils.getRandomVariableName(),

+ 27 - 35
src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts

@@ -3,7 +3,6 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
 
 import * as format from 'string-template';
 import * as format from 'string-template';
 
 
-import { ICustomNodeWithIdentifier } from '../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IStorage } from '../../interfaces/storages/IStorage';
 import { IStorage } from '../../interfaces/storages/IStorage';
 
 
@@ -11,7 +10,7 @@ import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
 
 
 import { initializable } from '../../decorators/Initializable';
 import { initializable } from '../../decorators/Initializable';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../options/presets/NoCustomNodes';
 
 
 import { AtobTemplate } from '../../templates/custom-nodes/AtobTemplate';
 import { AtobTemplate } from '../../templates/custom-nodes/AtobTemplate';
 import { Rc4Template } from '../../templates/custom-nodes/Rc4Template';
 import { Rc4Template } from '../../templates/custom-nodes/Rc4Template';
@@ -24,24 +23,24 @@ import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 
 
 @injectable()
 @injectable()
-export class StringArrayCallsWrapper extends AbstractCustomNode implements ICustomNodeWithIdentifier {
+export class StringArrayCallsWrapper extends AbstractCustomNode {
     /**
     /**
      * @type {IStorage <string>}
      * @type {IStorage <string>}
      */
      */
     @initializable()
     @initializable()
-    private stringArray: IStorage <string>;
+    private stringArrayStorage: IStorage <string>;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
     @initializable()
     @initializable()
-    private stringArrayCallsWrapperName: string;
+    private stringArrayName: string;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
     @initializable()
     @initializable()
-    private stringArrayName: string;
+    private stringArrayCallsWrapperName: string;
 
 
     /**
     /**
      * @param options
      * @param options
@@ -53,46 +52,20 @@ export class StringArrayCallsWrapper extends AbstractCustomNode implements ICust
     }
     }
 
 
     /**
     /**
-     * @param stringArray
+     * @param stringArrayStorage
      * @param stringArrayName
      * @param stringArrayName
      * @param stringArrayCallsWrapperName
      * @param stringArrayCallsWrapperName
      */
      */
     public initialize (
     public initialize (
-        stringArray: IStorage <string>,
+        stringArrayStorage: IStorage <string>,
         stringArrayName: string,
         stringArrayName: string,
         stringArrayCallsWrapperName: string
         stringArrayCallsWrapperName: string
     ): void {
     ): void {
-        this.stringArray = stringArray;
+        this.stringArrayStorage = stringArrayStorage;
         this.stringArrayName = stringArrayName;
         this.stringArrayName = stringArrayName;
         this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
         this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
     }
     }
 
 
-    /**
-     * @returns {string}
-     */
-    public getCode (): string {
-        const decodeNodeTemplate: string = this.getDecodeStringArrayTemplate();
-
-        return JavaScriptObfuscator.obfuscate(
-            format(StringArrayCallsWrapperTemplate(), {
-                decodeNodeTemplate,
-                stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
-                stringArrayName: this.stringArrayName
-            }),
-            {
-                ...NO_CUSTOM_NODES_PRESET,
-                seed: this.options.seed
-            }
-        ).getObfuscatedCode();
-    }
-
-    /**
-     * @returns {string}
-     */
-    public getNodeIdentifier (): string {
-        return this.stringArrayCallsWrapperName;
-    };
-
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
@@ -130,4 +103,23 @@ export class StringArrayCallsWrapper extends AbstractCustomNode implements ICust
 
 
         return decodeStringArrayTemplate;
         return decodeStringArrayTemplate;
     }
     }
+
+    /**
+     * @returns {string}
+     */
+    protected getTemplate (): string {
+        const decodeNodeTemplate: string = this.getDecodeStringArrayTemplate();
+
+        return JavaScriptObfuscator.obfuscate(
+            format(StringArrayCallsWrapperTemplate(), {
+                decodeNodeTemplate,
+                stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
+                stringArrayName: this.stringArrayName
+            }),
+            {
+                ...NO_CUSTOM_NODES_PRESET,
+                seed: this.options.seed
+            }
+        ).getObfuscatedCode();
+    }
 }
 }

+ 15 - 28
src/custom-nodes/string-array-nodes/StringArrayNode.ts

@@ -5,7 +5,6 @@ import * as format from 'string-template';
 
 
 import { TStatement } from '../../types/node/TStatement';
 import { TStatement } from '../../types/node/TStatement';
 
 
-import { ICustomNodeWithData } from '../../interfaces/custom-nodes/ICustomNodeWithData';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IStorage } from '../../interfaces/storages/IStorage';
 import { IStorage } from '../../interfaces/storages/IStorage';
 
 
@@ -17,17 +16,12 @@ import { AbstractCustomNode } from '../AbstractCustomNode';
 import { StringArrayStorage } from '../../storages/string-array/StringArrayStorage';
 import { StringArrayStorage } from '../../storages/string-array/StringArrayStorage';
 
 
 @injectable()
 @injectable()
-export class StringArrayNode extends AbstractCustomNode implements ICustomNodeWithData {
-    /**
-     * @type {number}
-     */
-    public static ARRAY_RANDOM_LENGTH: number = 4;
-
+export class StringArrayNode extends AbstractCustomNode {
     /**
     /**
      * @type {IStorage <string>}
      * @type {IStorage <string>}
      */
      */
     @initializable()
     @initializable()
-    private stringArray: IStorage <string>;
+    private stringArrayStorage: IStorage <string>;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
@@ -51,43 +45,36 @@ export class StringArrayNode extends AbstractCustomNode implements ICustomNodeWi
     }
     }
 
 
     /**
     /**
-     * @param stringArray
+     * @param stringArrayStorage
      * @param stringArrayName
      * @param stringArrayName
      * @param stringArrayRotateValue
      * @param stringArrayRotateValue
      */
      */
     public initialize (
     public initialize (
-        stringArray: IStorage <string>,
+        stringArrayStorage: IStorage <string>,
         stringArrayName: string,
         stringArrayName: string,
         stringArrayRotateValue: number
         stringArrayRotateValue: number
     ): void {
     ): void {
-        this.stringArray = stringArray;
+        this.stringArrayStorage = stringArrayStorage;
         this.stringArrayName = stringArrayName;
         this.stringArrayName = stringArrayName;
         this.stringArrayRotateValue = stringArrayRotateValue;
         this.stringArrayRotateValue = stringArrayRotateValue;
     }
     }
 
 
     /**
     /**
-     * @returns {string}
+     * @returns {TStatement[]}
      */
      */
-    public getCode (): string {
-        return format(StringArrayTemplate(), {
-            stringArrayName: this.stringArrayName,
-            stringArray: this.stringArray.toString()
-        });
-    }
+    public getNode (): TStatement[] {
+        (<StringArrayStorage>this.stringArrayStorage).rotateArray(this.stringArrayRotateValue);
 
 
-    /**
-     * @returns {IStorage <string>}
-     */
-    public getNodeData (): IStorage <string> {
-        return this.stringArray;
+        return super.getNode();
     }
     }
 
 
     /**
     /**
-     * @returns {TStatement[]}
+     * @returns {string}
      */
      */
-    public getNode (): TStatement[] {
-        (<StringArrayStorage>this.stringArray).rotateArray(this.stringArrayRotateValue);
-
-        return super.getNode();
+    protected getTemplate (): string {
+        return format(StringArrayTemplate(), {
+            stringArrayName: this.stringArrayName,
+            stringArray: this.stringArrayStorage.toString()
+        });
     }
     }
 }
 }

+ 6 - 6
src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts

@@ -8,7 +8,7 @@ import { IStorage } from '../../interfaces/storages/IStorage';
 
 
 import { initializable } from '../../decorators/Initializable';
 import { initializable } from '../../decorators/Initializable';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../options/presets/NoCustomNodes';
 
 
 import { SelfDefendingTemplate } from '../../templates/custom-nodes/string-array-nodes/string-array-rotate-function-node/SelfDefendingTemplate';
 import { SelfDefendingTemplate } from '../../templates/custom-nodes/string-array-nodes/string-array-rotate-function-node/SelfDefendingTemplate';
 import { StringArrayRotateFunctionTemplate } from '../../templates/custom-nodes/string-array-nodes/string-array-rotate-function-node/StringArrayRotateFunctionTemplate';
 import { StringArrayRotateFunctionTemplate } from '../../templates/custom-nodes/string-array-nodes/string-array-rotate-function-node/StringArrayRotateFunctionTemplate';
@@ -24,7 +24,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
      * @type {IStorage <string>}
      * @type {IStorage <string>}
      */
      */
     @initializable()
     @initializable()
-    private stringArray: IStorage <string>;
+    private stringArrayStorage: IStorage <string>;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
@@ -48,16 +48,16 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
     }
     }
 
 
     /**
     /**
-     * @param stringArray
+     * @param stringArrayStorage
      * @param stringArrayName
      * @param stringArrayName
      * @param stringArrayRotateValue
      * @param stringArrayRotateValue
      */
      */
     public initialize (
     public initialize (
-        stringArray: IStorage <string>,
+        stringArrayStorage: IStorage <string>,
         stringArrayName: string,
         stringArrayName: string,
         stringArrayRotateValue: number
         stringArrayRotateValue: number
     ): void {
     ): void {
-        this.stringArray = stringArray;
+        this.stringArrayStorage = stringArrayStorage;
         this.stringArrayName = stringArrayName;
         this.stringArrayName = stringArrayName;
         this.stringArrayRotateValue = stringArrayRotateValue;
         this.stringArrayRotateValue = stringArrayRotateValue;
     }
     }
@@ -65,7 +65,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getCode (): string {
+    protected getTemplate (): string {
         let code: string = '',
         let code: string = '',
             timesName: string = RandomGeneratorUtils.getRandomVariableName(),
             timesName: string = RandomGeneratorUtils.getRandomVariableName(),
             whileFunctionName: string = RandomGeneratorUtils.getRandomVariableName();
             whileFunctionName: string = RandomGeneratorUtils.getRandomVariableName();

+ 13 - 11
src/custom-nodes/string-array-nodes/group/StringArrayCustomNodeGroup.ts

@@ -16,12 +16,10 @@ import { initializable } from '../../../decorators/Initializable';
 import { CustomNodes } from '../../../enums/container/CustomNodes';
 import { CustomNodes } from '../../../enums/container/CustomNodes';
 import { ObfuscationEvents } from '../../../enums/ObfuscationEvents';
 import { ObfuscationEvents } from '../../../enums/ObfuscationEvents';
 
 
-import { StringArrayNode } from '../StringArrayNode';
-
 import { AbstractCustomNodeGroup } from '../../AbstractCustomNodeGroup';
 import { AbstractCustomNodeGroup } from '../../AbstractCustomNodeGroup';
 import { NodeAppender } from '../../../node/NodeAppender';
 import { NodeAppender } from '../../../node/NodeAppender';
 import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
-import { StringArrayStorage } from '../../../storages/string-array/StringArrayStorage';
+import { Utils } from '../../../utils/Utils';
 
 
 @injectable()
 @injectable()
 export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
 export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
@@ -50,22 +48,25 @@ export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
      * @type {IStorage <string>}
      * @type {IStorage <string>}
      */
      */
     @initializable()
     @initializable()
-    private stringArray: IStorage <string>;
+    private stringArrayStorage: IStorage <string>;
 
 
     /**
     /**
      * @param customNodeFactory
      * @param customNodeFactory
      * @param obfuscationEventEmitter
      * @param obfuscationEventEmitter
+     * @param stringArrayStorage
      * @param options
      * @param options
      */
      */
     constructor (
     constructor (
         @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
         @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
         @inject(ServiceIdentifiers.IObfuscationEventEmitter) obfuscationEventEmitter: IObfuscationEventEmitter,
         @inject(ServiceIdentifiers.IObfuscationEventEmitter) obfuscationEventEmitter: IObfuscationEventEmitter,
+        @inject(ServiceIdentifiers['IStorage<string>']) stringArrayStorage: IStorage<string>,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);
 
 
         this.customNodeFactory = customNodeFactory;
         this.customNodeFactory = customNodeFactory;
         this.obfuscationEventEmitter = obfuscationEventEmitter;
         this.obfuscationEventEmitter = obfuscationEventEmitter;
+        this.stringArrayStorage = stringArrayStorage;
     }
     }
 
 
     /**
     /**
@@ -73,7 +74,7 @@ export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
      * @param stackTraceData
      * @param stackTraceData
      */
      */
     public appendCustomNodes (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
     public appendCustomNodes (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
-        if (!this.stringArray.getLength()) {
+        if (!this.stringArrayStorage.getLength()) {
             return;
             return;
         }
         }
 
 
@@ -95,7 +96,6 @@ export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
 
 
     public initialize (): void {
     public initialize (): void {
         this.customNodes = new Map <CustomNodes, ICustomNode> ();
         this.customNodes = new Map <CustomNodes, ICustomNode> ();
-        this.stringArray = new StringArrayStorage();
 
 
         if (!this.options.stringArray) {
         if (!this.options.stringArray) {
             return;
             return;
@@ -105,8 +105,10 @@ export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
         const stringArrayCallsWrapper: ICustomNode = this.customNodeFactory(CustomNodes.StringArrayCallsWrapper);
         const stringArrayCallsWrapper: ICustomNode = this.customNodeFactory(CustomNodes.StringArrayCallsWrapper);
         const stringArrayRotateFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.StringArrayRotateFunctionNode);
         const stringArrayRotateFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.StringArrayRotateFunctionNode);
 
 
-        const stringArrayName: string = RandomGeneratorUtils.getRandomVariableName(StringArrayNode.ARRAY_RANDOM_LENGTH);
-        const stringArrayCallsWrapperName: string = RandomGeneratorUtils.getRandomVariableName(StringArrayNode.ARRAY_RANDOM_LENGTH);
+        const stringArrayStorageId: string = this.stringArrayStorage.getStorageId();
+
+        const stringArrayName: string = `_${Utils.hexadecimalPrefix}${stringArrayStorageId}`;
+        const stringArrayCallsWrapperName: string = `_${Utils.hexadecimalPrefix}${Utils.stringRotate(stringArrayStorageId, 2)}`;
 
 
         let stringArrayRotateValue: number;
         let stringArrayRotateValue: number;
 
 
@@ -116,9 +118,9 @@ export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
             stringArrayRotateValue = 0;
             stringArrayRotateValue = 0;
         }
         }
 
 
-        stringArrayNode.initialize(this.stringArray, stringArrayName, stringArrayRotateValue);
-        stringArrayCallsWrapper.initialize(this.stringArray, stringArrayName, stringArrayCallsWrapperName);
-        stringArrayRotateFunctionNode.initialize(this.stringArray, stringArrayName, stringArrayRotateValue);
+        stringArrayNode.initialize(this.stringArrayStorage, stringArrayName, stringArrayRotateValue);
+        stringArrayCallsWrapper.initialize(this.stringArrayStorage, stringArrayName, stringArrayCallsWrapperName);
+        stringArrayRotateFunctionNode.initialize(this.stringArrayStorage, stringArrayName, stringArrayRotateValue);
 
 
         this.customNodes.set(CustomNodes.StringArrayNode, stringArrayNode);
         this.customNodes.set(CustomNodes.StringArrayNode, stringArrayNode);
         this.customNodes.set(CustomNodes.StringArrayCallsWrapper, stringArrayCallsWrapper);
         this.customNodes.set(CustomNodes.StringArrayCallsWrapper, stringArrayCallsWrapper);

+ 1 - 1
src/declarations/ESTree.d.ts

@@ -4,8 +4,8 @@ import * as ESTree from 'estree';
 
 
 declare module 'estree' {
 declare module 'estree' {
     interface BaseNode {
     interface BaseNode {
-        parentNode?: ESTree.Node;
         obfuscated?: boolean;
         obfuscated?: boolean;
+        parentNode?: ESTree.Node;
     }
     }
 
 
     interface SimpleLiteral extends ESTree.BaseNode, ESTree.BaseExpression {
     interface SimpleLiteral extends ESTree.BaseNode, ESTree.BaseExpression {

+ 10 - 0
src/declarations/escodegen.d.ts

@@ -0,0 +1,10 @@
+import { IGeneratorOutput } from '../interfaces/IGeneratorOutput';
+
+declare module 'escodegen' {
+    /**
+     * @param ast
+     * @param options
+     * @returns IGeneratorOutput
+     */
+    export function generate(ast: any, options?: GenerateOptions): IGeneratorOutput;
+}

+ 26 - 30
src/decorators/Initializable.ts

@@ -12,46 +12,42 @@ export function initializable (
     const decoratorName: string = Object.keys(this)[0];
     const decoratorName: string = Object.keys(this)[0];
 
 
     return (target: IInitializable, propertyKey: string | symbol): any => {
     return (target: IInitializable, propertyKey: string | symbol): any => {
+        const descriptor: PropertyDescriptor = {
+            configurable: true,
+            enumerable: true
+        };
         const initializeMethod: any = (<any>target)[initializeMethodKey];
         const initializeMethod: any = (<any>target)[initializeMethodKey];
 
 
         if (!initializeMethod || typeof initializeMethod !== 'function') {
         if (!initializeMethod || typeof initializeMethod !== 'function') {
            throw new Error(`\`${initializeMethodKey}\` method with initialization logic not found. \`@${decoratorName}\` decorator requires \`${initializeMethodKey}\` method`);
            throw new Error(`\`${initializeMethodKey}\` method with initialization logic not found. \`@${decoratorName}\` decorator requires \`${initializeMethodKey}\` method`);
         }
         }
 
 
-        const methodDescriptor: PropertyDescriptor = Object.getOwnPropertyDescriptor(target, initializeMethodKey) || {
-            configurable: true,
-            enumerable: true
-        };
-        const originalMethod: Function = methodDescriptor.value;
-
-        methodDescriptor.value = function (): void {
-            originalMethod.apply(this, arguments);
-
-            // call property getter to activate initialization check inside it
-            if (this[propertyKey]) {}
-        };
-
-        Object.defineProperty(target, initializeMethodKey, methodDescriptor);
-
         const metadataPropertyKey: string = `_${propertyKey}`;
         const metadataPropertyKey: string = `_${propertyKey}`;
-        const propertyDescriptor: PropertyDescriptor = Object.getOwnPropertyDescriptor(target, metadataPropertyKey) || {
-            configurable: true,
-            enumerable: true
-        };
+        const propertyDescriptor: PropertyDescriptor = Object.getOwnPropertyDescriptor(target, metadataPropertyKey) || descriptor;
+        const methodDescriptor: PropertyDescriptor = Object.getOwnPropertyDescriptor(target, initializeMethodKey) || descriptor;
+        const originalMethod: Function = methodDescriptor.value;
 
 
-        propertyDescriptor.get = function(): any {
-            if (this[metadataPropertyKey] === undefined) {
-                throw new Error(`Property \`${propertyKey}\` is not initialized! Initialize it first!`);
+        Object.defineProperty(target, propertyKey, {
+            ...propertyDescriptor,
+            get: function (): any {
+                if (this[metadataPropertyKey] === undefined) {
+                    throw new Error(`Property \`${propertyKey}\` is not initialized! Initialize it first!`);
+                }
+
+                return this[metadataPropertyKey];
+            },
+            set: function (newVal: any): void {
+                this[metadataPropertyKey] = newVal;
             }
             }
+        });
+        Object.defineProperty(target, initializeMethodKey, {
+            ...methodDescriptor,
+            value: function (): void {
+                originalMethod.apply(this, arguments);
 
 
-            return this[metadataPropertyKey];
-        };
-
-        propertyDescriptor.set = function (newVal: any): void {
-            this[metadataPropertyKey] = newVal;
-        };
-
-        Object.defineProperty(target, propertyKey, propertyDescriptor);
+                if (this[propertyKey]) {}
+            }
+        });
 
 
         return propertyDescriptor;
         return propertyDescriptor;
     };
     };

+ 2 - 0
src/enums/NodeType.ts

@@ -25,6 +25,8 @@ export const NodeType: any = Utils.strEnumify({
     Program: 'Program',
     Program: 'Program',
     Property: 'Property',
     Property: 'Property',
     ReturnStatement: 'ReturnStatement',
     ReturnStatement: 'ReturnStatement',
+    SwitchCase: 'SwitchCase',
+    SwitchStatement: 'SwitchStatement',
     TryStatement: 'TryStatement',
     TryStatement: 'TryStatement',
     UnaryExpression: 'UnaryExpression',
     UnaryExpression: 'UnaryExpression',
     UpdateExpression: 'UpdateExpression',
     UpdateExpression: 'UpdateExpression',

+ 0 - 5
src/interfaces/IGenerator.d.ts

@@ -1,5 +0,0 @@
-import { IGeneratorOutput } from './IGeneratorOutput';
-
-declare module 'escodegen' {
-    export function generate(ast: any, options?: GenerateOptions): IGeneratorOutput;
-}

+ 4 - 0
src/interfaces/IJavaScriptObfsucator.d.ts

@@ -1,5 +1,9 @@
 import { IObfuscationResult } from './IObfuscationResult';
 import { IObfuscationResult } from './IObfuscationResult';
 
 
 export interface IJavaScriptObfuscator {
 export interface IJavaScriptObfuscator {
+    /**
+     * @param sourceCode
+     * @returns IObfuscationResult
+     */
     obfuscate (sourceCode: string): IObfuscationResult;
     obfuscate (sourceCode: string): IObfuscationResult;
 }
 }

+ 4 - 0
src/interfaces/IObfuscator.d.ts

@@ -1,5 +1,9 @@
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 export interface IObfuscator {
 export interface IObfuscator {
+    /**
+     * @param astTree
+     * @returns ESTree.Program
+     */
     obfuscateAstTree (astTree: ESTree.Program): ESTree.Program;
     obfuscateAstTree (astTree: ESTree.Program): ESTree.Program;
 }
 }

+ 5 - 0
src/interfaces/ISourceMapCorrector.d.ts

@@ -1,5 +1,10 @@
 import { IObfuscationResult } from './IObfuscationResult';
 import { IObfuscationResult } from './IObfuscationResult';
 
 
 export interface ISourceMapCorrector {
 export interface ISourceMapCorrector {
+    /**
+     * @param obfuscatedCode
+     * @param sourceMap
+     * @returns IObfuscationResult
+     */
     correct (obfuscatedCode: string, sourceMap: string): IObfuscationResult;
     correct (obfuscatedCode: string, sourceMap: string): IObfuscationResult;
 }
 }

+ 0 - 8
src/interfaces/custom-nodes/ICustomNodeWithData.d.ts

@@ -1,8 +0,0 @@
-import { ICustomNode } from './ICustomNode';
-
-export interface ICustomNodeWithData extends ICustomNode {
-    /**
-     * @returns any
-     */
-    getNodeData (): any;
-}

+ 0 - 5
src/interfaces/custom-nodes/ICustomNodeWithIdentifier.d.ts

@@ -1,5 +0,0 @@
-import { ICustomNode } from './ICustomNode';
-
-export interface ICustomNodeWithIdentifier extends ICustomNode {
-    getNodeIdentifier (): string;
-}

+ 11 - 0
src/interfaces/event-emitters/IObfuscationEventEmitter.d.ts

@@ -3,6 +3,17 @@ import Events = NodeJS.Events;
 import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
 
 export interface IObfuscationEventEmitter extends Events {
 export interface IObfuscationEventEmitter extends Events {
+    /**
+     * @param event
+     * @param listener
+     * @returns this
+     */
     on(event: TObfuscationEvent, listener: Function): this;
     on(event: TObfuscationEvent, listener: Function): this;
+
+    /**
+     * @param event
+     * @param listener
+     * @returns this
+     */
     once(event: TObfuscationEvent, listener: Function): this;
     once(event: TObfuscationEvent, listener: Function): this;
 }
 }

+ 8 - 3
src/interfaces/node-transformers/IControlFlowReplacer.d.ts

@@ -4,10 +4,15 @@ import { ICustomNode } from '../custom-nodes/ICustomNode';
 import { IStorage } from '../storages/IStorage';
 import { IStorage } from '../storages/IStorage';
 
 
 export interface IControlFlowReplacer {
 export interface IControlFlowReplacer {
+    /**
+     * @param node
+     * @param parentNode
+     * @param controlFlowStorage
+     * @returns ESTree.Node
+     */
     replace (
     replace (
         node: ESTree.Node,
         node: ESTree.Node,
         parentNode: ESTree.Node,
         parentNode: ESTree.Node,
-        controlFlowStorage: IStorage <ICustomNode>,
-        controlFlowStorageCustomNodeName: string
-    ): ICustomNode | undefined;
+        controlFlowStorage: IStorage <ICustomNode>
+    ): ESTree.Node;
 }
 }

+ 4 - 0
src/interfaces/node-transformers/INodeTransformer.d.ts

@@ -1,5 +1,9 @@
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 export interface INodeTransformer {
 export interface INodeTransformer {
+    /**
+     * @param node
+     * @param parentNode
+     */
     transformNode (node: ESTree.Node, parentNode?: ESTree.Node): void;
     transformNode (node: ESTree.Node, parentNode?: ESTree.Node): void;
 }
 }

+ 7 - 0
src/interfaces/node-transformers/IObfuscatorReplacer.d.ts

@@ -0,0 +1,7 @@
+export interface IObfuscatorReplacer {
+    /**
+     * @param nodeValue
+     * @param nodeIdentifier
+     */
+    replace (nodeValue: any, nodeIdentifier?: string): string;
+}

+ 9 - 0
src/interfaces/node-transformers/IObfuscatorReplacerWithStorage.d.ts

@@ -0,0 +1,9 @@
+import { IObfuscatorReplacer } from './IObfuscatorReplacer';
+
+export interface IObfuscatorReplacerWithStorage extends IObfuscatorReplacer {
+    /**
+     * @param nodeValue
+     * @param nodeIdentifier
+     */
+    storeNames (nodeValue: any, nodeIdentifier: string): void;
+}

+ 0 - 3
src/interfaces/node-transformers/IReplacer.d.ts

@@ -1,3 +0,0 @@
-export interface IReplacer {
-    replace (nodeValue: any): string;
-}

+ 1 - 0
src/interfaces/options/IOptions.d.ts

@@ -4,6 +4,7 @@ import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
 export interface IOptions {
 export interface IOptions {
     readonly compact: boolean;
     readonly compact: boolean;
     readonly controlFlowFlattening: boolean;
     readonly controlFlowFlattening: boolean;
+    readonly controlFlowFlatteningThreshold: number;
     readonly debugProtection: boolean;
     readonly debugProtection: boolean;
     readonly debugProtectionInterval: boolean;
     readonly debugProtectionInterval: boolean;
     readonly disableConsoleOutput: boolean;
     readonly disableConsoleOutput: boolean;

+ 5 - 0
src/interfaces/stack-trace-analyzer/ICalleeDataExtractor.d.ts

@@ -3,5 +3,10 @@ import * as ESTree from 'estree';
 import { ICalleeData } from './ICalleeData';
 import { ICalleeData } from './ICalleeData';
 
 
 export interface ICalleeDataExtractor {
 export interface ICalleeDataExtractor {
+    /**
+     * @param blockScopeBody
+     * @param callee
+     * @returns ICalleeData|null
+     */
     extract (blockScopeBody: ESTree.Node[], callee: ESTree.Node): ICalleeData|null;
     extract (blockScopeBody: ESTree.Node[], callee: ESTree.Node): ICalleeData|null;
 }
 }

+ 4 - 0
src/interfaces/stack-trace-analyzer/IStackTraceAnalyzer.d.ts

@@ -3,5 +3,9 @@ import * as ESTree from 'estree';
 import { IStackTraceData } from './IStackTraceData';
 import { IStackTraceData } from './IStackTraceData';
 
 
 export interface IStackTraceAnalyzer {
 export interface IStackTraceAnalyzer {
+    /**
+     * @param blockScopeBody
+     * @returns IStackTraceData[]
+     */
     analyze (blockScopeBody: ESTree.Node[]): IStackTraceData[];
     analyze (blockScopeBody: ESTree.Node[]): IStackTraceData[];
 }
 }

+ 41 - 0
src/interfaces/storages/IStorage.d.ts

@@ -1,11 +1,52 @@
 import { IInitializable } from '../IInitializable';
 import { IInitializable } from '../IInitializable';
 
 
 export interface IStorage <T> extends IInitializable {
 export interface IStorage <T> extends IInitializable {
+    /**
+     * @param key
+     * @returns T
+     */
     get (key: string | number): T;
     get (key: string | number): T;
+
+    /**
+     * @param value
+     * @returns string | number | null
+     */
     getKeyOf (value: T): string | number | null;
     getKeyOf (value: T): string | number | null;
+
+    /**
+     * @returns number
+     */
     getLength (): number;
     getLength (): number;
+
+    /**
+     * @returns any
+     */
     getStorage (): any;
     getStorage (): any;
+
+    /**
+     * @returns string
+     */
+    getStorageId (): string;
+
+    /**
+     * @param args
+     */
     initialize (...args: any[]): void;
     initialize (...args: any[]): void;
+
+    /**
+     * @param storage
+     * @param mergeId
+     */
+    mergeWith (storage: this, mergeId: boolean): void;
+
+    /**
+     * @param key
+     * @param value
+     */
     set (key: string | number | null, value: T): void;
     set (key: string | number | null, value: T): void;
+
+    /**
+     * @returns string
+     */
     toString (): string;
     toString (): string;
 }
 }

+ 97 - 22
src/node-transformers/node-control-flow-transformers/FunctionControlFlowTransformer.ts

@@ -3,10 +3,12 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
 
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
+import * as _ from 'lodash';
 
 
 import { TControlFlowReplacerFactory } from '../../types/container/TControlFlowReplacerFactory';
 import { TControlFlowReplacerFactory } from '../../types/container/TControlFlowReplacerFactory';
 import { TControlFlowStorageFactory } from '../../types/container/TControlFlowStorageFactory';
 import { TControlFlowStorageFactory } from '../../types/container/TControlFlowStorageFactory';
 import { TCustomNodeFactory } from '../../types/container/TCustomNodeFactory';
 import { TCustomNodeFactory } from '../../types/container/TCustomNodeFactory';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
 import { TStatement } from '../../types/node/TStatement';
 import { TStatement } from '../../types/node/TStatement';
 
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
@@ -20,6 +22,7 @@ import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
 import { Node } from '../../node/Node';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeControlFlowReplacers } from '../../enums/container/NodeControlFlowReplacers';
 import { NodeControlFlowReplacers } from '../../enums/container/NodeControlFlowReplacers';
+import { NodeUtils } from '../../node/NodeUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 
 
 @injectable()
 @injectable()
@@ -27,10 +30,30 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
     /**
     /**
      * @type {Map <string, NodeControlFlowReplacers>}
      * @type {Map <string, NodeControlFlowReplacers>}
      */
      */
-    private static readonly controlFlowReplacersMap: Map <string, NodeControlFlowReplacers> = new Map <string, NodeControlFlowReplacers> ([
+    private static readonly controlFlowReplacersMap: Map <string, NodeControlFlowReplacers> = new Map([
         [NodeType.BinaryExpression, NodeControlFlowReplacers.BinaryExpressionControlFlowReplacer]
         [NodeType.BinaryExpression, NodeControlFlowReplacers.BinaryExpressionControlFlowReplacer]
     ]);
     ]);
 
 
+    /**
+     * @type {number}
+     */
+    private static readonly hostNodeSearchMinDepth: number = 2;
+
+    /**
+     * @type {number}
+     */
+    private static readonly hostNodeSearchMaxDepth: number = 10;
+
+    /**
+     * @type {Map<ESTree.Node, IStorage<ICustomNode>>}
+     */
+    private controlFlowData: Map <ESTree.Node, IStorage<ICustomNode>> = new Map();
+
+    /**
+     * @type {TStatement[][]}
+     */
+    private readonly controlFlowNodesList: TStatement[][] = [];
+
     /**
     /**
      * @type {TControlFlowReplacerFactory}
      * @type {TControlFlowReplacerFactory}
      */
      */
@@ -65,6 +88,51 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
         this.customNodeFactory = customNodeFactory;
         this.customNodeFactory = customNodeFactory;
     }
     }
 
 
+    /**
+     * @param functionNode
+     * @returns {TNodeWithBlockStatement}
+     */
+    private static getHostNode (functionNode: ESTree.FunctionDeclaration | ESTree.FunctionExpression): TNodeWithBlockStatement {
+        const blockScopesOfNode: TNodeWithBlockStatement[] = NodeUtils.getBlockScopesOfNode(functionNode);
+
+        if (blockScopesOfNode.length === 1) {
+            return functionNode.body;
+        } else {
+            blockScopesOfNode.pop();
+        }
+
+        if (blockScopesOfNode.length > FunctionControlFlowTransformer.hostNodeSearchMinDepth) {
+            blockScopesOfNode.splice(0, FunctionControlFlowTransformer.hostNodeSearchMinDepth);
+        }
+
+        if (blockScopesOfNode.length > FunctionControlFlowTransformer.hostNodeSearchMaxDepth) {
+            blockScopesOfNode.length = FunctionControlFlowTransformer.hostNodeSearchMaxDepth;
+        }
+
+        return RandomGeneratorUtils.getRandomGenerator().pickone(blockScopesOfNode);
+    }
+
+    /**
+     * @param hostNodeBody
+     * @param controlFlowNodesList
+     */
+    private static removeOldControlFlowNodeFromHostNodeBody (
+        hostNodeBody: TStatement[],
+        controlFlowNodesList: TStatement[][]
+    ): TStatement[] {
+        for (let controlFlowNode of controlFlowNodesList) {
+            const firstIndexOfNode: number = hostNodeBody.indexOf(controlFlowNode[0]);
+
+            if (firstIndexOfNode === -1) {
+                continue;
+            }
+
+            return _.difference(hostNodeBody, controlFlowNode);
+        }
+
+        return hostNodeBody;
+    }
+
     /**
     /**
      * @param functionNode
      * @param functionNode
      */
      */
@@ -81,35 +149,39 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
         }
         }
 
 
         const controlFlowStorage: IStorage <ICustomNode> = this.controlFlowStorageFactory();
         const controlFlowStorage: IStorage <ICustomNode> = this.controlFlowStorageFactory();
-        const controlFlowStorageCustomNodeName: string = RandomGeneratorUtils.getRandomVariableName(6);
+        const hostNode: TNodeWithBlockStatement = FunctionControlFlowTransformer.getHostNode(functionNode);
 
 
-        estraverse.replace(functionNode.body, {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
-                const controlFlowReplacerName: NodeControlFlowReplacers | undefined = FunctionControlFlowTransformer
-                    .controlFlowReplacersMap.get(node.type);
+        if (!this.controlFlowData.has(hostNode)) {
+            this.controlFlowData.set(hostNode, controlFlowStorage);
+        } else {
+            hostNode.body = <ESTree.Statement[]>FunctionControlFlowTransformer
+                .removeOldControlFlowNodeFromHostNodeBody(hostNode.body, this.controlFlowNodesList);
 
 
-                if (controlFlowReplacerName === undefined) {
-                    return;
-                }
+            const hostControlFlowStorage: IStorage<ICustomNode> = <IStorage<ICustomNode>>this.controlFlowData.get(hostNode);
 
 
-                const controlFlowStorageCallCustomNode: ICustomNode | undefined = this.controlFlowReplacerFactory(controlFlowReplacerName)
-                    .replace(node, parentNode, controlFlowStorage, controlFlowStorageCustomNodeName);
+            controlFlowStorage.mergeWith(hostControlFlowStorage, true);
 
 
-                if (!controlFlowStorageCallCustomNode) {
+            this.controlFlowData.set(hostNode, controlFlowStorage);
+        }
+
+        estraverse.replace(functionNode.body, {
+            enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
+                if (RandomGeneratorUtils.getRandomFloat(0, 1) > this.options.controlFlowFlatteningThreshold) {
                     return;
                     return;
                 }
                 }
 
 
-                // controlFlowStorageCallCustomNode will always have only one TStatement node,
-                // so we can get it by index `0`
-                // also we need to return `expression` property of `ExpressionStatement` node because bug:
-                // https://github.com/estools/escodegen/issues/289
-                const statementNode: TStatement = controlFlowStorageCallCustomNode.getNode()[0];
+                const controlFlowReplacerName: NodeControlFlowReplacers | undefined = FunctionControlFlowTransformer
+                    .controlFlowReplacersMap.get(node.type);
 
 
-                if (!statementNode || !Node.isExpressionStatementNode(statementNode)) {
-                    throw new Error(`\`controlFlowStorageCallCustomNode.getNode()\` should returns array with \`ExpressionStatement\` node`);
+                if (controlFlowReplacerName === undefined) {
+                    return;
                 }
                 }
 
 
-                return statementNode.expression;
+                return {
+                    ...this.controlFlowReplacerFactory(controlFlowReplacerName)
+                        .replace(node, parentNode, controlFlowStorage),
+                    parentNode
+                };
             }
             }
         });
         });
 
 
@@ -119,8 +191,11 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
 
 
         const controlFlowStorageCustomNode: ICustomNode = this.customNodeFactory(CustomNodes.ControlFlowStorageNode);
         const controlFlowStorageCustomNode: ICustomNode = this.customNodeFactory(CustomNodes.ControlFlowStorageNode);
 
 
-        controlFlowStorageCustomNode.initialize(controlFlowStorage, controlFlowStorageCustomNodeName);
+        controlFlowStorageCustomNode.initialize(controlFlowStorage);
+
+        const controlFlowStorageNode: TStatement[] = controlFlowStorageCustomNode.getNode();
 
 
-        NodeAppender.prependNode(functionNode.body, controlFlowStorageCustomNode.getNode());
+        this.controlFlowNodesList.push(controlFlowStorageNode);
+        NodeAppender.prependNode(hostNode, controlFlowStorageNode);
     }
     }
 }
 }

+ 3 - 17
src/node-transformers/node-control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts

@@ -8,8 +8,6 @@ import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IStorage } from '../../../interfaces/storages/IStorage';
 import { IStorage } from '../../../interfaces/storages/IStorage';
 
 
-import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
-
 @injectable()
 @injectable()
 export abstract class AbstractControlFlowReplacer implements IControlFlowReplacer {
 export abstract class AbstractControlFlowReplacer implements IControlFlowReplacer {
     /**
     /**
@@ -26,27 +24,15 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace
         this.options = options;
         this.options = options;
     }
     }
 
 
-    /**
-     * @returns {string}
-     */
-    protected static getStorageKey (): string {
-        return RandomGeneratorUtils.getRandomGenerator().string({
-            length: 3,
-            pool: RandomGeneratorUtils.randomGeneratorPool
-        });
-    }
-
     /**
     /**
      * @param node
      * @param node
      * @param parentNode
      * @param parentNode
      * @param controlFlowStorage
      * @param controlFlowStorage
-     * @param controlFlowStorageCustomNodeName
-     * @returns {ICustomNode | undefined}
+     * @returns {ESTree.Node}
      */
      */
     public abstract replace (
     public abstract replace (
         node: ESTree.Node,
         node: ESTree.Node,
         parentNode: ESTree.Node,
         parentNode: ESTree.Node,
-        controlFlowStorage: IStorage <ICustomNode>,
-        controlFlowStorageCustomNodeName: string
-    ): ICustomNode | undefined;
+        controlFlowStorage: IStorage <ICustomNode>
+    ): ESTree.Node;
 }
 }

+ 73 - 24
src/node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts

@@ -1,10 +1,10 @@
 import { injectable, inject } from 'inversify';
 import { injectable, inject } from 'inversify';
 import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 
-import * as escodegen from 'escodegen';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
 import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
+import { TStatement } from '../../../types/node/TStatement';
 
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
@@ -13,9 +13,22 @@ import { IStorage } from '../../../interfaces/storages/IStorage';
 import { CustomNodes } from '../../../enums/container/CustomNodes';
 import { CustomNodes } from '../../../enums/container/CustomNodes';
 
 
 import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
 import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
+import { Node } from '../../../node/Node';
+import { NodeUtils } from '../../../node/NodeUtils';
+import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
 
 
 @injectable()
 @injectable()
 export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowReplacer {
 export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowReplacer {
+    /**
+     * @type {number}
+     */
+    private static readonly useExistingOperatorKeyThreshold: number = 0.5;
+
+    /**
+     * @type {Map<string, Map<ESTree.BinaryOperator, string[]>>}
+     */
+    private readonly binaryOperatorsDataByControlFlowStorageId: Map <string, Map<ESTree.BinaryOperator, string[]>> = new Map();
+
     /**
     /**
      * @type {TCustomNodeFactory}
      * @type {TCustomNodeFactory}
      */
      */
@@ -35,42 +48,78 @@ export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowRepl
     }
     }
 
 
     /**
     /**
-     * @param expressionNode
-     * @returns {string}
+     * @param binaryOperatorsDataByControlFlowStorageId
+     * @param controlFlowStorageId
+     * @returns {Map<ESTree.BinaryOperator, string[]>}
      */
      */
-    private static getExpressionValue (expressionNode: ESTree.Expression): string {
-        return escodegen.generate(expressionNode, {
-            sourceMapWithCode: true
-        }).code;
+    private static getStorageKeysByBinaryOperatorForCurrentStorage (
+        binaryOperatorsDataByControlFlowStorageId: Map<string, Map<ESTree.BinaryOperator, string[]>>,
+        controlFlowStorageId: string
+    ): Map<ESTree.BinaryOperator, string[]> {
+        let storageKeysByBinaryOperator: Map<ESTree.BinaryOperator, string[]>;
+
+        if (binaryOperatorsDataByControlFlowStorageId.has(controlFlowStorageId)) {
+            storageKeysByBinaryOperator = <Map<ESTree.BinaryOperator, string[]>>binaryOperatorsDataByControlFlowStorageId
+                .get(controlFlowStorageId);
+        } else {
+            storageKeysByBinaryOperator = new Map <ESTree.BinaryOperator, string[]> ();
+        }
+
+        return storageKeysByBinaryOperator;
     }
     }
 
 
     /**
     /**
      * @param binaryExpressionNode
      * @param binaryExpressionNode
      * @param parentNode
      * @param parentNode
      * @param controlFlowStorage
      * @param controlFlowStorage
-     * @param controlFlowStorageCustomNodeName
-     * @returns {ICustomNode}
+     * @returns {ESTree.Node}
      */
      */
     public replace (
     public replace (
         binaryExpressionNode: ESTree.BinaryExpression,
         binaryExpressionNode: ESTree.BinaryExpression,
         parentNode: ESTree.Node,
         parentNode: ESTree.Node,
-        controlFlowStorage: IStorage <ICustomNode>,
-        controlFlowStorageCustomNodeName: string
-    ): ICustomNode {
-        const key: string = AbstractControlFlowReplacer.getStorageKey();
-        const binaryExpressionFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.BinaryExpressionFunctionNode);
-        const controlFlowStorageCallNode: ICustomNode = this.customNodeFactory(CustomNodes.ControlFlowStorageCallNode);
-
-        binaryExpressionFunctionNode.initialize(binaryExpressionNode.operator);
-        controlFlowStorageCallNode.initialize(
-            controlFlowStorageCustomNodeName,
-            key,
-            BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.left),
-            BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.right)
+        controlFlowStorage: IStorage <ICustomNode>
+    ): ESTree.Node {
+        const controlFlowStorageId: string = controlFlowStorage.getStorageId();
+        const controlFlowStorageCallCustomNode: ICustomNode = this.customNodeFactory(CustomNodes.ControlFlowStorageCallNode);
+        const storageKeysByBinaryOperator: Map<ESTree.BinaryOperator, string[]> = BinaryExpressionControlFlowReplacer
+            .getStorageKeysByBinaryOperatorForCurrentStorage(
+                this.binaryOperatorsDataByControlFlowStorageId,
+                controlFlowStorageId
+            );
+
+        let storageKeysForCurrentOperator: string[] | undefined = storageKeysByBinaryOperator.get(binaryExpressionNode.operator);
+        let storageKey: string;
+
+        if (
+            RandomGeneratorUtils.getRandomFloat(0, 1) > BinaryExpressionControlFlowReplacer.useExistingOperatorKeyThreshold &&
+            storageKeysForCurrentOperator &&
+            storageKeysForCurrentOperator.length
+        ) {
+            storageKey = RandomGeneratorUtils.getRandomGenerator().pickone(storageKeysForCurrentOperator);
+        } else {
+            const binaryExpressionFunctionCustomNode: ICustomNode = this.customNodeFactory(CustomNodes.BinaryExpressionFunctionNode);
+
+            binaryExpressionFunctionCustomNode.initialize(binaryExpressionNode.operator);
+
+            storageKey = RandomGeneratorUtils.getRandomString(3);
+            storageKeysByBinaryOperator.set(binaryExpressionNode.operator, [storageKey]);
+            this.binaryOperatorsDataByControlFlowStorageId.set(controlFlowStorageId, storageKeysByBinaryOperator);
+            controlFlowStorage.set(storageKey, binaryExpressionFunctionCustomNode);
+        }
+
+        controlFlowStorageCallCustomNode.initialize(
+            controlFlowStorageId,
+            storageKey,
+            NodeUtils.convertStructureToCode([binaryExpressionNode.left]),
+            NodeUtils.convertStructureToCode([binaryExpressionNode.right])
         );
         );
 
 
-        controlFlowStorage.set(key, binaryExpressionFunctionNode);
+        const statementNode: TStatement = controlFlowStorageCallCustomNode.getNode()[0];
+
+        if (!statementNode || !Node.isExpressionStatementNode(statementNode)) {
+            throw new Error(`\`controlFlowStorageCallNode.getNode()[0]\` should returns array with \`ExpressionStatement\` node`);
+        }
 
 
-        return controlFlowStorageCallNode;
+        return statementNode.expression;
     }
     }
 }
 }

+ 15 - 16
src/node-transformers/node-obfuscators/CatchClauseObfuscator.ts

@@ -5,13 +5,13 @@ import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
+import { IObfuscatorReplacerWithStorage } from '../../interfaces/node-transformers/IObfuscatorReplacerWithStorage';
 
 
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 import { NodeType } from '../../enums/NodeType';
 
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
-import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
 import { NodeUtils } from '../../node/NodeUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
@@ -27,53 +27,52 @@ import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 @injectable()
 @injectable()
 export class CatchClauseObfuscator extends AbstractNodeTransformer {
 export class CatchClauseObfuscator extends AbstractNodeTransformer {
     /**
     /**
-     * @type {IdentifierReplacer}
+     * @type {IObfuscatorReplacerWithStorage}
      */
      */
-    private readonly identifierReplacer: IReplacer & IdentifierReplacer;
+    private readonly identifierReplacer: IObfuscatorReplacerWithStorage;
 
 
     /**
     /**
      * @param replacersFactory
      * @param replacersFactory
      * @param options
      * @param options
      */
      */
     constructor(
     constructor(
-        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers['Factory<IObfuscatorReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);
 
 
-        this.identifierReplacer = <IdentifierReplacer>replacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
+        this.identifierReplacer = <IObfuscatorReplacerWithStorage>replacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
     }
 
 
     /**
     /**
      * @param catchClauseNode
      * @param catchClauseNode
      */
      */
     public transformNode (catchClauseNode: ESTree.CatchClause): void {
     public transformNode (catchClauseNode: ESTree.CatchClause): void {
-        this.identifierReplacer.setPrefix(RandomGeneratorUtils.getRandomGenerator().string({
-            length: 5,
-            pool: RandomGeneratorUtils.randomGeneratorPool
-        }));
+        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
 
 
-        this.storeCatchClauseParam(catchClauseNode);
-        this.replaceCatchClauseParam(catchClauseNode);
+        this.storeCatchClauseParam(catchClauseNode, nodeIdentifier);
+        this.replaceCatchClauseParam(catchClauseNode, nodeIdentifier);
     }
     }
 
 
     /**
     /**
      * @param catchClauseNode
      * @param catchClauseNode
+     * @param nodeIdentifier
      */
      */
-    private storeCatchClauseParam (catchClauseNode: ESTree.CatchClause): void {
+    private storeCatchClauseParam (catchClauseNode: ESTree.CatchClause, nodeIdentifier: string): void {
         NodeUtils.typedTraverse(catchClauseNode.param, NodeType.Identifier, {
         NodeUtils.typedTraverse(catchClauseNode.param, NodeType.Identifier, {
-            enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name)
+            enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
         });
         });
     }
     }
 
 
     /**
     /**
      * @param catchClauseNode
      * @param catchClauseNode
+     * @param nodeIdentifier
      */
      */
-    private replaceCatchClauseParam (catchClauseNode: ESTree.CatchClause): void {
+    private replaceCatchClauseParam (catchClauseNode: ESTree.CatchClause, nodeIdentifier: string): void {
         estraverse.replace(catchClauseNode, {
         estraverse.replace(catchClauseNode, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (Node.isReplaceableIdentifierNode(node, parentNode)) {
                 if (Node.isReplaceableIdentifierNode(node, parentNode)) {
-                    node.name = this.identifierReplacer.replace(node.name);
+                    node.name = this.identifierReplacer.replace(node.name, nodeIdentifier);
                 }
                 }
             }
             }
         });
         });

+ 16 - 18
src/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.ts

@@ -7,13 +7,13 @@ import * as ESTree from 'estree';
 import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
+import { IObfuscatorReplacerWithStorage } from '../../interfaces/node-transformers/IObfuscatorReplacerWithStorage';
 
 
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 import { NodeType } from '../../enums/NodeType';
 
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
-import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
 import { NodeUtils } from '../../node/NodeUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
@@ -30,21 +30,21 @@ import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 @injectable()
 @injectable()
 export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
 export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
     /**
     /**
-     * @type {IdentifierReplacer}
+     * @type {IObfuscatorReplacerWithStorage}
      */
      */
-    private readonly identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IObfuscatorReplacerWithStorage;
 
 
     /**
     /**
      * @param nodeObfuscatorsReplacersFactory
      * @param nodeObfuscatorsReplacersFactory
      * @param options
      * @param options
      */
      */
     constructor(
     constructor(
-        @inject(ServiceIdentifiers['Factory<IReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers['Factory<IObfuscatorReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);
 
 
-        this.identifierReplacer = <IdentifierReplacer>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
+        this.identifierReplacer = <IObfuscatorReplacerWithStorage>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
     }
 
 
     /**
     /**
@@ -52,39 +52,37 @@ export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
      * @param parentNode
      * @param parentNode
      */
      */
     public transformNode (functionDeclarationNode: ESTree.FunctionDeclaration, parentNode: ESTree.Node): void {
     public transformNode (functionDeclarationNode: ESTree.FunctionDeclaration, parentNode: ESTree.Node): void {
-        this.identifierReplacer.setPrefix(RandomGeneratorUtils.getRandomGenerator().string({
-            length: 5,
-            pool: RandomGeneratorUtils.randomGeneratorPool
-        }));
-
+        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
         const blockScopeOfFunctionDeclarationNode: TNodeWithBlockStatement = NodeUtils
         const blockScopeOfFunctionDeclarationNode: TNodeWithBlockStatement = NodeUtils
-            .getBlockScopeOfNode(functionDeclarationNode);
+            .getBlockScopesOfNode(functionDeclarationNode)[0];
 
 
         if (blockScopeOfFunctionDeclarationNode.type === NodeType.Program) {
         if (blockScopeOfFunctionDeclarationNode.type === NodeType.Program) {
             return;
             return;
         }
         }
 
 
-        this.storeFunctionName(functionDeclarationNode);
-        this.replaceFunctionName(blockScopeOfFunctionDeclarationNode);
+        this.storeFunctionName(functionDeclarationNode, nodeIdentifier);
+        this.replaceFunctionName(blockScopeOfFunctionDeclarationNode, nodeIdentifier);
     }
     }
 
 
     /**
     /**
      * @param functionDeclarationNode
      * @param functionDeclarationNode
+     * @param nodeIdentifier
      */
      */
-    private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration): void {
+    private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration, nodeIdentifier: string): void {
         NodeUtils.typedTraverse(functionDeclarationNode.id, NodeType.Identifier, {
         NodeUtils.typedTraverse(functionDeclarationNode.id, NodeType.Identifier, {
-            enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name)
+            enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
         });
         });
     }
     }
 
 
     /**
     /**
      * @param scopeNode
      * @param scopeNode
+     * @param nodeIdentifier
      */
      */
-    private replaceFunctionName (scopeNode: ESTree.Node): void {
+    private replaceFunctionName (scopeNode: ESTree.Node, nodeIdentifier: string): void {
         estraverse.replace(scopeNode, {
         estraverse.replace(scopeNode, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (Node.isReplaceableIdentifierNode(node, parentNode)) {
                 if (Node.isReplaceableIdentifierNode(node, parentNode)) {
-                    node.name = this.identifierReplacer.replace(node.name);
+                    node.name = this.identifierReplacer.replace(node.name, nodeIdentifier);
                 }
                 }
             }
             }
         });
         });

+ 15 - 16
src/node-transformers/node-obfuscators/FunctionObfuscator.ts

@@ -5,13 +5,13 @@ import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
+import { IObfuscatorReplacerWithStorage } from '../../interfaces/node-transformers/IObfuscatorReplacerWithStorage';
 
 
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 import { NodeType } from '../../enums/NodeType';
 
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
-import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
 import { NodeUtils } from '../../node/NodeUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
@@ -27,56 +27,55 @@ import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 @injectable()
 @injectable()
 export class FunctionObfuscator extends AbstractNodeTransformer {
 export class FunctionObfuscator extends AbstractNodeTransformer {
     /**
     /**
-     * @type {IdentifierReplacer}
+     * @type {IObfuscatorReplacerWithStorage}
      */
      */
-    private readonly identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IObfuscatorReplacerWithStorage;
 
 
     /**
     /**
      * @param nodeObfuscatorsReplacersFactory
      * @param nodeObfuscatorsReplacersFactory
      * @param options
      * @param options
      */
      */
     constructor(
     constructor(
-        @inject(ServiceIdentifiers['Factory<IReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers['Factory<IObfuscatorReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);
 
 
-        this.identifierReplacer = <IdentifierReplacer>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
+        this.identifierReplacer = <IObfuscatorReplacerWithStorage>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
     }
 
 
     /**
     /**
      * @param functionNode
      * @param functionNode
      */
      */
     public transformNode (functionNode: ESTree.Function): void {
     public transformNode (functionNode: ESTree.Function): void {
-        this.identifierReplacer.setPrefix(RandomGeneratorUtils.getRandomGenerator().string({
-            length: 5,
-            pool: RandomGeneratorUtils.randomGeneratorPool
-        }));
+        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
 
 
-        this.storeFunctionParams(functionNode);
-        this.replaceFunctionParams(functionNode);
+        this.storeFunctionParams(functionNode, nodeIdentifier);
+        this.replaceFunctionParams(functionNode, nodeIdentifier);
     }
     }
 
 
     /**
     /**
      * @param functionNode
      * @param functionNode
+     * @param nodeIdentifier
      */
      */
-    private storeFunctionParams (functionNode: ESTree.Function): void {
+    private storeFunctionParams (functionNode: ESTree.Function, nodeIdentifier: string): void {
         functionNode.params
         functionNode.params
             .forEach((paramsNode: ESTree.Node) => {
             .forEach((paramsNode: ESTree.Node) => {
                 NodeUtils.typedTraverse(paramsNode, NodeType.Identifier, {
                 NodeUtils.typedTraverse(paramsNode, NodeType.Identifier, {
-                    enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name)
+                    enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
                 });
                 });
             });
             });
     }
     }
 
 
     /**
     /**
      * @param functionNode
      * @param functionNode
+     * @param nodeIdentifier
      */
      */
-    private replaceFunctionParams (functionNode: ESTree.Function): void {
+    private replaceFunctionParams (functionNode: ESTree.Function, nodeIdentifier: string): void {
         let traverseVisitor: estraverse.Visitor = {
         let traverseVisitor: estraverse.Visitor = {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (Node.isReplaceableIdentifierNode(node, parentNode)) {
                 if (Node.isReplaceableIdentifierNode(node, parentNode)) {
-                    const newNodeName: string = this.identifierReplacer.replace(node.name);
+                    const newNodeName: string = this.identifierReplacer.replace(node.name, nodeIdentifier);
 
 
                     if (node.name !== newNodeName) {
                     if (node.name !== newNodeName) {
                         node.name = newNodeName;
                         node.name = newNodeName;

+ 15 - 16
src/node-transformers/node-obfuscators/LabeledStatementObfuscator.ts

@@ -5,13 +5,13 @@ import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
+import { IObfuscatorReplacerWithStorage } from '../../interfaces/node-transformers/IObfuscatorReplacerWithStorage';
 
 
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 import { NodeType } from '../../enums/NodeType';
 
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
-import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
 import { NodeUtils } from '../../node/NodeUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
@@ -35,53 +35,52 @@ import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 @injectable()
 @injectable()
 export class LabeledStatementObfuscator extends AbstractNodeTransformer {
 export class LabeledStatementObfuscator extends AbstractNodeTransformer {
     /**
     /**
-     * @type {IdentifierReplacer}
+     * @type {IObfuscatorReplacerWithStorage}
      */
      */
-    private readonly identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IObfuscatorReplacerWithStorage;
 
 
     /**
     /**
      * @param nodeObfuscatorsReplacersFactory
      * @param nodeObfuscatorsReplacersFactory
      * @param options
      * @param options
      */
      */
     constructor(
     constructor(
-        @inject(ServiceIdentifiers['Factory<IReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers['Factory<IObfuscatorReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);
 
 
-        this.identifierReplacer = <IdentifierReplacer>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
+        this.identifierReplacer = <IObfuscatorReplacerWithStorage>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
     }
 
 
     /**
     /**
      * @param labeledStatementNode
      * @param labeledStatementNode
      */
      */
     public transformNode (labeledStatementNode: ESTree.LabeledStatement): void {
     public transformNode (labeledStatementNode: ESTree.LabeledStatement): void {
-        this.identifierReplacer.setPrefix(RandomGeneratorUtils.getRandomGenerator().string({
-            length: 5,
-            pool: RandomGeneratorUtils.randomGeneratorPool
-        }));
+        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
 
 
-        this.storeLabeledStatementName(labeledStatementNode);
-        this.replaceLabeledStatementName(labeledStatementNode);
+        this.storeLabeledStatementName(labeledStatementNode, nodeIdentifier);
+        this.replaceLabeledStatementName(labeledStatementNode, nodeIdentifier);
     }
     }
 
 
     /**
     /**
      * @param labeledStatementNode
      * @param labeledStatementNode
+     * @param nodeIdentifier
      */
      */
-    private storeLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement): void {
+    private storeLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement, nodeIdentifier: string): void {
         NodeUtils.typedTraverse(labeledStatementNode.label, NodeType.Identifier, {
         NodeUtils.typedTraverse(labeledStatementNode.label, NodeType.Identifier, {
-            enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name)
+            enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
         });
         });
     }
     }
 
 
     /**
     /**
      * @param labeledStatementNode
      * @param labeledStatementNode
+     * @param nodeIdentifier
      */
      */
-    private replaceLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement): void {
+    private replaceLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement, nodeIdentifier: string): void {
         estraverse.replace(labeledStatementNode, {
         estraverse.replace(labeledStatementNode, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (Node.isLabelIdentifierNode(node, parentNode)) {
                 if (Node.isLabelIdentifierNode(node, parentNode)) {
-                    node.name = this.identifierReplacer.replace(node.name);
+                    node.name = this.identifierReplacer.replace(node.name, nodeIdentifier);
                 }
                 }
             }
             }
         });
         });

+ 4 - 4
src/node-transformers/node-obfuscators/LiteralObfuscator.ts

@@ -5,7 +5,7 @@ import * as escodegen from 'escodegen';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
 
 
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 
 
@@ -15,16 +15,16 @@ import { Node } from '../../node/Node';
 @injectable()
 @injectable()
 export class LiteralObfuscator extends AbstractNodeTransformer {
 export class LiteralObfuscator extends AbstractNodeTransformer {
     /**
     /**
-     * @type {(replacer: NodeObfuscatorsReplacers) => IReplacer}
+     * @type {(replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer}
      */
      */
-    private readonly replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer;
+    private readonly replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer;
 
 
     /**
     /**
      * @param replacersFactory
      * @param replacersFactory
      * @param options
      * @param options
      */
      */
     constructor(
     constructor(
-        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers['Factory<IObfuscatorReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);

+ 4 - 4
src/node-transformers/node-obfuscators/MemberExpressionObfuscator.ts

@@ -6,7 +6,7 @@ import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
 
 
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 import { NodeType } from '../../enums/NodeType';
@@ -17,16 +17,16 @@ import { Node } from '../../node/Node';
 @injectable()
 @injectable()
 export class MemberExpressionObfuscator extends AbstractNodeTransformer {
 export class MemberExpressionObfuscator extends AbstractNodeTransformer {
     /**
     /**
-     * @type {IReplacer}
+     * @type {IObfuscatorReplacer}
      */
      */
-    private readonly stringLiteralReplacer: IReplacer;
+    private readonly stringLiteralReplacer: IObfuscatorReplacer;
 
 
     /**
     /**
      * @param replacersFactory
      * @param replacersFactory
      * @param options
      * @param options
      */
      */
     constructor(
     constructor(
-        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers['Factory<IObfuscatorReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);

+ 5 - 6
src/node-transformers/node-obfuscators/MethodDefinitionObfuscator.ts

@@ -5,13 +5,12 @@ import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
 
 
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
 import { Node } from '../../node/Node';
-import { Utils } from '../../utils/Utils';
 
 
 /**
 /**
  * replaces:
  * replaces:
@@ -23,9 +22,9 @@ import { Utils } from '../../utils/Utils';
 @injectable()
 @injectable()
 export class MethodDefinitionObfuscator extends AbstractNodeTransformer {
 export class MethodDefinitionObfuscator extends AbstractNodeTransformer {
     /**
     /**
-     * @type {IReplacer}
+     * @type {IObfuscatorReplacer}
      */
      */
-    private readonly stringLiteralReplacer: IReplacer;
+    private readonly stringLiteralReplacer: IObfuscatorReplacer;
 
 
     /**
     /**
      * @type {string[]}
      * @type {string[]}
@@ -37,7 +36,7 @@ export class MethodDefinitionObfuscator extends AbstractNodeTransformer {
      * @param options
      * @param options
      */
      */
     constructor(
     constructor(
-        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers['Factory<IObfuscatorReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);
@@ -61,7 +60,7 @@ export class MethodDefinitionObfuscator extends AbstractNodeTransformer {
             enter: (node: ESTree.Node): any => {
             enter: (node: ESTree.Node): any => {
                 if (
                 if (
                     Node.isIdentifierNode(node) &&
                     Node.isIdentifierNode(node) &&
-                    !Utils.arrayContains(MethodDefinitionObfuscator.ignoredNames, node.name) &&
+                    !MethodDefinitionObfuscator.ignoredNames.includes(node.name) &&
                     methodDefinitionNode.computed === false
                     methodDefinitionNode.computed === false
                 ) {
                 ) {
                     methodDefinitionNode.computed = true;
                     methodDefinitionNode.computed = true;

+ 16 - 18
src/node-transformers/node-obfuscators/VariableDeclarationObfuscator.ts

@@ -7,13 +7,13 @@ import * as ESTree from 'estree';
 import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
-import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
+import { IObfuscatorReplacerWithStorage } from '../../interfaces/node-transformers/IObfuscatorReplacerWithStorage';
 
 
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 import { NodeType } from '../../enums/NodeType';
 
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
-import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
 import { NodeUtils } from '../../node/NodeUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
@@ -31,21 +31,21 @@ import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 @injectable()
 @injectable()
 export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
 export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
     /**
     /**
-     * @type {IdentifierReplacer}
+     * @type {IObfuscatorReplacerWithStorage}
      */
      */
-    private readonly identifierReplacer: IReplacer & IdentifierReplacer;
+    private readonly identifierReplacer: IObfuscatorReplacerWithStorage;
 
 
     /**
     /**
      * @param replacersFactory
      * @param replacersFactory
      * @param options
      * @param options
      */
      */
     constructor(
     constructor(
-        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers['Factory<IObfuscatorReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);
 
 
-        this.identifierReplacer = <IdentifierReplacer>replacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
+        this.identifierReplacer = <IObfuscatorReplacerWithStorage>replacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
     }
 
 
     /**
     /**
@@ -53,46 +53,44 @@ export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
      * @param parentNode
      * @param parentNode
      */
      */
     public transformNode (variableDeclarationNode: ESTree.VariableDeclaration, parentNode: ESTree.Node): void {
     public transformNode (variableDeclarationNode: ESTree.VariableDeclaration, parentNode: ESTree.Node): void {
-        this.identifierReplacer.setPrefix(RandomGeneratorUtils.getRandomGenerator().string({
-            length: 5,
-            pool: RandomGeneratorUtils.randomGeneratorPool
-        }));
-
         const blockScopeOfVariableDeclarationNode: TNodeWithBlockStatement = NodeUtils
         const blockScopeOfVariableDeclarationNode: TNodeWithBlockStatement = NodeUtils
-            .getBlockScopeOfNode(variableDeclarationNode);
+            .getBlockScopesOfNode(variableDeclarationNode)[0];
 
 
         if (blockScopeOfVariableDeclarationNode.type === NodeType.Program) {
         if (blockScopeOfVariableDeclarationNode.type === NodeType.Program) {
             return;
             return;
         }
         }
 
 
+        const nodeIdentifier: string = RandomGeneratorUtils.getRandomString(7);
         const scopeNode: ESTree.Node = variableDeclarationNode.kind === 'var'
         const scopeNode: ESTree.Node = variableDeclarationNode.kind === 'var'
             ? blockScopeOfVariableDeclarationNode
             ? blockScopeOfVariableDeclarationNode
             : parentNode;
             : parentNode;
 
 
-        this.storeVariableNames(variableDeclarationNode);
-        this.replaceVariableNames(scopeNode);
+        this.storeVariableNames(variableDeclarationNode, nodeIdentifier);
+        this.replaceVariableNames(scopeNode, nodeIdentifier);
     }
     }
 
 
     /**
     /**
      * @param variableDeclarationNode
      * @param variableDeclarationNode
+     * @param nodeIdentifier
      */
      */
-    private storeVariableNames (variableDeclarationNode: ESTree.VariableDeclaration): void {
+    private storeVariableNames (variableDeclarationNode: ESTree.VariableDeclaration, nodeIdentifier: string): void {
         variableDeclarationNode.declarations
         variableDeclarationNode.declarations
             .forEach((declarationNode: ESTree.VariableDeclarator) => {
             .forEach((declarationNode: ESTree.VariableDeclarator) => {
                 NodeUtils.typedTraverse(declarationNode.id, NodeType.Identifier, {
                 NodeUtils.typedTraverse(declarationNode.id, NodeType.Identifier, {
-                    enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name)
+                    enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
                 });
                 });
             });
             });
     }
     }
 
 
     /**
     /**
      * @param scopeNode
      * @param scopeNode
+     * @param nodeIdentifier
      */
      */
-    private replaceVariableNames (scopeNode: ESTree.Node): void {
+    private replaceVariableNames (scopeNode: ESTree.Node, nodeIdentifier: string): void {
         estraverse.replace(scopeNode, {
         estraverse.replace(scopeNode, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (!node.obfuscated && Node.isReplaceableIdentifierNode(node, parentNode)) {
                 if (!node.obfuscated && Node.isReplaceableIdentifierNode(node, parentNode)) {
-                    node.name = this.identifierReplacer.replace(node.name);
+                    node.name = this.identifierReplacer.replace(node.name, nodeIdentifier);
                 }
                 }
             }
             }
         });
         });

+ 4 - 3
src/node-transformers/node-obfuscators/replacers/AbstractReplacer.ts

@@ -2,10 +2,10 @@ import { injectable, inject } from 'inversify';
 import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
-import { IReplacer } from '../../../interfaces/node-transformers/IReplacer';
+import { IObfuscatorReplacer } from '../../../interfaces/node-transformers/IObfuscatorReplacer';
 
 
 @injectable()
 @injectable()
-export abstract class AbstractReplacer implements IReplacer {
+export abstract class AbstractReplacer implements IObfuscatorReplacer {
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
@@ -22,7 +22,8 @@ export abstract class AbstractReplacer implements IReplacer {
 
 
     /**
     /**
      * @param nodeValue
      * @param nodeValue
+     * @param nodeIdentifier
      * @returns {string}
      * @returns {string}
      */
      */
-    public abstract replace (nodeValue: any): string;
+    public abstract replace (nodeValue: any, nodeIdentifier?: string): string;
 }
 }

+ 9 - 22
src/node-transformers/node-obfuscators/replacers/IdentifierReplacer.ts

@@ -1,22 +1,18 @@
 import { injectable, inject } from 'inversify';
 import { injectable, inject } from 'inversify';
 import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 
+import { IObfuscatorReplacerWithStorage } from '../../../interfaces/node-transformers/IObfuscatorReplacerWithStorage';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
 
 
 import { AbstractReplacer } from './AbstractReplacer';
 import { AbstractReplacer } from './AbstractReplacer';
 import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
 
 
 @injectable()
 @injectable()
-export class IdentifierReplacer extends AbstractReplacer {
+export class IdentifierReplacer extends AbstractReplacer implements IObfuscatorReplacerWithStorage {
     /**
     /**
      * @type {Map<string, string>}
      * @type {Map<string, string>}
      */
      */
-    private readonly namesMap: Map<string, string> = new Map<string, string>();
-
-    /**
-     * @type {string}
-     */
-    private uniquePrefix: string;
+    private readonly namesMap: Map<string, string> = new Map();
 
 
     /**
     /**
      * @param options
      * @param options
@@ -29,10 +25,11 @@ export class IdentifierReplacer extends AbstractReplacer {
 
 
     /**
     /**
      * @param nodeValue
      * @param nodeValue
+     * @param nodeIdentifier
      * @returns {string}
      * @returns {string}
      */
      */
-    public replace (nodeValue: string): string {
-        const obfuscatedIdentifierName: string|undefined = this.namesMap.get(`${nodeValue}-${this.uniquePrefix}`);
+    public replace (nodeValue: string, nodeIdentifier: string): string {
+        const obfuscatedIdentifierName: string|undefined = this.namesMap.get(`${nodeValue}-${nodeIdentifier}`);
 
 
         if (!obfuscatedIdentifierName) {
         if (!obfuscatedIdentifierName) {
             return nodeValue;
             return nodeValue;
@@ -41,26 +38,16 @@ export class IdentifierReplacer extends AbstractReplacer {
         return obfuscatedIdentifierName;
         return obfuscatedIdentifierName;
     }
     }
 
 
-    /**
-     * @param uniquePrefix
-     */
-    public setPrefix (uniquePrefix: string): void {
-        this.uniquePrefix = uniquePrefix
-    }
-
     /**
     /**
      * Store all identifiers names as keys in given `namesMap` with random names as value.
      * Store all identifiers names as keys in given `namesMap` with random names as value.
      * Reserved names will be ignored.
      * Reserved names will be ignored.
      *
      *
      * @param nodeName
      * @param nodeName
+     * @param nodeIdentifier
      */
      */
-    public storeNames (nodeName: string): void {
-        if (!this.uniquePrefix) {
-            throw new Error('`uniquePrefix` is `undefined`. Set it before `storeNames`');
-        }
-
+    public storeNames (nodeName: string, nodeIdentifier: string): void {
         if (!this.isReservedName(nodeName)) {
         if (!this.isReservedName(nodeName)) {
-            this.namesMap.set(`${nodeName}-${this.uniquePrefix}`, RandomGeneratorUtils.getRandomVariableName());
+            this.namesMap.set(`${nodeName}-${nodeIdentifier}`, RandomGeneratorUtils.getRandomVariableName());
         }
         }
     }
     }
 
 

+ 1 - 1
src/node-transformers/node-obfuscators/replacers/NumberLiteralReplacer.ts

@@ -22,7 +22,7 @@ export class NumberLiteralReplacer extends AbstractReplacer {
      * @returns {string}
      * @returns {string}
      */
      */
     public replace (nodeValue: number): string {
     public replace (nodeValue: number): string {
-        if (!Utils.isInteger(nodeValue)) {
+        if (!Utils.isCeilNumber(nodeValue)) {
             return String(nodeValue);
             return String(nodeValue);
         }
         }
 
 

+ 15 - 19
src/node-transformers/node-obfuscators/replacers/StringLiteralReplacer.ts

@@ -1,10 +1,7 @@
 import { injectable, inject } from 'inversify';
 import { injectable, inject } from 'inversify';
 import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 
-import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { ICustomNodeGroup } from '../../../interfaces/custom-nodes/ICustomNodeGroup';
 import { ICustomNodeGroup } from '../../../interfaces/custom-nodes/ICustomNodeGroup';
-import { ICustomNodeWithData } from '../../../interfaces/custom-nodes/ICustomNodeWithData';
-import { ICustomNodeWithIdentifier } from '../../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IStorage } from '../../../interfaces/storages/IStorage';
 import { IStorage } from '../../../interfaces/storages/IStorage';
 
 
@@ -12,8 +9,6 @@ import { StringArrayEncoding } from '../../../enums/StringArrayEncoding';
 
 
 import { AbstractReplacer } from './AbstractReplacer';
 import { AbstractReplacer } from './AbstractReplacer';
 import { CryptUtils } from '../../../utils/CryptUtils';
 import { CryptUtils } from '../../../utils/CryptUtils';
-import { CustomNodes } from '../../../enums/container/CustomNodes';
-import { CustomNodeGroups } from '../../../enums/container/CustomNodeGroups';
 import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
 import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
 import { Utils } from '../../../utils/Utils';
 import { Utils } from '../../../utils/Utils';
 
 
@@ -35,17 +30,25 @@ export class StringLiteralReplacer extends AbstractReplacer {
      */
      */
     private readonly customNodeGroupStorage: IStorage<ICustomNodeGroup>;
     private readonly customNodeGroupStorage: IStorage<ICustomNodeGroup>;
 
 
+    /**
+     * @type {IStorage<string>}
+     */
+    private readonly stringArrayStorage: IStorage<string>;
+
     /**
     /**
      * @param customNodeGroupStorage
      * @param customNodeGroupStorage
+     * @param stringArrayStorage
      * @param options
      * @param options
      */
      */
     constructor (
     constructor (
         @inject(ServiceIdentifiers['IStorage<ICustomNodeGroup>']) customNodeGroupStorage: IStorage<ICustomNodeGroup>,
         @inject(ServiceIdentifiers['IStorage<ICustomNodeGroup>']) customNodeGroupStorage: IStorage<ICustomNodeGroup>,
+        @inject(ServiceIdentifiers['IStorage<string>']) stringArrayStorage: IStorage<string>,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
     ) {
         super(options);
         super(options);
 
 
         this.customNodeGroupStorage = customNodeGroupStorage;
         this.customNodeGroupStorage = customNodeGroupStorage;
+        this.stringArrayStorage = stringArrayStorage;
     }
     }
 
 
     /**
     /**
@@ -70,12 +73,6 @@ export class StringLiteralReplacer extends AbstractReplacer {
      * @returns {string}
      * @returns {string}
      */
      */
     private replaceStringLiteralWithStringArrayCall (value: string): string {
     private replaceStringLiteralWithStringArrayCall (value: string): string {
-        const stringArrayCustomNodeGroupNodes: Map <CustomNodes, ICustomNode> = this.customNodeGroupStorage
-            .get(CustomNodeGroups.StringArrayCustomNodeGroup)
-            .getCustomNodes();
-        const stringArrayNode: ICustomNodeWithData = <ICustomNodeWithData>stringArrayCustomNodeGroupNodes
-            .get(CustomNodes.StringArrayNode);
-
         let rc4Key: string = '';
         let rc4Key: string = '';
 
 
         switch (this.options.stringArrayEncoding) {
         switch (this.options.stringArrayEncoding) {
@@ -95,26 +92,25 @@ export class StringLiteralReplacer extends AbstractReplacer {
             value = Utils.stringToUnicodeEscapeSequence(value);
             value = Utils.stringToUnicodeEscapeSequence(value);
         }
         }
 
 
-        const stringArray: IStorage <string> = stringArrayNode.getNodeData();
-        const indexOfExistingValue: number = <number>stringArray.getKeyOf(value);
+        const indexOfExistingValue: number = <number>this.stringArrayStorage.getKeyOf(value);
 
 
         let indexOfValue: number;
         let indexOfValue: number;
 
 
         if (indexOfExistingValue >= 0) {
         if (indexOfExistingValue >= 0) {
             indexOfValue = indexOfExistingValue;
             indexOfValue = indexOfExistingValue;
         } else {
         } else {
-            indexOfValue = stringArray.getLength();
-            stringArray.set(null, value);
+            indexOfValue = this.stringArrayStorage.getLength();
+            this.stringArrayStorage.set(null, value);
         }
         }
 
 
-        const stringArrayCallsWrapper: ICustomNodeWithIdentifier = <ICustomNodeWithIdentifier>stringArrayCustomNodeGroupNodes
-            .get(CustomNodes.StringArrayCallsWrapper);
+        const rotatedStringArrayStorageId: string = Utils.stringRotate(this.stringArrayStorage.getStorageId(), 2);
+        const stringArrayStorageCallsWrapperName: string = `_${Utils.hexadecimalPrefix}${rotatedStringArrayStorageId}`;
         const hexadecimalIndex: string = `${Utils.hexadecimalPrefix}${Utils.decToHex(indexOfValue)}`;
         const hexadecimalIndex: string = `${Utils.hexadecimalPrefix}${Utils.decToHex(indexOfValue)}`;
 
 
         if (this.options.stringArrayEncoding === StringArrayEncoding.rc4) {
         if (this.options.stringArrayEncoding === StringArrayEncoding.rc4) {
-            return `${stringArrayCallsWrapper.getNodeIdentifier()}('${hexadecimalIndex}', '${Utils.stringToUnicodeEscapeSequence(rc4Key)}')`;
+            return `${stringArrayStorageCallsWrapperName}('${hexadecimalIndex}', '${Utils.stringToUnicodeEscapeSequence(rc4Key)}')`;
         }
         }
 
 
-        return `${stringArrayCallsWrapper.getNodeIdentifier()}('${hexadecimalIndex}')`;
+        return `${stringArrayStorageCallsWrapperName}('${hexadecimalIndex}')`;
     }
     }
 }
 }

+ 8 - 0
src/node/Node.ts

@@ -91,6 +91,14 @@ export class Node {
         return node.type === NodeType.Identifier;
         return node.type === NodeType.Identifier;
     }
     }
 
 
+    /**
+     * @param node
+     * @returns {boolean}
+     */
+    public static isIfStatementNode (node: ESTree.Node): node is ESTree.IfStatement {
+        return node.type === NodeType.IfStatement;
+    }
+
     /**
     /**
      * @param node
      * @param node
      * @param parentNode
      * @param parentNode

+ 1 - 1
src/node/NodeAppender.ts

@@ -155,7 +155,7 @@ export class NodeAppender {
         blockScopeNode: TNodeWithBlockStatement,
         blockScopeNode: TNodeWithBlockStatement,
         nodeBodyStatements: TStatement[]
         nodeBodyStatements: TStatement[]
     ): TStatement[] {
     ): TStatement[] {
-        for (let statement of nodeBodyStatements) {
+        for (const statement of nodeBodyStatements) {
             statement.parentNode = blockScopeNode;
             statement.parentNode = blockScopeNode;
         }
         }
 
 

+ 31 - 14
src/node/NodeUtils.ts

@@ -9,7 +9,6 @@ import { TStatement } from '../types/node/TStatement';
 import { NodeType } from '../enums/NodeType';
 import { NodeType } from '../enums/NodeType';
 
 
 import { Node } from './Node';
 import { Node } from './Node';
-import { Utils } from '../utils/Utils';
 
 
 export class NodeUtils {
 export class NodeUtils {
     /**
     /**
@@ -50,6 +49,22 @@ export class NodeUtils {
         return <TStatement[]>structure.body;
         return <TStatement[]>structure.body;
     }
     }
 
 
+    /**
+     * @param structure
+     * @returns {string}
+     */
+    public static convertStructureToCode (structure: ESTree.Node[]): string {
+        let code: string = '';
+
+        for (const node of structure) {
+            code += escodegen.generate(node, {
+                sourceMapWithCode: true
+            }).code;
+        }
+
+        return code;
+    }
+
     /**
     /**
      * @param node
      * @param node
      * @param index
      * @param index
@@ -69,10 +84,10 @@ export class NodeUtils {
 
 
     /**
     /**
      * @param node
      * @param node
-     * @param depth
+     * @param blockScopes
      * @returns {ESTree.Node}
      * @returns {ESTree.Node}
      */
      */
-    public static getBlockScopeOfNode (node: ESTree.Node, depth: number = 0): TNodeWithBlockStatement {
+    public static getBlockScopesOfNode (node: ESTree.Node, blockScopes: TNodeWithBlockStatement[] = []): TNodeWithBlockStatement[] {
         const parentNode: ESTree.Node | undefined = node.parentNode;
         const parentNode: ESTree.Node | undefined = node.parentNode;
 
 
         if (!parentNode) {
         if (!parentNode) {
@@ -84,20 +99,22 @@ export class NodeUtils {
                 throw new ReferenceError('`parentNode` property of `parentNode` of given node is `undefined`');
                 throw new ReferenceError('`parentNode` property of `parentNode` of given node is `undefined`');
             }
             }
 
 
-            if (!Utils.arrayContains(NodeUtils.nodesWithBlockScope, parentNode.parentNode.type)) {
-                return NodeUtils.getBlockScopeOfNode(parentNode, depth);
-            } else if (depth > 0) {
-                return NodeUtils.getBlockScopeOfNode(parentNode, --depth);
+            if (!NodeUtils.nodesWithBlockScope.includes(parentNode.parentNode.type)) {
+                return NodeUtils.getBlockScopesOfNode(parentNode, blockScopes);
             }
             }
 
 
-            return parentNode;
+            blockScopes.push(parentNode);
+
+            return NodeUtils.getBlockScopesOfNode(parentNode, blockScopes);
         }
         }
 
 
-        if (Node.isProgramNode(parentNode)) {
-            return parentNode;
+        if (!Node.isProgramNode(parentNode)) {
+            return NodeUtils.getBlockScopesOfNode(parentNode, blockScopes);
         }
         }
 
 
-        return NodeUtils.getBlockScopeOfNode(parentNode);
+        blockScopes.push(parentNode);
+
+        return blockScopes;
     }
     }
 
 
     /**
     /**
@@ -116,7 +133,7 @@ export class NodeUtils {
             return depth;
             return depth;
         }
         }
 
 
-        if (Node.isBlockStatementNode(node) && Utils.arrayContains(NodeUtils.nodesWithBlockScope, parentNode.type)) {
+        if (Node.isBlockStatementNode(node) && NodeUtils.nodesWithBlockScope.includes(parentNode.type)) {
             return NodeUtils.getNodeBlockScopeDepth(parentNode, ++depth);
             return NodeUtils.getNodeBlockScopeDepth(parentNode, ++depth);
         }
         }
 
 
@@ -180,12 +197,12 @@ export class NodeUtils {
         (<any>estraverse)[traverseType](node, {
         (<any>estraverse)[traverseType](node, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (node.type === nodeType && visitor.enter) {
                 if (node.type === nodeType && visitor.enter) {
-                    visitor.enter(node, parentNode);
+                    return visitor.enter(node, parentNode);
                 }
                 }
             },
             },
             leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (node.type === nodeType && visitor.leave) {
                 if (node.type === nodeType && visitor.leave) {
-                    visitor.leave(node, parentNode);
+                    return visitor.leave(node, parentNode);
                 }
                 }
             }
             }
         });
         });

+ 9 - 1
src/options/Options.ts

@@ -23,7 +23,7 @@ import { IOptions } from '../interfaces/options/IOptions';
 import { TSourceMapMode } from '../types/TSourceMapMode';
 import { TSourceMapMode } from '../types/TSourceMapMode';
 import { TStringArrayEncoding } from '../types/options/TStringArrayEncoding';
 import { TStringArrayEncoding } from '../types/options/TStringArrayEncoding';
 
 
-import { DEFAULT_PRESET } from '../preset-options/DefaultPreset';
+import { DEFAULT_PRESET } from './presets/Default';
 
 
 import { OptionsNormalizer } from './OptionsNormalizer';
 import { OptionsNormalizer } from './OptionsNormalizer';
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
@@ -51,6 +51,14 @@ export class Options implements IOptions {
     @IsBoolean()
     @IsBoolean()
     public readonly controlFlowFlattening: boolean;
     public readonly controlFlowFlattening: boolean;
 
 
+    /**
+     * @type {boolean}
+     */
+    @IsNumber()
+    @Min(0)
+    @Max(1)
+    public readonly controlFlowFlatteningThreshold: number;
+
     /**
     /**
      * @type {boolean}
      * @type {boolean}
      */
      */

+ 29 - 5
src/options/OptionsNormalizer.ts

@@ -10,7 +10,15 @@ export class OptionsNormalizer {
     /**
     /**
      * @type {TInputOptions}
      * @type {TInputOptions}
      */
      */
-    private static readonly DISABLED_UNICODE_ARRAY_OPTIONS: TInputOptions = {
+    private static readonly DISABLED_CONTROL_FLOW_FLATTENING_OPTIONS: TInputOptions = {
+        controlFlowFlattening: false,
+        controlFlowFlatteningThreshold: 0
+    };
+
+    /**
+     * @type {TInputOptions}
+     */
+    private static readonly DISABLED_STRING_ARRAY_OPTIONS: TInputOptions = {
         rotateStringArray: false,
         rotateStringArray: false,
         stringArray: false,
         stringArray: false,
         stringArrayEncoding: false,
         stringArrayEncoding: false,
@@ -28,7 +36,7 @@ export class OptionsNormalizer {
     /**
     /**
      * @type {TInputOptions}
      * @type {TInputOptions}
      */
      */
-    private static readonly UNICODE_ARRAY_ENCODING_OPTIONS: TInputOptions = {
+    private static readonly STRING_ARRAY_ENCODING_OPTIONS: TInputOptions = {
         stringArrayEncoding: 'base64'
         stringArrayEncoding: 'base64'
     };
     };
 
 
@@ -36,6 +44,7 @@ export class OptionsNormalizer {
      * @type {TOptionsNormalizerRule[]}
      * @type {TOptionsNormalizerRule[]}
      */
      */
     private static readonly normalizerRules: TOptionsNormalizerRule[] = [
     private static readonly normalizerRules: TOptionsNormalizerRule[] = [
+        OptionsNormalizer.controlFlowFlatteningThresholdRule,
         OptionsNormalizer.domainLockRule,
         OptionsNormalizer.domainLockRule,
         OptionsNormalizer.selfDefendingRule,
         OptionsNormalizer.selfDefendingRule,
         OptionsNormalizer.sourceMapBaseUrlRule,
         OptionsNormalizer.sourceMapBaseUrlRule,
@@ -61,6 +70,21 @@ export class OptionsNormalizer {
         return normalizedOptions;
         return normalizedOptions;
     }
     }
 
 
+    /**
+     * @param options
+     * @returns {IOptions}
+     */
+    private static controlFlowFlatteningThresholdRule (options: IOptions): IOptions {
+        if (options.controlFlowFlatteningThreshold === 0) {
+            options = {
+                ...options,
+                ...OptionsNormalizer.DISABLED_CONTROL_FLOW_FLATTENING_OPTIONS
+            };
+        }
+
+        return options;
+    }
+
     /**
     /**
      * @param options
      * @param options
      * @returns {IOptions}
      * @returns {IOptions}
@@ -152,7 +176,7 @@ export class OptionsNormalizer {
         if (!options.stringArray) {
         if (!options.stringArray) {
             options = {
             options = {
                 ...options,
                 ...options,
-                ...OptionsNormalizer.DISABLED_UNICODE_ARRAY_OPTIONS
+                ...OptionsNormalizer.DISABLED_STRING_ARRAY_OPTIONS
             };
             };
         }
         }
 
 
@@ -167,7 +191,7 @@ export class OptionsNormalizer {
         if (options.stringArrayEncoding === true) {
         if (options.stringArrayEncoding === true) {
             options = {
             options = {
                 ...options,
                 ...options,
-                ...OptionsNormalizer.UNICODE_ARRAY_ENCODING_OPTIONS
+                ...OptionsNormalizer.STRING_ARRAY_ENCODING_OPTIONS
             };
             };
         }
         }
 
 
@@ -182,7 +206,7 @@ export class OptionsNormalizer {
         if (options.stringArrayThreshold === 0) {
         if (options.stringArrayThreshold === 0) {
             options = {
             options = {
                 ...options,
                 ...options,
-                ...OptionsNormalizer.DISABLED_UNICODE_ARRAY_OPTIONS
+                ...OptionsNormalizer.DISABLED_STRING_ARRAY_OPTIONS
             };
             };
         }
         }
 
 

+ 3 - 2
src/preset-options/DefaultPreset.ts → src/options/presets/Default.ts

@@ -1,10 +1,11 @@
-import { TInputOptions } from '../types/options/TInputOptions';
+import { TInputOptions } from '../../types/options/TInputOptions';
 
 
-import { SourceMapMode } from '../enums/SourceMapMode';
+import { SourceMapMode } from '../../enums/SourceMapMode';
 
 
 export const DEFAULT_PRESET: TInputOptions = Object.freeze({
 export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     compact: true,
     compact: true,
     controlFlowFlattening: false,
     controlFlowFlattening: false,
+    controlFlowFlatteningThreshold: 0.75,
     debugProtection: false,
     debugProtection: false,
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: true,
     disableConsoleOutput: true,

+ 3 - 2
src/preset-options/NoCustomNodesPreset.ts → src/options/presets/NoCustomNodes.ts

@@ -1,10 +1,11 @@
-import { TInputOptions } from '../types/options/TInputOptions';
+import { TInputOptions } from '../../types/options/TInputOptions';
 
 
-import { SourceMapMode } from '../enums/SourceMapMode';
+import { SourceMapMode } from '../../enums/SourceMapMode';
 
 
 export const NO_CUSTOM_NODES_PRESET: TInputOptions = Object.freeze({
 export const NO_CUSTOM_NODES_PRESET: TInputOptions = Object.freeze({
     compact: true,
     compact: true,
     controlFlowFlattening: false,
     controlFlowFlattening: false,
+    controlFlowFlatteningThreshold: 0,
     debugProtection: false,
     debugProtection: false,
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,

+ 1 - 1
src/stack-trace-analyzer/StackTraceAnalyzer.ts

@@ -130,7 +130,7 @@ export class StackTraceAnalyzer implements IStackTraceAnalyzer {
                 enter: (node: ESTree.Node): void => {
                 enter: (node: ESTree.Node): void => {
                     if (
                     if (
                         !Node.isCallExpressionNode(node) ||
                         !Node.isCallExpressionNode(node) ||
-                        blockScopeBodyNode.parentNode !== NodeUtils.getBlockScopeOfNode(node)
+                        blockScopeBodyNode.parentNode !== NodeUtils.getBlockScopesOfNode(node)[0]
                     ) {
                     ) {
                         return;
                         return;
                     }
                     }

+ 1 - 1
src/stack-trace-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor.ts

@@ -21,7 +21,7 @@ export class FunctionDeclarationCalleeDataExtractor extends AbstractCalleeDataEx
 
 
         if (Node.isIdentifierNode(callee)) {
         if (Node.isIdentifierNode(callee)) {
             calleeBlockStatement = this.getCalleeBlockStatement(
             calleeBlockStatement = this.getCalleeBlockStatement(
-                NodeUtils.getBlockScopeOfNode(blockScopeBody[0]),
+                NodeUtils.getBlockScopesOfNode(blockScopeBody[0])[0],
                 callee.name
                 callee.name
             );
             );
         }
         }

+ 1 - 1
src/stack-trace-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor.ts

@@ -21,7 +21,7 @@ export class FunctionExpressionCalleeDataExtractor extends AbstractCalleeDataExt
 
 
         if (Node.isIdentifierNode(callee)) {
         if (Node.isIdentifierNode(callee)) {
             calleeBlockStatement = this.getCalleeBlockStatement(
             calleeBlockStatement = this.getCalleeBlockStatement(
-                NodeUtils.getBlockScopeOfNode(blockScopeBody[0]),
+                NodeUtils.getBlockScopesOfNode(blockScopeBody[0])[0],
                 callee.name
                 callee.name
             );
             );
         }
         }

+ 1 - 1
src/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts

@@ -34,7 +34,7 @@ export class ObjectExpressionCalleeDataExtractor extends AbstractCalleeDataExtra
 
 
             functionExpressionName = objectMembersCallsChain[objectMembersCallsChain.length - 1];
             functionExpressionName = objectMembersCallsChain[objectMembersCallsChain.length - 1];
             calleeBlockStatement = this.getCalleeBlockStatement(
             calleeBlockStatement = this.getCalleeBlockStatement(
-                NodeUtils.getBlockScopeOfNode(blockScopeBody[0]),
+                NodeUtils.getBlockScopesOfNode(blockScopeBody[0])[0],
                 objectMembersCallsChain
                 objectMembersCallsChain
             );
             );
         }
         }

+ 31 - 0
src/storages/ArrayStorage.ts

@@ -1,8 +1,19 @@
+import { injectable } from 'inversify';
+
 import { IStorage } from '../interfaces/storages/IStorage';
 import { IStorage } from '../interfaces/storages/IStorage';
 
 
 import { initializable } from '../decorators/Initializable';
 import { initializable } from '../decorators/Initializable';
 
 
+import { RandomGeneratorUtils } from '../utils/RandomGeneratorUtils';
+
+@injectable()
 export abstract class ArrayStorage <T> implements IStorage <T> {
 export abstract class ArrayStorage <T> implements IStorage <T> {
+    /**
+     * @type {string}
+     */
+    @initializable()
+    protected storageId: string;
+
     /**
     /**
      * @type {T[]}
      * @type {T[]}
      */
      */
@@ -45,11 +56,31 @@ export abstract class ArrayStorage <T> implements IStorage <T> {
         return this.storage;
         return this.storage;
     }
     }
 
 
+    /**
+     * @returns {string}
+     */
+    public getStorageId (): string {
+        return this.storageId;
+    }
+
     /**
     /**
      * @param args
      * @param args
      */
      */
     public initialize (...args: any[]): void {
     public initialize (...args: any[]): void {
         this.storage = [];
         this.storage = [];
+        this.storageId = RandomGeneratorUtils.getRandomString(6);
+    }
+
+    /**
+     * @param storage
+     * @param mergeId
+     */
+    public mergeWith (storage: this, mergeId: boolean = false): void {
+        this.storage = [...this.storage, ...storage.getStorage()];
+
+        if (mergeId) {
+            this.storageId = storage.getStorageId();
+        }
     }
     }
 
 
     /**
     /**

+ 28 - 1
src/storages/MapStorage.ts

@@ -4,10 +4,17 @@ import { IStorage } from '../interfaces/storages/IStorage';
 
 
 import { initializable } from '../decorators/Initializable';
 import { initializable } from '../decorators/Initializable';
 
 
+import { RandomGeneratorUtils } from '../utils/RandomGeneratorUtils';
 import { Utils } from '../utils/Utils';
 import { Utils } from '../utils/Utils';
 
 
 @injectable()
 @injectable()
 export abstract class MapStorage <T> implements IStorage <T> {
 export abstract class MapStorage <T> implements IStorage <T> {
+    /**
+     * @type {string}
+     */
+    @initializable()
+    protected storageId: string;
+
     /**
     /**
      * @type {Map <string | number, T>}
      * @type {Map <string | number, T>}
      */
      */
@@ -33,7 +40,7 @@ export abstract class MapStorage <T> implements IStorage <T> {
      * @returns {string | number | null}
      * @returns {string | number | null}
      */
      */
     public getKeyOf (value: T): string | number | null {
     public getKeyOf (value: T): string | number | null {
-        return Utils.mapGetFirstKeyOf(this.storage, value);
+        return Utils.mapGetFirstKeyOf <string | number, T> (this.storage, value);
     }
     }
 
 
     /**
     /**
@@ -50,11 +57,31 @@ export abstract class MapStorage <T> implements IStorage <T> {
         return this.storage;
         return this.storage;
     }
     }
 
 
+    /**
+     * @returns {string}
+     */
+    public getStorageId (): string {
+        return this.storageId;
+    }
+
     /**
     /**
      * @param args
      * @param args
      */
      */
     public initialize (...args: any[]): void {
     public initialize (...args: any[]): void {
         this.storage = new Map <string | number, T> ();
         this.storage = new Map <string | number, T> ();
+        this.storageId = RandomGeneratorUtils.getRandomString(6);
+    }
+
+    /**
+     * @param storage
+     * @param mergeId
+     */
+    public mergeWith (storage: this, mergeId: boolean = false): void {
+        this.storage = new Map <string | number, T> ([...this.storage, ...storage.getStorage()]);
+
+        if (mergeId) {
+            this.storageId = storage.getStorageId();
+        }
     }
     }
 
 
     /**
     /**

+ 2 - 0
src/storages/custom-node-group/CustomNodeGroupStorage.ts

@@ -9,6 +9,7 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { CustomNodeGroups } from '../../enums/container/CustomNodeGroups';
 import { CustomNodeGroups } from '../../enums/container/CustomNodeGroups';
 
 
 import { MapStorage } from '../MapStorage';
 import { MapStorage } from '../MapStorage';
+import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 
 
 @injectable()
 @injectable()
 export class CustomNodeGroupStorage extends MapStorage <ICustomNodeGroup> {
 export class CustomNodeGroupStorage extends MapStorage <ICustomNodeGroup> {
@@ -51,6 +52,7 @@ export class CustomNodeGroupStorage extends MapStorage <ICustomNodeGroup> {
 
 
     public initialize (): void {
     public initialize (): void {
         this.storage = new Map <string, ICustomNodeGroup> ();
         this.storage = new Map <string, ICustomNodeGroup> ();
+        this.storageId = RandomGeneratorUtils.getRandomString(6);
 
 
         CustomNodeGroupStorage.customNodeGroupsList.forEach((customNodeGroupName: CustomNodeGroups) => {
         CustomNodeGroupStorage.customNodeGroupsList.forEach((customNodeGroupName: CustomNodeGroups) => {
             const customNodeGroup: ICustomNodeGroup = this.customNodeGroupFactory(
             const customNodeGroup: ICustomNodeGroup = this.customNodeGroupFactory(

+ 14 - 1
src/storages/string-array/StringArrayStorage.ts

@@ -1,6 +1,10 @@
+import { injectable } from 'inversify';
+
 import { ArrayStorage } from '../ArrayStorage';
 import { ArrayStorage } from '../ArrayStorage';
+import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
 import { Utils } from '../../utils/Utils';
 import { Utils } from '../../utils/Utils';
 
 
+@injectable()
 export class StringArrayStorage extends ArrayStorage <string> {
 export class StringArrayStorage extends ArrayStorage <string> {
     constructor () {
     constructor () {
         super();
         super();
@@ -8,6 +12,15 @@ export class StringArrayStorage extends ArrayStorage <string> {
         this.initialize();
         this.initialize();
     }
     }
 
 
+    /**
+     * @param args
+     */
+    public initialize (...args: any[]): void {
+        super.initialize(args);
+
+        this.storageId = RandomGeneratorUtils.getRandomVariableName(4, false);
+    }
+
     /**
     /**
      * @param rotationValue
      * @param rotationValue
      */
      */
@@ -23,4 +36,4 @@ export class StringArrayStorage extends ArrayStorage <string> {
             return `'${value}'`;
             return `'${value}'`;
         }).toString();
         }).toString();
     }
     }
-}
+}

+ 2 - 2
src/utils/CryptUtils.ts

@@ -58,7 +58,7 @@ export class CryptUtils {
             return result;
             return result;
         };
         };
 
 
-        const randomString: string = RandomGeneratorUtils.randomGenerator.string({
+        const randomString: string = RandomGeneratorUtils.getRandomGenerator().string({
             length: length,
             length: length,
             pool: RandomGeneratorUtils.randomGeneratorPool
             pool: RandomGeneratorUtils.randomGeneratorPool
         });
         });
@@ -69,7 +69,7 @@ export class CryptUtils {
 
 
         const randomStringDiffArray: string[] = randomStringDiff.split('');
         const randomStringDiffArray: string[] = randomStringDiff.split('');
 
 
-        RandomGeneratorUtils.randomGenerator.shuffle(randomStringDiffArray);
+        RandomGeneratorUtils.getRandomGenerator().shuffle(randomStringDiffArray);
         randomStringDiff = randomStringDiffArray.join('');
         randomStringDiff = randomStringDiffArray.join('');
 
 
         return [randomMerge(str, randomStringDiff), randomStringDiff];
         return [randomMerge(str, randomStringDiff), randomStringDiff];

+ 21 - 10
src/utils/RandomGeneratorUtils.ts

@@ -16,7 +16,7 @@ export class RandomGeneratorUtils {
     /**
     /**
      * @type {Chance.Chance | Chance.SeededChance}
      * @type {Chance.Chance | Chance.SeededChance}
      */
      */
-    public static randomGenerator: Chance.Chance | Chance.SeededChance = new Chance();
+    private static randomGenerator: Chance.Chance | Chance.SeededChance = new Chance();
 
 
     /**
     /**
      * @param min
      * @param min
@@ -24,7 +24,7 @@ export class RandomGeneratorUtils {
      * @returns {number}
      * @returns {number}
      */
      */
     public static getRandomFloat (min: number, max: number): number {
     public static getRandomFloat (min: number, max: number): number {
-        return RandomGeneratorUtils.randomGenerator.floating({
+        return RandomGeneratorUtils.getRandomGenerator().floating({
             min: min,
             min: min,
             max: max,
             max: max,
             fixed: 7
             fixed: 7
@@ -50,7 +50,7 @@ export class RandomGeneratorUtils {
      * @returns {number}
      * @returns {number}
      */
      */
     public static getRandomInteger (min: number, max: number): number {
     public static getRandomInteger (min: number, max: number): number {
-        return RandomGeneratorUtils.randomGenerator.integer({
+        return RandomGeneratorUtils.getRandomGenerator().integer({
             min: min,
             min: min,
             max: max
             max: max
         });
         });
@@ -58,13 +58,24 @@ export class RandomGeneratorUtils {
 
 
     /**
     /**
      * @param length
      * @param length
+     * @param pool
      * @returns {string}
      * @returns {string}
      */
      */
-    public static getRandomVariableName (length: number = 6): string {
-        const rangeMinInteger: number = 10000,
-            rangeMaxInteger: number = 99999999;
+    public static getRandomString (length: number, pool: string = RandomGeneratorUtils.randomGeneratorPool): string {
+        return RandomGeneratorUtils.getRandomGenerator().string({ length, pool });
+    }
+
+    /**
+     * @param length
+     * @param withPrefix
+     * @returns {string}
+     */
+    public static getRandomVariableName (length: number = 6, withPrefix: boolean = true): string {
+        const prefix: string = withPrefix ? `_${Utils.hexadecimalPrefix}` : '';
+        const rangeMinInteger: number = 10000;
+        const rangeMaxInteger: number = 99999999;
 
 
-        return `_${Utils.hexadecimalPrefix}${(
+        return `${prefix}${(
             Utils.decToHex(
             Utils.decToHex(
                 RandomGeneratorUtils.getRandomInteger(rangeMinInteger, rangeMaxInteger)
                 RandomGeneratorUtils.getRandomInteger(rangeMinInteger, rangeMaxInteger)
             )
             )
@@ -72,9 +83,9 @@ export class RandomGeneratorUtils {
     }
     }
 
 
     /**
     /**
-     * @param randomGeneratorSeed
+     * @param randomGenerator
      */
      */
-    public static setRandomGeneratorSeed (randomGeneratorSeed: number): void {
-        RandomGeneratorUtils.randomGenerator = new Chance(randomGeneratorSeed);
+    public static setRandomGenerator (randomGenerator: Chance.Chance | Chance.SeededChance): void {
+        RandomGeneratorUtils.randomGenerator = randomGenerator;
     }
     }
 }
 }

+ 19 - 19
src/utils/Utils.ts

@@ -1,22 +1,12 @@
+import * as _ from 'lodash';
 import { JSFuck } from '../enums/JSFuck';
 import { JSFuck } from '../enums/JSFuck';
 
 
-const isEqual: any = require('is-equal');
-
 export class Utils {
 export class Utils {
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
     public static readonly hexadecimalPrefix: string = '0x';
     public static readonly hexadecimalPrefix: string = '0x';
 
 
-    /**
-     * @param array
-     * @param searchElement
-     * @returns {boolean}
-     */
-    public static arrayContains (array: any[], searchElement: any): boolean {
-        return array.indexOf(searchElement) >= 0;
-    }
-
     /**
     /**
      * @param array
      * @param array
      * @param times
      * @param times
@@ -31,8 +21,9 @@ export class Utils {
             return array;
             return array;
         }
         }
 
 
-        let newArray: T[] = array,
-            temp: T | undefined;
+        const newArray: T[] = array;
+
+        let temp: T | undefined;
 
 
         while (times--) {
         while (times--) {
             temp = newArray.pop()!;
             temp = newArray.pop()!;
@@ -74,18 +65,18 @@ export class Utils {
      * @param number
      * @param number
      * @returns {boolean}
      * @returns {boolean}
      */
      */
-    public static isInteger (number: number): boolean {
+    public static isCeilNumber (number: number): boolean {
         return number % 1 === 0;
         return number % 1 === 0;
     }
     }
 
 
     /**
     /**
      * @param map
      * @param map
      * @param value
      * @param value
-     * @returns {any}
+     * @returns {T | null}
      */
      */
-    public static mapGetFirstKeyOf(map: Map <any, any>, value: any): any {
-        for (let [key, storageValue] of map) {
-            if (isEqual(value, storageValue)) {
+    public static mapGetFirstKeyOf <T, U> (map: Map <T, U>, value: U): T | null {
+        for (const [key, storageValue] of map) {
+            if (_.isEqual(value, storageValue)) {
                 return key;
                 return key;
             }
             }
         }
         }
@@ -101,6 +92,15 @@ export class Utils {
         return obj;
         return obj;
     }
     }
 
 
+    /**
+     * @param string
+     * @param times
+     * @returns {string}
+     */
+    public static stringRotate (string: string, times: number): string {
+        return Utils.arrayRotate(Array.from(string), times).join('');
+    }
+
     /**
     /**
      * @param string
      * @param string
      * @returns {string}
      * @returns {string}
@@ -120,9 +120,9 @@ export class Utils {
      */
      */
     public static stringToUnicodeEscapeSequence (string: string): string {
     public static stringToUnicodeEscapeSequence (string: string): string {
         const radix: number = 16;
         const radix: number = 16;
+        const regexp: RegExp = new RegExp('[\x00-\x7F]');
 
 
         let prefix: string,
         let prefix: string,
-            regexp: RegExp = new RegExp('[\x00-\x7F]'),
             template: string;
             template: string;
 
 
         return `${string.replace(/[\s\S]/g, (escape: string): string => {
         return `${string.replace(/[\s\S]/g, (escape: string): string => {

+ 66 - 3
test/dev/dev.ts

@@ -1,5 +1,4 @@
 'use strict';
 'use strict';
-import { NO_CUSTOM_NODES_PRESET } from '../../src/preset-options/NoCustomNodesPreset';
 
 
 if (!(<any>global)._babelPolyfill) {
 if (!(<any>global)._babelPolyfill) {
     require('babel-polyfill');
     require('babel-polyfill');
@@ -11,15 +10,79 @@ if (!(<any>global)._babelPolyfill) {
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
         `
             (function(){
             (function(){
+                var result = 1,
+                    term1 = 0,
+                    term2 = 1,
+                    i = 1;
+                while(i < 10)
+                {
+                    var test = 10;
+                    result = term1 + term2;
+                    console.log(result);
+                    term1 = term2;
+                    term2 = result;
+                    i++;
+                }
+        
+                console.log(test);
+                
+                var test = function (test) {
+                    console.log(test);
+                    
+                    if (true) {
+                        var test = 5
+                    }
+                    
+                    return test;
+                }
+                
+                console.log(test(1));
+                
+                function test2 (abc) {
+                    function test1 () {
+                      console.log('inside', abc.item);
+                    }
+                    
+                    console.log('тест', abc);
+                    
+                    var abc = {};
+                    
+                    return abc.item = 15, test1();
+                };
+                
+                var regexptest = /version\\/(\\d+)/i;
+                console.log(regexptest);
+                
+                test2(22);
+                console.log(105.4);
+                console.log(true, false);
+                
+                var sA = 'shorthand1';
+                var sB = 'shorthand2';
+                
+                console.log({sA, sB});
+                
+                try {
+                } catch (error) {
+                    console.log(error);
+                }
+                
                 function foo () {
                 function foo () {
                     return function () {
                     return function () {
-                        var sum = 1 + 2;
+                        var sum1 = 10 + 20;
+                        var sum2 = 20 + 30;
+                        var sum3 = 30 + 50;
+                        var sub = sum3 - sum2;
+                        
+                        return sum1 + sub;
                     }
                     }
                 }
                 }
+                
+                console.log(foo()());
             })();
             })();
         `,
         `,
         {
         {
-            ...NO_CUSTOM_NODES_PRESET,
+            compact: false,
             controlFlowFlattening: true,
             controlFlowFlattening: true,
             disableConsoleOutput: false
             disableConsoleOutput: false
         }
         }

+ 18 - 18
test/fixtures/compile-performance.js

@@ -19813,8 +19813,8 @@
                 else {
                 else {
                     mergedOutputs = outputs;
                     mergedOutputs = outputs;
                 }
                 }
-                var mergedHost = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__facade_lang__["a" /* isPresent */])(dm.host) ? __WEBPACK_IMPORTED_MODULE_1__facade_collection__["b" /* StringMapWrapper */].merge(dm.host, host) : host;
-                var mergedQueries = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__facade_lang__["a" /* isPresent */])(dm.queries) ? __WEBPACK_IMPORTED_MODULE_1__facade_collection__["b" /* StringMapWrapper */].merge(dm.queries, queries) : queries;
+                var mergedHost = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__facade_lang__["a" /* isPresent */])(dm.host) ? __WEBPACK_IMPORTED_MODULE_1__facade_collection__["b" /* StringMapWrapper */].mergeWith(dm.host, host) : host;
+                var mergedQueries = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__facade_lang__["a" /* isPresent */])(dm.queries) ? __WEBPACK_IMPORTED_MODULE_1__facade_collection__["b" /* StringMapWrapper */].mergeWith(dm.queries, queries) : queries;
                 if (dm instanceof __WEBPACK_IMPORTED_MODULE_0__angular_core__["Component"]) {
                 if (dm instanceof __WEBPACK_IMPORTED_MODULE_0__angular_core__["Component"]) {
                     return new __WEBPACK_IMPORTED_MODULE_0__angular_core__["Component"]({
                     return new __WEBPACK_IMPORTED_MODULE_0__angular_core__["Component"]({
                         selector: dm.selector,
                         selector: dm.selector,
@@ -25918,7 +25918,7 @@
         }
         }
         function _createRootRenderer(rootRenderer /** TODO #9100 */, extraTokens) {
         function _createRootRenderer(rootRenderer /** TODO #9100 */, extraTokens) {
             __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__dom_adapter__["a" /* getDOM */])().setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement);
             __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__dom_adapter__["a" /* getDOM */])().setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement);
-            __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__dom_adapter__["a" /* getDOM */])().setGlobalVar(CORE_TOKENS_GLOBAL_NAME, __WEBPACK_IMPORTED_MODULE_1__facade_collection__["a" /* StringMapWrapper */].merge(CORE_TOKENS, _ngProbeTokensToMap(extraTokens || [])));
+            __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__dom_adapter__["a" /* getDOM */])().setGlobalVar(CORE_TOKENS_GLOBAL_NAME, __WEBPACK_IMPORTED_MODULE_1__facade_collection__["a" /* StringMapWrapper */].mergeWith(CORE_TOKENS, _ngProbeTokensToMap(extraTokens || [])));
             return new __WEBPACK_IMPORTED_MODULE_2__private_import_core__["b" /* DebugDomRootRenderer */](rootRenderer);
             return new __WEBPACK_IMPORTED_MODULE_2__private_import_core__["b" /* DebugDomRootRenderer */](rootRenderer);
         }
         }
         function _ngProbeTokensToMap(tokens) {
         function _ngProbeTokensToMap(tokens) {
@@ -38913,14 +38913,14 @@
                         if (!_this._finished) {
                         if (!_this._finished) {
                             var responseOptions_1 = new __WEBPACK_IMPORTED_MODULE_2__base_response_options__["a" /* ResponseOptions */]({ body: JSONP_ERR_NO_CALLBACK, type: __WEBPACK_IMPORTED_MODULE_3__enums__["a" /* ResponseType */].Error, url: url });
                             var responseOptions_1 = new __WEBPACK_IMPORTED_MODULE_2__base_response_options__["a" /* ResponseOptions */]({ body: JSONP_ERR_NO_CALLBACK, type: __WEBPACK_IMPORTED_MODULE_3__enums__["a" /* ResponseType */].Error, url: url });
                             if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_4__facade_lang__["a" /* isPresent */])(baseResponseOptions)) {
                             if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_4__facade_lang__["a" /* isPresent */])(baseResponseOptions)) {
-                                responseOptions_1 = baseResponseOptions.merge(responseOptions_1);
+                                responseOptions_1 = baseResponseOptions.mergeWith(responseOptions_1);
                             }
                             }
                             responseObserver.error(new __WEBPACK_IMPORTED_MODULE_6__static_response__["a" /* Response */](responseOptions_1));
                             responseObserver.error(new __WEBPACK_IMPORTED_MODULE_6__static_response__["a" /* Response */](responseOptions_1));
                             return;
                             return;
                         }
                         }
                         var responseOptions = new __WEBPACK_IMPORTED_MODULE_2__base_response_options__["a" /* ResponseOptions */]({ body: _this._responseData, url: url });
                         var responseOptions = new __WEBPACK_IMPORTED_MODULE_2__base_response_options__["a" /* ResponseOptions */]({ body: _this._responseData, url: url });
                         if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_4__facade_lang__["a" /* isPresent */])(_this.baseResponseOptions)) {
                         if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_4__facade_lang__["a" /* isPresent */])(_this.baseResponseOptions)) {
-                            responseOptions = _this.baseResponseOptions.merge(responseOptions);
+                            responseOptions = _this.baseResponseOptions.mergeWith(responseOptions);
                         }
                         }
                         responseObserver.next(new __WEBPACK_IMPORTED_MODULE_6__static_response__["a" /* Response */](responseOptions));
                         responseObserver.next(new __WEBPACK_IMPORTED_MODULE_6__static_response__["a" /* Response */](responseOptions));
                         responseObserver.complete();
                         responseObserver.complete();
@@ -38932,7 +38932,7 @@
                         _dom.cleanup(script);
                         _dom.cleanup(script);
                         var responseOptions = new __WEBPACK_IMPORTED_MODULE_2__base_response_options__["a" /* ResponseOptions */]({ body: error.message, type: __WEBPACK_IMPORTED_MODULE_3__enums__["a" /* ResponseType */].Error });
                         var responseOptions = new __WEBPACK_IMPORTED_MODULE_2__base_response_options__["a" /* ResponseOptions */]({ body: error.message, type: __WEBPACK_IMPORTED_MODULE_3__enums__["a" /* ResponseType */].Error });
                         if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_4__facade_lang__["a" /* isPresent */])(baseResponseOptions)) {
                         if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_4__facade_lang__["a" /* isPresent */])(baseResponseOptions)) {
-                            responseOptions = baseResponseOptions.merge(responseOptions);
+                            responseOptions = baseResponseOptions.mergeWith(responseOptions);
                         }
                         }
                         responseObserver.error(new __WEBPACK_IMPORTED_MODULE_6__static_response__["a" /* Response */](responseOptions));
                         responseObserver.error(new __WEBPACK_IMPORTED_MODULE_6__static_response__["a" /* Response */](responseOptions));
                     };
                     };
@@ -39075,7 +39075,7 @@
                         var statusText = _xhr.statusText || 'OK';
                         var statusText = _xhr.statusText || 'OK';
                         var responseOptions = new __WEBPACK_IMPORTED_MODULE_3__base_response_options__["a" /* ResponseOptions */]({ body: body, status: status, headers: headers, statusText: statusText, url: url });
                         var responseOptions = new __WEBPACK_IMPORTED_MODULE_3__base_response_options__["a" /* ResponseOptions */]({ body: body, status: status, headers: headers, statusText: statusText, url: url });
                         if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_5__facade_lang__["a" /* isPresent */])(baseResponseOptions)) {
                         if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_5__facade_lang__["a" /* isPresent */])(baseResponseOptions)) {
-                            responseOptions = baseResponseOptions.merge(responseOptions);
+                            responseOptions = baseResponseOptions.mergeWith(responseOptions);
                         }
                         }
                         var response = new __WEBPACK_IMPORTED_MODULE_9__static_response__["a" /* Response */](responseOptions);
                         var response = new __WEBPACK_IMPORTED_MODULE_9__static_response__["a" /* Response */](responseOptions);
                         response.ok = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_7__http_utils__["d" /* isSuccess */])(status);
                         response.ok = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_7__http_utils__["d" /* isSuccess */])(status);
@@ -39096,7 +39096,7 @@
                             statusText: _xhr.statusText,
                             statusText: _xhr.statusText,
                         });
                         });
                         if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_5__facade_lang__["a" /* isPresent */])(baseResponseOptions)) {
                         if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_5__facade_lang__["a" /* isPresent */])(baseResponseOptions)) {
-                            responseOptions = baseResponseOptions.merge(responseOptions);
+                            responseOptions = baseResponseOptions.mergeWith(responseOptions);
                         }
                         }
                         responseObserver.error(new __WEBPACK_IMPORTED_MODULE_9__static_response__["a" /* Response */](responseOptions));
                         responseObserver.error(new __WEBPACK_IMPORTED_MODULE_9__static_response__["a" /* Response */](responseOptions));
                     };
                     };
@@ -39355,7 +39355,7 @@
             var newOptions = defaultOpts;
             var newOptions = defaultOpts;
             if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__src_facade_lang__["a" /* isPresent */])(providedOpts)) {
             if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__src_facade_lang__["a" /* isPresent */])(providedOpts)) {
                 // Hack so Dart can used named parameters
                 // Hack so Dart can used named parameters
-                return newOptions.merge(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({
+                return newOptions.mergeWith(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({
                     method: providedOpts.method || method,
                     method: providedOpts.method || method,
                     url: providedOpts.url || url,
                     url: providedOpts.url || url,
                     search: providedOpts.search,
                     search: providedOpts.search,
@@ -39366,10 +39366,10 @@
                 }));
                 }));
             }
             }
             if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__src_facade_lang__["a" /* isPresent */])(method)) {
             if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__src_facade_lang__["a" /* isPresent */])(method)) {
-                return newOptions.merge(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ method: method, url: url }));
+                return newOptions.mergeWith(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ method: method, url: url }));
             }
             }
             else {
             else {
-                return newOptions.merge(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ url: url }));
+                return newOptions.mergeWith(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ url: url }));
             }
             }
         }
         }
         /**
         /**
@@ -39466,13 +39466,13 @@
              * Performs a request with `post` http method.
              * Performs a request with `post` http method.
              */
              */
             Http.prototype.post = function (url, body, options) {
             Http.prototype.post = function (url, body, options) {
-                return httpRequest(this._backend, new __WEBPACK_IMPORTED_MODULE_5__static_request__["a" /* Request */](mergeOptions(this._defaultOptions.merge(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ body: body })), options, __WEBPACK_IMPORTED_MODULE_3__enums__["b" /* RequestMethod */].Post, url)));
+                return httpRequest(this._backend, new __WEBPACK_IMPORTED_MODULE_5__static_request__["a" /* Request */](mergeOptions(this._defaultOptions.mergeWith(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ body: body })), options, __WEBPACK_IMPORTED_MODULE_3__enums__["b" /* RequestMethod */].Post, url)));
             };
             };
             /**
             /**
              * Performs a request with `put` http method.
              * Performs a request with `put` http method.
              */
              */
             Http.prototype.put = function (url, body, options) {
             Http.prototype.put = function (url, body, options) {
-                return httpRequest(this._backend, new __WEBPACK_IMPORTED_MODULE_5__static_request__["a" /* Request */](mergeOptions(this._defaultOptions.merge(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ body: body })), options, __WEBPACK_IMPORTED_MODULE_3__enums__["b" /* RequestMethod */].Put, url)));
+                return httpRequest(this._backend, new __WEBPACK_IMPORTED_MODULE_5__static_request__["a" /* Request */](mergeOptions(this._defaultOptions.mergeWith(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ body: body })), options, __WEBPACK_IMPORTED_MODULE_3__enums__["b" /* RequestMethod */].Put, url)));
             };
             };
             /**
             /**
              * Performs a request with `delete` http method.
              * Performs a request with `delete` http method.
@@ -39484,7 +39484,7 @@
              * Performs a request with `patch` http method.
              * Performs a request with `patch` http method.
              */
              */
             Http.prototype.patch = function (url, body, options) {
             Http.prototype.patch = function (url, body, options) {
-                return httpRequest(this._backend, new __WEBPACK_IMPORTED_MODULE_5__static_request__["a" /* Request */](mergeOptions(this._defaultOptions.merge(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ body: body })), options, __WEBPACK_IMPORTED_MODULE_3__enums__["b" /* RequestMethod */].Patch, url)));
+                return httpRequest(this._backend, new __WEBPACK_IMPORTED_MODULE_5__static_request__["a" /* Request */](mergeOptions(this._defaultOptions.mergeWith(new __WEBPACK_IMPORTED_MODULE_2__base_request_options__["a" /* RequestOptions */]({ body: body })), options, __WEBPACK_IMPORTED_MODULE_3__enums__["b" /* RequestMethod */].Patch, url)));
             };
             };
             /**
             /**
              * Performs a request with `head` http method.
              * Performs a request with `head` http method.
@@ -49300,7 +49300,7 @@
                 var lastIndex = stylesList.length - 1;
                 var lastIndex = stylesList.length - 1;
                 var lastItem = stylesList[lastIndex];
                 var lastItem = stylesList[lastIndex];
                 if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__facade_lang__["l" /* isStringMap */])(lastItem)) {
                 if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__facade_lang__["l" /* isStringMap */])(lastItem)) {
-                    stylesList[lastIndex] = __WEBPACK_IMPORTED_MODULE_1__facade_collection__["b" /* StringMapWrapper */].merge(lastItem, newItem);
+                    stylesList[lastIndex] = __WEBPACK_IMPORTED_MODULE_1__facade_collection__["b" /* StringMapWrapper */].mergeWith(lastItem, newItem);
                     return;
                     return;
                 }
                 }
             }
             }
@@ -53616,7 +53616,7 @@
                     hasExtraFirstStyles = true;
                     hasExtraFirstStyles = true;
                 }
                 }
             });
             });
-            var keyframeCollectedStyles = __WEBPACK_IMPORTED_MODULE_0__facade_collection__["d" /* StringMapWrapper */].merge({}, flatenedFirstKeyframeStyles);
+            var keyframeCollectedStyles = __WEBPACK_IMPORTED_MODULE_0__facade_collection__["d" /* StringMapWrapper */].mergeWith({}, flatenedFirstKeyframeStyles);
             // phase 2: normalize the final keyframe
             // phase 2: normalize the final keyframe
             var finalKeyframe = keyframes[limit];
             var finalKeyframe = keyframes[limit];
             __WEBPACK_IMPORTED_MODULE_0__facade_collection__["a" /* ListWrapper */].insert(finalKeyframe.styles.styles, 0, finalStateStyles);
             __WEBPACK_IMPORTED_MODULE_0__facade_collection__["a" /* ListWrapper */].insert(finalKeyframe.styles.styles, 0, finalStateStyles);
@@ -64367,7 +64367,7 @@
         "use strict";
         "use strict";
         var Observable_1 = __webpack_require__(0);
         var Observable_1 = __webpack_require__(0);
         var merge_1 = __webpack_require__(857);
         var merge_1 = __webpack_require__(857);
-        Observable_1.Observable.merge = merge_1.merge;
+        Observable_1.Observable.merge = merge_1.mergeWith;
 //# sourceMappingURL=merge.js.map
 //# sourceMappingURL=merge.js.map
 
 
         /***/ },
         /***/ },
@@ -64975,7 +64975,7 @@
         "use strict";
         "use strict";
         var Observable_1 = __webpack_require__(0);
         var Observable_1 = __webpack_require__(0);
         var merge_1 = __webpack_require__(404);
         var merge_1 = __webpack_require__(404);
-        Observable_1.Observable.prototype.merge = merge_1.merge;
+        Observable_1.Observable.prototype.merge = merge_1.mergeWith;
 //# sourceMappingURL=merge.js.map
 //# sourceMappingURL=merge.js.map
 
 
         /***/ },
         /***/ },

+ 3 - 0
test/fixtures/node-transformers/node-control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer-1.js

@@ -0,0 +1,3 @@
+(function () {
+    var variable = 1 + 2;
+})();

+ 4 - 0
test/fixtures/node-transformers/node-control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer-2.js

@@ -0,0 +1,4 @@
+(function () {
+    var variable1 = 1 + 2;
+    var variable2 = 2 + 3;
+})();

+ 13 - 1
test/functional-tests/JavaScriptObfuscator.spec.ts

@@ -1,14 +1,22 @@
 import { assert } from 'chai';
 import { assert } from 'chai';
 
 
+import { Chance } from 'chance';
+
 import { IObfuscationResult } from '../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../src/interfaces/IObfuscationResult';
 
 
 import { JavaScriptObfuscator } from '../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../src/JavaScriptObfuscator';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
+
 import { readFileAsString } from '../helpers/readFileAsString';
 import { readFileAsString } from '../helpers/readFileAsString';
+import { RandomGeneratorUtils } from '../../src/utils/RandomGeneratorUtils';
 
 
 describe('JavaScriptObfuscator', () => {
 describe('JavaScriptObfuscator', () => {
     describe('obfuscate (sourceCode: string, customOptions?: IObfuscatorOptions): IObfuscationResult', () => {
     describe('obfuscate (sourceCode: string, customOptions?: IObfuscatorOptions): IObfuscationResult', () => {
+        beforeEach(() => {
+            RandomGeneratorUtils.setRandomGenerator(new Chance());
+        });
+
         describe('if `sourceMap` option is `false`', () => {
         describe('if `sourceMap` option is `false`', () => {
             it('should returns object with obfuscated code and empty source map', () => {
             it('should returns object with obfuscated code and empty source map', () => {
                 let obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                 let obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
@@ -171,5 +179,9 @@ describe('JavaScriptObfuscator', () => {
             assert.notEqual(obfuscationResult1.getObfuscatedCode(), obfuscationResult2.getObfuscatedCode());
             assert.notEqual(obfuscationResult1.getObfuscatedCode(), obfuscationResult2.getObfuscatedCode());
             assert.notEqual(obfuscationResult3.getObfuscatedCode(), obfuscationResult4.getObfuscatedCode());
             assert.notEqual(obfuscationResult3.getObfuscatedCode(), obfuscationResult4.getObfuscatedCode());
         });
         });
+
+        afterEach(() => {
+            RandomGeneratorUtils.setRandomGenerator(new Chance());
+        });
     });
     });
 });
 });

+ 1 - 1
test/functional-tests/JavaScriptObfuscatorInternal.spec.ts

@@ -6,7 +6,7 @@ import { IInversifyContainerFacade } from '../../src/interfaces/container/IInver
 import { IJavaScriptObfuscator } from '../../src/interfaces/IJavaScriptObfsucator';
 import { IJavaScriptObfuscator } from '../../src/interfaces/IJavaScriptObfsucator';
 import { IObfuscationResult } from '../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
 
 
 import { InversifyContainerFacade } from '../../src/container/InversifyContainerFacade';
 import { InversifyContainerFacade } from '../../src/container/InversifyContainerFacade';
 
 

+ 1 - 1
test/functional-tests/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 

+ 1 - 1
test/functional-tests/custom-nodes/domain-lock-nodes/DomainLockNode.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 

+ 1 - 1
test/functional-tests/custom-nodes/string-array-nodes/StringArrayCallsWrapper.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 

+ 1 - 1
test/functional-tests/custom-nodes/string-array-nodes/StringArrayNode.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 

+ 1 - 1
test/functional-tests/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 

+ 77 - 0
test/functional-tests/node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.spec.ts

@@ -0,0 +1,77 @@
+import { assert } from 'chai';
+
+import { IObfuscationResult } from '../../../../../src/interfaces/IObfuscationResult';
+
+import { NO_CUSTOM_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
+
+import { readFileAsString } from '../../../../helpers/readFileAsString';
+
+import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscator';
+
+describe('BinaryExpressionControlFlowReplacer', () => {
+    describe('replace (binaryExpressionNode: ESTree.BinaryExpression,parentNode: ESTree.Node,controlFlowStorage: IStorage <ICustomNode>)', () => {
+        describe('variant #1 - single binary expression', () => {
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                readFileAsString(
+                    './test/fixtures/node-transformers/node-control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer-1.js'
+                ),
+                {
+                    ...NO_CUSTOM_NODES_PRESET,
+                    controlFlowFlattening: true,
+                    controlFlowFlatteningThreshold: 1
+                }
+            );
+            const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
+            const controlFlowStorageCallRegExp: RegExp = /var *_0x([a-z0-9]){4,6} *= *_0x([a-z0-9]){4,6}\['(\\x[a-f0-9]*){3}'\]\(0x1, *0x2\);/;
+
+            it('should replace binary expression node by call to control flow storage node', () => {
+                assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
+            });
+        });
+
+        describe('variant #2 - multiple binary expressions with threshold = 1', () => {
+            it('should replace binary expression node by call to control flow storage node', function () {
+                this.timeout(4000);
+
+                const samplesCount: number = 200;
+                const expectedValue: number = 0.5;
+                const delta: number = 0.1;
+
+                let equalsValue: number = 0;
+
+                for (let i = 0; i < samplesCount; i++) {
+                    const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                        readFileAsString(
+                            './test/fixtures/node-transformers/node-control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer-2.js'
+                        ),
+                        {
+                            ...NO_CUSTOM_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    );
+                    const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
+                    const controlFlowStorageCallRegExp1: RegExp = /var *_0x([a-z0-9]){4,6} *= *(_0x([a-z0-9]){4,6}\['(\\x[a-f0-9]*){3}'\])\(0x1, *0x2\);/;
+                    const controlFlowStorageCallRegExp2: RegExp = /var *_0x([a-z0-9]){4,6} *= *(_0x([a-z0-9]){4,6}\['(\\x[a-f0-9]*){3}'\])\(0x2, *0x3\);/;
+
+                    const firstMatchArray: RegExpMatchArray | null = obfuscatedCode.match(controlFlowStorageCallRegExp1);
+                    const secondMatchArray: RegExpMatchArray | null = obfuscatedCode.match(controlFlowStorageCallRegExp2);
+
+                    const firstMatch: string | undefined = firstMatchArray ? firstMatchArray[2] : undefined;
+                    const secondMatch: string | undefined = secondMatchArray ? secondMatchArray[2] : undefined;
+
+                    assert.match(obfuscatedCode, controlFlowStorageCallRegExp1);
+                    assert.match(obfuscatedCode, controlFlowStorageCallRegExp2);
+                    assert.isOk(firstMatch);
+                    assert.isOk(secondMatch);
+
+                    if (firstMatch === secondMatch) {
+                        equalsValue++;
+                    }
+                }
+
+                assert.closeTo(equalsValue / samplesCount, expectedValue, delta);
+            });
+        });
+    });
+});

+ 5 - 5
test/functional-tests/node-transformers/node-obfuscators/CatchClauseObfuscator.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { readFileAsString } from '../../../helpers/readFileAsString';
 import { readFileAsString } from '../../../helpers/readFileAsString';
 
 
@@ -28,11 +28,11 @@ describe('CatchClauseObfuscator', () => {
         });
         });
 
 
         it('catch clause arguments param name and param name in body should be same', () => {
         it('catch clause arguments param name and param name in body should be same', () => {
-            const firstMatchArray: RegExpMatchArray|null = obfuscatedCode.match(paramNameRegExp);
-            const secondMatchArray: RegExpMatchArray|null = obfuscatedCode.match(bodyParamNameRegExp);
+            const firstMatchArray: RegExpMatchArray | null = obfuscatedCode.match(paramNameRegExp);
+            const secondMatchArray: RegExpMatchArray | null = obfuscatedCode.match(bodyParamNameRegExp);
 
 
-            const firstMatch: string|undefined = firstMatchArray ? firstMatchArray[1] : undefined;
-            const secondMatch: string|undefined = secondMatchArray ? secondMatchArray[1] : undefined;
+            const firstMatch: string | undefined = firstMatchArray ? firstMatchArray[1] : undefined;
+            const secondMatch: string | undefined = secondMatchArray ? secondMatchArray[1] : undefined;
 
 
             assert.isOk(firstMatch);
             assert.isOk(firstMatch);
             assert.isOk(secondMatch);
             assert.isOk(secondMatch);

+ 1 - 1
test/functional-tests/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 

+ 1 - 1
test/functional-tests/node-transformers/node-obfuscators/FunctionObfuscator.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 

+ 1 - 1
test/functional-tests/node-transformers/node-obfuscators/LabeledStatementObfuscator.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { readFileAsString } from '../../../helpers/readFileAsString';
 import { readFileAsString } from '../../../helpers/readFileAsString';
 
 

+ 1 - 1
test/functional-tests/node-transformers/node-obfuscators/LiteralObfuscator.spec.ts

@@ -2,7 +2,7 @@ import { assert } from 'chai';
 
 
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
 
 
-import { NO_CUSTOM_NODES_PRESET } from '../../../../src/preset-options/NoCustomNodesPreset';
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 

部分文件因文件數量過多而無法顯示