Browse Source

Merge remote-tracking branch 'origin/dev' into dev

Conflicts:
	package.json
sanex3339 8 years ago
parent
commit
21f6253cad
100 changed files with 1801 additions and 1023 deletions
  1. 5 0
      CHANGELOG.md
  2. 9 1
      README.md
  3. 420 290
      dist/index.js
  4. 11 10
      package.json
  5. 4 6
      src/JavaScriptObfuscator.ts
  6. 33 26
      src/JavaScriptObfuscatorInternal.ts
  7. 58 77
      src/Obfuscator.ts
  8. 4 4
      src/SourceMapCorrector.ts
  9. 0 53
      src/StringArray.ts
  10. 73 13
      src/Utils.ts
  11. 2 2
      src/cli/CLIUtils.ts
  12. 10 8
      src/cli/JavaScriptObfuscatorCLI.ts
  13. 10 2
      src/custom-nodes/AbstractCustomNode.ts
  14. 7 24
      src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts
  15. 49 0
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionNode.ts
  16. 80 0
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode.ts
  17. 64 0
      src/custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts
  18. 6 10
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts
  19. 6 10
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts
  20. 6 22
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts
  21. 9 13
      src/custom-nodes/domain-lock-nodes/DomainLockNode.ts
  22. 13 14
      src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts
  23. 13 13
      src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts
  24. 28 28
      src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts
  25. 16 32
      src/custom-nodes/string-array-nodes/StringArrayNode.ts
  26. 20 29
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  27. 0 2
      src/enums/JSFuck.ts
  28. 6 0
      src/enums/VisitorDirection.ts
  29. 13 0
      src/interfaces/IControlFlowReplacer.d.ts
  30. 0 9
      src/interfaces/INodeObfuscator.d.ts
  31. 5 0
      src/interfaces/INodeTransformer.d.ts
  32. 5 0
      src/interfaces/INodeTransformersFactory.d.ts
  33. 1 1
      src/interfaces/IObfuscator.d.ts
  34. 2 0
      src/interfaces/IObfuscatorOptions.d.ts
  35. 2 0
      src/interfaces/IOptions.d.ts
  36. 8 0
      src/interfaces/IStorage.d.ts
  37. 5 0
      src/interfaces/custom-nodes/ICustomNode.d.ts
  38. 0 5
      src/interfaces/custom-nodes/ICustomNodeWithData.d.ts
  39. 3 1
      src/interfaces/stack-trace-analyzer/IStackTraceAnalyzer.d.ts
  40. 3 3
      src/node-groups/AbstractNodesGroup.ts
  41. 4 6
      src/node-groups/StringArrayNodesGroup.ts
  42. 6 6
      src/node-transformers/AbstractNodeTransformer.ts
  43. 49 0
      src/node-transformers/AbstractNodeTransformersFactory.ts
  44. 98 0
      src/node-transformers/node-control-flow-transformers/FunctionControlFlowTransformer.ts
  45. 53 0
      src/node-transformers/node-control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts
  46. 47 0
      src/node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts
  47. 16 0
      src/node-transformers/node-control-flow-transformers/factory/NodeControlFlowTransformersFactory.ts
  48. 10 10
      src/node-transformers/node-obfuscators/CatchClauseObfuscator.ts
  49. 11 11
      src/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.ts
  50. 13 16
      src/node-transformers/node-obfuscators/FunctionObfuscator.ts
  51. 10 10
      src/node-transformers/node-obfuscators/LabeledStatementObfuscator.ts
  52. 5 5
      src/node-transformers/node-obfuscators/LiteralObfuscator.ts
  53. 16 16
      src/node-transformers/node-obfuscators/MemberExpressionObfuscator.ts
  54. 6 6
      src/node-transformers/node-obfuscators/MethodDefinitionObfuscator.ts
  55. 17 17
      src/node-transformers/node-obfuscators/ObjectExpressionObfuscator.ts
  56. 11 11
      src/node-transformers/node-obfuscators/VariableDeclarationObfuscator.ts
  57. 36 0
      src/node-transformers/node-obfuscators/factory/NodeObfuscatorsFactory.ts
  58. 5 5
      src/node-transformers/node-obfuscators/replacers/AbstractReplacer.ts
  59. 1 1
      src/node-transformers/node-obfuscators/replacers/BooleanLiteralReplacer.ts
  60. 2 2
      src/node-transformers/node-obfuscators/replacers/IdentifierReplacer.ts
  61. 1 1
      src/node-transformers/node-obfuscators/replacers/NumberLiteralReplacer.ts
  62. 17 19
      src/node-transformers/node-obfuscators/replacers/StringLiteralReplacer.ts
  63. 1 4
      src/node/NodeAppender.ts
  64. 26 3
      src/node/NodeUtils.ts
  65. 13 1
      src/options/Options.ts
  66. 6 6
      src/options/OptionsNormalizer.ts
  67. 4 3
      src/options/ValidationErrorsFormatter.ts
  68. 2 0
      src/preset-options/DefaultPreset.ts
  69. 2 0
      src/preset-options/NoCustomNodesPreset.ts
  70. 7 18
      src/stack-trace-analyzer/StackTraceAnalyzer.ts
  71. 52 0
      src/storages/ArrayStorage.ts
  72. 54 0
      src/storages/MapStorage.ts
  73. 19 0
      src/storages/control-flow/ControlFlowStorage.ts
  74. 20 0
      src/storages/string-array/StringArrayStorage.ts
  75. 10 0
      src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionTemplate.ts
  76. 6 0
      src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallTemplate.ts
  77. 8 0
      src/templates/custom-nodes/control-flow-storage-nodes/ControlFlowStorageTemplate.ts
  78. 1 3
      src/templates/custom-nodes/debug-protection-nodes/debug-protection-function-node/DebugProtectionFunctionTemplate.ts
  79. 5 0
      src/types/TControlFlowReplacer.d.ts
  80. 0 5
      src/types/TNodeObfuscator.d.ts
  81. 5 0
      src/types/TNodeTransformer.d.ts
  82. 1 0
      src/types/TVisitorDirection.d.ts
  83. 0 3
      src/types/custom-nodes/TStringArrayCallsWrapper.d.ts
  84. 0 4
      src/types/custom-nodes/TStringArrayNode.d.ts
  85. 13 63
      test/dev/dev.ts
  86. 1 1
      test/fixtures/compile-performance.js
  87. 0 0
      test/fixtures/node-transformers/node-obfuscators/catch-clause-obfuscator/catch-clause-obfuscator.js
  88. 0 0
      test/fixtures/node-transformers/node-obfuscators/labeled-statement-obfuscator/labeled-statement-obfuscator.js
  89. 36 0
      test/functional-tests/JavaScriptObfuscator.spec.ts
  90. 17 17
      test/functional-tests/JavaScriptObfuscatorInternal.spec.ts
  91. 8 6
      test/functional-tests/node-transformers/node-obfuscators/CatchClauseObfuscator.spec.ts
  92. 3 3
      test/functional-tests/node-transformers/node-obfuscators/FunctionObfuscator.spec.ts
  93. 8 6
      test/functional-tests/node-transformers/node-obfuscators/LabeledStatementObfuscator.spec.ts
  94. 3 3
      test/functional-tests/node-transformers/node-obfuscators/LiteralObfuscator.spec.ts
  95. 3 3
      test/functional-tests/node-transformers/node-obfuscators/MemberExpressionObfuscator.spec.ts
  96. 3 3
      test/functional-tests/node-transformers/node-obfuscators/MethodDefinitionObfuscator.spec.ts
  97. 3 3
      test/functional-tests/node-transformers/node-obfuscators/ObjectExpressionObfuscator.spec.ts
  98. 3 3
      test/functional-tests/node-transformers/node-obfuscators/VariableDeclarationObfuscator.spec.ts
  99. 13 10
      test/functional-tests/stack-trace-analyzer/StackTraceAnalyzer.spec.ts
  100. 2 2
      test/functional-tests/templates/custom-nodes/domain-lock-nodes/DomainLockNodeTemplate.spec.ts

+ 5 - 0
CHANGELOG.md

@@ -1,5 +1,10 @@
 Change Log
 Change Log
 ===
 ===
+v0.8.2
+---
+* New option `seed` sets seed for random generator. This is useful for creating repeatable results.
+* IE8 runtime error fix.
+
 v0.8.1
 v0.8.1
 ---
 ---
 * `disableConsoleOutput` option now replaces `console.xxx` functions on empty function instead of infinity loop.
 * `disableConsoleOutput` option now replaces `console.xxx` functions on empty function instead of infinity loop.

+ 9 - 1
README.md

@@ -123,6 +123,7 @@ Following options available for the JS Obfuscator:
     disableConsoleOutput: true,
     disableConsoleOutput: true,
     reservedNames: [],
     reservedNames: [],
     rotateStringArray: true,
     rotateStringArray: true,
+    seed: 0,
     selfDefending: true,
     selfDefending: true,
     sourceMap: false,
     sourceMap: false,
     sourceMapBaseUrl: '',
     sourceMapBaseUrl: '',
@@ -131,7 +132,7 @@ Following options available for the JS Obfuscator:
     stringArray: true,
     stringArray: true,
     stringArrayEncoding: false,
     stringArrayEncoding: false,
     stringArrayThreshold: 0.8,
     stringArrayThreshold: 0.8,
-    unicdeEscapeSequence: true
+    unicodeEscapeSequence: true
 }
 }
 ```
 ```
 
 
@@ -148,6 +149,7 @@ Following options available for the JS Obfuscator:
     --disableConsoleOutput <boolean>
     --disableConsoleOutput <boolean>
     --reservedNames <list> (comma separated)
     --reservedNames <list> (comma separated)
     --rotateStringArray <boolean>
     --rotateStringArray <boolean>
+    --seed <number>
     --selfDefending <boolean>
     --selfDefending <boolean>
     --sourceMap <boolean>
     --sourceMap <boolean>
     --sourceMapBaseUrl <string>
     --sourceMapBaseUrl <string>
@@ -218,6 +220,12 @@ Shift the `stringArray` array by a fixed and random (generated at the code obfus
 
 
 This option is recommended if your original source code isn't small, as the helper function can attract attention.
 This option is recommended if your original source code isn't small, as the helper function can attract attention.
 
 
+### `seed`
+Type: `number` Default: `0`
+
+This option sets seed for random generator. This is useful for creating repeatable results.
+
+If seed is `0` - random generator will work without seed.
 
 
 ### `selfDefending`
 ### `selfDefending`
 Type: `boolean` Default: `true`
 Type: `boolean` Default: `true`

File diff suppressed because it is too large
+ 420 - 290
dist/index.js


+ 11 - 10
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "javascript-obfuscator",
   "name": "javascript-obfuscator",
-  "version": "0.8.1",
+  "version": "0.8.2",
   "description": "JavaScript obfuscator",
   "description": "JavaScript obfuscator",
   "keywords": [
   "keywords": [
     "obfuscator",
     "obfuscator",
@@ -27,27 +27,28 @@
     "escodegen": "1.8.1",
     "escodegen": "1.8.1",
     "esprima": "3.1.1",
     "esprima": "3.1.1",
     "estraverse": "4.2.0",
     "estraverse": "4.2.0",
-    "format-unicorn": "1.1.0",
+    "is-equal": "^1.5.3",
     "mkdirp": "0.5.1",
     "mkdirp": "0.5.1",
-    "source-map-support": "0.4.6"
+    "source-map-support": "0.4.6",
+    "string-template": "^1.0.0"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@types/chai": "3.4.34",
     "@types/chai": "3.4.34",
     "@types/chance": "0.7.31",
     "@types/chance": "0.7.31",
-    "@types/commander": "2.3.30",
+    "@types/commander": "2.3.31",
     "@types/escodegen": "0.0.6",
     "@types/escodegen": "0.0.6",
     "@types/esprima": "2.1.33",
     "@types/esprima": "2.1.33",
     "@types/estraverse": "0.0.6",
     "@types/estraverse": "0.0.6",
     "@types/estree": "0.0.34",
     "@types/estree": "0.0.34",
-    "@types/format-unicorn": "0.0.29",
     "@types/joi": "9.0.33",
     "@types/joi": "9.0.33",
     "@types/mkdirp": "0.3.29",
     "@types/mkdirp": "0.3.29",
     "@types/mocha": "2.2.33",
     "@types/mocha": "2.2.33",
-    "@types/node": "6.0.47",
-    "@types/sinon": "1.16.31",
+    "@types/node": "6.0.49",
+    "@types/sinon": "1.16.32",
+    "@types/string-template": "^1.0.2",
     "awesome-typescript-loader": "2.2.4",
     "awesome-typescript-loader": "2.2.4",
     "babel-cli": "6.18.0",
     "babel-cli": "6.18.0",
-    "babel-loader": "6.2.7",
+    "babel-loader": "6.2.8",
     "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",
@@ -55,9 +56,9 @@
     "mocha": "3.1.2",
     "mocha": "3.1.2",
     "sinon": "2.0.0-pre.3",
     "sinon": "2.0.0-pre.3",
     "ts-node": "1.7.0",
     "ts-node": "1.7.0",
-    "tslint": "3.15.1",
+    "tslint": "4.0.1",
     "typescript": "2.0.10",
     "typescript": "2.0.10",
-    "webpack": "2.1.0-beta.25",
+    "webpack": "2.1.0-beta.27",
     "webpack-node-externals": "1.5.4"
     "webpack-node-externals": "1.5.4"
   },
   },
   "repository": {
   "repository": {

+ 4 - 6
src/JavaScriptObfuscator.ts

@@ -3,6 +3,7 @@ import { IObfuscatorOptions } from './interfaces/IObfuscatorOptions';
 
 
 import { JavaScriptObfuscatorCLI } from './cli/JavaScriptObfuscatorCLI';
 import { JavaScriptObfuscatorCLI } from './cli/JavaScriptObfuscatorCLI';
 import { JavaScriptObfuscatorInternal } from './JavaScriptObfuscatorInternal';
 import { JavaScriptObfuscatorInternal } from './JavaScriptObfuscatorInternal';
+import { Options } from './options/Options';
 
 
 export class JavaScriptObfuscator {
 export class JavaScriptObfuscator {
     /**
     /**
@@ -11,14 +12,11 @@ export class JavaScriptObfuscator {
      * @returns {string}
      * @returns {string}
      */
      */
     public static obfuscate (sourceCode: string, obfuscatorOptions: IObfuscatorOptions = {}): IObfuscationResult {
     public static obfuscate (sourceCode: string, obfuscatorOptions: IObfuscatorOptions = {}): IObfuscationResult {
-        let javaScriptObfuscator: JavaScriptObfuscatorInternal = new JavaScriptObfuscatorInternal(
-            sourceCode,
-            obfuscatorOptions
+        const javaScriptObfuscator: JavaScriptObfuscatorInternal = new JavaScriptObfuscatorInternal(
+            new Options(obfuscatorOptions)
         );
         );
 
 
-        javaScriptObfuscator.obfuscate();
-
-        return javaScriptObfuscator.getObfuscationResult();
+        return javaScriptObfuscator.obfuscate(sourceCode);
     }
     }
 
 
     /**
     /**

+ 33 - 26
src/JavaScriptObfuscatorInternal.ts

@@ -2,47 +2,43 @@ 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 { IObfuscatorOptions } from './interfaces/IObfuscatorOptions';
+import { Chance } from 'chance';
+
 import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
 import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
 import { IOptions } from './interfaces/IOptions';
 import { IOptions } from './interfaces/IOptions';
 
 
 import { ObfuscationResult } from './ObfuscationResult';
 import { ObfuscationResult } from './ObfuscationResult';
 import { Obfuscator } from './Obfuscator';
 import { Obfuscator } from './Obfuscator';
-import { Options } from './options/Options';
 import { SourceMapCorrector } from './SourceMapCorrector';
 import { SourceMapCorrector } from './SourceMapCorrector';
+import { Utils } from './Utils';
 
 
 export class JavaScriptObfuscatorInternal {
 export class JavaScriptObfuscatorInternal {
     /**
     /**
      * @type {GenerateOptions}
      * @type {GenerateOptions}
      */
      */
-    private static escodegenParams: escodegen.GenerateOptions = {
+    private static readonly escodegenParams: escodegen.GenerateOptions = {
         verbatim: 'x-verbatim-property',
         verbatim: 'x-verbatim-property',
         sourceMapWithCode: true
         sourceMapWithCode: true
     };
     };
 
 
     /**
     /**
-     * @type {IGeneratorOutput}
+     * @type {esprima.Options}
      */
      */
-    private generatorOutput: IGeneratorOutput;
+    private static readonly esprimaParams: esprima.Options = {
+        loc: true
+    };
 
 
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
-    private options: IOptions;
+    private readonly options: IOptions;
 
 
     /**
     /**
-     * @type {string}
-     */
-    private sourceCode: string;
-
-    /**
-     * @param sourceCode
-     * @param obfuscatorOptions
+     * @param options
      */
      */
-    constructor (sourceCode: string, obfuscatorOptions: IObfuscatorOptions = {}) {
-        this.sourceCode = sourceCode;
-        this.options = new Options(obfuscatorOptions);
+    constructor (options: IOptions) {
+        this.options = options;
     }
     }
 
 
     /**
     /**
@@ -50,7 +46,7 @@ export class JavaScriptObfuscatorInternal {
      * @param astTree
      * @param astTree
      * @param options
      * @param options
      */
      */
-    private static generateCode (sourceCode: string, astTree: ESTree.Node, options: IOptions): IGeneratorOutput {
+    private static generateCode (sourceCode: string, astTree: ESTree.Program, options: IOptions): IGeneratorOutput {
         const escodegenParams: escodegen.GenerateOptions = Object.assign(
         const escodegenParams: escodegen.GenerateOptions = Object.assign(
             {},
             {},
             JavaScriptObfuscatorInternal.escodegenParams
             JavaScriptObfuscatorInternal.escodegenParams
@@ -73,26 +69,37 @@ export class JavaScriptObfuscatorInternal {
     }
     }
 
 
     /**
     /**
+     * @param generatorOutput
      * @returns {IObfuscationResult}
      * @returns {IObfuscationResult}
      */
      */
-    public getObfuscationResult (): IObfuscationResult {
+    public getObfuscationResult (generatorOutput: IGeneratorOutput): IObfuscationResult {
         return new SourceMapCorrector(
         return new SourceMapCorrector(
             new ObfuscationResult(
             new ObfuscationResult(
-                this.generatorOutput.code,
-                this.generatorOutput.map
+                generatorOutput.code,
+                generatorOutput.map
             ),
             ),
             this.options.sourceMapBaseUrl + this.options.sourceMapFileName,
             this.options.sourceMapBaseUrl + this.options.sourceMapFileName,
             this.options.sourceMapMode
             this.options.sourceMapMode
         ).correct();
         ).correct();
     }
     }
 
 
-    public obfuscate (): void {
-        let astTree: ESTree.Node = esprima.parse(this.sourceCode, {
-            loc: true
-        });
+    /**
+     * @param sourceCode
+     * @returns {IObfuscationResult}
+     */
+    public obfuscate (sourceCode: string): IObfuscationResult {
+        if (this.options.seed !== 0) {
+            Utils.setRandomGenerator(new Chance(this.options.seed));
+        }
 
 
-        astTree = new Obfuscator(this.options).obfuscateNode(astTree);
+        const astTree: ESTree.Program = esprima.parse(sourceCode, JavaScriptObfuscatorInternal.esprimaParams);
+        const obfuscatedAstTree: ESTree.Program = new Obfuscator(this.options).obfuscateAstTree(astTree);
+        const generatorOutput: IGeneratorOutput = JavaScriptObfuscatorInternal.generateCode(
+            sourceCode,
+            obfuscatedAstTree,
+            this.options
+        );
 
 
-        this.generatorOutput = JavaScriptObfuscatorInternal.generateCode(this.sourceCode, astTree, this.options);
+        return this.getObfuscationResult(generatorOutput);
     }
     }
 }
 }

+ 58 - 77
src/Obfuscator.ts

@@ -2,39 +2,34 @@ import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { TNodeGroup } from './types/TNodeGroup';
 import { TNodeGroup } from './types/TNodeGroup';
-import { TNodeObfuscator } from './types/TNodeObfuscator';
+import { TVisitorDirection } from './types/TVisitorDirection';
 
 
 import { ICustomNode } from './interfaces/custom-nodes/ICustomNode';
 import { ICustomNode } from './interfaces/custom-nodes/ICustomNode';
 import { IObfuscator } from './interfaces/IObfuscator';
 import { IObfuscator } from './interfaces/IObfuscator';
 import { IOptions } from './interfaces/IOptions';
 import { IOptions } from './interfaces/IOptions';
+import { INodeTransformer } from './interfaces/INodeTransformer';
+import { INodeTransformersFactory } from './interfaces/INodeTransformersFactory';
 import { IStackTraceData } from './interfaces/stack-trace-analyzer/IStackTraceData';
 import { IStackTraceData } from './interfaces/stack-trace-analyzer/IStackTraceData';
 
 
 import { AppendState } from './enums/AppendState';
 import { AppendState } from './enums/AppendState';
-import { NodeType } from './enums/NodeType';
+import { VisitorDirection } from './enums/VisitorDirection';
 
 
-import { CatchClauseObfuscator } from './node-obfuscators/CatchClauseObfuscator';
 import { ConsoleOutputNodesGroup } from './node-groups/ConsoleOutputNodesGroup';
 import { ConsoleOutputNodesGroup } from './node-groups/ConsoleOutputNodesGroup';
 import { DebugProtectionNodesGroup } from './node-groups/DebugProtectionNodesGroup';
 import { DebugProtectionNodesGroup } from './node-groups/DebugProtectionNodesGroup';
 import { DomainLockNodesGroup } from './node-groups/DomainLockNodesGroup';
 import { DomainLockNodesGroup } from './node-groups/DomainLockNodesGroup';
-import { FunctionDeclarationObfuscator } from './node-obfuscators/FunctionDeclarationObfuscator';
-import { FunctionObfuscator } from './node-obfuscators/FunctionObfuscator';
-import { LabeledStatementObfuscator } from './node-obfuscators/LabeledStatementObfuscator';
-import { LiteralObfuscator } from './node-obfuscators/LiteralObfuscator';
-import { MemberExpressionObfuscator } from './node-obfuscators/MemberExpressionObfuscator';
-import { MethodDefinitionObfuscator } from './node-obfuscators/MethodDefinitionObfuscator';
 import { Node } from './node/Node';
 import { Node } from './node/Node';
+import { NodeControlFlowTransformersFactory } from './node-transformers/node-control-flow-transformers/factory/NodeControlFlowTransformersFactory';
+import { NodeObfuscatorsFactory } from './node-transformers/node-obfuscators/factory/NodeObfuscatorsFactory';
 import { NodeUtils } from './node/NodeUtils';
 import { NodeUtils } from './node/NodeUtils';
-import { ObjectExpressionObfuscator } from './node-obfuscators/ObjectExpressionObfuscator';
 import { SelfDefendingNodesGroup } from './node-groups/SelfDefendingNodesGroup';
 import { SelfDefendingNodesGroup } from './node-groups/SelfDefendingNodesGroup';
 import { StackTraceAnalyzer } from './stack-trace-analyzer/StackTraceAnalyzer';
 import { StackTraceAnalyzer } from './stack-trace-analyzer/StackTraceAnalyzer';
 import { StringArrayNodesGroup } from './node-groups/StringArrayNodesGroup';
 import { StringArrayNodesGroup } from './node-groups/StringArrayNodesGroup';
-import { VariableDeclarationObfuscator } from './node-obfuscators/VariableDeclarationObfuscator';
 
 
 export class Obfuscator implements IObfuscator {
 export class Obfuscator implements IObfuscator {
     /**
     /**
      * @type {TNodeGroup[]}
      * @type {TNodeGroup[]}
      */
      */
-    private static nodeGroups: TNodeGroup[] = [
+    private static readonly nodeGroups: TNodeGroup[] = [
         DomainLockNodesGroup,
         DomainLockNodesGroup,
         SelfDefendingNodesGroup,
         SelfDefendingNodesGroup,
         ConsoleOutputNodesGroup,
         ConsoleOutputNodesGroup,
@@ -42,35 +37,15 @@ export class Obfuscator implements IObfuscator {
         StringArrayNodesGroup
         StringArrayNodesGroup
     ];
     ];
 
 
-    /**
-     * @type {Map<string, TNodeObfuscator[]>}
-     */
-    private static nodeObfuscators: Map <string, TNodeObfuscator[]> = new Map <string, TNodeObfuscator[]> ([
-        [NodeType.ArrowFunctionExpression, [FunctionObfuscator]],
-        [NodeType.ClassDeclaration, [FunctionDeclarationObfuscator]],
-        [NodeType.CatchClause, [CatchClauseObfuscator]],
-        [NodeType.FunctionDeclaration, [
-            FunctionDeclarationObfuscator,
-            FunctionObfuscator
-        ]],
-        [NodeType.FunctionExpression, [FunctionObfuscator]],
-        [NodeType.MemberExpression, [MemberExpressionObfuscator]],
-        [NodeType.MethodDefinition, [MethodDefinitionObfuscator]],
-        [NodeType.ObjectExpression, [ObjectExpressionObfuscator]],
-        [NodeType.VariableDeclaration, [VariableDeclarationObfuscator]],
-        [NodeType.LabeledStatement, [LabeledStatementObfuscator]],
-        [NodeType.Literal, [LiteralObfuscator]]
-    ]);
-
     /**
     /**
      * @type {Map<string, AbstractCustomNode>}
      * @type {Map<string, AbstractCustomNode>}
      */
      */
-    private customNodes: Map <string, ICustomNode> = new Map <string, ICustomNode> ();
+    private customNodes: Map <string, ICustomNode>;
 
 
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
-    private options: IOptions;
+    private readonly options: IOptions;
 
 
     /**
     /**
      * @param options
      * @param options
@@ -80,34 +55,47 @@ export class Obfuscator implements IObfuscator {
     }
     }
 
 
     /**
     /**
-     * @param node
-     * @returns {ESTree.Node}
+     * @param astTree
+     * @returns {ESTree.Program}
      */
      */
-    public obfuscateNode (node: ESTree.Program): ESTree.Node {
-        if (Node.isProgramNode(node) && !node.body.length) {
-            return node;
+    public obfuscateAstTree (astTree: ESTree.Program): ESTree.Program {
+        if (Node.isProgramNode(astTree) && !astTree.body.length) {
+            return astTree;
         }
         }
 
 
-        NodeUtils.parentize(node);
+        NodeUtils.parentize(astTree);
+        this.initializeCustomNodes(new StackTraceAnalyzer().analyze(astTree.body));
 
 
-        const stackTraceData: IStackTraceData[] = new StackTraceAnalyzer(node.body).analyze();
+        // tasks before nodes transformation
+        this.beforeTransform(astTree);
 
 
-        this.initializeCustomNodes(stackTraceData);
+        // first pass: control flow flattening
+        if (this.options.controlFlowFlattening) {
+            this.transformAstTree(astTree, VisitorDirection.leave, new NodeControlFlowTransformersFactory(
+                this.customNodes,
+                this.options
+            ));
+        }
+
+        // second pass: nodes obfuscation
+        this.transformAstTree(astTree, VisitorDirection.enter, new NodeObfuscatorsFactory(
+            this.customNodes,
+            this.options
+        ));
 
 
-        this.beforeObfuscation(node);
-        this.obfuscate(node);
-        this.afterObfuscation(node);
+        // tasks after nodes transformation
+        this.afterTransform(astTree);
 
 
-        return node;
+        return astTree;
     }
     }
 
 
     /**
     /**
      * @param astTree
      * @param astTree
      */
      */
-    private afterObfuscation (astTree: ESTree.Node): void {
-        this.customNodes.forEach((node: ICustomNode) => {
-            if (node.getAppendState() === AppendState.AfterObfuscation) {
-                node.appendNode(astTree);
+    private afterTransform (astTree: ESTree.Program): void {
+        this.customNodes.forEach((customNode: ICustomNode) => {
+            if (customNode.getAppendState() === AppendState.AfterObfuscation) {
+                customNode.appendNode(astTree);
             }
             }
         });
         });
     }
     }
@@ -115,10 +103,10 @@ export class Obfuscator implements IObfuscator {
     /**
     /**
      * @param astTree
      * @param astTree
      */
      */
-    private beforeObfuscation (astTree: ESTree.Node): void {
-        this.customNodes.forEach((node: ICustomNode) => {
-            if (node.getAppendState() === AppendState.BeforeObfuscation) {
-                node.appendNode(astTree);
+    private beforeTransform (astTree: ESTree.Program): void {
+        this.customNodes.forEach((customNode: ICustomNode) => {
+            if (customNode.getAppendState() === AppendState.BeforeObfuscation) {
+                customNode.appendNode(astTree);
             }
             }
         });
         });
     };
     };
@@ -127,7 +115,7 @@ export class Obfuscator implements IObfuscator {
      * @param stackTraceData
      * @param stackTraceData
      */
      */
     private initializeCustomNodes (stackTraceData: IStackTraceData[]): void {
     private initializeCustomNodes (stackTraceData: IStackTraceData[]): void {
-        let customNodes: [string, ICustomNode][] = [];
+        const customNodes: [string, ICustomNode][] = [];
 
 
         Obfuscator.nodeGroups.forEach((nodeGroupConstructor: TNodeGroup) => {
         Obfuscator.nodeGroups.forEach((nodeGroupConstructor: TNodeGroup) => {
             const nodeGroupNodes: Map <string, ICustomNode> | undefined = new nodeGroupConstructor(
             const nodeGroupNodes: Map <string, ICustomNode> | undefined = new nodeGroupConstructor(
@@ -144,30 +132,23 @@ export class Obfuscator implements IObfuscator {
         this.customNodes = new Map <string, ICustomNode> (customNodes);
         this.customNodes = new Map <string, ICustomNode> (customNodes);
     }
     }
 
 
-
-    /**
-     * @param node
-     * @param parentNode
-     */
-    private initializeNodeObfuscators (node: ESTree.Node, parentNode: ESTree.Node): void {
-        let nodeObfuscators: TNodeObfuscator[] | undefined = Obfuscator.nodeObfuscators.get(node.type);
-
-        if (!nodeObfuscators) {
-            return;
-        }
-
-        nodeObfuscators.forEach((obfuscator: TNodeObfuscator) => {
-            new obfuscator(this.customNodes, this.options).obfuscateNode(node, parentNode);
-        });
-    }
-
     /**
     /**
-     * @param node
+     * @param astTree
+     * @param direction
+     * @param nodeTransformersFactory
      */
      */
-    private obfuscate (node: ESTree.Node): void {
-        estraverse.traverse(node, {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node): void => {
-                this.initializeNodeObfuscators(node, parentNode);
+    private transformAstTree (
+        astTree: ESTree.Program,
+        direction: TVisitorDirection,
+        nodeTransformersFactory: INodeTransformersFactory
+    ): void {
+        estraverse.traverse(astTree, {
+            [direction]: (node: ESTree.Node, parentNode: ESTree.Node): void => {
+                const nodeTransformers: INodeTransformer[] = nodeTransformersFactory.initializeNodeTransformers(node.type);
+
+                nodeTransformers.forEach((nodeTransformer: INodeTransformer) => {
+                    nodeTransformer.transformNode(node, parentNode);
+                });
             }
             }
         });
         });
     }
     }

+ 4 - 4
src/SourceMapCorrector.ts

@@ -12,22 +12,22 @@ export class SourceMapCorrector implements ISourceMapCorrector {
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
-    private obfuscatedCode: string;
+    private readonly obfuscatedCode: string;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
-    private sourceMap: string;
+    private readonly sourceMap: string;
 
 
     /**
     /**
      * @type {TSourceMapMode}
      * @type {TSourceMapMode}
      */
      */
-    private sourceMapMode: TSourceMapMode;
+    private readonly sourceMapMode: TSourceMapMode;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
-    private sourceMapUrl: string;
+    private readonly sourceMapUrl: string;
 
 
     /**
     /**
      * @param obfuscationResult
      * @param obfuscationResult

+ 0 - 53
src/StringArray.ts

@@ -1,53 +0,0 @@
-import { Utils } from './Utils';
-
-export class StringArray {
-    /**
-     * @type {string[]}
-     */
-    private array: string[] = [];
-
-    /**
-     * @param value
-     */
-    public addToArray (value: string): void {
-        this.array.push(value);
-    }
-
-    /**
-     * @returns {string[]}
-     */
-    public getArray (): string[] {
-        return this.array;
-    }
-
-    /**
-     * @param value
-     * @returns {number}
-     */
-    public getIndexOf(value: string): number {
-        return this.array.indexOf(value);
-    }
-
-    /**
-     * @returns {number}
-     */
-    public getLength (): number {
-        return this.array.length;
-    }
-
-    /**
-     * @param rotationValue
-     */
-    public rotateArray (rotationValue: number): void {
-        this.array = Utils.arrayRotate(this.array, rotationValue);
-    }
-
-    /**
-     * @returns {string}
-     */
-    public toString (): string {
-        return this.array.map((value: string) => {
-            return `'${value}'`;
-        }).toString()
-    }
-}

+ 73 - 13
src/Utils.ts

@@ -2,11 +2,18 @@ import { Chance } from 'chance';
 
 
 import { JSFuck } from './enums/JSFuck';
 import { JSFuck } from './enums/JSFuck';
 
 
+const isEqual = require('is-equal');
+
 export class Utils {
 export class Utils {
     /**
     /**
-     * @type {Chance.Chance}
+     * @type {string}
+     */
+    public static readonly randomGeneratorPool: string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+    /**
+     * @type {Chance.Chance | Chance.SeededChance}
      */
      */
-    private static randomGenerator: Chance.Chance = new Chance();
+    private static randomGenerator: Chance.Chance | Chance.SeededChance = new Chance();
 
 
     /**
     /**
      * @param array
      * @param array
@@ -99,13 +106,44 @@ export class Utils {
         return domain;
         return domain;
     }
     }
 
 
+    /**
+     * @param min
+     * @param max
+     * @returns {number}
+     */
+    public static getRandomFloat (min: number, max: number): number {
+        return Utils.getRandomGenerator().floating({
+            min: min,
+            max: max,
+            fixed: 7
+        });
+    }
+
     /**
     /**
      * @returns {Chance.Chance}
      * @returns {Chance.Chance}
      */
      */
     public static getRandomGenerator (): Chance.Chance {
     public static getRandomGenerator (): Chance.Chance {
+        const randomGenerator: Chance.Chance = Utils.randomGenerator;
+
+        if (!randomGenerator) {
+            throw new Error(`\`randomGenerator\` static property is undefined`);
+        }
+
         return Utils.randomGenerator;
         return Utils.randomGenerator;
     }
     }
 
 
+    /**
+     * @param min
+     * @param max
+     * @returns {number}
+     */
+    public static getRandomInteger (min: number, max: number): number {
+        return Utils.getRandomGenerator().integer({
+            min: min,
+            max: max
+        });
+    }
+
     /**
     /**
      * @param length
      * @param length
      * @returns {string}
      * @returns {string}
@@ -117,10 +155,7 @@ export class Utils {
 
 
         return `${prefix}${(
         return `${prefix}${(
             Utils.decToHex(
             Utils.decToHex(
-                Utils.getRandomGenerator().integer({
-                    min: rangeMinInteger,
-                    max: rangeMaxInteger
-                })
+                Utils.getRandomInteger(rangeMinInteger, rangeMaxInteger)
             )
             )
         ).substr(0, length)}`;
         ).substr(0, length)}`;
     }
     }
@@ -140,7 +175,7 @@ export class Utils {
                 result: string = '';
                 result: string = '';
 
 
             while (i1 < s1.length || i2 < s2.length) {
             while (i1 < s1.length || i2 < s2.length) {
-                if (Math.random() < 0.5 && i2 < s2.length) {
+                if (Utils.getRandomFloat(0, 1) < 0.5 && i2 < s2.length) {
                     result += s2.charAt(++i2);
                     result += s2.charAt(++i2);
                 } else {
                 } else {
                     result += s1.charAt(++i1);
                     result += s1.charAt(++i1);
@@ -150,13 +185,16 @@ export class Utils {
             return result;
             return result;
         };
         };
 
 
-        const customPool: string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+        const randomString: string = Utils.randomGenerator.string({
+            length: length,
+            pool: Utils.randomGeneratorPool
+        });
+
+        let randomStringDiff: string = randomString.replace(
+            new RegExp('[' + escapeRegExp(str) + ']', 'g'),
+        '');
 
 
-        let randomString: string = Utils.randomGenerator.string({length: length, pool: customPool}),
-            randomStringDiff: string = randomString.replace(
-                new RegExp('[' + escapeRegExp(str) + ']', 'g'),
-            ''),
-            randomStringDiffArray: string[] = randomStringDiff.split('');
+        const randomStringDiffArray: string[] = randomStringDiff.split('');
 
 
         Utils.randomGenerator.shuffle(randomStringDiffArray);
         Utils.randomGenerator.shuffle(randomStringDiffArray);
         randomStringDiff = randomStringDiffArray.join('');
         randomStringDiff = randomStringDiffArray.join('');
@@ -173,6 +211,21 @@ export class Utils {
         return number % 1 === 0;
         return number % 1 === 0;
     }
     }
 
 
+    /**
+     * @param map
+     * @param value
+     * @returns {any}
+     */
+    public static mapGetFirstKeyOf(map: Map <any, any>, value: any): any {
+        for (var [key, storageValue] of map) {
+            if (isEqual(value, storageValue)) {
+                return key;
+            }
+        }
+
+        return null;
+    }
+
     /**
     /**
      * RC4 symmetric cipher encryption/decryption
      * RC4 symmetric cipher encryption/decryption
      * https://gist.github.com/farhadi/2185197
      * https://gist.github.com/farhadi/2185197
@@ -213,6 +266,13 @@ export class Utils {
         return result;
         return result;
     }
     }
 
 
+    /**
+     * @param randomGenerator
+     */
+    public static setRandomGenerator (randomGenerator: Chance.Chance | Chance.SeededChance): void {
+        Utils.randomGenerator = randomGenerator;
+    }
+
     /**
     /**
      * @param obj
      * @param obj
      * @returns {T}
      * @returns {T}

+ 2 - 2
src/cli/CLIUtils.ts

@@ -10,14 +10,14 @@ export class CLIUtils {
     /**
     /**
      * @type {string[]}
      * @type {string[]}
      */
      */
-    private static availableInputExtensions: string[] = [
+    private static readonly availableInputExtensions: string[] = [
         '.js'
         '.js'
     ];
     ];
 
 
     /**
     /**
      * @type {BufferEncoding}
      * @type {BufferEncoding}
      */
      */
-    private static encoding: BufferEncoding = 'utf8';
+    private static readonly encoding: BufferEncoding = 'utf8';
 
 
     /**
     /**
      * @param outputPath
      * @param outputPath

+ 10 - 8
src/cli/JavaScriptObfuscatorCLI.ts

@@ -19,7 +19,7 @@ export class JavaScriptObfuscatorCLI {
     /**
     /**
      * @type {string[]}
      * @type {string[]}
      */
      */
-    private arguments: string[];
+    private readonly arguments: string[];
 
 
     /**
     /**
      * @type {commander.ICommand}
      * @type {commander.ICommand}
@@ -69,7 +69,7 @@ export class JavaScriptObfuscatorCLI {
      * @returns {string}
      * @returns {string}
      */
      */
     private static parseSourceMapMode (value: string): string {
     private static parseSourceMapMode (value: string): string {
-        let availableMode: boolean = Object
+        const availableMode: boolean = Object
             .keys(SourceMapMode)
             .keys(SourceMapMode)
             .some((key: string): boolean => {
             .some((key: string): boolean => {
                 return SourceMapMode[key] === value;
                 return SourceMapMode[key] === value;
@@ -121,8 +121,8 @@ export class JavaScriptObfuscatorCLI {
      * @returns {IObfuscatorOptions}
      * @returns {IObfuscatorOptions}
      */
      */
     private buildOptions (): IObfuscatorOptions {
     private buildOptions (): IObfuscatorOptions {
-        let obfuscatorOptions: IObfuscatorOptions = {},
-            availableOptions: string[] = Object.keys(DEFAULT_PRESET);
+        const obfuscatorOptions: IObfuscatorOptions = {};
+        const availableOptions: string[] = Object.keys(DEFAULT_PRESET);
 
 
         for (const option in this.commands) {
         for (const option in this.commands) {
             if (!this.commands.hasOwnProperty(option)) {
             if (!this.commands.hasOwnProperty(option)) {
@@ -145,12 +145,14 @@ export class JavaScriptObfuscatorCLI {
             .usage('<inputPath> [options]')
             .usage('<inputPath> [options]')
             .option('-o, --output <path>', 'Output path for obfuscated code')
             .option('-o, --output <path>', 'Output path for obfuscated code')
             .option('--compact <boolean>', 'Disable one line output code compacting', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--compact <boolean>', 'Disable one line output code compacting', JavaScriptObfuscatorCLI.parseBoolean)
+            .option('--controlFlowFlattening <boolean>', 'Enables control flow flattening', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--debugProtection <boolean>', 'Disable browser Debug panel (can cause DevTools enabled browser freeze)', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--debugProtection <boolean>', 'Disable browser Debug panel (can cause DevTools enabled browser freeze)', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--debugProtectionInterval <boolean>', 'Disable browser Debug panel even after page was loaded (can cause DevTools enabled browser freeze)', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--debugProtectionInterval <boolean>', 'Disable browser Debug panel even after page was loaded (can cause DevTools enabled browser freeze)', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--disableConsoleOutput <boolean>', 'Allow console.log, console.info, console.error and console.warn messages output into browser console', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--disableConsoleOutput <boolean>', 'Allow console.log, console.info, console.error and console.warn messages output into browser console', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--domainLock <list>', 'Blocks the execution of the code in domains that do not match the passed RegExp patterns (comma separated)', (val: string) => val.split(','))
             .option('--domainLock <list>', 'Blocks the execution of the code in domains that do not match the passed RegExp patterns (comma separated)', (val: string) => val.split(','))
             .option('--reservedNames <list>', 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)', (val: string) => val.split(','))
             .option('--reservedNames <list>', 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)', (val: string) => val.split(','))
             .option('--rotateStringArray <boolean>', 'Disable rotation of unicode array values during obfuscation', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--rotateStringArray <boolean>', 'Disable rotation of unicode array values during obfuscation', JavaScriptObfuscatorCLI.parseBoolean)
+            .option('--seed <number>', 'Sets seed for random generator. This is useful for creating repeatable results.', parseFloat)
             .option('--selfDefending <boolean>', 'Disables self-defending for obfuscated code', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--selfDefending <boolean>', 'Disables self-defending for obfuscated code', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--sourceMap <boolean>', 'Enables source map generation', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--sourceMap <boolean>', 'Enables source map generation', JavaScriptObfuscatorCLI.parseBoolean)
             .option('--sourceMapBaseUrl <string>', 'Sets base url to the source map import url when `--sourceMapMode=separate`')
             .option('--sourceMapBaseUrl <string>', 'Sets base url to the source map import url when `--sourceMapMode=separate`')
@@ -179,8 +181,8 @@ export class JavaScriptObfuscatorCLI {
     }
     }
 
 
     private processData (): void {
     private processData (): void {
-        let options: IObfuscatorOptions = this.buildOptions(),
-            outputCodePath: string = CLIUtils.getOutputCodePath((<any>this.commands).output, this.inputPath);
+        const options: IObfuscatorOptions = this.buildOptions();
+        const outputCodePath: string = CLIUtils.getOutputCodePath((<any>this.commands).output, this.inputPath);
 
 
         if (options.sourceMap) {
         if (options.sourceMap) {
             this.processDataWithSourceMap(outputCodePath, options);
             this.processDataWithSourceMap(outputCodePath, options);
@@ -194,7 +196,7 @@ export class JavaScriptObfuscatorCLI {
      * @param options
      * @param options
      */
      */
     private processDataWithoutSourceMap (outputCodePath: string, options: IObfuscatorOptions): void {
     private processDataWithoutSourceMap (outputCodePath: string, options: IObfuscatorOptions): void {
-        let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(this.data, options).getObfuscatedCode();
+        const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(this.data, options).getObfuscatedCode();
 
 
         CLIUtils.writeFile(outputCodePath, obfuscatedCode);
         CLIUtils.writeFile(outputCodePath, obfuscatedCode);
     }
     }
@@ -204,7 +206,7 @@ export class JavaScriptObfuscatorCLI {
      * @param options
      * @param options
      */
      */
     private processDataWithSourceMap (outputCodePath: string, options: IObfuscatorOptions): void {
     private processDataWithSourceMap (outputCodePath: string, options: IObfuscatorOptions): void {
-        let outputSourceMapPath: string = CLIUtils.getOutputSourceMapPath(
+        const outputSourceMapPath: string = CLIUtils.getOutputSourceMapPath(
             outputCodePath,
             outputCodePath,
             options.sourceMapFileName || ''
             options.sourceMapFileName || ''
         );
         );

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

@@ -5,6 +5,7 @@ import { IOptions } from '../interfaces/IOptions';
 import { TStatement } from '../types/TStatement';
 import { TStatement } from '../types/TStatement';
 
 
 import { AppendState } from '../enums/AppendState';
 import { AppendState } from '../enums/AppendState';
+import { NodeUtils } from '../node/NodeUtils';
 
 
 export abstract class AbstractCustomNode implements ICustomNode {
 export abstract class AbstractCustomNode implements ICustomNode {
     /**
     /**
@@ -15,7 +16,7 @@ export abstract class AbstractCustomNode implements ICustomNode {
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
-    protected options: IOptions;
+    protected readonly options: IOptions;
 
 
     /**
     /**
      * @param options
      * @param options
@@ -36,6 +37,11 @@ export abstract class AbstractCustomNode implements ICustomNode {
         return this.appendState;
         return this.appendState;
     }
     }
 
 
+    /**
+     * @returns {string}
+     */
+    public abstract getCode (): string;
+
     /**
     /**
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
@@ -53,5 +59,7 @@ export abstract class AbstractCustomNode implements ICustomNode {
     /**
     /**
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
-    protected abstract getNodeStructure (): TStatement[];
+    protected getNodeStructure (): TStatement[] {
+        return NodeUtils.convertCodeToStructure(this.getCode());
+    }
 }
 }

+ 7 - 24
src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts

@@ -1,7 +1,6 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
@@ -12,7 +11,6 @@ import { ConsoleOutputDisableExpressionTemplate } from '../../templates/custom-n
 
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 import { Utils } from '../../Utils';
 import { Utils } from '../../Utils';
 
 
 export class ConsoleOutputDisableExpressionNode extends AbstractCustomNode {
 export class ConsoleOutputDisableExpressionNode extends AbstractCustomNode {
@@ -68,27 +66,12 @@ export class ConsoleOutputDisableExpressionNode extends AbstractCustomNode {
     }
     }
 
 
     /**
     /**
-     *  JSCrush version of following code
-     *
-     *  (function () {
-     *      var _console = []["filter"]["constructor"]("return this")().console;
-     *      var _function = function () {};
-     *
-     *      _console.log = _function;
-     *      _console.info = _function;
-     *      _console.warn = _function;
-     *      _console.error = _function;
-     *  _console
-     *  })();
-     *
-     * @returns {TStatement[]}
+     * @returns {string}
      */
      */
-    protected getNodeStructure (): TStatement[] {
-        return NodeUtils.convertCodeToStructure(
-            ConsoleOutputDisableExpressionTemplate().formatUnicorn({
-                consoleLogDisableFunctionName: Utils.getRandomVariableName(),
-                singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-            })
-        );
+    public getCode (): string {
+        return format(ConsoleOutputDisableExpressionTemplate(), {
+            consoleLogDisableFunctionName: Utils.getRandomVariableName(),
+            singleNodeCallControllerFunctionName: this.callsControllerFunctionName
+        });
     }
     }
 }
 }

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

@@ -0,0 +1,49 @@
+import * as format from 'string-template';
+
+import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
+
+import { IOptions } from '../../../interfaces/IOptions';
+
+import { AppendState } from '../../../enums/AppendState';
+
+import { BinaryExpressionFunctionTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionTemplate';
+
+import { AbstractCustomNode } from '../../AbstractCustomNode';
+import { Utils } from '../../../Utils';
+
+export class BinaryExpressionFunctionNode extends AbstractCustomNode {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.BeforeObfuscation;
+
+    /**
+     * @type {string}
+     */
+    private operator: string;
+
+    /**
+     * @param operator
+     * @param options
+     */
+    constructor (operator: string, options: IOptions) {
+        super(options);
+
+        this.operator = operator;
+    }
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {}
+
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return format(BinaryExpressionFunctionTemplate(), {
+            functionName: Utils.getRandomVariableName(1),
+            operator: this.operator
+        });
+    }
+}

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

@@ -0,0 +1,80 @@
+import * as format from 'string-template';
+
+import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
+
+import { IOptions } from '../../../interfaces/IOptions';
+
+import { AppendState } from '../../../enums/AppendState';
+
+import { ControlFlowStorageCallTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallTemplate';
+
+import { AbstractCustomNode } from '../../AbstractCustomNode';
+import { NodeAppender } from '../../../node/NodeAppender';
+
+export class ControlFlowStorageCallNode extends AbstractCustomNode {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.AfterObfuscation;
+
+    /**
+     * @type {string}
+     */
+    private controlFlowStorageKey: string;
+
+    /**
+     * @type {string}
+     */
+    private controlFlowStorageName: string;
+
+    /**
+     * @type {string}
+     */
+    private leftValue: string;
+
+    /**
+     * @type {string}
+     */
+    private rightValue: string;
+
+    /**
+     * @param controlFlowStorageName
+     * @param controlFlowStorageKey
+     * @param leftValue
+     * @param rightValue
+     * @param options
+     */
+    constructor (
+        controlFlowStorageName: string,
+        controlFlowStorageKey: string,
+        leftValue: string,
+        rightValue: string,
+        options: IOptions
+    ) {
+        super(options);
+
+        this.controlFlowStorageName = controlFlowStorageName;
+        this.controlFlowStorageKey = controlFlowStorageKey;
+        this.leftValue = leftValue;
+        this.rightValue = rightValue;
+    }
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+        NodeAppender.prependNode(blockScopeNode, this.getNode());
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return format(ControlFlowStorageCallTemplate(), {
+            controlFlowStorageKey: this.controlFlowStorageKey,
+            controlFlowStorageName: this.controlFlowStorageName,
+            leftValue: this.leftValue,
+            rightValue: this.rightValue
+        });
+    }
+}

+ 64 - 0
src/custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts

@@ -0,0 +1,64 @@
+import * as format from 'string-template';
+
+import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
+
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/IOptions';
+import { IStorage } from '../../interfaces/IStorage';
+
+import { AppendState } from '../../enums/AppendState';
+
+import { ControlFlowStorageTemplate } from '../../templates/custom-nodes/control-flow-storage-nodes/ControlFlowStorageTemplate';
+
+import { AbstractCustomNode } from '../AbstractCustomNode';
+import { NodeAppender } from '../../node/NodeAppender';
+
+export class ControlFlowStorageNode extends AbstractCustomNode {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.AfterObfuscation;
+
+    /**
+     * @type {IStorage <ICustomNode>}
+     */
+    private controlFlowStorage: IStorage <ICustomNode>;
+
+    /**
+     * @type {string}
+     */
+    private controlFlowStorageName: string;
+
+    /**
+     * @param controlFlowStorage
+     * @param controlFlowStorageName
+     * @param options
+     */
+    constructor (
+        controlFlowStorage: IStorage <ICustomNode>,
+        controlFlowStorageName: string,
+        options: IOptions
+    ) {
+        super(options);
+
+        this.controlFlowStorage = controlFlowStorage;
+        this.controlFlowStorageName = controlFlowStorageName;
+    }
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+        NodeAppender.prependNode(blockScopeNode, this.getNode());
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return format(ControlFlowStorageTemplate(), {
+            controlFlowStorage: this.controlFlowStorage.toString(),
+            controlFlowStorageName: this.controlFlowStorageName
+        });
+    }
+}

+ 6 - 10
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts

@@ -1,7 +1,6 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
 
 
@@ -11,7 +10,6 @@ import { DebugProtectionFunctionCallTemplate } from '../../templates/custom-node
 
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 
 
 export class DebugProtectionFunctionCallNode extends AbstractCustomNode {
 export class DebugProtectionFunctionCallNode extends AbstractCustomNode {
     /**
     /**
@@ -42,13 +40,11 @@ export class DebugProtectionFunctionCallNode extends AbstractCustomNode {
     }
     }
 
 
     /**
     /**
-     * @returns {TStatement[]}
+     * @returns {string}
      */
      */
-    protected getNodeStructure (): TStatement[] {
-        return NodeUtils.convertCodeToStructure(
-            DebugProtectionFunctionCallTemplate().formatUnicorn({
-                debugProtectionFunctionName: this.debugProtectionFunctionName
-            })
-        );
+    public getCode (): string {
+        return format(DebugProtectionFunctionCallTemplate(), {
+            debugProtectionFunctionName: this.debugProtectionFunctionName
+        });
     }
     }
 }
 }

+ 6 - 10
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts

@@ -1,7 +1,6 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
 
 
@@ -11,7 +10,6 @@ import { DebugProtectionFunctionIntervalTemplate } from '../../templates/custom-
 
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 
 
 export class DebugProtectionFunctionIntervalNode extends AbstractCustomNode {
 export class DebugProtectionFunctionIntervalNode extends AbstractCustomNode {
     /**
     /**
@@ -42,13 +40,11 @@ export class DebugProtectionFunctionIntervalNode extends AbstractCustomNode {
     }
     }
 
 
     /**
     /**
-     * @returns {TStatement[]}
+     * @returns {string}
      */
      */
-    protected getNodeStructure (): TStatement[] {
-        return NodeUtils.convertCodeToStructure(
-            DebugProtectionFunctionIntervalTemplate().formatUnicorn({
-                debugProtectionFunctionName: this.debugProtectionFunctionName
-            })
-        );
+    public getCode (): string {
+        return format(DebugProtectionFunctionIntervalTemplate(), {
+            debugProtectionFunctionName: this.debugProtectionFunctionName
+        });
     }
     }
 }
 }

+ 6 - 22
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts

@@ -1,7 +1,6 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
 
 
@@ -11,7 +10,6 @@ import { DebugProtectionFunctionTemplate } from '../../templates/custom-nodes/de
 
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 import { Utils } from '../../Utils';
 import { Utils } from '../../Utils';
 
 
 export class DebugProtectionFunctionNode extends AbstractCustomNode {
 export class DebugProtectionFunctionNode extends AbstractCustomNode {
@@ -40,10 +38,7 @@ export class DebugProtectionFunctionNode extends AbstractCustomNode {
      */
      */
     public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
     public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
         let programBodyLength: number = blockScopeNode.body.length,
         let programBodyLength: number = blockScopeNode.body.length,
-            randomIndex: number = Utils.getRandomGenerator().integer({
-                min: 0,
-                max: programBodyLength
-            });
+            randomIndex: number = Utils.getRandomInteger(0, programBodyLength);
 
 
         NodeAppender.insertNodeAtIndex(blockScopeNode, this.getNode(), randomIndex);
         NodeAppender.insertNodeAtIndex(blockScopeNode, this.getNode(), randomIndex);
     }
     }
@@ -51,20 +46,9 @@ export class DebugProtectionFunctionNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getNodeIdentifier (): string {
-        return this.debugProtectionFunctionName;
-    }
-
-    /**
-     * Found this trick in JScrambler
-     *
-     * @returns {TStatement[]}
-     */
-    protected getNodeStructure (): TStatement[] {
-        return NodeUtils.convertCodeToStructure(
-            DebugProtectionFunctionTemplate().formatUnicorn({
-                debugProtectionFunctionName: this.debugProtectionFunctionName
-            })
-        );
+    public getCode (): string {
+        return format(DebugProtectionFunctionTemplate(), {
+            debugProtectionFunctionName: this.debugProtectionFunctionName
+        });
     }
     }
 }
 }

+ 9 - 13
src/custom-nodes/domain-lock-nodes/DomainLockNode.ts

@@ -1,7 +1,6 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
@@ -12,7 +11,6 @@ import { DomainLockNodeTemplate } from '../../templates/custom-nodes/domain-lock
 
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 import { Utils } from '../../Utils';
 import { Utils } from '../../Utils';
 
 
 export class DomainLockNode extends AbstractCustomNode {
 export class DomainLockNode extends AbstractCustomNode {
@@ -68,19 +66,17 @@ export class DomainLockNode extends AbstractCustomNode {
     }
     }
 
 
     /**
     /**
-     * @returns {TStatement[]}
+     * @returns {string}
      */
      */
-    protected getNodeStructure (): TStatement[] {
+    public getCode (): string {
         let domainsString: string = this.options.domainLock.join(';'),
         let domainsString: string = this.options.domainLock.join(';'),
             [hiddenDomainsString, diff]: string[] = Utils.hideString(domainsString, domainsString.length * 3);
             [hiddenDomainsString, diff]: string[] = Utils.hideString(domainsString, domainsString.length * 3);
 
 
-        return NodeUtils.convertCodeToStructure(
-            DomainLockNodeTemplate().formatUnicorn({
-                domainLockFunctionName: Utils.getRandomVariableName(),
-                diff: diff,
-                domains: hiddenDomainsString,
-                singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-            })
-        );
+        return format(DomainLockNodeTemplate(), {
+            domainLockFunctionName: Utils.getRandomVariableName(),
+            diff: diff,
+            domains: hiddenDomainsString,
+            singleNodeCallControllerFunctionName: this.callsControllerFunctionName
+        });
     }
     }
 }
 }

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

@@ -1,7 +1,6 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
@@ -15,7 +14,6 @@ import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 
 
 export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
 export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
     /**
     /**
@@ -74,21 +72,22 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
     }
     }
 
 
     /**
     /**
-     * @returns {TStatement[]}
+     * @returns {string}
      */
      */
-    protected getNodeStructure (): TStatement[] {
+    public getCode (): string {
         if (this.appendState === AppendState.AfterObfuscation) {
         if (this.appendState === AppendState.AfterObfuscation) {
-            return NodeUtils.convertCodeToStructure(
-                JavaScriptObfuscator.obfuscate(SingleNodeCallControllerTemplate().formatUnicorn({
+            return JavaScriptObfuscator.obfuscate(
+                format(SingleNodeCallControllerTemplate(), {
                     singleNodeCallControllerFunctionName: this.callsControllerFunctionName
                     singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-                }), NO_CUSTOM_NODES_PRESET).getObfuscatedCode()
-            );
+                }),
+                Object.assign({}, NO_CUSTOM_NODES_PRESET, {
+                    seed: this.options.seed
+                })
+            ).getObfuscatedCode();
         }
         }
 
 
-        return NodeUtils.convertCodeToStructure(
-            SingleNodeCallControllerTemplate().formatUnicorn({
-                singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-            })
-        );
+        return format(SingleNodeCallControllerTemplate(), {
+            singleNodeCallControllerFunctionName: this.callsControllerFunctionName
+        });
     }
     }
 }
 }

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

@@ -1,5 +1,6 @@
+import * as format from 'string-template';
+
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
@@ -13,7 +14,6 @@ import { SelfDefendingTemplate } from '../../templates/custom-nodes/self-defendi
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
-import { NodeUtils } from '../../node/NodeUtils';
 import { Utils } from '../../Utils';
 import { Utils } from '../../Utils';
 
 
 export class SelfDefendingUnicodeNode extends AbstractCustomNode {
 export class SelfDefendingUnicodeNode extends AbstractCustomNode {
@@ -69,17 +69,17 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode {
     }
     }
 
 
     /**
     /**
-     * @returns {TStatement[]}
+     * @returns {string}
      */
      */
-    protected getNodeStructure (): TStatement[] {
-        return NodeUtils.convertCodeToStructure(
-            JavaScriptObfuscator.obfuscate(
-                SelfDefendingTemplate().formatUnicorn({
-                    selfDefendingFunctionName: Utils.getRandomVariableName(),
-                    singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-                }),
-                NO_CUSTOM_NODES_PRESET
-            ).getObfuscatedCode()
-        );
+    public getCode (): string {
+        return JavaScriptObfuscator.obfuscate(
+            format(SelfDefendingTemplate(), {
+                selfDefendingFunctionName: Utils.getRandomVariableName(),
+                singleNodeCallControllerFunctionName: this.callsControllerFunctionName
+            }),
+            Object.assign({},  NO_CUSTOM_NODES_PRESET, {
+                seed: this.options.seed
+            })
+        ).getObfuscatedCode();
     }
     }
 }
 }

+ 28 - 28
src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts

@@ -1,9 +1,11 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TStatement } from '../../types/TStatement';
 import { TStatement } from '../../types/TStatement';
 
 
+import { ICustomNodeWithIdentifier } from '../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
+import { IStorage } from '../../interfaces/IStorage';
 
 
 import { AppendState } from '../../enums/AppendState';
 import { AppendState } from '../../enums/AppendState';
 import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
 import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
@@ -20,19 +22,17 @@ import { StringArrayRc4DecodeNodeTemplate } from '../../templates/custom-nodes/s
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
-import { StringArray } from '../../StringArray';
 
 
-export class StringArrayCallsWrapper extends AbstractCustomNode {
+export class StringArrayCallsWrapper extends AbstractCustomNode implements ICustomNodeWithIdentifier {
     /**
     /**
      * @type {AppendState}
      * @type {AppendState}
      */
      */
     protected appendState: AppendState = AppendState.AfterObfuscation;
     protected appendState: AppendState = AppendState.AfterObfuscation;
 
 
     /**
     /**
-     * @type {StringArray}
+     * @type {IStorage <string>}
      */
      */
-    private stringArray: StringArray;
+    private stringArray: IStorage <string>;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
@@ -53,7 +53,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
     constructor (
     constructor (
         stringArrayCallsWrapperName: string,
         stringArrayCallsWrapperName: string,
         stringArrayName: string,
         stringArrayName: string,
-        stringArray: StringArray,
+        stringArray: IStorage <string>,
         options: IOptions
         options: IOptions
     ) {
     ) {
         super(options);
         super(options);
@@ -74,6 +74,24 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
         NodeAppender.insertNodeAtIndex(blockScopeNode, this.getNode(), 1);
         NodeAppender.insertNodeAtIndex(blockScopeNode, this.getNode(), 1);
     }
     }
 
 
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        const decodeNodeTemplate: string = this.getDecodeStringArrayTemplate();
+
+        return JavaScriptObfuscator.obfuscate(
+            format(StringArrayCallsWrapperTemplate(), {
+                decodeNodeTemplate,
+                stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
+                stringArrayName: this.stringArrayName
+            }),
+            Object.assign({}, NO_CUSTOM_NODES_PRESET, {
+                seed: this.options.seed
+            })
+        ).getObfuscatedCode();
+    }
+
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
@@ -96,7 +114,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
             selfDefendingCode: string = '';
             selfDefendingCode: string = '';
 
 
         if (this.options.selfDefending) {
         if (this.options.selfDefending) {
-            selfDefendingCode = SelfDefendingTemplate().formatUnicorn({
+            selfDefendingCode = format(SelfDefendingTemplate(), {
                 stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
                 stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
                 stringArrayName: this.stringArrayName
                 stringArrayName: this.stringArrayName
             });
             });
@@ -104,7 +122,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
 
 
         switch (this.options.stringArrayEncoding) {
         switch (this.options.stringArrayEncoding) {
             case StringArrayEncoding.base64:
             case StringArrayEncoding.base64:
-                decodeStringArrayTemplate = StringArrayBase64DecodeNodeTemplate().formatUnicorn({
+                decodeStringArrayTemplate = format(StringArrayBase64DecodeNodeTemplate(), {
                     atobPolyfill: AtobTemplate(),
                     atobPolyfill: AtobTemplate(),
                     selfDefendingCode,
                     selfDefendingCode,
                     stringArrayCallsWrapperName: this.stringArrayCallsWrapperName
                     stringArrayCallsWrapperName: this.stringArrayCallsWrapperName
@@ -113,7 +131,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
                 break;
                 break;
 
 
             case StringArrayEncoding.rc4:
             case StringArrayEncoding.rc4:
-                decodeStringArrayTemplate = StringArrayRc4DecodeNodeTemplate().formatUnicorn({
+                decodeStringArrayTemplate = format(StringArrayRc4DecodeNodeTemplate(), {
                     atobPolyfill: AtobTemplate(),
                     atobPolyfill: AtobTemplate(),
                     rc4Polyfill: Rc4Template(),
                     rc4Polyfill: Rc4Template(),
                     selfDefendingCode,
                     selfDefendingCode,
@@ -125,22 +143,4 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
 
 
         return decodeStringArrayTemplate;
         return decodeStringArrayTemplate;
     }
     }
-
-    /**
-     * @returns {TStatement[]}
-     */
-    protected getNodeStructure (): TStatement[] {
-        const decodeNodeTemplate: string = this.getDecodeStringArrayTemplate();
-
-        return NodeUtils.convertCodeToStructure(
-            JavaScriptObfuscator.obfuscate(
-                StringArrayCallsWrapperTemplate().formatUnicorn({
-                    decodeNodeTemplate,
-                    stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
-                    stringArrayName: this.stringArrayName
-                }),
-                NO_CUSTOM_NODES_PRESET
-            ).getObfuscatedCode()
-        );
-    }
 }
 }

+ 16 - 32
src/custom-nodes/string-array-nodes/StringArrayNode.ts

@@ -1,21 +1,21 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TStatement } from '../../types/TStatement';
 import { TStatement } from '../../types/TStatement';
 
 
+import { ICustomNodeWithData } from '../../interfaces/custom-nodes/ICustomNodeWithData';
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
+import { IStorage } from '../../interfaces/IStorage';
 
 
 import { AppendState } from '../../enums/AppendState';
 import { AppendState } from '../../enums/AppendState';
 
 
-import { StringArray } from '../../StringArray';
-
 import { StringArrayTemplate } from '../../templates/custom-nodes/string-array-nodes/string-array-node/StringArrayTemplate';
 import { StringArrayTemplate } from '../../templates/custom-nodes/string-array-nodes/string-array-node/StringArrayTemplate';
 
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
+import { StringArrayStorage } from '../../storages/string-array/StringArrayStorage';
 
 
-export class StringArrayNode extends AbstractCustomNode {
+export class StringArrayNode extends AbstractCustomNode implements ICustomNodeWithData {
     /**
     /**
      * @type {number}
      * @type {number}
      */
      */
@@ -27,9 +27,9 @@ export class StringArrayNode extends AbstractCustomNode {
     protected appendState: AppendState = AppendState.AfterObfuscation;
     protected appendState: AppendState = AppendState.AfterObfuscation;
 
 
     /**
     /**
-     * @type {StringArray}
+     * @type {IStorage <string>}
      */
      */
-    private stringArray: StringArray;
+    private stringArray: IStorage <string>;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
@@ -48,7 +48,7 @@ export class StringArrayNode extends AbstractCustomNode {
      * @param options
      * @param options
      */
      */
     constructor (
     constructor (
-        stringArray: StringArray,
+        stringArray: IStorage <string>,
         stringArrayName: string,
         stringArrayName: string,
         stringArrayRotateValue: number = 0,
         stringArrayRotateValue: number = 0,
         options: IOptions
         options: IOptions
@@ -74,14 +74,17 @@ export class StringArrayNode extends AbstractCustomNode {
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */
-    public getNodeIdentifier (): string {
-        return this.stringArrayName;
+    public getCode (): string {
+        return format(StringArrayTemplate(), {
+            stringArrayName: this.stringArrayName,
+            stringArray: this.stringArray.toString()
+        });
     }
     }
 
 
     /**
     /**
-     * @returns {StringArray}
+     * @returns {IStorage <string>}
      */
      */
-    public getNodeData (): StringArray {
+    public getNodeData (): IStorage <string> {
         return this.stringArray;
         return this.stringArray;
     }
     }
 
 
@@ -89,27 +92,8 @@ export class StringArrayNode extends AbstractCustomNode {
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
     public getNode (): TStatement[] {
     public getNode (): TStatement[] {
-        this.stringArray.rotateArray(this.stringArrayRotateValue);
+        (<StringArrayStorage>this.stringArray).rotateArray(this.stringArrayRotateValue);
 
 
         return super.getNode();
         return super.getNode();
     }
     }
-
-    /**
-     * @param data
-     */
-    public updateNodeData (data: string): void {
-        this.stringArray.addToArray(data);
-    }
-
-    /**
-     * @returns {TStatement[]}
-     */
-    protected getNodeStructure (): TStatement[] {
-        return NodeUtils.convertCodeToStructure(
-            StringArrayTemplate().formatUnicorn({
-                stringArrayName: this.stringArrayName,
-                stringArray: this.stringArray.toString()
-            })
-        );
-    }
 }
 }

+ 20 - 29
src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts

@@ -1,9 +1,9 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IOptions } from '../../interfaces/IOptions';
+import { IStorage } from '../../interfaces/IStorage';
 
 
 import { AppendState } from '../../enums/AppendState';
 import { AppendState } from '../../enums/AppendState';
 
 
@@ -15,8 +15,6 @@ import { StringArrayRotateFunctionTemplate } from '../../templates/custom-nodes/
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
-import { StringArray } from '../../StringArray';
 import { Utils } from '../../Utils';
 import { Utils } from '../../Utils';
 
 
 export class StringArrayRotateFunctionNode extends AbstractCustomNode {
 export class StringArrayRotateFunctionNode extends AbstractCustomNode {
@@ -26,9 +24,9 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
     protected appendState: AppendState = AppendState.AfterObfuscation;
     protected appendState: AppendState = AppendState.AfterObfuscation;
 
 
     /**
     /**
-     * @type {StringArray}
+     * @type {IStorage <string>}
      */
      */
-    private stringArray: StringArray;
+    private stringArray: IStorage <string>;
 
 
     /**
     /**
      * @type {string}
      * @type {string}
@@ -48,7 +46,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
      */
      */
     constructor (
     constructor (
         stringArrayName: string,
         stringArrayName: string,
-        stringArray: StringArray,
+        stringArray: IStorage <string>,
         stringArrayRotateValue: number,
         stringArrayRotateValue: number,
         options: IOptions
         options: IOptions
     ) {
     ) {
@@ -71,22 +69,15 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
     }
     }
 
 
     /**
     /**
-     * @returns {TStatement[]}
+     * @returns {string}
      */
      */
-    public getNode (): TStatement[] {
-        return super.getNode();
-    }
-
-    /**
-     * @returns {TStatement[]}
-     */
-    protected getNodeStructure (): TStatement[] {
+    public getCode (): string {
         let code: string = '',
         let code: string = '',
             timesName: string = Utils.getRandomVariableName(),
             timesName: string = Utils.getRandomVariableName(),
             whileFunctionName: string = Utils.getRandomVariableName();
             whileFunctionName: string = Utils.getRandomVariableName();
 
 
         if (this.options.selfDefending) {
         if (this.options.selfDefending) {
-            code = SelfDefendingTemplate().formatUnicorn({
+            code = format(SelfDefendingTemplate(), {
                 timesName,
                 timesName,
                 whileFunctionName
                 whileFunctionName
             });
             });
@@ -94,17 +85,17 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
             code = `${whileFunctionName}(++${timesName})`;
             code = `${whileFunctionName}(++${timesName})`;
         }
         }
 
 
-        return NodeUtils.convertCodeToStructure(
-            JavaScriptObfuscator.obfuscate(
-                StringArrayRotateFunctionTemplate().formatUnicorn({
-                    code,
-                    timesName,
-                    stringArrayName: this.stringArrayName,
-                    stringArrayRotateValue: Utils.decToHex(this.stringArrayRotateValue),
-                    whileFunctionName
-                }),
-                NO_CUSTOM_NODES_PRESET
-            ).getObfuscatedCode()
-        );
+        return JavaScriptObfuscator.obfuscate(
+            format(StringArrayRotateFunctionTemplate(), {
+                code,
+                timesName,
+                stringArrayName: this.stringArrayName,
+                stringArrayRotateValue: Utils.decToHex(this.stringArrayRotateValue),
+                whileFunctionName
+            }),
+            Object.assign({}, NO_CUSTOM_NODES_PRESET, {
+                seed: this.options.seed
+            })
+        ).getObfuscatedCode();
     }
     }
 }
 }

+ 0 - 2
src/enums/JSFuck.ts

@@ -1,6 +1,4 @@
 export const JSFuck: any = {
 export const JSFuck: any = {
-    Window: '[]["filter"]["constructor"]("return this")()',
-
     False: '![]',
     False: '![]',
     True: '!![]',
     True: '!![]',
 
 

+ 6 - 0
src/enums/VisitorDirection.ts

@@ -0,0 +1,6 @@
+import { Utils } from '../Utils';
+
+export const VisitorDirection: any = Utils.strEnumify({
+    enter: 'enter',
+    leave: 'leave'
+});

+ 13 - 0
src/interfaces/IControlFlowReplacer.d.ts

@@ -0,0 +1,13 @@
+import * as ESTree from 'estree';
+
+import { ICustomNode } from './custom-nodes/ICustomNode';
+import { IStorage } from './IStorage';
+
+export interface IControlFlowReplacer {
+    replace (
+        node: ESTree.Node,
+        parentNode: ESTree.Node,
+        controlFlowStorage: IStorage <ICustomNode>,
+        controlFlowStorageCustomNodeName: string
+    ): ICustomNode | undefined;
+}

+ 0 - 9
src/interfaces/INodeObfuscator.d.ts

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

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

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

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

@@ -0,0 +1,5 @@
+import { INodeTransformer } from './INodeTransformer';
+
+export interface INodeTransformersFactory {
+    initializeNodeTransformers (nodeType: string): INodeTransformer[];
+}

+ 1 - 1
src/interfaces/IObfuscator.d.ts

@@ -1,5 +1,5 @@
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 export interface IObfuscator {
 export interface IObfuscator {
-    obfuscateNode (node: ESTree.Node): ESTree.Node;
+    obfuscateAstTree (node: ESTree.Program): ESTree.Program;
 }
 }

+ 2 - 0
src/interfaces/IObfuscatorOptions.d.ts

@@ -3,12 +3,14 @@ import { TStringArrayEncoding } from '../types/TStringArrayEncoding';
 
 
 export interface IObfuscatorOptions {
 export interface IObfuscatorOptions {
     compact?: boolean;
     compact?: boolean;
+    controlFlowFlattening?: boolean;
     debugProtection?: boolean;
     debugProtection?: boolean;
     debugProtectionInterval?: boolean;
     debugProtectionInterval?: boolean;
     disableConsoleOutput?: boolean;
     disableConsoleOutput?: boolean;
     domainLock?: string[];
     domainLock?: string[];
     reservedNames?: string[];
     reservedNames?: string[];
     rotateStringArray?: boolean;
     rotateStringArray?: boolean;
+    seed?: number;
     selfDefending?: boolean;
     selfDefending?: boolean;
     sourceMap?: boolean;
     sourceMap?: boolean;
     sourceMapBaseUrl?: string;
     sourceMapBaseUrl?: string;

+ 2 - 0
src/interfaces/IOptions.d.ts

@@ -3,12 +3,14 @@ import { TStringArrayEncoding } from '../types/TStringArrayEncoding';
 
 
 export interface IOptions {
 export interface IOptions {
     readonly compact: boolean;
     readonly compact: boolean;
+    readonly controlFlowFlattening: boolean;
     readonly debugProtection: boolean;
     readonly debugProtection: boolean;
     readonly debugProtectionInterval: boolean;
     readonly debugProtectionInterval: boolean;
     readonly disableConsoleOutput: boolean;
     readonly disableConsoleOutput: boolean;
     readonly domainLock: string[];
     readonly domainLock: string[];
     readonly reservedNames: string[];
     readonly reservedNames: string[];
     readonly rotateStringArray: boolean;
     readonly rotateStringArray: boolean;
+    readonly seed: number;
     readonly selfDefending: boolean;
     readonly selfDefending: boolean;
     readonly sourceMap: boolean;
     readonly sourceMap: boolean;
     readonly sourceMapBaseUrl: string;
     readonly sourceMapBaseUrl: string;

+ 8 - 0
src/interfaces/IStorage.d.ts

@@ -0,0 +1,8 @@
+export interface IStorage <T> {
+    get (key: string | number): T;
+    getKeyOf (value: T): string | number | null;
+    getLength (): number;
+    getStorage (): any;
+    set (key: string | number | null, value: T): void;
+    toString (): string;
+}

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

@@ -15,6 +15,11 @@ export interface ICustomNode {
      */
      */
     getAppendState (): AppendState;
     getAppendState (): AppendState;
 
 
+    /**
+     * @returns {string}
+     */
+    getCode (): string;
+
     /**
     /**
      * @returns ESTree.Node[]
      * @returns ESTree.Node[]
      */
      */

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

@@ -5,9 +5,4 @@ export interface ICustomNodeWithData extends ICustomNode {
      * @returns any
      * @returns any
      */
      */
     getNodeData (): any;
     getNodeData (): any;
-
-    /**
-     * @param data
-     */
-    updateNodeData (data: any): void;
 }
 }

+ 3 - 1
src/interfaces/stack-trace-analyzer/IStackTraceAnalyzer.d.ts

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

+ 3 - 3
src/node-groups/AbstractNodesGroup.ts

@@ -9,17 +9,17 @@ export abstract class AbstractNodesGroup implements INodesGroup {
     /**
     /**
      * @type {AppendState}
      * @type {AppendState}
      */
      */
-    protected appendState: AppendState = AppendState.BeforeObfuscation;
+    protected readonly appendState: AppendState = AppendState.BeforeObfuscation;
 
 
     /**
     /**
      * @type {IStackTraceData[]}
      * @type {IStackTraceData[]}
      */
      */
-    protected stackTraceData: IStackTraceData[];
+    protected readonly stackTraceData: IStackTraceData[];
 
 
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
-    protected options: IOptions;
+    protected readonly options: IOptions;
 
 
     /**
     /**
      * @param stackTraceData
      * @param stackTraceData

+ 4 - 6
src/node-groups/StringArrayNodesGroup.ts

@@ -7,8 +7,9 @@ import { StringArrayNode } from '../custom-nodes/string-array-nodes/StringArrayN
 import { StringArrayRotateFunctionNode } from '../custom-nodes/string-array-nodes/StringArrayRotateFunctionNode';
 import { StringArrayRotateFunctionNode } from '../custom-nodes/string-array-nodes/StringArrayRotateFunctionNode';
 
 
 import { AbstractNodesGroup } from './AbstractNodesGroup';
 import { AbstractNodesGroup } from './AbstractNodesGroup';
-import { StringArray } from '../StringArray';
+import { StringArrayStorage } from '../storages/string-array/StringArrayStorage';
 import { Utils } from '../Utils';
 import { Utils } from '../Utils';
+import { IStorage } from '../interfaces/IStorage';
 
 
 export class StringArrayNodesGroup extends AbstractNodesGroup {
 export class StringArrayNodesGroup extends AbstractNodesGroup {
     /**
     /**
@@ -40,15 +41,12 @@ export class StringArrayNodesGroup extends AbstractNodesGroup {
         }
         }
 
 
         if (this.options.rotateStringArray) {
         if (this.options.rotateStringArray) {
-            this.stringArrayRotateValue = Utils.getRandomGenerator().integer({
-                min: 100,
-                max: 500
-            });
+            this.stringArrayRotateValue = Utils.getRandomInteger(100, 500);
         } else {
         } else {
             this.stringArrayRotateValue = 0;
             this.stringArrayRotateValue = 0;
         }
         }
 
 
-        const stringArray: StringArray = new StringArray();
+        const stringArray: IStorage <string> = new StringArrayStorage();
         const stringArrayNode: ICustomNode = new StringArrayNode(
         const stringArrayNode: ICustomNode = new StringArrayNode(
             stringArray,
             stringArray,
             this.stringArrayName,
             this.stringArrayName,

+ 6 - 6
src/node-obfuscators/AbstractNodeObfuscator.ts → src/node-transformers/AbstractNodeTransformer.ts

@@ -1,25 +1,25 @@
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
 import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
 import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { INodeObfuscator } from '../interfaces/INodeObfuscator';
+import { INodeTransformer } from '../interfaces/INodeTransformer';
 import { IOptions } from '../interfaces/IOptions';
 import { IOptions } from '../interfaces/IOptions';
 
 
-export abstract class AbstractNodeObfuscator implements INodeObfuscator {
+export abstract class AbstractNodeTransformer implements INodeTransformer {
     /**
     /**
      * @type Map <string, AbstractCustomNode>
      * @type Map <string, AbstractCustomNode>
      */
      */
-    protected nodes: Map <string, ICustomNode>;
+    protected readonly nodes: Map <string, ICustomNode>;
 
 
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
-    protected options: IOptions;
+    protected readonly options: IOptions;
 
 
     /**
     /**
      * @param nodes
      * @param nodes
      * @param options
      * @param options
      */
      */
-    constructor(nodes: Map <string, ICustomNode>, options: IOptions) {
+    constructor (nodes: Map <string, ICustomNode>, options: IOptions) {
         this.nodes = nodes;
         this.nodes = nodes;
         this.options = options;
         this.options = options;
     }
     }
@@ -28,5 +28,5 @@ export abstract class AbstractNodeObfuscator implements INodeObfuscator {
      * @param node
      * @param node
      * @param parentNode
      * @param parentNode
      */
      */
-    public abstract obfuscateNode (node: ESTree.Node, parentNode?: ESTree.Node): void;
+    public abstract transformNode (node: ESTree.Node, parentNode?: ESTree.Node): void;
 }
 }

+ 49 - 0
src/node-transformers/AbstractNodeTransformersFactory.ts

@@ -0,0 +1,49 @@
+import { TNodeTransformer } from '../types/TNodeTransformer';
+
+import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
+import { INodeTransformer } from '../interfaces/INodeTransformer';
+import { INodeTransformersFactory } from '../interfaces/INodeTransformersFactory';
+import { IOptions } from '../interfaces/IOptions';
+
+export abstract class AbstractNodeTransformersFactory implements INodeTransformersFactory {
+    /**
+     * @type {Map<string, TNodeTransformer[]>}
+     */
+    protected abstract readonly nodeTransformers: Map <string, TNodeTransformer[]>;
+
+    /**
+     * @type Map <string, AbstractCustomNode>
+     */
+    protected readonly customNodes: Map <string, ICustomNode>;
+
+    /**
+     * @type {IOptions}
+     */
+    protected readonly options: IOptions;
+
+    /**
+     * @param customNodes
+     * @param options
+     */
+    constructor (customNodes: Map <string, ICustomNode>, options: IOptions) {
+        this.customNodes = customNodes;
+        this.options = options;
+    }
+
+    /**
+     * @param nodeType
+     * @returns {INodeTransformer[]}
+     */
+    public initializeNodeTransformers (nodeType: string): INodeTransformer[] {
+        const nodeTransformers: TNodeTransformer[] = this.nodeTransformers.get(nodeType) || [];
+        const instancesArray: INodeTransformer[] = [];
+
+        nodeTransformers.forEach((transformer: TNodeTransformer) => {
+            instancesArray.push(
+                new transformer(this.customNodes, this.options)
+            );
+        });
+
+        return instancesArray;
+    }
+}

+ 98 - 0
src/node-transformers/node-control-flow-transformers/FunctionControlFlowTransformer.ts

@@ -0,0 +1,98 @@
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { TControlFlowReplacer } from '../../types/TControlFlowReplacer';
+import { TStatement } from '../../types/TStatement';
+
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/IOptions';
+import { IStorage } from '../../interfaces/IStorage';
+
+import { NodeType } from '../../enums/NodeType';
+
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { BinaryExpressionControlFlowReplacer } from './control-flow-replacers/BinaryExpressionControlFlowReplacer';
+import { ControlFlowStorage } from '../../storages/control-flow/ControlFlowStorage';
+import { ControlFlowStorageNode } from '../../custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode';
+import { Node } from '../../node/Node';
+import { NodeAppender } from '../../node/NodeAppender';
+import { Utils } from '../../Utils';
+import { NodeUtils } from '../../node/NodeUtils';
+
+export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {Map <string, IReplacer>}
+     */
+    private static readonly controlFlowReplacers: Map <string, TControlFlowReplacer> = new Map <string, TControlFlowReplacer> ([
+        [NodeType.BinaryExpression, BinaryExpressionControlFlowReplacer]
+    ]);
+
+    /**
+     * @param nodes
+     * @param options
+     */
+    constructor (nodes: Map <string, ICustomNode>, options: IOptions) {
+        super(nodes, options);
+    }
+
+    /**
+     * @param functionNode
+     */
+    public transformNode (functionNode: ESTree.Function): void {
+        this.changeFunctionBodyControlFlow(functionNode);
+    }
+
+    /**
+     * @param functionNode
+     */
+    private changeFunctionBodyControlFlow (functionNode: ESTree.Function): void {
+        if (Node.isArrowFunctionExpressionNode(functionNode)) {
+            return;
+        }
+
+        const controlFlowStorage: IStorage <ICustomNode> = new ControlFlowStorage();
+        const controlFlowStorageCustomNodeName: string = Utils.getRandomVariableName(6);
+
+        console.log(NodeUtils.getNodeBlockScopeDepth(functionNode.body));
+
+        estraverse.replace(functionNode.body, {
+            enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
+                const controlFlowReplacer: TControlFlowReplacer | undefined = FunctionControlFlowTransformer
+                    .controlFlowReplacers.get(node.type);
+
+                if (!controlFlowReplacer) {
+                    return;
+                }
+
+                const controlFlowStorageCallCustomNode: ICustomNode | undefined = new controlFlowReplacer(
+                    this.nodes,
+                    this.options
+                ).replace(node, parentNode, controlFlowStorage, controlFlowStorageCustomNodeName);
+
+                if (!controlFlowStorageCallCustomNode) {
+                    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 | undefined = controlFlowStorageCallCustomNode.getNode()[0];
+
+                if (!statementNode || !Node.isExpressionStatementNode(statementNode)) {
+                    throw new Error(`\`controlFlowStorageCallCustomNode.getNode()\` should returns array with \`ExpressionStatement\` node`);
+                }
+
+                return statementNode.expression;
+            }
+        });
+
+        const controlFlowStorageCustomNode: ICustomNode = new ControlFlowStorageNode(
+            controlFlowStorage,
+            controlFlowStorageCustomNodeName,
+            this.options
+        );
+
+        NodeAppender.prependNode(functionNode.body, controlFlowStorageCustomNode.getNode());
+    }
+}

+ 53 - 0
src/node-transformers/node-control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts

@@ -0,0 +1,53 @@
+import * as ESTree from 'estree';
+
+import { IControlFlowReplacer } from '../../../interfaces/IControlFlowReplacer';
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/IOptions';
+import { IStorage } from '../../../interfaces/IStorage';
+
+import { Utils } from '../../../Utils';
+
+export abstract class AbstractControlFlowReplacer implements IControlFlowReplacer {
+    /**
+     * @type Map <string, AbstractCustomNode>
+     */
+    protected readonly nodes: Map <string, ICustomNode>;
+
+    /**
+     * @type {IOptions}
+     */
+    protected readonly options : IOptions;
+
+    /**
+     * @param nodes
+     * @param options
+     */
+    constructor (nodes: Map <string, ICustomNode>, options: IOptions) {
+        this.nodes = nodes;
+        this.options = options;
+    }
+
+    /**
+     * @returns {string}
+     */
+    protected static getStorageKey (): string {
+        return Utils.getRandomGenerator().string({
+            length: 3,
+            pool: Utils.randomGeneratorPool
+        });
+    }
+
+    /**
+     * @param node
+     * @param parentNode
+     * @param controlFlowStorage
+     * @param controlFlowStorageCustomNodeName
+     * @returns {ICustomNode | undefined}
+     */
+    public abstract replace (
+        node: ESTree.Node,
+        parentNode: ESTree.Node,
+        controlFlowStorage: IStorage <ICustomNode>,
+        controlFlowStorageCustomNodeName: string
+    ): ICustomNode | undefined;
+}

+ 47 - 0
src/node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts

@@ -0,0 +1,47 @@
+import * as escodegen from 'escodegen';
+import * as ESTree from 'estree';
+
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IStorage } from '../../../interfaces/IStorage';
+
+import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
+import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionNode';
+import { ControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode';
+
+export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowReplacer {
+    /**
+     * @param expressionNode
+     * @returns {string}
+     */
+    private static getExpressionValue (expressionNode: ESTree.Expression): string {
+        return escodegen.generate(expressionNode, {
+            sourceMapWithCode: true
+        }).code;
+    }
+
+    /**
+     * @param binaryExpressionNode
+     * @param parentNode
+     * @param controlFlowStorage
+     * @param controlFlowStorageCustomNodeName
+     * @returns {ICustomNode | undefined}
+     */
+    public replace (
+        binaryExpressionNode: ESTree.BinaryExpression,
+        parentNode: ESTree.Node,
+        controlFlowStorage: IStorage <ICustomNode>,
+        controlFlowStorageCustomNodeName: string
+    ): ICustomNode | undefined {
+        const key: string = AbstractControlFlowReplacer.getStorageKey();
+
+        controlFlowStorage.set(key, new BinaryExpressionFunctionNode(binaryExpressionNode.operator, this.options));
+
+        return new ControlFlowStorageCallNode(
+            controlFlowStorageCustomNodeName,
+            key,
+            BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.left),
+            BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.right),
+            this.options
+        );
+    }
+}

+ 16 - 0
src/node-transformers/node-control-flow-transformers/factory/NodeControlFlowTransformersFactory.ts

@@ -0,0 +1,16 @@
+import { TNodeTransformer } from '../../../types/TNodeTransformer';
+
+import { NodeType } from '../../../enums/NodeType';
+
+import { FunctionControlFlowTransformer } from '../FunctionControlFlowTransformer';
+import { AbstractNodeTransformersFactory } from '../../AbstractNodeTransformersFactory';
+
+export class NodeControlFlowTransformersFactory extends AbstractNodeTransformersFactory {
+    /**
+     * @type {Map<string, TNodeTransformer[]>}
+     */
+    protected readonly nodeTransformers: Map <string, TNodeTransformer[]> = new Map <string, TNodeTransformer[]> ([
+        [NodeType.FunctionDeclaration, [FunctionControlFlowTransformer]],
+        [NodeType.FunctionExpression, [FunctionControlFlowTransformer]]
+    ]);
+}

+ 10 - 10
src/node-obfuscators/CatchClauseObfuscator.ts → src/node-transformers/node-obfuscators/CatchClauseObfuscator.ts

@@ -1,15 +1,15 @@
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../interfaces/IOptions';
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/IOptions';
 
 
-import { NodeType } from '../enums/NodeType';
+import { NodeType } from '../../enums/NodeType';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
-import { Node } from '../node/Node';
-import { NodeUtils } from '../node/NodeUtils';
+import { Node } from '../../node/Node';
+import { NodeUtils } from '../../node/NodeUtils';
 
 
 /**
 /**
  * replaces:
  * replaces:
@@ -19,11 +19,11 @@ import { NodeUtils } from '../node/NodeUtils';
  *     try {} catch (_0x12d45f) { console.log(_0x12d45f); };
  *     try {} catch (_0x12d45f) { console.log(_0x12d45f); };
  *
  *
  */
  */
-export class CatchClauseObfuscator extends AbstractNodeObfuscator {
+export class CatchClauseObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @type {IdentifierReplacer}
      * @type {IdentifierReplacer}
      */
      */
-    private identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IdentifierReplacer;
 
 
     /**
     /**
      * @param nodes
      * @param nodes
@@ -38,7 +38,7 @@ export class CatchClauseObfuscator extends AbstractNodeObfuscator {
     /**
     /**
      * @param catchClauseNode
      * @param catchClauseNode
      */
      */
-    public obfuscateNode (catchClauseNode: ESTree.CatchClause): void {
+    public transformNode (catchClauseNode: ESTree.CatchClause): void {
         this.storeCatchClauseParam(catchClauseNode);
         this.storeCatchClauseParam(catchClauseNode);
         this.replaceCatchClauseParam(catchClauseNode);
         this.replaceCatchClauseParam(catchClauseNode);
     }
     }
@@ -47,7 +47,7 @@ export class CatchClauseObfuscator extends AbstractNodeObfuscator {
      * @param catchClauseNode
      * @param catchClauseNode
      */
      */
     private storeCatchClauseParam (catchClauseNode: ESTree.CatchClause): void {
     private storeCatchClauseParam (catchClauseNode: ESTree.CatchClause): void {
-        NodeUtils.typedReplace(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)
         });
         });
     }
     }

+ 11 - 11
src/node-obfuscators/FunctionDeclarationObfuscator.ts → src/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.ts

@@ -1,17 +1,17 @@
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { TNodeWithBlockStatement } from '../types/TNodeWithBlockStatement';
+import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 
 
-import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../interfaces/IOptions';
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/IOptions';
 
 
-import { NodeType } from '../enums/NodeType';
+import { NodeType } from '../../enums/NodeType';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
-import { Node } from '../node/Node';
-import { NodeUtils } from '../node/NodeUtils';
+import { Node } from '../../node/Node';
+import { NodeUtils } from '../../node/NodeUtils';
 
 
 /**
 /**
  * replaces:
  * replaces:
@@ -22,11 +22,11 @@ import { NodeUtils } from '../node/NodeUtils';
  *     function _0x12d45f () { //... };
  *     function _0x12d45f () { //... };
  *     _0x12d45f();
  *     _0x12d45f();
  */
  */
-export class FunctionDeclarationObfuscator extends AbstractNodeObfuscator {
+export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @type {IdentifierReplacer}
      * @type {IdentifierReplacer}
      */
      */
-    private identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IdentifierReplacer;
 
 
     /**
     /**
      * @param nodes
      * @param nodes
@@ -42,7 +42,7 @@ export class FunctionDeclarationObfuscator extends AbstractNodeObfuscator {
      * @param functionDeclarationNode
      * @param functionDeclarationNode
      * @param parentNode
      * @param parentNode
      */
      */
-    public obfuscateNode (functionDeclarationNode: ESTree.FunctionDeclaration, parentNode: ESTree.Node): void {
+    public transformNode (functionDeclarationNode: ESTree.FunctionDeclaration, parentNode: ESTree.Node): void {
         const blockScopeOfFunctionDeclarationNode: TNodeWithBlockStatement = NodeUtils
         const blockScopeOfFunctionDeclarationNode: TNodeWithBlockStatement = NodeUtils
             .getBlockScopeOfNode(functionDeclarationNode);
             .getBlockScopeOfNode(functionDeclarationNode);
 
 
@@ -58,7 +58,7 @@ export class FunctionDeclarationObfuscator extends AbstractNodeObfuscator {
      * @param functionDeclarationNode
      * @param functionDeclarationNode
      */
      */
     private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration): void {
     private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration): void {
-        NodeUtils.typedReplace(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)
         });
         });
     }
     }

+ 13 - 16
src/node-obfuscators/FunctionObfuscator.ts → src/node-transformers/node-obfuscators/FunctionObfuscator.ts

@@ -1,15 +1,15 @@
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../interfaces/IOptions';
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/IOptions';
 
 
-import { NodeType } from '../enums/NodeType';
+import { NodeType } from '../../enums/NodeType';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
-import { Node } from '../node/Node';
-import { NodeUtils } from '../node/NodeUtils';
+import { Node } from '../../node/Node';
+import { NodeUtils } from '../../node/NodeUtils';
 
 
 /**
 /**
  * replaces:
  * replaces:
@@ -19,11 +19,11 @@ import { NodeUtils } from '../node/NodeUtils';
  *     function foo (_0x12d45f) { return _0x12d45f; };
  *     function foo (_0x12d45f) { return _0x12d45f; };
  *
  *
  */
  */
-export class FunctionObfuscator extends AbstractNodeObfuscator {
+export class FunctionObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @type {IdentifierReplacer}
      * @type {IdentifierReplacer}
      */
      */
-    private identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IdentifierReplacer;
 
 
     /**
     /**
      * @param nodes
      * @param nodes
@@ -38,7 +38,7 @@ export class FunctionObfuscator extends AbstractNodeObfuscator {
     /**
     /**
      * @param functionNode
      * @param functionNode
      */
      */
-    public obfuscateNode (functionNode: ESTree.Function): void {
+    public transformNode (functionNode: ESTree.Function): void {
         this.storeFunctionParams(functionNode);
         this.storeFunctionParams(functionNode);
         this.replaceFunctionParams(functionNode);
         this.replaceFunctionParams(functionNode);
     }
     }
@@ -49,7 +49,7 @@ export class FunctionObfuscator extends AbstractNodeObfuscator {
     private storeFunctionParams (functionNode: ESTree.Function): void {
     private storeFunctionParams (functionNode: ESTree.Function): void {
         functionNode.params
         functionNode.params
             .forEach((paramsNode: ESTree.Node) => {
             .forEach((paramsNode: ESTree.Node) => {
-                NodeUtils.typedReplace(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)
                 });
                 });
             });
             });
@@ -59,7 +59,7 @@ export class FunctionObfuscator extends AbstractNodeObfuscator {
      * @param functionNode
      * @param functionNode
      */
      */
     private replaceFunctionParams (functionNode: ESTree.Function): void {
     private replaceFunctionParams (functionNode: ESTree.Function): void {
-        let replaceVisitor: 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);
@@ -72,11 +72,8 @@ export class FunctionObfuscator extends AbstractNodeObfuscator {
             }
             }
         };
         };
 
 
-        functionNode.params
-            .forEach((paramsNode: ESTree.Node) => {
-                estraverse.replace(paramsNode, replaceVisitor);
-            });
+        functionNode.params.forEach((paramsNode: ESTree.Node) => estraverse.replace(paramsNode, traverseVisitor));
 
 
-        estraverse.replace(functionNode.body, replaceVisitor);
+        estraverse.replace(functionNode.body, traverseVisitor);
     }
     }
 }
 }

+ 10 - 10
src/node-obfuscators/LabeledStatementObfuscator.ts → src/node-transformers/node-obfuscators/LabeledStatementObfuscator.ts

@@ -1,15 +1,15 @@
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../interfaces/IOptions';
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/IOptions';
 
 
-import { NodeType } from '../enums/NodeType';
+import { NodeType } from '../../enums/NodeType';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
-import { Node } from '../node/Node';
-import { NodeUtils } from '../node/NodeUtils';
+import { Node } from '../../node/Node';
+import { NodeUtils } from '../../node/NodeUtils';
 
 
 /**
 /**
  * replaces:
  * replaces:
@@ -27,11 +27,11 @@ import { NodeUtils } from '../node/NodeUtils';
  *     }
  *     }
  *
  *
  */
  */
-export class LabeledStatementObfuscator extends AbstractNodeObfuscator {
+export class LabeledStatementObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @type {IdentifierReplacer}
      * @type {IdentifierReplacer}
      */
      */
-    private identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IdentifierReplacer;
 
 
     /**
     /**
      * @param nodes
      * @param nodes
@@ -46,7 +46,7 @@ export class LabeledStatementObfuscator extends AbstractNodeObfuscator {
     /**
     /**
      * @param labeledStatementNode
      * @param labeledStatementNode
      */
      */
-    public obfuscateNode (labeledStatementNode: ESTree.LabeledStatement): void {
+    public transformNode (labeledStatementNode: ESTree.LabeledStatement): void {
         this.storeLabeledStatementName(labeledStatementNode);
         this.storeLabeledStatementName(labeledStatementNode);
         this.replaceLabeledStatementName(labeledStatementNode);
         this.replaceLabeledStatementName(labeledStatementNode);
     }
     }
@@ -55,7 +55,7 @@ export class LabeledStatementObfuscator extends AbstractNodeObfuscator {
      * @param labeledStatementNode
      * @param labeledStatementNode
      */
      */
     private storeLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement): void {
     private storeLabeledStatementName (labeledStatementNode: ESTree.LabeledStatement): void {
-        NodeUtils.typedReplace(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)
         });
         });
     }
     }

+ 5 - 5
src/node-obfuscators/LiteralObfuscator.ts → src/node-transformers/node-obfuscators/LiteralObfuscator.ts

@@ -1,18 +1,18 @@
 import * as escodegen from 'escodegen';
 import * as escodegen from 'escodegen';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { BooleanLiteralReplacer } from './replacers/BooleanLiteralReplacer';
 import { BooleanLiteralReplacer } from './replacers/BooleanLiteralReplacer';
-import { Node } from '../node/Node';
+import { Node } from '../../node/Node';
 import { NumberLiteralReplacer } from './replacers/NumberLiteralReplacer';
 import { NumberLiteralReplacer } from './replacers/NumberLiteralReplacer';
 import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 
 
-export class LiteralObfuscator extends AbstractNodeObfuscator {
+export class LiteralObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @param literalNode
      * @param literalNode
      * @param parentNode
      * @param parentNode
      */
      */
-    public obfuscateNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): void {
+    public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): void {
         if (Node.isPropertyNode(parentNode) && parentNode.key === literalNode) {
         if (Node.isPropertyNode(parentNode) && parentNode.key === literalNode) {
             return;
             return;
         }
         }
@@ -34,7 +34,7 @@ export class LiteralObfuscator extends AbstractNodeObfuscator {
 
 
             case 'string':
             case 'string':
                 content = new StringLiteralReplacer(this.nodes, this.options)
                 content = new StringLiteralReplacer(this.nodes, this.options)
-                        .replace(<string>literalNode.value);
+                    .replace(<string>literalNode.value);
 
 
                 break;
                 break;
 
 

+ 16 - 16
src/node-obfuscators/MemberExpressionObfuscator.ts → src/node-transformers/node-obfuscators/MemberExpressionObfuscator.ts

@@ -2,18 +2,18 @@ import * as escodegen from 'escodegen';
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { NodeType } from '../enums/NodeType';
+import { NodeType } from '../../enums/NodeType';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
-import { Node } from '../node/Node';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { Node } from '../../node/Node';
 import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 
 
-export class MemberExpressionObfuscator extends AbstractNodeObfuscator {
+export class MemberExpressionObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @param memberExpressionNode
      * @param memberExpressionNode
      */
      */
-    public obfuscateNode (memberExpressionNode: ESTree.MemberExpression): void {
-        estraverse.replace(memberExpressionNode.property, {
+    public transformNode (memberExpressionNode: ESTree.MemberExpression): void {
+        estraverse.traverse(memberExpressionNode.property, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 if (Node.isLiteralNode(node)) {
                 if (Node.isLiteralNode(node)) {
                     this.obfuscateLiteralProperty(node);
                     this.obfuscateLiteralProperty(node);
@@ -46,16 +46,16 @@ export class MemberExpressionObfuscator extends AbstractNodeObfuscator {
      * @param node
      * @param node
      */
      */
     private obfuscateIdentifierProperty (node: ESTree.Identifier): void {
     private obfuscateIdentifierProperty (node: ESTree.Identifier): void {
-        let nodeValue: string = node.name,
-            literalNode: ESTree.Literal = {
-                raw: `'${nodeValue}'`,
-                'x-verbatim-property': {
-                    content : new StringLiteralReplacer(this.nodes, this.options).replace(nodeValue),
-                    precedence: escodegen.Precedence.Primary
-                },
-                type: NodeType.Literal,
-                value: nodeValue
-            };
+        const nodeValue: string = node.name;
+        const literalNode: ESTree.Literal = {
+            raw: `'${nodeValue}'`,
+            'x-verbatim-property': {
+                content : new StringLiteralReplacer(this.nodes, this.options).replace(nodeValue),
+                precedence: escodegen.Precedence.Primary
+            },
+            type: NodeType.Literal,
+            value: nodeValue
+        };
 
 
         delete node.name;
         delete node.name;
 
 

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

@@ -1,9 +1,9 @@
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
-import { Node } from '../node/Node';
-import { Utils } from '../Utils';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { Node } from '../../node/Node';
+import { Utils } from '../../Utils';
 import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 
 
 /**
 /**
@@ -13,17 +13,17 @@ import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
  * on:
  * on:
  *     [_0x9a4e('0x0')] { //... };
  *     [_0x9a4e('0x0')] { //... };
  */
  */
-export class MethodDefinitionObfuscator extends AbstractNodeObfuscator {
+export class MethodDefinitionObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @type {string[]}
      * @type {string[]}
      */
      */
-    private static ignoredNames: string[] = ['constructor'];
+    private static readonly ignoredNames: string[] = ['constructor'];
 
 
     /**
     /**
      * @param methodDefinitionNode
      * @param methodDefinitionNode
      * @param parentNode
      * @param parentNode
      */
      */
-    public obfuscateNode (methodDefinitionNode: ESTree.MethodDefinition, parentNode: ESTree.Node): void {
+    public transformNode (methodDefinitionNode: ESTree.MethodDefinition, parentNode: ESTree.Node): void {
         this.replaceMethodName(methodDefinitionNode);
         this.replaceMethodName(methodDefinitionNode);
     }
     }
 
 

+ 17 - 17
src/node-obfuscators/ObjectExpressionObfuscator.ts → src/node-transformers/node-obfuscators/ObjectExpressionObfuscator.ts

@@ -2,11 +2,11 @@ import * as escodegen from 'escodegen';
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { NodeType } from '../enums/NodeType';
+import { NodeType } from '../../enums/NodeType';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
-import { Node } from '../node/Node';
-import { Utils } from '../Utils';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { Node } from '../../node/Node';
+import { Utils } from '../../Utils';
 
 
 /**
 /**
  * replaces:
  * replaces:
@@ -18,18 +18,18 @@ import { Utils } from '../Utils';
  * on:
  * on:
  *     var object = { '\u0050\u0053\u0045\u0055\u0044\u004f': 1 };
  *     var object = { '\u0050\u0053\u0045\u0055\u0044\u004f': 1 };
  */
  */
-export class ObjectExpressionObfuscator extends AbstractNodeObfuscator {
+export class ObjectExpressionObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @param objectExpressionNode
      * @param objectExpressionNode
      */
      */
-    public obfuscateNode (objectExpressionNode: ESTree.ObjectExpression): void {
+    public transformNode (objectExpressionNode: ESTree.ObjectExpression): void {
         objectExpressionNode.properties
         objectExpressionNode.properties
             .forEach((property: ESTree.Property) => {
             .forEach((property: ESTree.Property) => {
                 if (property.shorthand) {
                 if (property.shorthand) {
                     property.shorthand = false;
                     property.shorthand = false;
                 }
                 }
 
 
-                estraverse.replace(property.key, {
+                estraverse.traverse(property.key, {
                     enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                     enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                         if (Node.isLiteralNode(node)) {
                         if (Node.isLiteralNode(node)) {
                             this.obfuscateLiteralPropertyKey(node);
                             this.obfuscateLiteralPropertyKey(node);
@@ -61,16 +61,16 @@ export class ObjectExpressionObfuscator extends AbstractNodeObfuscator {
      * @param node
      * @param node
      */
      */
     private obfuscateIdentifierPropertyKey (node: ESTree.Identifier): void {
     private obfuscateIdentifierPropertyKey (node: ESTree.Identifier): void {
-        let nodeValue: string = node.name,
-            literalNode: ESTree.Literal = {
-                raw: `'${nodeValue}'`,
-                'x-verbatim-property': {
-                    content : `'${Utils.stringToUnicodeEscapeSequence(nodeValue)}'`,
-                    precedence: escodegen.Precedence.Primary
-                },
-                type: NodeType.Literal,
-                value: nodeValue
-            };
+        const nodeValue: string = node.name;
+        const literalNode: ESTree.Literal = {
+            raw: `'${nodeValue}'`,
+            'x-verbatim-property': {
+                content : `'${Utils.stringToUnicodeEscapeSequence(nodeValue)}'`,
+                precedence: escodegen.Precedence.Primary
+            },
+            type: NodeType.Literal,
+            value: nodeValue
+        };
 
 
         delete node.name;
         delete node.name;
 
 

+ 11 - 11
src/node-obfuscators/VariableDeclarationObfuscator.ts → src/node-transformers/node-obfuscators/VariableDeclarationObfuscator.ts

@@ -1,17 +1,17 @@
 import * as estraverse from 'estraverse';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 import * as ESTree from 'estree';
 
 
-import { TNodeWithBlockStatement } from '../types/TNodeWithBlockStatement';
+import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
 
 
-import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../interfaces/IOptions';
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/IOptions';
 
 
-import { NodeType } from '../enums/NodeType';
+import { NodeType } from '../../enums/NodeType';
 
 
-import { AbstractNodeObfuscator } from './AbstractNodeObfuscator';
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
-import { Node } from '../node/Node';
-import { NodeUtils } from '../node/NodeUtils';
+import { Node } from '../../node/Node';
+import { NodeUtils } from '../../node/NodeUtils';
 
 
 /**
 /**
  * replaces:
  * replaces:
@@ -23,11 +23,11 @@ import { NodeUtils } from '../node/NodeUtils';
  *     _0x12d45f++;
  *     _0x12d45f++;
  *
  *
  */
  */
-export class VariableDeclarationObfuscator extends AbstractNodeObfuscator {
+export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
     /**
     /**
      * @type {IdentifierReplacer}
      * @type {IdentifierReplacer}
      */
      */
-    private identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IdentifierReplacer;
 
 
     /**
     /**
      * @param nodes
      * @param nodes
@@ -43,7 +43,7 @@ export class VariableDeclarationObfuscator extends AbstractNodeObfuscator {
      * @param variableDeclarationNode
      * @param variableDeclarationNode
      * @param parentNode
      * @param parentNode
      */
      */
-    public obfuscateNode (variableDeclarationNode: ESTree.VariableDeclaration, parentNode: ESTree.Node): void {
+    public transformNode (variableDeclarationNode: ESTree.VariableDeclaration, parentNode: ESTree.Node): void {
         const blockScopeOfVariableDeclarationNode: TNodeWithBlockStatement = NodeUtils
         const blockScopeOfVariableDeclarationNode: TNodeWithBlockStatement = NodeUtils
             .getBlockScopeOfNode(variableDeclarationNode);
             .getBlockScopeOfNode(variableDeclarationNode);
 
 
@@ -65,7 +65,7 @@ export class VariableDeclarationObfuscator extends AbstractNodeObfuscator {
     private storeVariableNames (variableDeclarationNode: ESTree.VariableDeclaration): void {
     private storeVariableNames (variableDeclarationNode: ESTree.VariableDeclaration): void {
         variableDeclarationNode.declarations
         variableDeclarationNode.declarations
             .forEach((declarationNode: ESTree.VariableDeclarator) => {
             .forEach((declarationNode: ESTree.VariableDeclarator) => {
-                NodeUtils.typedReplace(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)
                 });
                 });
             });
             });

+ 36 - 0
src/node-transformers/node-obfuscators/factory/NodeObfuscatorsFactory.ts

@@ -0,0 +1,36 @@
+import { TNodeTransformer } from '../../../types/TNodeTransformer';
+
+import { NodeType } from '../../../enums/NodeType';
+
+import { CatchClauseObfuscator } from '../CatchClauseObfuscator';
+import { FunctionDeclarationObfuscator } from '../FunctionDeclarationObfuscator';
+import { FunctionObfuscator } from '../FunctionObfuscator';
+import { LabeledStatementObfuscator } from '../LabeledStatementObfuscator';
+import { LiteralObfuscator } from '../LiteralObfuscator';
+import { MemberExpressionObfuscator } from '../MemberExpressionObfuscator';
+import { MethodDefinitionObfuscator } from '../MethodDefinitionObfuscator';
+import { ObjectExpressionObfuscator } from '../ObjectExpressionObfuscator';
+import { VariableDeclarationObfuscator } from '../VariableDeclarationObfuscator';
+import { AbstractNodeTransformersFactory } from '../../AbstractNodeTransformersFactory';
+
+export class NodeObfuscatorsFactory extends AbstractNodeTransformersFactory {
+    /**
+     * @type {Map<string, TNodeTransformer[]>}
+     */
+    protected readonly nodeTransformers: Map <string, TNodeTransformer[]> = new Map <string, TNodeTransformer[]> ([
+        [NodeType.ArrowFunctionExpression, [FunctionObfuscator]],
+        [NodeType.ClassDeclaration, [FunctionDeclarationObfuscator]],
+        [NodeType.CatchClause, [CatchClauseObfuscator]],
+        [NodeType.FunctionDeclaration, [
+            FunctionDeclarationObfuscator,
+            FunctionObfuscator
+        ]],
+        [NodeType.FunctionExpression, [FunctionObfuscator]],
+        [NodeType.MemberExpression, [MemberExpressionObfuscator]],
+        [NodeType.MethodDefinition, [MethodDefinitionObfuscator]],
+        [NodeType.ObjectExpression, [ObjectExpressionObfuscator]],
+        [NodeType.VariableDeclaration, [VariableDeclarationObfuscator]],
+        [NodeType.LabeledStatement, [LabeledStatementObfuscator]],
+        [NodeType.Literal, [LiteralObfuscator]]
+    ]);
+}

+ 5 - 5
src/node-obfuscators/replacers/AbstractReplacer.ts → src/node-transformers/node-obfuscators/replacers/AbstractReplacer.ts

@@ -1,17 +1,17 @@
-import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../interfaces/IOptions';
-import { IReplacer } from '../../interfaces/IReplacer';
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/IOptions';
+import { IReplacer } from '../../../interfaces/IReplacer';
 
 
 export abstract class AbstractReplacer implements IReplacer {
 export abstract class AbstractReplacer implements IReplacer {
     /**
     /**
      * @type Map <string, AbstractCustomNode>
      * @type Map <string, AbstractCustomNode>
      */
      */
-    protected nodes: Map <string, ICustomNode>;
+    protected readonly nodes: Map <string, ICustomNode>;
 
 
     /**
     /**
      * @type {IOptions}
      * @type {IOptions}
      */
      */
-    protected options : IOptions;
+    protected readonly options : IOptions;
 
 
     /**
     /**
      * @param nodes
      * @param nodes

+ 1 - 1
src/node-obfuscators/replacers/BooleanLiteralReplacer.ts → src/node-transformers/node-obfuscators/replacers/BooleanLiteralReplacer.ts

@@ -1,4 +1,4 @@
-import { JSFuck } from '../../enums/JSFuck';
+import { JSFuck } from '../../../enums/JSFuck';
 
 
 import { AbstractReplacer } from './AbstractReplacer';
 import { AbstractReplacer } from './AbstractReplacer';
 
 

+ 2 - 2
src/node-obfuscators/replacers/IdentifierReplacer.ts → src/node-transformers/node-obfuscators/replacers/IdentifierReplacer.ts

@@ -1,11 +1,11 @@
 import { AbstractReplacer } from './AbstractReplacer';
 import { AbstractReplacer } from './AbstractReplacer';
-import { Utils } from '../../Utils';
+import { Utils } from '../../../Utils';
 
 
 export class IdentifierReplacer extends AbstractReplacer {
 export class IdentifierReplacer extends AbstractReplacer {
     /**
     /**
      * @type {Map<string, string>}
      * @type {Map<string, string>}
      */
      */
-    private namesMap: Map<string, string> = new Map<string, string>();
+    private readonly namesMap: Map<string, string> = new Map<string, string>();
 
 
     /**
     /**
      * @param nodeValue
      * @param nodeValue

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

@@ -1,5 +1,5 @@
 import { AbstractReplacer } from './AbstractReplacer';
 import { AbstractReplacer } from './AbstractReplacer';
-import { Utils } from '../../Utils';
+import { Utils } from '../../../Utils';
 
 
 export class NumberLiteralReplacer extends AbstractReplacer {
 export class NumberLiteralReplacer extends AbstractReplacer {
     /**
     /**

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

@@ -1,23 +1,23 @@
-import { TStringArrayCallsWrapper } from '../../types/custom-nodes/TStringArrayCallsWrapper';
-import { TStringArrayNode } from '../../types/custom-nodes/TStringArrayNode';
+import { ICustomNodeWithData } from '../../../interfaces/custom-nodes/ICustomNodeWithData';
+import { IStorage } from '../../../interfaces/IStorage';
 
 
-import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
+import { StringArrayEncoding } from '../../../enums/StringArrayEncoding';
 
 
 import { AbstractReplacer } from './AbstractReplacer';
 import { AbstractReplacer } from './AbstractReplacer';
 import { NumberLiteralReplacer } from './NumberLiteralReplacer';
 import { NumberLiteralReplacer } from './NumberLiteralReplacer';
-import { StringArray } from '../../StringArray';
-import { Utils } from '../../Utils';
+import { Utils } from '../../../Utils';
+import { ICustomNodeWithIdentifier } from '../../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
 
 
 export class StringLiteralReplacer extends AbstractReplacer {
 export class StringLiteralReplacer extends AbstractReplacer {
     /**
     /**
      * @type {number}
      * @type {number}
      */
      */
-    private static minimumLengthForStringArray: number = 3;
+    private static readonly minimumLengthForStringArray: number = 3;
 
 
     /**
     /**
      * @type {string[]}
      * @type {string[]}
      */
      */
-    private static rc4Keys: string[] = Utils.getRandomGenerator()
+    private static readonly rc4Keys: string[] = Utils.getRandomGenerator()
         .n(() => Utils.getRandomGenerator().string({length: 4}), 50);
         .n(() => Utils.getRandomGenerator().string({length: 4}), 50);
 
 
     /**
     /**
@@ -27,7 +27,7 @@ export class StringLiteralReplacer extends AbstractReplacer {
     public replace (nodeValue: string): string {
     public replace (nodeValue: string): string {
         const replaceWithStringArrayFlag: boolean = (
         const replaceWithStringArrayFlag: boolean = (
             nodeValue.length >= StringLiteralReplacer.minimumLengthForStringArray
             nodeValue.length >= StringLiteralReplacer.minimumLengthForStringArray
-            && Math.random() <= this.options.stringArrayThreshold
+            && Utils.getRandomFloat(0, 1) <= this.options.stringArrayThreshold
         );
         );
 
 
         if (this.options.stringArray && replaceWithStringArrayFlag) {
         if (this.options.stringArray && replaceWithStringArrayFlag) {
@@ -42,10 +42,10 @@ export class StringLiteralReplacer extends AbstractReplacer {
      * @returns {string}
      * @returns {string}
      */
      */
     private replaceStringLiteralWithStringArrayCall (value: string): string {
     private replaceStringLiteralWithStringArrayCall (value: string): string {
-        const stringArrayNode: TStringArrayNode = <TStringArrayNode>this.nodes.get('stringArrayNode');
+        const stringArrayNode: ICustomNodeWithData = <ICustomNodeWithData>this.nodes.get('stringArrayNode');
 
 
         if (!stringArrayNode) {
         if (!stringArrayNode) {
-            throw new ReferenceError('`stringArrayNode` node is not found in Map with custom node.');
+            throw new ReferenceError('`stringArrayNode` node is not found in Map with custom nodes.');
         }
         }
 
 
         let rc4Key: string = '';
         let rc4Key: string = '';
@@ -67,22 +67,20 @@ export class StringLiteralReplacer extends AbstractReplacer {
             value = Utils.stringToUnicodeEscapeSequence(value);
             value = Utils.stringToUnicodeEscapeSequence(value);
         }
         }
 
 
-        let stringArray: StringArray = stringArrayNode.getNodeData(),
-            indexOfExistingValue: number = stringArray.getIndexOf(value),
-            indexOfValue: number,
-            hexadecimalIndex: string;
+        const stringArray: IStorage <string> = stringArrayNode.getNodeData();
+        const indexOfExistingValue: number = <number>stringArray.getKeyOf(value);
+
+        let indexOfValue: number;
 
 
         if (indexOfExistingValue >= 0) {
         if (indexOfExistingValue >= 0) {
             indexOfValue = indexOfExistingValue;
             indexOfValue = indexOfExistingValue;
         } else {
         } else {
             indexOfValue = stringArray.getLength();
             indexOfValue = stringArray.getLength();
-            stringArrayNode.updateNodeData(value);
+            stringArray.set(null, value);
         }
         }
 
 
-        hexadecimalIndex = new NumberLiteralReplacer(this.nodes, this.options)
-            .replace(indexOfValue);
-
-        const stringArrayCallsWrapper: TStringArrayCallsWrapper = <TStringArrayCallsWrapper>this.nodes.get('stringArrayCallsWrapper');
+        const stringArrayCallsWrapper: ICustomNodeWithIdentifier = <ICustomNodeWithIdentifier>this.nodes.get('stringArrayCallsWrapper');
+        const hexadecimalIndex: string = new NumberLiteralReplacer(this.nodes, this.options).replace(indexOfValue);
 
 
         if (!stringArrayCallsWrapper) {
         if (!stringArrayCallsWrapper) {
             throw new ReferenceError('`stringArrayCallsWrapper` node is not found in Map with custom node.');
             throw new ReferenceError('`stringArrayCallsWrapper` node is not found in Map with custom node.');

+ 1 - 4
src/node/NodeAppender.ts

@@ -101,10 +101,7 @@ export class NodeAppender {
      * @param stackTraceRootLength
      * @param stackTraceRootLength
      */
      */
     public static getRandomStackTraceIndex (stackTraceRootLength: number): number {
     public static getRandomStackTraceIndex (stackTraceRootLength: number): number {
-        return Utils.getRandomGenerator().integer({
-            min: 0,
-            max: Math.max(0, Math.round(stackTraceRootLength - 1))
-        });
+        return Utils.getRandomInteger(0, Math.max(0, Math.round(stackTraceRootLength - 1)));
     }
     }
 
 
     /**
     /**

+ 26 - 3
src/node/NodeUtils.ts

@@ -15,7 +15,7 @@ export class NodeUtils {
     /**
     /**
      * @type {string[]}
      * @type {string[]}
      */
      */
-    private static nodesWithBlockScope: string[] = [
+    private static readonly nodesWithBlockScope: string[] = [
         NodeType.ArrowFunctionExpression,
         NodeType.ArrowFunctionExpression,
         NodeType.FunctionDeclaration,
         NodeType.FunctionDeclaration,
         NodeType.FunctionExpression,
         NodeType.FunctionExpression,
@@ -42,7 +42,7 @@ export class NodeUtils {
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
     public static convertCodeToStructure (code: string): TStatement[] {
     public static convertCodeToStructure (code: string): TStatement[] {
-        let structure: ESTree.Program = esprima.parse(code);
+        const structure: ESTree.Program = esprima.parse(code);
 
 
         NodeUtils.addXVerbatimPropertyToLiterals(structure);
         NodeUtils.addXVerbatimPropertyToLiterals(structure);
         NodeUtils.parentize(structure);
         NodeUtils.parentize(structure);
@@ -73,7 +73,7 @@ export class NodeUtils {
      * @returns {ESTree.Node}
      * @returns {ESTree.Node}
      */
      */
     public static getBlockScopeOfNode (node: ESTree.Node, depth: number = 0): TNodeWithBlockStatement {
     public static getBlockScopeOfNode (node: ESTree.Node, depth: number = 0): TNodeWithBlockStatement {
-        let parentNode: ESTree.Node | undefined = node.parentNode;
+        const parentNode: ESTree.Node | undefined = node.parentNode;
 
 
         if (!parentNode) {
         if (!parentNode) {
             throw new ReferenceError('`parentNode` property of given node is `undefined`');
             throw new ReferenceError('`parentNode` property of given node is `undefined`');
@@ -100,6 +100,29 @@ export class NodeUtils {
         return NodeUtils.getBlockScopeOfNode(parentNode);
         return NodeUtils.getBlockScopeOfNode(parentNode);
     }
     }
 
 
+    /**
+     * @param node
+     * @param depth
+     * @returns {number}
+     */
+    public static getNodeBlockScopeDepth (node: ESTree.Node, depth: number = 0): number {
+        const parentNode: ESTree.Node | undefined = node.parentNode;
+
+        if (!parentNode) {
+            throw new ReferenceError('`parentNode` property of given node is `undefined`');
+        }
+
+        if (Node.isProgramNode(parentNode)) {
+            return depth;
+        }
+
+        if (Node.isBlockStatementNode(node)) {
+            return NodeUtils.getNodeBlockScopeDepth(parentNode, ++depth);
+        }
+
+        return NodeUtils.getNodeBlockScopeDepth(parentNode, depth);
+    }
+
     /**
     /**
      * @param node
      * @param node
      */
      */

+ 13 - 1
src/options/Options.ts

@@ -41,6 +41,12 @@ export class Options implements IOptions {
     @IsBoolean()
     @IsBoolean()
     public readonly compact: boolean;
     public readonly compact: boolean;
 
 
+    /**
+     * @type {boolean}
+     */
+    @IsBoolean()
+    public readonly controlFlowFlattening: boolean;
+
     /**
     /**
      * @type {boolean}
      * @type {boolean}
      */
      */
@@ -85,6 +91,12 @@ export class Options implements IOptions {
     @IsBoolean()
     @IsBoolean()
     public readonly rotateStringArray: boolean;
     public readonly rotateStringArray: boolean;
 
 
+    /**
+     * @type {number}
+     */
+    @IsNumber()
+    public readonly seed: number;
+
     /**
     /**
      * @type {boolean}
      * @type {boolean}
      */
      */
@@ -152,7 +164,7 @@ export class Options implements IOptions {
     constructor (obfuscatorOptions: IObfuscatorOptions) {
     constructor (obfuscatorOptions: IObfuscatorOptions) {
         Object.assign(this, DEFAULT_PRESET, obfuscatorOptions);
         Object.assign(this, DEFAULT_PRESET, obfuscatorOptions);
 
 
-        let errors: ValidationError[] = validateSync(this, Options.validatorOptions);
+        const errors: ValidationError[] = validateSync(this, Options.validatorOptions);
 
 
         if (errors.length) {
         if (errors.length) {
             throw new ReferenceError(`Validation failed. errors:\n${ValidationErrorsFormatter.format(errors)}`);
             throw new ReferenceError(`Validation failed. errors:\n${ValidationErrorsFormatter.format(errors)}`);

+ 6 - 6
src/options/OptionsNormalizer.ts

@@ -9,7 +9,7 @@ export class OptionsNormalizer {
     /**
     /**
      * @type {IObfuscatorOptions}
      * @type {IObfuscatorOptions}
      */
      */
-    private static DISABLED_UNICODE_ARRAY_OPTIONS: IObfuscatorOptions = {
+    private static readonly DISABLED_UNICODE_ARRAY_OPTIONS: IObfuscatorOptions = {
         rotateStringArray: false,
         rotateStringArray: false,
         stringArray: false,
         stringArray: false,
         stringArrayEncoding: false,
         stringArrayEncoding: false,
@@ -19,7 +19,7 @@ export class OptionsNormalizer {
     /**
     /**
      * @type {IObfuscatorOptions}
      * @type {IObfuscatorOptions}
      */
      */
-    private static SELF_DEFENDING_OPTIONS: IObfuscatorOptions = {
+    private static readonly SELF_DEFENDING_OPTIONS: IObfuscatorOptions = {
         compact: true,
         compact: true,
         selfDefending: true
         selfDefending: true
     };
     };
@@ -27,14 +27,14 @@ export class OptionsNormalizer {
     /**
     /**
      * @type {IObfuscatorOptions}
      * @type {IObfuscatorOptions}
      */
      */
-    private static UNICODE_ARRAY_ENCODING_OPTIONS: IObfuscatorOptions = {
+    private static readonly UNICODE_ARRAY_ENCODING_OPTIONS: IObfuscatorOptions = {
         stringArrayEncoding: 'base64'
         stringArrayEncoding: 'base64'
     };
     };
 
 
     /**
     /**
      * @type {TOptionsNormalizerRule[]}
      * @type {TOptionsNormalizerRule[]}
      */
      */
-    private static normalizerRules: TOptionsNormalizerRule[] = [
+    private static readonly normalizerRules: TOptionsNormalizerRule[] = [
         OptionsNormalizer.domainLockRule,
         OptionsNormalizer.domainLockRule,
         OptionsNormalizer.selfDefendingRule,
         OptionsNormalizer.selfDefendingRule,
         OptionsNormalizer.sourceMapBaseUrlRule,
         OptionsNormalizer.sourceMapBaseUrlRule,
@@ -64,7 +64,7 @@ export class OptionsNormalizer {
      */
      */
     private static domainLockRule (options: IOptions): IOptions {
     private static domainLockRule (options: IOptions): IOptions {
         if (options.domainLock.length) {
         if (options.domainLock.length) {
-            let normalizedDomains: string[] = [];
+            const normalizedDomains: string[] = [];
 
 
             for (const domain of options.domainLock) {
             for (const domain of options.domainLock) {
                 normalizedDomains.push(Utils.extractDomainFromUrl(domain));
                 normalizedDomains.push(Utils.extractDomainFromUrl(domain));
@@ -95,7 +95,7 @@ export class OptionsNormalizer {
      * @returns {IOptions}
      * @returns {IOptions}
      */
      */
     private static sourceMapBaseUrlRule (options: IOptions): IOptions {
     private static sourceMapBaseUrlRule (options: IOptions): IOptions {
-        let sourceMapBaseUrl: string = options.sourceMapBaseUrl;
+        const sourceMapBaseUrl: string = options.sourceMapBaseUrl;
 
 
         if (!options.sourceMapFileName) {
         if (!options.sourceMapFileName) {
             Object.assign(options, {
             Object.assign(options, {

+ 4 - 3
src/options/ValidationErrorsFormatter.ts

@@ -6,7 +6,7 @@ export class ValidationErrorsFormatter {
      * @returns {string}
      * @returns {string}
      */
      */
     public static format (validationErrors: ValidationError[]): string {
     public static format (validationErrors: ValidationError[]): string {
-        let errorsArray: string[] = [];
+        const errorsArray: string[] = [];
 
 
         for (const error of validationErrors) {
         for (const error of validationErrors) {
             errorsArray.push(ValidationErrorsFormatter.formatError(error));
             errorsArray.push(ValidationErrorsFormatter.formatError(error));
@@ -20,8 +20,9 @@ export class ValidationErrorsFormatter {
      * @returns {string}
      * @returns {string}
      */
      */
     private static formatError (validationError: ValidationError): string {
     private static formatError (validationError: ValidationError): string {
-        let errorString: string = `\`${validationError.property}\` errors:\n`,
-            constraints: {[type: string]: string} = validationError.constraints;
+        const constraints: {[type: string]: string} = validationError.constraints;
+
+        let errorString: string = `\`${validationError.property}\` errors:\n`;
 
 
         for (const constraint in constraints) {
         for (const constraint in constraints) {
             if (!constraints.hasOwnProperty(constraint)) {
             if (!constraints.hasOwnProperty(constraint)) {

+ 2 - 0
src/preset-options/DefaultPreset.ts

@@ -4,12 +4,14 @@ import { SourceMapMode } from '../enums/SourceMapMode';
 
 
 export const DEFAULT_PRESET: IObfuscatorOptions = Object.freeze({
 export const DEFAULT_PRESET: IObfuscatorOptions = Object.freeze({
     compact: true,
     compact: true,
+    controlFlowFlattening: false,
     debugProtection: false,
     debugProtection: false,
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: true,
     disableConsoleOutput: true,
     domainLock: [],
     domainLock: [],
     reservedNames: [],
     reservedNames: [],
     rotateStringArray: true,
     rotateStringArray: true,
+    seed: 0,
     selfDefending: true,
     selfDefending: true,
     sourceMap: false,
     sourceMap: false,
     sourceMapBaseUrl: '',
     sourceMapBaseUrl: '',

+ 2 - 0
src/preset-options/NoCustomNodesPreset.ts

@@ -4,12 +4,14 @@ import { SourceMapMode } from '../enums/SourceMapMode';
 
 
 export const NO_CUSTOM_NODES_PRESET: IObfuscatorOptions = Object.freeze({
 export const NO_CUSTOM_NODES_PRESET: IObfuscatorOptions = Object.freeze({
     compact: true,
     compact: true,
+    controlFlowFlattening: false,
     debugProtection: false,
     debugProtection: false,
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
     reservedNames: [],
     reservedNames: [],
     rotateStringArray: false,
     rotateStringArray: false,
+    seed: 0,
     selfDefending: false,
     selfDefending: false,
     sourceMap: false,
     sourceMap: false,
     sourceMapBaseUrl: '',
     sourceMapBaseUrl: '',

+ 7 - 18
src/stack-trace-analyzer/StackTraceAnalyzer.ts

@@ -51,34 +51,22 @@ export class StackTraceAnalyzer implements IStackTraceAnalyzer {
     /**
     /**
      * @type {number}
      * @type {number}
      */
      */
-    private static limitThresholdActivationLength: number = 25;
+    private static readonly limitThresholdActivationLength: number = 25;
 
 
     /**
     /**
      * @type {number}
      * @type {number}
      */
      */
-    private static limitThreshold: number = 0.002;
-
-    /**
-     * @type {ESTree.Node[]}
-     */
-    private blockScopeBody: ESTree.Node[];
+    private static readonly limitThreshold: number = 0.002;
 
 
     /**
     /**
      * @type {Map<string, TCalleeDataExtractor>}
      * @type {Map<string, TCalleeDataExtractor>}
      */
      */
-    private calleeDataExtractors: Map <string, TCalleeDataExtractor> = new Map <string, TCalleeDataExtractor> ([
+    private readonly calleeDataExtractors: Map <string, TCalleeDataExtractor> = new Map <string, TCalleeDataExtractor> ([
         [NodeType.FunctionDeclaration, FunctionDeclarationCalleeDataExtractor],
         [NodeType.FunctionDeclaration, FunctionDeclarationCalleeDataExtractor],
         [NodeType.FunctionExpression, FunctionExpressionCalleeDataExtractor],
         [NodeType.FunctionExpression, FunctionExpressionCalleeDataExtractor],
         [NodeType.ObjectExpression, ObjectExpressionCalleeDataExtractor]
         [NodeType.ObjectExpression, ObjectExpressionCalleeDataExtractor]
     ]);
     ]);
 
 
-    /**
-     * @param blockScopeBody
-     */
-    constructor (blockScopeBody: ESTree.Node[]) {
-        this.blockScopeBody = blockScopeBody;
-    }
-
     /**
     /**
      * @param blockScopeBodyLength
      * @param blockScopeBodyLength
      * @returns {number}
      * @returns {number}
@@ -103,10 +91,11 @@ export class StackTraceAnalyzer implements IStackTraceAnalyzer {
     }
     }
 
 
     /**
     /**
+     * @param blockScopeBody
      * @returns {IStackTraceData[]}
      * @returns {IStackTraceData[]}
      */
      */
-    public analyze (): IStackTraceData[] {
-        return this.analyzeRecursive(this.blockScopeBody);
+    public analyze (blockScopeBody: ESTree.Node[]): IStackTraceData[] {
+        return this.analyzeRecursive(blockScopeBody);
     }
     }
 
 
     /**
     /**
@@ -122,7 +111,7 @@ export class StackTraceAnalyzer implements IStackTraceAnalyzer {
             index < blockScopeBodyLength;
             index < blockScopeBodyLength;
             index++
             index++
         ) {
         ) {
-            let rootNode: ESTree.Node = blockScopeBody[index];
+            const rootNode: ESTree.Node = blockScopeBody[index];
 
 
             if (index > limitIndex) {
             if (index > limitIndex) {
                 break;
                 break;

+ 52 - 0
src/storages/ArrayStorage.ts

@@ -0,0 +1,52 @@
+import { IStorage } from '../interfaces/IStorage';
+
+export abstract class ArrayStorage <T> implements IStorage <T> {
+    /**
+     * @type {T[]}
+     */
+    protected storage: T[] = [];
+
+    /**
+     * @param key
+     * @returns {T}
+     */
+    public get (key: number): T {
+        const value: T | undefined = this.storage[key];
+
+        if (!value) {
+            throw new Error(`No value found in array storage with key \`${key}\``);
+        }
+
+        return value;
+    }
+
+    /**
+     * @param value
+     * @returns {string | number}
+     */
+    public getKeyOf (value: T): string | number {
+        return this.storage.indexOf(value);
+    }
+
+    /**
+     * @returns {number}
+     */
+    public getLength (): number {
+        return this.storage.length;
+    }
+
+    /**
+     * @returns {T[]}
+     */
+    public getStorage (): T[] {
+        return this.storage;
+    }
+
+    /**
+     * @param key
+     * @param value
+     */
+    public set (key: string | null, value: T): void {
+        this.storage.push(value);
+    }
+}

+ 54 - 0
src/storages/MapStorage.ts

@@ -0,0 +1,54 @@
+import { IStorage } from '../interfaces/IStorage';
+
+import { Utils } from '../Utils';
+
+export abstract class MapStorage <T> implements IStorage <T> {
+    /**
+     * @type {Map <string | number, T>}
+     */
+    protected storage: Map <string | number, T> = new Map <string | number, T> ();
+
+    /**
+     * @param key
+     * @returns {T}
+     */
+    public get (key: string | number): T {
+        const value: T | undefined = this.storage.get(key);
+
+        if (!value) {
+            throw new Error(`No value found in map storage with key \`${key}\``);
+        }
+
+        return value;
+    }
+
+    /**
+     * @param value
+     * @returns {string | number | null}
+     */
+    public getKeyOf (value: T): string | number | null {
+        return Utils.mapGetFirstKeyOf(this.storage, value);
+    }
+
+    /**
+     * @returns {number}
+     */
+    public getLength (): number {
+        return Array.from(this.storage).length;
+    }
+
+    /**
+     * @returns {Map <string | number, T>}
+     */
+    public getStorage (): Map <string | number, T> {
+        return this.storage;
+    }
+
+    /**
+     * @param key
+     * @param value
+     */
+    public set (key: string | number, value: T): void {
+        this.storage.set(key, value);
+    }
+}

+ 19 - 0
src/storages/control-flow/ControlFlowStorage.ts

@@ -0,0 +1,19 @@
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+
+import { MapStorage } from '../MapStorage';
+
+export class ControlFlowStorage extends MapStorage <ICustomNode> {
+    /**
+     * @returns {string}
+     */
+    public toString (): string {
+        return Array
+            .from(this.storage)
+            .reduce((controlFlowStorageItems: string[], [key, value]: [string, ICustomNode]) => {
+                controlFlowStorageItems.push(`${key}: ${value.getCode()}`);
+
+                return controlFlowStorageItems;
+            }, [])
+            .join(',');
+    }
+}

+ 20 - 0
src/storages/string-array/StringArrayStorage.ts

@@ -0,0 +1,20 @@
+import { ArrayStorage } from '../ArrayStorage';
+import { Utils } from '../../Utils';
+
+export class StringArrayStorage extends ArrayStorage <string> {
+    /**
+     * @param rotationValue
+     */
+    public rotateArray (rotationValue: number): void {
+        this.storage = Utils.arrayRotate(this.storage, rotationValue);
+    }
+
+    /**
+     * @returns {string}
+     */
+    public toString (): string {
+        return this.storage.map((value: string) => {
+            return `'${value}'`;
+        }).toString();
+    }
+}

+ 10 - 0
src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionTemplate.ts

@@ -0,0 +1,10 @@
+/**
+ * @returns {string}
+ */
+export function BinaryExpressionFunctionTemplate (): string {
+    return `
+        function {functionName} (x, y) {
+            return x {operator} y;
+        }
+    `;
+}

+ 6 - 0
src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallTemplate.ts

@@ -0,0 +1,6 @@
+/**
+ * @returns {string}
+ */
+export function ControlFlowStorageCallTemplate (): string {
+    return '{controlFlowStorageName}.{controlFlowStorageKey}({leftValue}, {rightValue})';
+}

+ 8 - 0
src/templates/custom-nodes/control-flow-storage-nodes/ControlFlowStorageTemplate.ts

@@ -0,0 +1,8 @@
+/**
+ * @returns {string}
+ */
+export function ControlFlowStorageTemplate (): string {
+    return `
+        var {controlFlowStorageName} = { {controlFlowStorage} };
+    `;
+}

+ 1 - 3
src/templates/custom-nodes/debug-protection-nodes/debug-protection-function-node/DebugProtectionFunctionTemplate.ts

@@ -1,5 +1,3 @@
-import { Utils } from '../../../../Utils';
-
 /**
 /**
  * @returns {string}
  * @returns {string}
  */
  */
@@ -10,7 +8,7 @@ export function DebugProtectionFunctionTemplate (): string {
                 if (('' + counter / counter)['length'] !== 1 || counter % 20 === 0) {
                 if (('' + counter / counter)['length'] !== 1 || counter % 20 === 0) {
                     (function () {}.constructor('debugger')());
                     (function () {}.constructor('debugger')());
                 } else {
                 } else {
-                    [].filter.constructor(${Utils.stringToJSFuck('debugger')})();
+                    (function () {}.constructor('debugger')());
                 }
                 }
                 
                 
                 debuggerProtection(++counter);
                 debuggerProtection(++counter);

+ 5 - 0
src/types/TControlFlowReplacer.d.ts

@@ -0,0 +1,5 @@
+import { IControlFlowReplacer } from '../interfaces/IControlFlowReplacer';
+import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../interfaces/IOptions';
+
+export type TControlFlowReplacer = (new (nodes: Map <string, ICustomNode>, options: IOptions) => IControlFlowReplacer);

+ 0 - 5
src/types/TNodeObfuscator.d.ts

@@ -1,5 +0,0 @@
-import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { INodeObfuscator } from '../interfaces/INodeObfuscator';
-import { IOptions } from '../interfaces/IOptions';
-
-export type TNodeObfuscator = (new (nodes: Map <string, ICustomNode>, options: IOptions) => INodeObfuscator);

+ 5 - 0
src/types/TNodeTransformer.d.ts

@@ -0,0 +1,5 @@
+import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
+import { INodeTransformer } from '../interfaces/INodeTransformer';
+import { IOptions } from '../interfaces/IOptions';
+
+export type TNodeTransformer = (new (nodes: Map <string, ICustomNode>, options: IOptions) => INodeTransformer);

+ 1 - 0
src/types/TVisitorDirection.d.ts

@@ -0,0 +1 @@
+export type TVisitorDirection = 'enter' | 'leave';

+ 0 - 3
src/types/custom-nodes/TStringArrayCallsWrapper.d.ts

@@ -1,3 +0,0 @@
-import { ICustomNodeWithIdentifier } from '../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
-
-export type TStringArrayCallsWrapper = ICustomNodeWithIdentifier;

+ 0 - 4
src/types/custom-nodes/TStringArrayNode.d.ts

@@ -1,4 +0,0 @@
-import { ICustomNodeWithData } from '../../interfaces/custom-nodes/ICustomNodeWithData';
-import { ICustomNodeWithIdentifier } from '../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
-
-export type TStringArrayNode = ICustomNodeWithData & ICustomNodeWithIdentifier;

+ 13 - 63
test/dev/dev.ts

@@ -1,4 +1,5 @@
 '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');
@@ -7,72 +8,21 @@ if (!(<any>global)._babelPolyfill) {
 (function () {
 (function () {
     const JavaScriptObfuscator: any = require("../../index");
     const JavaScriptObfuscator: any = require("../../index");
 
 
-    let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
-        `
-    (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);
+    let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(`
+        (function(){
+            function t () {
+                return function () {
+                    var t = 1 * 2;
+                }
             }
             }
             
             
-            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);
-        }
-    })();
+            var s = 1 - 3;
+        })();
     `,
     `,
-        {
-            disableConsoleOutput: false,
-            selfDefending: true,
-            stringArrayEncoding: 'rc4'
-        }
+        Object.assign({}, NO_CUSTOM_NODES_PRESET,  {
+            controlFlowFlattening: true,
+            disableConsoleOutput: false
+        })
     ).getObfuscatedCode();
     ).getObfuscatedCode();
 
 
     console.log(obfuscatedCode);
     console.log(obfuscatedCode);

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

@@ -6371,7 +6371,7 @@
     /* 42 */
     /* 42 */
     /***/ function(module, exports, __webpack_require__) {
     /***/ function(module, exports, __webpack_require__) {
 
 
-// to indexed object, toObject with fallback for non-array-like ES3 strings
+// to indexed object, toString with fallback for non-array-like ES3 strings
         var IObject = __webpack_require__(118)
         var IObject = __webpack_require__(118)
             , defined = __webpack_require__(59);
             , defined = __webpack_require__(59);
         module.exports = function(it){
         module.exports = function(it){

+ 0 - 0
test/fixtures/node-obfuscators/catch-clause-obfuscator/catch-clause-obfuscator.js → test/fixtures/node-transformers/node-obfuscators/catch-clause-obfuscator/catch-clause-obfuscator.js


+ 0 - 0
test/fixtures/node-obfuscators/labeled-statement-obfuscator/labeled-statement-obfuscator.js → test/fixtures/node-transformers/node-obfuscators/labeled-statement-obfuscator/labeled-statement-obfuscator.js


+ 36 - 0
test/functional-tests/JavaScriptObfuscator.spec.ts

@@ -3,6 +3,7 @@ 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/preset-options/NoCustomNodesPreset';
+import { readFileAsString } from '../helpers/readFileAsString';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 
@@ -125,5 +126,40 @@ describe('JavaScriptObfuscator', () => {
             assert.match(obfuscatedCode2, pattern1);
             assert.match(obfuscatedCode2, pattern1);
             assert.match(obfuscatedCode2, pattern2);
             assert.match(obfuscatedCode2, pattern2);
         });
         });
+
+        it('should returns same code every time with same `seed`', () => {
+            const code: string = readFileAsString('./test/fixtures/sample.js');
+            const seed: number = 12345;
+
+            const obfuscationResult1: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code, { seed: seed }
+            );
+            const obfuscationResult2: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code, { seed: seed }
+            );
+
+            assert.equal(obfuscationResult1.getObfuscatedCode(), obfuscationResult2.getObfuscatedCode());
+        });
+
+        it('should returns different code with different `seed` option value', () => {
+            const code: string = readFileAsString('./test/fixtures/sample.js');
+
+            const obfuscationResult1: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code, { seed: 12345 }
+            );
+            const obfuscationResult2: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code, { seed: 12346 }
+            );
+
+            const obfuscationResult3: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code, { seed: 0 }
+            );
+            const obfuscationResult4: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code, { seed: 0 }
+            );
+
+            assert.notEqual(obfuscationResult1.getObfuscatedCode(), obfuscationResult2.getObfuscatedCode());
+            assert.notEqual(obfuscationResult3.getObfuscatedCode(), obfuscationResult4.getObfuscatedCode());
+        });
     });
     });
 });
 });

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

@@ -4,6 +4,8 @@ import { JavaScriptObfuscatorInternal } from '../../src/JavaScriptObfuscatorInte
 
 
 import { NO_CUSTOM_NODES_PRESET } from '../../src/preset-options/NoCustomNodesPreset';
 import { NO_CUSTOM_NODES_PRESET } from '../../src/preset-options/NoCustomNodesPreset';
 
 
+import { Options } from '../../src/options/Options';
+
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 
 describe('JavaScriptObfuscatorInternal', () => {
 describe('JavaScriptObfuscatorInternal', () => {
@@ -14,16 +16,15 @@ describe('JavaScriptObfuscatorInternal', () => {
 
 
         it('should link obfuscated code with source map', () => {
         it('should link obfuscated code with source map', () => {
             javaScriptObfuscator = new JavaScriptObfuscatorInternal(
             javaScriptObfuscator = new JavaScriptObfuscatorInternal(
-                `var test = 1;`,
-                Object.assign({}, NO_CUSTOM_NODES_PRESET, {
-                    sourceMap: true,
-                    sourceMapFileName: sourceMapUrl
-                })
+                new Options(
+                    Object.assign({}, NO_CUSTOM_NODES_PRESET, {
+                        sourceMap: true,
+                        sourceMapFileName: sourceMapUrl
+                    })
+                )
             );
             );
 
 
-            javaScriptObfuscator.obfuscate();
-
-            obfuscationResult = javaScriptObfuscator.getObfuscationResult();
+            obfuscationResult = javaScriptObfuscator.obfuscate('var test = 1;');
 
 
             assert.match(
             assert.match(
                 obfuscationResult.getObfuscatedCode(),
                 obfuscationResult.getObfuscatedCode(),
@@ -36,17 +37,16 @@ describe('JavaScriptObfuscatorInternal', () => {
             let sourceMapBaseUrl: string = 'http://localhost:9000';
             let sourceMapBaseUrl: string = 'http://localhost:9000';
 
 
             javaScriptObfuscator = new JavaScriptObfuscatorInternal(
             javaScriptObfuscator = new JavaScriptObfuscatorInternal(
-                `var test = 1;`,
-                Object.assign({}, NO_CUSTOM_NODES_PRESET, {
-                    sourceMap: true,
-                    sourceMapBaseUrl: sourceMapBaseUrl,
-                    sourceMapFileName: sourceMapUrl
-                })
+                new Options(
+                    Object.assign({}, NO_CUSTOM_NODES_PRESET, {
+                        sourceMap: true,
+                        sourceMapBaseUrl: sourceMapBaseUrl,
+                        sourceMapFileName: sourceMapUrl
+                    })
+                )
             );
             );
 
 
-            javaScriptObfuscator.obfuscate();
-
-            obfuscationResult = javaScriptObfuscator.getObfuscationResult();
+            obfuscationResult = javaScriptObfuscator.obfuscate('var test = 1;');
 
 
             assert.match(
             assert.match(
                 obfuscationResult.getObfuscatedCode(),
                 obfuscationResult.getObfuscatedCode(),

+ 8 - 6
test/functional-tests/node-obfuscators/CatchClauseObfuscator.spec.ts → test/functional-tests/node-transformers/node-obfuscators/CatchClauseObfuscator.spec.ts

@@ -1,17 +1,19 @@
-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/preset-options/NoCustomNodesPreset';
 
 
-import { readFileAsString } from '../../helpers/readFileAsString';
+import { readFileAsString } from '../../../helpers/readFileAsString';
 
 
-import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 
 describe('CatchClauseObfuscator', () => {
 describe('CatchClauseObfuscator', () => {
-    describe('obfuscateNode (catchClauseNode: ESTree.CatchClause): void', () => {
+    describe('changeControlFlow (catchClauseNode: ESTree.CatchClause): void', () => {
         const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
         const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
-            readFileAsString('./test/fixtures/node-obfuscators/catch-clause-obfuscator/catch-clause-obfuscator.js'),
+            readFileAsString(
+                './test/fixtures/node-transformers/node-obfuscators/catch-clause-obfuscator/catch-clause-obfuscator.js'
+            ),
             Object.assign({}, NO_CUSTOM_NODES_PRESET)
             Object.assign({}, NO_CUSTOM_NODES_PRESET)
         );
         );
         const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
         const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();

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

@@ -1,8 +1,8 @@
-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/preset-options/NoCustomNodesPreset';
 
 
-import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 

+ 8 - 6
test/functional-tests/node-obfuscators/LabeledStatementObfuscator.spec.ts → test/functional-tests/node-transformers/node-obfuscators/LabeledStatementObfuscator.spec.ts

@@ -1,17 +1,19 @@
-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/preset-options/NoCustomNodesPreset';
 
 
-import { readFileAsString } from '../../helpers/readFileAsString';
+import { readFileAsString } from '../../../helpers/readFileAsString';
 
 
-import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 
 describe('LabeledStatementObfuscator', () => {
 describe('LabeledStatementObfuscator', () => {
-    describe('obfuscateNode (labeledStatementNode: ESTree.LabeledStatement): void', () => {
+    describe('changeControlFlow (labeledStatementNode: ESTree.LabeledStatement): void', () => {
         const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
         const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
-            readFileAsString('./test/fixtures/node-obfuscators/labeled-statement-obfuscator/labeled-statement-obfuscator.js'),
+            readFileAsString(
+                './test/fixtures/node-transformers/node-obfuscators/labeled-statement-obfuscator/labeled-statement-obfuscator.js'
+            ),
             Object.assign({}, NO_CUSTOM_NODES_PRESET)
             Object.assign({}, NO_CUSTOM_NODES_PRESET)
         );
         );
         const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
         const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();

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

@@ -1,8 +1,8 @@
-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/preset-options/NoCustomNodesPreset';
 
 
-import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 

+ 3 - 3
test/functional-tests/node-obfuscators/MemberExpressionObfuscator.spec.ts → test/functional-tests/node-transformers/node-obfuscators/MemberExpressionObfuscator.spec.ts

@@ -1,8 +1,8 @@
-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/preset-options/NoCustomNodesPreset';
 
 
-import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 

+ 3 - 3
test/functional-tests/node-obfuscators/MethodDefinitionObfuscator.spec.ts → test/functional-tests/node-transformers/node-obfuscators/MethodDefinitionObfuscator.spec.ts

@@ -1,8 +1,8 @@
-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/preset-options/NoCustomNodesPreset';
 
 
-import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 

+ 3 - 3
test/functional-tests/node-obfuscators/ObjectExpressionObfuscator.spec.ts → test/functional-tests/node-transformers/node-obfuscators/ObjectExpressionObfuscator.spec.ts

@@ -1,8 +1,8 @@
-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/preset-options/NoCustomNodesPreset';
 
 
-import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 

+ 3 - 3
test/functional-tests/node-obfuscators/VariableDeclarationObfuscator.spec.ts → test/functional-tests/node-transformers/node-obfuscators/VariableDeclarationObfuscator.spec.ts

@@ -1,8 +1,8 @@
-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/preset-options/NoCustomNodesPreset';
 
 
-import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
 
 
 const assert: Chai.AssertStatic = require('chai').assert;
 const assert: Chai.AssertStatic = require('chai').assert;
 
 

+ 13 - 10
test/functional-tests/stack-trace-analyzer/StackTraceAnalyzer.spec.ts

@@ -4,6 +4,7 @@ import * as ESTree from 'estree';
 
 
 import { TNodeWithBlockStatement } from '../../../src/types/TNodeWithBlockStatement';
 import { TNodeWithBlockStatement } from '../../../src/types/TNodeWithBlockStatement';
 
 
+import { IStackTraceAnalyzer } from '../../../src/interfaces/stack-trace-analyzer/IStackTraceAnalyzer';
 import { IStackTraceData } from '../../../src/interfaces/stack-trace-analyzer/IStackTraceData';
 import { IStackTraceData } from '../../../src/interfaces/stack-trace-analyzer/IStackTraceData';
 
 
 import { readFileAsString } from '../../helpers/readFileAsString';
 import { readFileAsString } from '../../helpers/readFileAsString';
@@ -145,6 +146,8 @@ function getObjectFunctionExpressionByName (astTree: ESTree.Node, objectName: st
 
 
 describe('StackTraceAnalyzer', () => {
 describe('StackTraceAnalyzer', () => {
     describe('extract (): IStackTraceData[]', () => {
     describe('extract (): IStackTraceData[]', () => {
+        const stackTraceAnalyzer: IStackTraceAnalyzer = new StackTraceAnalyzer();
+
         let astTree: TNodeWithBlockStatement,
         let astTree: TNodeWithBlockStatement,
             stackTraceData: IStackTraceData[],
             stackTraceData: IStackTraceData[],
             expectedStackTraceData: IStackTraceData[];
             expectedStackTraceData: IStackTraceData[];
@@ -191,7 +194,7 @@ describe('StackTraceAnalyzer', () => {
                 }
                 }
             ];
             ];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -227,7 +230,7 @@ describe('StackTraceAnalyzer', () => {
                 }
                 }
             ];
             ];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -263,7 +266,7 @@ describe('StackTraceAnalyzer', () => {
                 }
                 }
             ];
             ];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -283,7 +286,7 @@ describe('StackTraceAnalyzer', () => {
                 }
                 }
             ];
             ];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -339,7 +342,7 @@ describe('StackTraceAnalyzer', () => {
                 }
                 }
             ];
             ];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -364,7 +367,7 @@ describe('StackTraceAnalyzer', () => {
                 },
                 },
             ];
             ];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -378,7 +381,7 @@ describe('StackTraceAnalyzer', () => {
 
 
             expectedStackTraceData = [];
             expectedStackTraceData = [];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -392,7 +395,7 @@ describe('StackTraceAnalyzer', () => {
 
 
             expectedStackTraceData = [];
             expectedStackTraceData = [];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -424,7 +427,7 @@ describe('StackTraceAnalyzer', () => {
                 }
                 }
             ];
             ];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });
@@ -444,7 +447,7 @@ describe('StackTraceAnalyzer', () => {
                 }
                 }
             ];
             ];
 
 
-            stackTraceData = new StackTraceAnalyzer(astTree.body).analyze();
+            stackTraceData = stackTraceAnalyzer.analyze(astTree.body);
 
 
             assert.deepEqual(stackTraceData, expectedStackTraceData);
             assert.deepEqual(stackTraceData, expectedStackTraceData);
         });
         });

+ 2 - 2
test/functional-tests/templates/custom-nodes/domain-lock-nodes/DomainLockNodeTemplate.spec.ts

@@ -1,4 +1,4 @@
-import 'format-unicorn';
+import * as format from 'string-template';
 
 
 import { DomainLockNodeTemplate } from '../../../../../src/templates/custom-nodes/domain-lock-nodes/domain-lock-node/DomainLockNodeTemplate';
 import { DomainLockNodeTemplate } from '../../../../../src/templates/custom-nodes/domain-lock-nodes/domain-lock-node/DomainLockNodeTemplate';
 
 
@@ -14,7 +14,7 @@ const assert: Chai.AssertStatic = require('chai').assert;
  * @returns {Function}
  * @returns {Function}
  */
  */
 function getFunctionFromTemplate (templateData: any, callsControllerFunctionName: string,  currentDomain: string) {
 function getFunctionFromTemplate (templateData: any, callsControllerFunctionName: string,  currentDomain: string) {
-    let domainLockTemplate: string = DomainLockNodeTemplate().formatUnicorn(templateData);
+    let domainLockTemplate: string = format(DomainLockNodeTemplate(), templateData);
 
 
     return Function(`
     return Function(`
         document = {
         document = {

Some files were not shown because too many files changed in this diff