Sfoglia il codice sorgente

First draw of control flow change. Right now for binary expressions.

sanex3339 8 anni fa
parent
commit
3466eeb334
49 ha cambiato i file con 1326 aggiunte e 461 eliminazioni
  1. 395 303
      dist/index.js
  2. 50 0
      src/ControlFlowStorage.ts
  3. 40 0
      src/Obfuscator.ts
  4. 9 1
      src/custom-nodes/AbstractCustomNode.ts
  5. 6 23
      src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts
  6. 39 0
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionDivideFunctionNode.ts
  7. 39 0
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionExponentiationFunctionNode.ts
  8. 39 0
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionMultiplyFunctionNode.ts
  9. 39 0
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSubtractFunctionNode.ts
  10. 39 0
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSumFunctionNode.ts
  11. 80 0
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode.ts
  12. 87 0
      src/custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts
  13. 5 9
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts
  14. 5 9
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts
  15. 7 13
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts
  16. 8 12
      src/custom-nodes/domain-lock-nodes/DomainLockNode.ts
  17. 10 13
      src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts
  18. 9 13
      src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts
  19. 16 19
      src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts
  20. 10 13
      src/custom-nodes/string-array-nodes/StringArrayNode.ts
  21. 12 23
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  22. 13 0
      src/interfaces/IControlFlowReplacer.d.ts
  23. 9 0
      src/interfaces/INodeControlFlowChanger.d.ts
  24. 1 0
      src/interfaces/IObfuscatorOptions.d.ts
  25. 1 0
      src/interfaces/IOptions.d.ts
  26. 5 0
      src/interfaces/custom-nodes/ICustomNode.d.ts
  27. 32 0
      src/node-control-flow-changers/AbstractNodeControlFlowChanger.ts
  28. 87 0
      src/node-control-flow-changers/FunctionControlFlowChanger.ts
  29. 53 0
      src/node-control-flow-changers/control-flow-replacers/AbstractControlFlowReplacer.ts
  30. 83 0
      src/node-control-flow-changers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts
  31. 6 0
      src/options/Options.ts
  32. 1 0
      src/preset-options/DefaultPreset.ts
  33. 1 0
      src/preset-options/NoCustomNodesPreset.ts
  34. 10 0
      src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionDivideFunctionTemplate.ts
  35. 10 0
      src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionExponentitionFunctionTemplate.ts
  36. 10 0
      src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionMultiplyFunctionTemplate.ts
  37. 10 0
      src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSubtractFunctionTemplate.ts
  38. 10 0
      src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSumFunctionTemplate.ts
  39. 6 0
      src/templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallTemplate.ts
  40. 8 0
      src/templates/custom-nodes/control-flow-storage-nodes/ControlFlowStorageTemplate.ts
  41. 5 0
      src/types/TControlFlowReplacer.d.ts
  42. 5 0
      src/types/TNodeControlFlowChanger.d.ts
  43. 4 0
      src/types/custom-nodes/TControlFlowStorageNode.d.ts
  44. 7 5
      test/dev/dev.ts
  45. 1 1
      test/fixtures/compile-performance.js
  46. 1 1
      test/functional-tests/node-obfuscators/CatchClauseObfuscator.spec.ts
  47. 1 1
      test/functional-tests/node-obfuscators/LabeledStatementObfuscator.spec.ts
  48. 1 1
      test/unit-tests/node-obfuscators/FunctionDeclarationObfuscator.spec.ts
  49. 1 1
      test/unit-tests/node-obfuscators/FunctionObfuscator.spec.ts

File diff suppressed because it is too large
+ 395 - 303
dist/index.js


+ 50 - 0
src/ControlFlowStorage.ts

@@ -0,0 +1,50 @@
+import { ICustomNode } from './interfaces/custom-nodes/ICustomNode';
+
+export class ControlFlowStorage {
+    /**
+     * @type {Map <string, ICustomNode>}
+     */
+    private storage: Map <string, ICustomNode> = new Map <string, ICustomNode> ();
+
+    /**
+     * @param key
+     * @param value
+     */
+    public addToStorage (key: string, value: ICustomNode): void {
+        this.storage.set(key, value);
+    }
+
+    /**
+     * @returns {Map <string, Function>}
+     */
+    public getStorage (): Map <string, ICustomNode> {
+        return this.storage;
+    }
+
+    /**
+     * @param key
+     * @returns {Function}
+     */
+    public getStorageItem(key: string): ICustomNode {
+        const value: ICustomNode | undefined = this.storage.get(key);
+
+        if (!value) {
+            throw new Error(`No value found in ControlFlowStorage with key \`${key}\``);
+        }
+
+        return value;
+    }
+
+    /**
+     * @returns {string}
+     */
+    public toString (): string {
+        return `{
+            ${Array.from(this.storage).reduce((string: string, [key, value]: [string, ICustomNode]) => {
+                string += `${key}: ${value.getCode()},`;
+    
+                return string;
+            }, '')}
+        }`;
+    }
+}

+ 40 - 0
src/Obfuscator.ts

@@ -1,6 +1,7 @@
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
+import { TNodeControlFlowChanger } from './types/TNodeControlFlowChanger';
 import { TNodeGroup } from './types/TNodeGroup';
 import { TNodeObfuscator } from './types/TNodeObfuscator';
 
@@ -29,8 +30,17 @@ import { SelfDefendingNodesGroup } from './node-groups/SelfDefendingNodesGroup';
 import { StackTraceAnalyzer } from './stack-trace-analyzer/StackTraceAnalyzer';
 import { StringArrayNodesGroup } from './node-groups/StringArrayNodesGroup';
 import { VariableDeclarationObfuscator } from './node-obfuscators/VariableDeclarationObfuscator';
+import { FunctionControlFlowChanger } from './node-control-flow-changers/FunctionControlFlowChanger';
 
 export class Obfuscator implements IObfuscator {
+    /**
+     * @type {Map<string, TNodeControlFlowChanger[]>}
+     */
+    private static nodeControlFlowChangers: Map <string, TNodeControlFlowChanger[]> = new Map <string, TNodeControlFlowChanger[]> ([
+        [NodeType.FunctionDeclaration, [FunctionControlFlowChanger]],
+        [NodeType.FunctionExpression, [FunctionControlFlowChanger]]
+    ]);
+
     /**
      * @type {TNodeGroup[]}
      */
@@ -98,6 +108,10 @@ export class Obfuscator implements IObfuscator {
         this.obfuscate(node);
         this.afterObfuscation(node);
 
+        if (this.options.controlFlow) {
+            this.changeControlFlow(node);
+        }
+
         return node;
     }
 
@@ -123,6 +137,17 @@ export class Obfuscator implements IObfuscator {
         });
     };
 
+    /**
+     * @param node
+     */
+    private changeControlFlow (node: ESTree.Node): void {
+        estraverse.traverse(node, {
+            leave: (node: ESTree.Node, parentNode: ESTree.Node): void => {
+                this.initializeNodeControlFlowChangers(node, parentNode);
+            }
+        });
+    }
+
     /**
      * @param stackTraceData
      */
@@ -144,6 +169,21 @@ export class Obfuscator implements IObfuscator {
         this.customNodes = new Map <string, ICustomNode> (customNodes);
     }
 
+    /**
+     * @param node
+     * @param parentNode
+     */
+    private initializeNodeControlFlowChangers (node: ESTree.Node, parentNode: ESTree.Node): void {
+        let nodeControlFlowChangers: TNodeControlFlowChanger[] | undefined = Obfuscator.nodeControlFlowChangers.get(node.type);
+
+        if (!nodeControlFlowChangers) {
+            return;
+        }
+
+        nodeControlFlowChangers.forEach((controlFlowChanger: TNodeControlFlowChanger) => {
+            new controlFlowChanger(this.customNodes, this.options).changeControlFlow(node, parentNode);
+        });
+    }
 
     /**
      * @param node

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

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

+ 6 - 23
src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts

@@ -1,7 +1,6 @@
 import 'format-unicorn';
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
@@ -12,7 +11,6 @@ import { ConsoleOutputDisableExpressionTemplate } from '../../templates/custom-n
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 import { Utils } from '../../Utils';
 
 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 ConsoleOutputDisableExpressionTemplate().formatUnicorn({
+            consoleLogDisableFunctionName: Utils.getRandomVariableName(),
+            singleNodeCallControllerFunctionName: this.callsControllerFunctionName
+        });
     }
 }

+ 39 - 0
src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionDivideFunctionNode.ts

@@ -0,0 +1,39 @@
+import 'format-unicorn';
+
+import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
+
+import { AppendState } from '../../../enums/AppendState';
+
+import { NO_CUSTOM_NODES_PRESET } from '../../../preset-options/NoCustomNodesPreset';
+
+import { BinaryExpressionDivideFunctionTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionDivideFunctionTemplate';
+
+import { AbstractCustomNode } from '../../AbstractCustomNode';
+import { JavaScriptObfuscator } from '../../../JavaScriptObfuscator';
+import { Utils } from '../../../Utils';
+
+export class BinaryExpressionDivideFunctionNode extends AbstractCustomNode {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.BeforeObfuscation;
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return JavaScriptObfuscator.obfuscate(
+            BinaryExpressionDivideFunctionTemplate().formatUnicorn({
+                functionName: Utils.getRandomVariableName()
+            }),
+            NO_CUSTOM_NODES_PRESET
+        ).getObfuscatedCode();
+    }
+}

+ 39 - 0
src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionExponentiationFunctionNode.ts

@@ -0,0 +1,39 @@
+import 'format-unicorn';
+
+import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
+
+import { AppendState } from '../../../enums/AppendState';
+
+import { NO_CUSTOM_NODES_PRESET } from '../../../preset-options/NoCustomNodesPreset';
+
+import { BinaryExpressionExponentiationFunctionTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionExponentitionFunctionTemplate';
+
+import { AbstractCustomNode } from '../../AbstractCustomNode';
+import { JavaScriptObfuscator } from '../../../JavaScriptObfuscator';
+import { Utils } from '../../../Utils';
+
+export class BinaryExpressionExponentiationFunctionNode extends AbstractCustomNode {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.BeforeObfuscation;
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return JavaScriptObfuscator.obfuscate(
+            BinaryExpressionExponentiationFunctionTemplate().formatUnicorn({
+                functionName: Utils.getRandomVariableName()
+            }),
+            NO_CUSTOM_NODES_PRESET
+        ).getObfuscatedCode();
+    }
+}

+ 39 - 0
src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionMultiplyFunctionNode.ts

@@ -0,0 +1,39 @@
+import 'format-unicorn';
+
+import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
+
+import { AppendState } from '../../../enums/AppendState';
+
+import { NO_CUSTOM_NODES_PRESET } from '../../../preset-options/NoCustomNodesPreset';
+
+import { BinaryExpressionMultiplyFunctionTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionMultiplyFunctionTemplate';
+
+import { AbstractCustomNode } from '../../AbstractCustomNode';
+import { JavaScriptObfuscator } from '../../../JavaScriptObfuscator';
+import { Utils } from '../../../Utils';
+
+export class BinaryExpressionMultiplyFunctionNode extends AbstractCustomNode {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.BeforeObfuscation;
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return JavaScriptObfuscator.obfuscate(
+            BinaryExpressionMultiplyFunctionTemplate().formatUnicorn({
+                functionName: Utils.getRandomVariableName()
+            }),
+            NO_CUSTOM_NODES_PRESET
+        ).getObfuscatedCode();
+    }
+}

+ 39 - 0
src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSubtractFunctionNode.ts

@@ -0,0 +1,39 @@
+import 'format-unicorn';
+
+import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
+
+import { AppendState } from '../../../enums/AppendState';
+
+import { NO_CUSTOM_NODES_PRESET } from '../../../preset-options/NoCustomNodesPreset';
+
+import { BinaryExpressionSubtractFunctionTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSubtractFunctionTemplate';
+
+import { AbstractCustomNode } from '../../AbstractCustomNode';
+import { JavaScriptObfuscator } from '../../../JavaScriptObfuscator';
+import { Utils } from '../../../Utils';
+
+export class BinaryExpressionSubtractFunctionNode extends AbstractCustomNode {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.BeforeObfuscation;
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return JavaScriptObfuscator.obfuscate(
+            BinaryExpressionSubtractFunctionTemplate().formatUnicorn({
+                functionName: Utils.getRandomVariableName()
+            }),
+            NO_CUSTOM_NODES_PRESET
+        ).getObfuscatedCode();
+    }
+}

+ 39 - 0
src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSumFunctionNode.ts

@@ -0,0 +1,39 @@
+import 'format-unicorn';
+
+import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
+
+import { AppendState } from '../../../enums/AppendState';
+
+import { NO_CUSTOM_NODES_PRESET } from '../../../preset-options/NoCustomNodesPreset';
+
+import { BinaryExpressionSumFunctionTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSumFunctionTemplate';
+
+import { AbstractCustomNode } from '../../AbstractCustomNode';
+import { JavaScriptObfuscator } from '../../../JavaScriptObfuscator';
+import { Utils } from '../../../Utils';
+
+export class BinaryExpressionSumFunctionNode extends AbstractCustomNode {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.BeforeObfuscation;
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return JavaScriptObfuscator.obfuscate(
+            BinaryExpressionSumFunctionTemplate().formatUnicorn({
+                functionName: Utils.getRandomVariableName()
+            }),
+            NO_CUSTOM_NODES_PRESET
+        ).getObfuscatedCode();
+    }
+}

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

@@ -0,0 +1,80 @@
+import 'format-unicorn';
+
+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 ControlFlowStorageCallTemplate().formatUnicorn({
+            controlFlowStorageKey: this.controlFlowStorageKey,
+            controlFlowStorageName: this.controlFlowStorageName,
+            leftValue: this.leftValue,
+            rightValue: this.rightValue
+        });
+    }
+}

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

@@ -0,0 +1,87 @@
+import 'format-unicorn';
+
+import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
+
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/IOptions';
+
+import { AppendState } from '../../enums/AppendState';
+
+import { ControlFlowStorage } from '../../ControlFlowStorage';
+
+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 {ControlFlowStorage}
+     */
+    private controlFlowStorage: ControlFlowStorage;
+
+    /**
+     * @type {string}
+     */
+    private controlFlowStorageName: string;
+
+    /**
+     * @param controlFlowStorage
+     * @param controlFlowStorageName
+     * @param options
+     */
+    constructor (
+        controlFlowStorage: ControlFlowStorage,
+        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 ControlFlowStorageTemplate().formatUnicorn({
+            controlFlowStorage: this.controlFlowStorage.toString(),
+            controlFlowStorageName: this.controlFlowStorageName
+        });
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getNodeIdentifier (): string {
+        return this.controlFlowStorageName;
+    }
+
+    /**
+     * @returns {StringArray}
+     */
+    public getNodeData (): ControlFlowStorage {
+        return this.controlFlowStorage;
+    }
+
+    /**
+     * @param key
+     * @param value
+     */
+    public updateNodeData (key: string, value: ICustomNode): void {
+        this.controlFlowStorage.addToStorage(key, value);
+    }
+}

+ 5 - 9
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts

@@ -1,7 +1,6 @@
 import 'format-unicorn';
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 import { IOptions } from '../../interfaces/IOptions';
 
@@ -11,7 +10,6 @@ import { DebugProtectionFunctionCallTemplate } from '../../templates/custom-node
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 
 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 DebugProtectionFunctionCallTemplate().formatUnicorn({
+            debugProtectionFunctionName: this.debugProtectionFunctionName
+        });
     }
 }

+ 5 - 9
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts

@@ -1,7 +1,6 @@
 import 'format-unicorn';
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 import { IOptions } from '../../interfaces/IOptions';
 
@@ -11,7 +10,6 @@ import { DebugProtectionFunctionIntervalTemplate } from '../../templates/custom-
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 
 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 DebugProtectionFunctionIntervalTemplate().formatUnicorn({
+            debugProtectionFunctionName: this.debugProtectionFunctionName
+        });
     }
 }

+ 7 - 13
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts

@@ -1,7 +1,6 @@
 import 'format-unicorn';
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 import { IOptions } from '../../interfaces/IOptions';
 
@@ -11,7 +10,6 @@ import { DebugProtectionFunctionTemplate } from '../../templates/custom-nodes/de
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 import { Utils } from '../../Utils';
 
 export class DebugProtectionFunctionNode extends AbstractCustomNode {
@@ -51,20 +49,16 @@ export class DebugProtectionFunctionNode extends AbstractCustomNode {
     /**
      * @returns {string}
      */
-    public getNodeIdentifier (): string {
-        return this.debugProtectionFunctionName;
+    public getCode (): string {
+        return DebugProtectionFunctionTemplate().formatUnicorn({
+            debugProtectionFunctionName: this.debugProtectionFunctionName
+        });
     }
 
     /**
-     * Found this trick in JScrambler
-     *
-     * @returns {TStatement[]}
+     * @returns {string}
      */
-    protected getNodeStructure (): TStatement[] {
-        return NodeUtils.convertCodeToStructure(
-            DebugProtectionFunctionTemplate().formatUnicorn({
-                debugProtectionFunctionName: this.debugProtectionFunctionName
-            })
-        );
+    public getNodeIdentifier (): string {
+        return this.debugProtectionFunctionName;
     }
 }

+ 8 - 12
src/custom-nodes/domain-lock-nodes/DomainLockNode.ts

@@ -1,7 +1,6 @@
 import 'format-unicorn';
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 import { IOptions } from '../../interfaces/IOptions';
 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 { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 import { Utils } from '../../Utils';
 
 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(';'),
             [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 DomainLockNodeTemplate().formatUnicorn({
+            domainLockFunctionName: Utils.getRandomVariableName(),
+            diff: diff,
+            domains: hiddenDomainsString,
+            singleNodeCallControllerFunctionName: this.callsControllerFunctionName
+        });
     }
 }

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

@@ -1,7 +1,6 @@
 import 'format-unicorn';
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 import { IOptions } from '../../interfaces/IOptions';
 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 { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 
 export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
     /**
@@ -74,21 +72,20 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
     }
 
     /**
-     * @returns {TStatement[]}
+     * @returns {string}
      */
-    protected getNodeStructure (): TStatement[] {
+    public getCode (): string {
         if (this.appendState === AppendState.AfterObfuscation) {
-            return NodeUtils.convertCodeToStructure(
-                JavaScriptObfuscator.obfuscate(SingleNodeCallControllerTemplate().formatUnicorn({
+            return JavaScriptObfuscator.obfuscate(
+                SingleNodeCallControllerTemplate().formatUnicorn({
                     singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-                }), NO_CUSTOM_NODES_PRESET).getObfuscatedCode()
-            );
+                }),
+                NO_CUSTOM_NODES_PRESET
+            ).getObfuscatedCode();
         }
 
-        return NodeUtils.convertCodeToStructure(
-            SingleNodeCallControllerTemplate().formatUnicorn({
-                singleNodeCallControllerFunctionName: this.callsControllerFunctionName
-            })
-        );
+        return SingleNodeCallControllerTemplate().formatUnicorn({
+            singleNodeCallControllerFunctionName: this.callsControllerFunctionName
+        });
     }
 }

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

@@ -1,5 +1,4 @@
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 import { IOptions } from '../../interfaces/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
@@ -13,7 +12,6 @@ import { SelfDefendingTemplate } from '../../templates/custom-nodes/self-defendi
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
-import { NodeUtils } from '../../node/NodeUtils';
 import { Utils } from '../../Utils';
 
 export class SelfDefendingUnicodeNode extends AbstractCustomNode {
@@ -69,17 +67,15 @@ 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(
+            SelfDefendingTemplate().formatUnicorn({
+                selfDefendingFunctionName: Utils.getRandomVariableName(),
+                singleNodeCallControllerFunctionName: this.callsControllerFunctionName
+            }),
+            NO_CUSTOM_NODES_PRESET
+        ).getObfuscatedCode();
     }
 }

+ 16 - 19
src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts

@@ -20,7 +20,6 @@ import { StringArrayRc4DecodeNodeTemplate } from '../../templates/custom-nodes/s
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 import { StringArray } from '../../StringArray';
 
 export class StringArrayCallsWrapper extends AbstractCustomNode {
@@ -74,6 +73,22 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
         NodeAppender.insertNodeAtIndex(blockScopeNode, this.getNode(), 1);
     }
 
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        const decodeNodeTemplate: string = this.getDecodeStringArrayTemplate();
+
+        return JavaScriptObfuscator.obfuscate(
+            StringArrayCallsWrapperTemplate().formatUnicorn({
+                decodeNodeTemplate,
+                stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
+                stringArrayName: this.stringArrayName
+            }),
+            NO_CUSTOM_NODES_PRESET
+        ).getObfuscatedCode();
+    }
+
     /**
      * @returns {string}
      */
@@ -125,22 +140,4 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
 
         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()
-        );
-    }
 }

+ 10 - 13
src/custom-nodes/string-array-nodes/StringArrayNode.ts

@@ -13,7 +13,6 @@ import { StringArrayTemplate } from '../../templates/custom-nodes/string-array-n
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 
 export class StringArrayNode extends AbstractCustomNode {
     /**
@@ -71,6 +70,16 @@ export class StringArrayNode extends AbstractCustomNode {
         NodeAppender.prependNode(blockScopeNode, this.getNode());
     }
 
+    /**
+     * @returns {string}
+     */
+    public getCode (): string {
+        return StringArrayTemplate().formatUnicorn({
+            stringArrayName: this.stringArrayName,
+            stringArray: this.stringArray.toString()
+        });
+    }
+
     /**
      * @returns {string}
      */
@@ -100,16 +109,4 @@ export class StringArrayNode extends AbstractCustomNode {
     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()
-            })
-        );
-    }
 }

+ 12 - 23
src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts

@@ -1,7 +1,6 @@
 import 'format-unicorn';
 
 import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TStatement } from '../../types/TStatement';
 
 import { IOptions } from '../../interfaces/IOptions';
 
@@ -15,7 +14,6 @@ import { StringArrayRotateFunctionTemplate } from '../../templates/custom-nodes/
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
-import { NodeUtils } from '../../node/NodeUtils';
 import { StringArray } from '../../StringArray';
 import { Utils } from '../../Utils';
 
@@ -71,16 +69,9 @@ 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 = '',
             timesName: string = Utils.getRandomVariableName(),
             whileFunctionName: string = Utils.getRandomVariableName();
@@ -94,17 +85,15 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
             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(
+            StringArrayRotateFunctionTemplate().formatUnicorn({
+                code,
+                timesName,
+                stringArrayName: this.stringArrayName,
+                stringArrayRotateValue: Utils.decToHex(this.stringArrayRotateValue),
+                whileFunctionName
+            }),
+            NO_CUSTOM_NODES_PRESET
+        ).getObfuscatedCode();
     }
 }

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

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

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

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

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

@@ -3,6 +3,7 @@ import { TStringArrayEncoding } from '../types/TStringArrayEncoding';
 
 export interface IObfuscatorOptions {
     compact?: boolean;
+    controlFlow?: boolean;
     debugProtection?: boolean;
     debugProtectionInterval?: boolean;
     disableConsoleOutput?: boolean;

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

@@ -3,6 +3,7 @@ import { TStringArrayEncoding } from '../types/TStringArrayEncoding';
 
 export interface IOptions {
     readonly compact: boolean;
+    readonly controlFlow: boolean;
     readonly debugProtection: boolean;
     readonly debugProtectionInterval: boolean;
     readonly disableConsoleOutput: boolean;

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

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

+ 32 - 0
src/node-control-flow-changers/AbstractNodeControlFlowChanger.ts

@@ -0,0 +1,32 @@
+import * as ESTree from 'estree';
+
+import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
+import { INodeControlFlowChanger } from '../interfaces/INodeControlFlowChanger';
+import { IOptions } from '../interfaces/IOptions';
+
+export abstract class AbstractNodeControlFlowChanger implements INodeControlFlowChanger {
+    /**
+     * @type Map <string, AbstractCustomNode>
+     */
+    protected nodes: Map <string, ICustomNode>;
+
+    /**
+     * @type {IOptions}
+     */
+    protected options: IOptions;
+
+    /**
+     * @param nodes
+     * @param options
+     */
+    constructor(nodes: Map <string, ICustomNode>, options: IOptions) {
+        this.nodes = nodes;
+        this.options = options;
+    }
+
+    /**
+     * @param node
+     * @param parentNode
+     */
+    public abstract changeControlFlow (node: ESTree.Node, parentNode?: ESTree.Node): void;
+}

+ 87 - 0
src/node-control-flow-changers/FunctionControlFlowChanger.ts

@@ -0,0 +1,87 @@
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { TControlFlowReplacer } from '../types/TControlFlowReplacer';
+
+import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../interfaces/IOptions';
+
+import { NodeType } from '../enums/NodeType';
+
+import { AbstractNodeControlFlowChanger } from './AbstractNodeControlFlowChanger';
+import { BinaryExpressionControlFlowReplacer } from './control-flow-replacers/BinaryExpressionControlFlowReplacer';
+import { ControlFlowStorage } from '../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';
+
+export class FunctionControlFlowChanger extends AbstractNodeControlFlowChanger {
+    /**
+     * @type {Map <string, IReplacer>}
+     */
+    private static 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 changeControlFlow (functionNode: ESTree.Function): void {
+        this.changeFunctionBodyControlFlow(functionNode);
+    }
+
+    /**
+     * @param functionNode
+     */
+    private changeFunctionBodyControlFlow (functionNode: ESTree.Function): void {
+        const controlFlowStorage: ControlFlowStorage = new ControlFlowStorage();
+        const controlFlowStorageCustomNodeName: string = Utils.getRandomVariableName(6);
+
+        if (!Node.isFunctionDeclarationNode(functionNode) && !Node.isFunctionExpressionNode(functionNode)) {
+            return;
+        }
+
+        estraverse.replace(functionNode.body, {
+            enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
+                const controlFlowReplacerConstructor: TControlFlowReplacer | undefined = FunctionControlFlowChanger.controlFlowReplacers
+                    .get(node.type);
+
+                if (!controlFlowReplacerConstructor) {
+                    return;
+                }
+
+                const controlFlowStorageCallCustomNode: ICustomNode | undefined = new controlFlowReplacerConstructor(
+                    this.nodes,
+                    this.options
+                ).replace(node, parentNode, controlFlowStorage, controlFlowStorageCustomNodeName);
+
+                if (!controlFlowStorageCallCustomNode) {
+                    return;
+                }
+
+                // controlFlowStorageCallCustomNode will always has only one TStatement node,
+                // so we can get it by index `0`
+                // also we need to `expression` property of `ExpressionStatement` node because bug:
+                // https://github.com/estools/escodegen/issues/289
+                return (<ESTree.ExpressionStatement>controlFlowStorageCallCustomNode.getNode()[0]).expression;
+            }
+        });
+
+        const controlFlowStorageCustomNode: ControlFlowStorageNode = new ControlFlowStorageNode(
+            controlFlowStorage,
+            controlFlowStorageCustomNodeName,
+            this.options
+        );
+
+        NodeAppender.prependNode(functionNode.body, controlFlowStorageCustomNode.getNode());
+    }
+}

+ 53 - 0
src/node-control-flow-changers/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 { ControlFlowStorage } from '../../ControlFlowStorage';
+import { Utils } from '../../Utils';
+
+export abstract class AbstractControlFlowReplacer implements IControlFlowReplacer {
+    /**
+     * @type Map <string, AbstractCustomNode>
+     */
+    protected nodes: Map <string, ICustomNode>;
+
+    /**
+     * @type {IOptions}
+     */
+    protected 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: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+        });
+    }
+
+    /**
+     * @param node
+     * @param parentNode
+     * @param controlFlowStorage
+     * @param controlFlowStorageCustomNodeName
+     * @returns {ICustomNode | undefined}
+     */
+    public abstract replace (
+        node: ESTree.Node,
+        parentNode: ESTree.Node,
+        controlFlowStorage: ControlFlowStorage,
+        controlFlowStorageCustomNodeName: string
+    ): ICustomNode | undefined;
+}

+ 83 - 0
src/node-control-flow-changers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts

@@ -0,0 +1,83 @@
+import * as escodegen from 'escodegen';
+import * as ESTree from 'estree';
+
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+
+import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
+import { BinaryExpressionSumFunctionNode } from '../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSumFunctionNode';
+import { BinaryExpressionSubtractFunctionNode } from '../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionSubtractFunctionNode';
+import { BinaryExpressionMultiplyFunctionNode } from '../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionMultiplyFunctionNode';
+import { BinaryExpressionDivideFunctionNode } from '../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionDivideFunctionNode';
+import { BinaryExpressionExponentiationFunctionNode } from '../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionExponentiationFunctionNode';
+import { ControlFlowStorage } from '../../ControlFlowStorage';
+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: ControlFlowStorage,
+        controlFlowStorageCustomNodeName: string
+    ): ICustomNode | undefined {
+        let binaryExpressionFunctionNode: ICustomNode;
+
+        switch (binaryExpressionNode.operator) {
+            case '+':
+                binaryExpressionFunctionNode = new BinaryExpressionSumFunctionNode(this.options);
+
+                break;
+
+            case '-':
+                binaryExpressionFunctionNode = new BinaryExpressionSubtractFunctionNode(this.options);
+
+                break;
+
+            case '*':
+                binaryExpressionFunctionNode = new BinaryExpressionMultiplyFunctionNode(this.options);
+
+                break;
+
+            case '/':
+                binaryExpressionFunctionNode = new BinaryExpressionDivideFunctionNode(this.options);
+
+                break;
+
+            case '**':
+                binaryExpressionFunctionNode = new BinaryExpressionExponentiationFunctionNode(this.options);
+
+                break;
+
+            default:
+                return;
+        }
+
+        const key: string = AbstractControlFlowReplacer.getStorageKey();
+
+        controlFlowStorage.addToStorage(key, binaryExpressionFunctionNode);
+
+        return new ControlFlowStorageCallNode(
+            controlFlowStorageCustomNodeName,
+            key,
+            BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.left),
+            BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.right),
+            this.options
+        );
+    }
+}

+ 6 - 0
src/options/Options.ts

@@ -41,6 +41,12 @@ export class Options implements IOptions {
     @IsBoolean()
     public readonly compact: boolean;
 
+    /**
+     * @type {boolean}
+     */
+    @IsBoolean()
+    public readonly controlFlow: boolean;
+
     /**
      * @type {boolean}
      */

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

@@ -4,6 +4,7 @@ import { SourceMapMode } from '../enums/SourceMapMode';
 
 export const DEFAULT_PRESET: IObfuscatorOptions = Object.freeze({
     compact: true,
+    controlFlow: false,
     debugProtection: false,
     debugProtectionInterval: false,
     disableConsoleOutput: true,

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

@@ -4,6 +4,7 @@ import { SourceMapMode } from '../enums/SourceMapMode';
 
 export const NO_CUSTOM_NODES_PRESET: IObfuscatorOptions = Object.freeze({
     compact: true,
+    controlFlow: false,
     debugProtection: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,

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

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

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

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

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

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

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

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

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

@@ -0,0 +1,10 @@
+/**
+ * @returns {string}
+ */
+export function BinaryExpressionSumFunctionTemplate (): string {
+    return `
+        function {functionName} (x, y) {
+            return x + 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};
+    `;
+}

+ 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);

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

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

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

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

+ 7 - 5
test/dev/dev.ts

@@ -1,4 +1,5 @@
 'use strict';
+import { NO_CUSTOM_NODES_PRESET } from '../../src/preset-options/NoCustomNodesPreset';
 
 if (!(<any>global)._babelPolyfill) {
     require('babel-polyfill');
@@ -66,13 +67,14 @@ if (!(<any>global)._babelPolyfill) {
         } catch (error) {
             console.log(error);
         }
+        
+        var left = 5;
+        console.log(left + 15);
     })();
     `,
-        {
-            disableConsoleOutput: false,
-            selfDefending: true,
-            stringArrayEncoding: 'rc4'
-        }
+        Object.assign({}, NO_CUSTOM_NODES_PRESET, {
+            controlFlow: true
+        })
     ).getObfuscatedCode();
 
     console.log(obfuscatedCode);

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

@@ -6371,7 +6371,7 @@
     /* 42 */
     /***/ 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)
             , defined = __webpack_require__(59);
         module.exports = function(it){

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

@@ -9,7 +9,7 @@ import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
 const assert: Chai.AssertStatic = require('chai').assert;
 
 describe('CatchClauseObfuscator', () => {
-    describe('obfuscateNode (catchClauseNode: ESTree.CatchClause): void', () => {
+    describe('changeControlFlow (catchClauseNode: ESTree.CatchClause): void', () => {
         const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
             readFileAsString('./test/fixtures/node-obfuscators/catch-clause-obfuscator/catch-clause-obfuscator.js'),
             Object.assign({}, NO_CUSTOM_NODES_PRESET)

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

@@ -9,7 +9,7 @@ import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
 const assert: Chai.AssertStatic = require('chai').assert;
 
 describe('LabeledStatementObfuscator', () => {
-    describe('obfuscateNode (labeledStatementNode: ESTree.LabeledStatement): void', () => {
+    describe('changeControlFlow (labeledStatementNode: ESTree.LabeledStatement): void', () => {
         const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
             readFileAsString('./test/fixtures/node-obfuscators/labeled-statement-obfuscator/labeled-statement-obfuscator.js'),
             Object.assign({}, NO_CUSTOM_NODES_PRESET)

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

@@ -11,7 +11,7 @@ import { Options } from '../../../src/options/Options';
 const assert: Chai.AssertStatic = require('chai').assert;
 
 describe('FunctionDeclarationObfuscator', () => {
-    describe('obfuscateNode (functionDeclarationNode: IFunctionDeclarationNode, parentNode: INode): void', () => {
+    describe('changeControlFlow (functionDeclarationNode: IFunctionDeclarationNode, parentNode: INode): void', () => {
         let expressionStatementNode: ESTree.ExpressionStatement,
             functionDeclarationObfuscator: FunctionDeclarationObfuscator,
             functionDeclarationNode: ESTree.FunctionDeclaration,

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

@@ -12,7 +12,7 @@ import { Options } from '../../../src/options/Options';
 const assert: Chai.AssertStatic = require('chai').assert;
 
 describe('FunctionObfuscator', () => {
-    describe('obfuscateNode (functionNode: IFunctionNode): void', () => {
+    describe('changeControlFlow (functionNode: IFunctionNode): void', () => {
         let blockStatementNode: ESTree.BlockStatement,
             expressionStatementNode1: ESTree.ExpressionStatement,
             expressionStatementNode2: ESTree.ExpressionStatement,

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