瀏覽代碼

Merge pull request #169 from javascript-obfuscator/obfuscation-of-eval-expressions

Obfuscation of eval expressions
Timofey Kachalov 7 年之前
父節點
當前提交
8c758a6e7f
共有 55 個文件被更改,包括 1224 次插入441 次删除
  1. 2 0
      CHANGELOG.md
  2. 0 0
      dist/index.js
  3. 11 11
      package.json
  4. 35 71
      src/JavaScriptObfuscator.ts
  5. 2 0
      src/container/InversifyContainerFacade.ts
  6. 6 0
      src/container/modules/node-transformers/FinalizingTransformersModule.ts
  7. 5 0
      src/container/modules/node-transformers/PreparingTransformersModule.ts
  8. 0 18
      src/custom-nodes/AbstractCustomNode.ts
  9. 6 6
      src/custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode.ts
  10. 5 0
      src/declarations/js-string-escape.d.ts
  11. 2 6
      src/enums/logger/LoggingMessage.ts
  12. 1 0
      src/enums/node-transformers/NodeTransformer.ts
  13. 8 0
      src/enums/node-transformers/TransformationStage.ts
  14. 0 5
      src/interfaces/custom-nodes/ICustomNode.d.ts
  15. 11 2
      src/interfaces/node-transformers/INodeTransformer.d.ts
  16. 7 1
      src/interfaces/node-transformers/ITransformersRunner.d.ts
  17. 3 3
      src/interfaces/source-map/ISourceMapCorrector.d.ts
  18. 5 2
      src/node-transformers/AbstractNodeTransformer.ts
  19. 16 5
      src/node-transformers/TransformersRunner.ts
  20. 20 10
      src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts
  21. 23 15
      src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts
  22. 17 9
      src/node-transformers/converting-transformers/MemberExpressionTransformer.ts
  23. 17 9
      src/node-transformers/converting-transformers/MethodDefinitionTransformer.ts
  24. 23 14
      src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts
  25. 18 9
      src/node-transformers/converting-transformers/TemplateLiteralTransformer.ts
  26. 119 96
      src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts
  27. 17 9
      src/node-transformers/obfuscating-transformers/CatchClauseTransformer.ts
  28. 17 9
      src/node-transformers/obfuscating-transformers/ClassDeclarationTransformer.ts
  29. 17 9
      src/node-transformers/obfuscating-transformers/FunctionDeclarationTransformer.ts
  30. 23 15
      src/node-transformers/obfuscating-transformers/FunctionTransformer.ts
  31. 17 9
      src/node-transformers/obfuscating-transformers/LabeledStatementTransformer.ts
  32. 17 9
      src/node-transformers/obfuscating-transformers/LiteralTransformer.ts
  33. 17 9
      src/node-transformers/obfuscating-transformers/ObjectExpressionTransformer.ts
  34. 17 9
      src/node-transformers/obfuscating-transformers/VariableDeclarationTransformer.ts
  35. 18 9
      src/node-transformers/preparing-transformers/CommentsTransformer.ts
  36. 184 0
      src/node-transformers/preparing-transformers/EvaCallExpressionTransformer.ts
  37. 15 7
      src/node-transformers/preparing-transformers/ObfuscatingGuardsTransformer.ts
  38. 16 7
      src/node-transformers/preparing-transformers/ParentificationTransformer.ts
  39. 6 10
      test/dev/dev.ts
  40. 26 3
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/BlockStatementControlFlowTransformer.spec.ts
  41. 29 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/class-declaration.js
  42. 326 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/EvalCallExpressionTransformer.spec.ts
  43. 7 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/call-expression-identifier-reference.js
  44. 5 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/control-flow-flattening-integration.js
  45. 7 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/eval-expression-as-argument.js
  46. 7 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/eval-expression-template-literal.js
  47. 7 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/identifier-reference.js
  48. 7 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/multiple-statements-eval.js
  49. 7 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/nested-eval-expressions.js
  50. 7 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/string-array-calls-wrapper-call.js
  51. 7 0
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/wrong-eval-string.js
  52. 1 0
      test/index.spec.ts
  53. 0 1
      tslint.json
  54. 2 1
      webpack.config.js
  55. 36 33
      yarn.lock

+ 2 - 0
CHANGELOG.md

@@ -3,8 +3,10 @@ Change Log
 v0.14.0
 ---
 * **New option:** `transformObjectKeys` enables object keys transformation and obfuscation.
+* **New feature:** `eval` expressions obfuscation.
 * **Breaking change:** Now CLI obfuscating directory recursively. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/157
 * Fixed runtime errors when `deadCodeInjection` is enabled and `identifierNamesGenerator` is set to `mangled`
+* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/171
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/166
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/156
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/159

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


+ 11 - 11
package.json

@@ -22,15 +22,16 @@
     "chalk": "2.3.0",
     "chance": "1.0.13",
     "class-validator": "0.7.3",
-    "commander": "2.12.2",
+    "commander": "2.13.0",
     "escodegen-wallaby": "1.6.16",
     "esprima": "4.0.0",
     "estraverse": "4.2.0",
     "inversify": "4.9.0",
+    "js-string-escape": "1.0.1",
     "md5": "2.2.1",
     "mkdirp": "0.5.1",
     "opencollective": "1.0.3",
-    "pjson": "^1.0.9",
+    "pjson": "1.0.9",
     "reflect-metadata": "0.1.10",
     "source-map-support": "0.5.0",
     "string-template": "1.0.0",
@@ -39,7 +40,6 @@
   "devDependencies": {
     "@types/chai": "4.1.0",
     "@types/chance": "0.7.36",
-    "@types/commander": "2.12.2",
     "@types/escodegen": "0.0.6",
     "@types/esprima": "4.0.1",
     "@types/estraverse": "0.0.6",
@@ -47,9 +47,9 @@
     "@types/md5": "2.1.32",
     "@types/mkdirp": "0.5.2",
     "@types/mocha": "2.2.46",
-    "@types/node": "8.5.7",
-    "@types/rimraf": "^2.0.2",
-    "@types/sinon": "4.1.2",
+    "@types/node": "9.3.0",
+    "@types/rimraf": "2.0.2",
+    "@types/sinon": "4.1.3",
     "@types/string-template": "1.0.2",
     "@types/webpack-env": "1.13.3",
     "awesome-typescript-loader": "3.4.1",
@@ -62,13 +62,13 @@
     "istanbul": "1.1.0-alpha.1",
     "mocha": "4.1.0",
     "pre-commit": "1.2.2",
-    "rimraf": "^2.6.2",
-    "sinon": "4.1.3",
-    "threads": "^0.10.0",
+    "rimraf": "2.6.2",
+    "sinon": "4.1.4",
+    "threads": "0.10.0",
     "ts-node": "4.1.0",
-    "tslint": "5.8.0",
+    "tslint": "5.9.1",
     "tslint-eslint-rules": "4.1.1",
-    "tslint-language-service": "0.9.7",
+    "tslint-language-service": "0.9.8",
     "tslint-webpack-plugin": "1.0.0",
     "typescript": "2.6.2",
     "webpack": "3.10.0",

+ 35 - 71
src/JavaScriptObfuscator.ts

@@ -23,6 +23,7 @@ import { ITransformersRunner } from './interfaces/node-transformers/ITransformer
 import { LoggingMessage } from './enums/logger/LoggingMessage';
 import { NodeTransformer } from './enums/node-transformers/NodeTransformer';
 import { ObfuscationEvent } from './enums/event-emitters/ObfuscationEvent';
+import { TransformationStage } from './enums/node-transformers/TransformationStage';
 
 import { NodeGuards } from './node/NodeGuards';
 
@@ -40,51 +41,28 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
     /**
      * @type {NodeTransformer[]}
      */
-    private static readonly controlFlowTransformersList: NodeTransformer[] = [
+    private static readonly transformersList: NodeTransformer[] = [
         NodeTransformer.BlockStatementControlFlowTransformer,
-        NodeTransformer.FunctionControlFlowTransformer
-    ];
-
-    /**
-     * @type {NodeTransformer[]}
-     */
-    private static readonly convertingTransformersList: NodeTransformer[] = [
-        NodeTransformer.MemberExpressionTransformer,
-        NodeTransformer.MethodDefinitionTransformer,
-        NodeTransformer.ObjectExpressionKeysTransformer,
-        NodeTransformer.TemplateLiteralTransformer
-    ];
-
-    /**
-     * @type {NodeTransformer[]}
-     */
-    private static readonly deadCodeInjectionTransformersList: NodeTransformer[] = [
-        NodeTransformer.DeadCodeInjectionTransformer
-    ];
-
-    /**
-     * @type {NodeTransformer[]}
-     */
-    private static readonly obfuscatingTransformersList: NodeTransformer[] = [
-        NodeTransformer.CatchClauseTransformer,
         NodeTransformer.ClassDeclarationTransformer,
+        NodeTransformer.CommentsTransformer,
+        NodeTransformer.DeadCodeInjectionTransformer,
+        NodeTransformer.EvalCallExpressionTransformer,
+        NodeTransformer.FunctionControlFlowTransformer,
+        NodeTransformer.CatchClauseTransformer,
         NodeTransformer.FunctionDeclarationTransformer,
         NodeTransformer.FunctionTransformer,
         NodeTransformer.LabeledStatementTransformer,
         NodeTransformer.LiteralTransformer,
+        NodeTransformer.MemberExpressionTransformer,
+        NodeTransformer.MethodDefinitionTransformer,
+        NodeTransformer.ObfuscatingGuardsTransformer,
+        NodeTransformer.ObjectExpressionKeysTransformer,
         NodeTransformer.ObjectExpressionTransformer,
+        NodeTransformer.ParentificationTransformer,
+        NodeTransformer.TemplateLiteralTransformer,
         NodeTransformer.VariableDeclarationTransformer
     ];
 
-    /**
-     * @type {NodeTransformer[]}
-     */
-    private static readonly preparingTransformersList: NodeTransformer[] = [
-        NodeTransformer.CommentsTransformer,
-        NodeTransformer.ObfuscatingGuardsTransformer,
-        NodeTransformer.ParentificationTransformer
-    ];
-
     /**
      * @type {IStorage<ICustomNodeGroup>}
      */
@@ -206,18 +184,11 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
             return astTree;
         }
 
-        // first pass: AST-tree preparation
-        this.logger.info(LoggingMessage.StagePreparingASTTree);
-        astTree = this.transformersRunner.transform(
-            astTree,
-            JavaScriptObfuscator.preparingTransformersList
-        );
+        astTree = this.runTransformationStage(astTree, TransformationStage.Preparing);
 
-        // second pass: AST-tree analyzing
-        this.logger.info(LoggingMessage.StageAnalyzingASTTree);
+        this.logger.info(LoggingMessage.AnalyzingASTTreeStage);
         const stackTraceData: IStackTraceData[] = this.stackTraceAnalyzer.analyze(astTree);
 
-        // initialize custom node groups and configure custom nodes
         this.customNodeGroupStorage
             .getStorage()
             .forEach((customNodeGroup: ICustomNodeGroup) => {
@@ -231,39 +202,17 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
 
         this.obfuscationEventEmitter.emit(ObfuscationEvent.BeforeObfuscation, astTree, stackTraceData);
 
-        // third pass: dead code injection transformer
         if (this.options.deadCodeInjection) {
-            this.logger.info(LoggingMessage.StageDeadCodeInjection);
-
-            astTree = this.transformersRunner.transform(
-                astTree,
-                JavaScriptObfuscator.deadCodeInjectionTransformersList
-            );
+            astTree = this.runTransformationStage(astTree, TransformationStage.DeadCodeInjection);
         }
 
-        // fourth pass: control flow flattening transformers
         if (this.options.controlFlowFlattening) {
-            this.logger.info(LoggingMessage.StageControlFlowFlattening);
-
-            astTree = this.transformersRunner.transform(
-                astTree,
-                JavaScriptObfuscator.controlFlowTransformersList
-            );
+            astTree = this.runTransformationStage(astTree, TransformationStage.ControlFlowFlattening);
         }
 
-        // fifth pass: converting transformers
-        this.logger.info(LoggingMessage.StagePreObfuscation);
-        astTree = this.transformersRunner.transform(
-            astTree,
-            JavaScriptObfuscator.convertingTransformersList
-        );
-
-        // sixth pass: obfuscating transformers
-        this.logger.info(LoggingMessage.StageObfuscation);
-        astTree = this.transformersRunner.transform(
-            astTree,
-            JavaScriptObfuscator.obfuscatingTransformersList
-        );
+        astTree = this.runTransformationStage(astTree, TransformationStage.Converting);
+        astTree = this.runTransformationStage(astTree, TransformationStage.Obfuscating);
+        astTree = this.runTransformationStage(astTree, TransformationStage.Finalizing);
 
         this.obfuscationEventEmitter.emit(ObfuscationEvent.AfterObfuscation, astTree, stackTraceData);
 
@@ -307,4 +256,19 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
             generatorOutput.map
         );
     }
+
+    /**
+     * @param {Program} astTree
+     * @param {TransformationStage} transformationStage
+     * @returns {Program}
+     */
+    private runTransformationStage (astTree: ESTree.Program, transformationStage: TransformationStage): ESTree.Program {
+        this.logger.info(LoggingMessage.TransformationStage, transformationStage);
+
+        return this.transformersRunner.transform(
+            astTree,
+            JavaScriptObfuscator.transformersList,
+            transformationStage
+        );
+    }
 }

+ 2 - 0
src/container/InversifyContainerFacade.ts

@@ -5,6 +5,7 @@ import { analyzersModule } from './modules/analyzers/AnalyzersModule';
 import { controlFlowTransformersModule } from './modules/node-transformers/ControlFlowTransformersModule';
 import { convertingTransformersModule } from './modules/node-transformers/ConvertingTransformersModule';
 import { customNodesModule } from './modules/custom-nodes/CustomNodesModule';
+import { finalizingTransformersModule } from './modules/node-transformers/FinalizingTransformersModule';
 import { generatorsModule } from './modules/generators/GeneratorsModule';
 import { nodeTransformersModule } from './modules/node-transformers/NodeTransformersModule';
 import { obfuscatingTransformersModule } from './modules/node-transformers/ObfuscatingTransformersModule';
@@ -198,6 +199,7 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
         this.container.load(controlFlowTransformersModule);
         this.container.load(convertingTransformersModule);
         this.container.load(customNodesModule);
+        this.container.load(finalizingTransformersModule);
         this.container.load(generatorsModule);
         this.container.load(nodeTransformersModule);
         this.container.load(obfuscatingTransformersModule);

+ 6 - 0
src/container/modules/node-transformers/FinalizingTransformersModule.ts

@@ -0,0 +1,6 @@
+import { ContainerModule, interfaces } from 'inversify';
+
+export const finalizingTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
+    // finalizing transformers
+
+});

+ 5 - 0
src/container/modules/node-transformers/PreparingTransformersModule.ts

@@ -11,6 +11,7 @@ import { ObfuscatingGuard } from '../../../enums/node-transformers/preparing-tra
 import { BlackListObfuscatingGuard } from '../../../node-transformers/preparing-transformers/obfuscating-guards/BlackListObfuscatingGuard';
 import { CommentsTransformer } from '../../../node-transformers/preparing-transformers/CommentsTransformer';
 import { ConditionalCommentObfuscatingGuard } from '../../../node-transformers/preparing-transformers/obfuscating-guards/ConditionalCommentObfuscatingGuard';
+import { EvalCallExpressionTransformer } from '../../../node-transformers/preparing-transformers/EvaCallExpressionTransformer';
 import { ObfuscatingGuardsTransformer } from '../../../node-transformers/preparing-transformers/ObfuscatingGuardsTransformer';
 import { ParentificationTransformer } from '../../../node-transformers/preparing-transformers/ParentificationTransformer';
 
@@ -20,6 +21,10 @@ export const preparingTransformersModule: interfaces.ContainerModule = new Conta
         .to(CommentsTransformer)
         .whenTargetNamed(NodeTransformer.CommentsTransformer);
 
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(EvalCallExpressionTransformer)
+        .whenTargetNamed(NodeTransformer.EvalCallExpressionTransformer);
+
     bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
         .to(ObfuscatingGuardsTransformer)
         .whenTargetNamed(NodeTransformer.ObfuscatingGuardsTransformer);

+ 0 - 18
src/custom-nodes/AbstractCustomNode.ts

@@ -12,8 +12,6 @@ import { IRandomGenerator } from '../interfaces/utils/IRandomGenerator';
 import { GlobalVariableTemplate1 } from '../templates/GlobalVariableTemplate1';
 import { GlobalVariableTemplate2 } from '../templates/GlobalVariableTemplate2';
 
-import { NodeUtils } from '../node/NodeUtils';
-
 @injectable()
 export abstract class AbstractCustomNode implements ICustomNode {
     /**
@@ -24,11 +22,6 @@ export abstract class AbstractCustomNode implements ICustomNode {
         GlobalVariableTemplate2()
     ];
 
-    /**
-     * @type {string}
-     */
-    protected cachedCode: string;
-
     /**
      * @type {TStatement[]}
      */
@@ -70,17 +63,6 @@ export abstract class AbstractCustomNode implements ICustomNode {
      */
     public abstract initialize (...args: any[]): void;
 
-    /**
-     * @returns {string}
-     */
-    public getCode (): string {
-        if (!this.cachedCode) {
-            this.cachedCode = NodeUtils.convertStructureToCode(this.getNode());
-        }
-
-        return this.cachedCode;
-    }
-
     /**
      * @returns {TStatement[]}
      */

+ 6 - 6
src/custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode.ts

@@ -27,7 +27,7 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
      * @type {BlockStatement}
      */
     @initializable()
-    private randomBlockStatementNode: BlockStatement;
+    private deadCodeInjectionRootAstHostNode: BlockStatement;
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
@@ -45,14 +45,14 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
 
     /**
      * @param {BlockStatement} blockStatementNode
-     * @param {BlockStatement} randomBlockStatementNode
+     * @param {BlockStatement} deadCodeInjectionRootAstHostNode
      */
     public initialize (
         blockStatementNode: BlockStatement,
-        randomBlockStatementNode: BlockStatement
+        deadCodeInjectionRootAstHostNode: BlockStatement
     ): void {
         this.blockStatementNode = blockStatementNode;
-        this.randomBlockStatementNode = randomBlockStatementNode;
+        this.deadCodeInjectionRootAstHostNode = deadCodeInjectionRootAstHostNode;
     }
 
     /**
@@ -67,8 +67,8 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
         const rightString: string = random2 ? leftString : this.randomGenerator.getRandomString(5);
 
         const [consequent, alternate]: [BlockStatement, BlockStatement] = random1 === random2
-            ? [this.blockStatementNode, this.randomBlockStatementNode]
-            : [this.randomBlockStatementNode, this.blockStatementNode];
+            ? [this.blockStatementNode, this.deadCodeInjectionRootAstHostNode]
+            : [this.deadCodeInjectionRootAstHostNode, this.blockStatementNode];
 
         const structure: BlockStatement = Nodes.getBlockStatementNode([
             Nodes.getIfStatementNode(

+ 5 - 0
src/declarations/js-string-escape.d.ts

@@ -0,0 +1,5 @@
+declare module 'js-string-escape' {
+    function jsStringEscape (input: string): string;
+
+    export = jsStringEscape;
+}

+ 2 - 6
src/enums/logger/LoggingMessage.ts

@@ -1,13 +1,9 @@
 export enum LoggingMessage {
+    AnalyzingASTTreeStage = 'Stage: analyzing AST-tree...',
     EmptySourceCode = 'Empty source code. Obfuscation canceled...',
     ObfuscationCompleted = 'Obfuscation completed. Total time: %s sec.',
     ObfuscationStarted = 'Obfuscation started...',
     RandomGeneratorSeed = 'Random generator seed: %s...',
-    StagePreparingASTTree = 'Stage: preparing AST-tree...',
-    StageAnalyzingASTTree = 'Stage: analyzing AST-tree...',
-    StageControlFlowFlattening = 'Stage: control flow flattening...',
-    StageDeadCodeInjection = 'Stage: dead code injection...',
-    StagePreObfuscation = 'Stage: pre-obfuscation...',
-    StageObfuscation = 'Stage: obfuscation...',
+    TransformationStage = 'Transformation stage: %s...',
     Version = 'Version: %s'
 }

+ 1 - 0
src/enums/node-transformers/NodeTransformer.ts

@@ -3,6 +3,7 @@ export enum NodeTransformer {
     ClassDeclarationTransformer = 'ClassDeclarationTransformer',
     CommentsTransformer = 'CommentsTransformer',
     DeadCodeInjectionTransformer = 'DeadCodeInjectionTransformer',
+    EvalCallExpressionTransformer = 'EvalCallExpressionTransformer',
     FunctionControlFlowTransformer = 'FunctionControlFlowTransformer',
     CatchClauseTransformer = 'CatchClauseTransformer',
     FunctionDeclarationTransformer = 'FunctionDeclarationTransformer',

+ 8 - 0
src/enums/node-transformers/TransformationStage.ts

@@ -0,0 +1,8 @@
+export enum TransformationStage {
+    Preparing = 'Preparing',
+    DeadCodeInjection = 'DeadCodeInjection',
+    ControlFlowFlattening = 'ControlFlowFlattening',
+    Converting = 'Converting',
+    Obfuscating = 'Obfuscating',
+    Finalizing = 'Finalizing'
+}

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

@@ -3,11 +3,6 @@ import { TStatement } from '../../types/node/TStatement';
 import { IInitializable } from '../IInitializable';
 
 export interface ICustomNode extends IInitializable {
-    /**
-     * @returns {string}
-     */
-    getCode (): string;
-
     /**
      * @returns ESTree.Node[]
      */

+ 11 - 2
src/interfaces/node-transformers/INodeTransformer.d.ts

@@ -3,11 +3,14 @@ import * as ESTree from 'estree';
 
 import { IVisitor } from './IVisitor';
 
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
+
 export interface INodeTransformer {
     /**
-     * @returns {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    getVisitor (): IVisitor;
+    getVisitor (transformationStage: TransformationStage): IVisitor | null;
 
     /**
      * @param {Node} node
@@ -15,6 +18,12 @@ export interface INodeTransformer {
      */
     analyzeNode ? (node: ESTree.Node, parentNode: ESTree.Node | null): void;
 
+    /**
+     * @param {Node} node
+     * @param {Node | null} parentNode
+     */
+    restoreNode ? (node: ESTree.Node, parentNode: ESTree.Node | null): void;
+
     /**
      * @param {Node} node
      * @param {Node | null} parentNode

+ 7 - 1
src/interfaces/node-transformers/ITransformersRunner.d.ts

@@ -1,12 +1,18 @@
 import * as ESTree from 'estree';
 
 import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 export interface ITransformersRunner {
     /**
      * @param {T} astTree
      * @param {NodeTransformer[]} nodeTransformers
+     * @param {TransformationStage} transformationStage
      * @returns {T}
      */
-    transform <T extends ESTree.Node = ESTree.Program> (astTree: T, nodeTransformers: NodeTransformer[]): T;
+    transform <T extends ESTree.Node = ESTree.Program> (
+        astTree: T,
+        nodeTransformers: NodeTransformer[],
+        transformationStage: TransformationStage
+    ): T;
 }

+ 3 - 3
src/interfaces/source-map/ISourceMapCorrector.d.ts

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

+ 5 - 2
src/node-transformers/AbstractNodeTransformer.ts

@@ -12,6 +12,8 @@ import { IVisitor } from '../interfaces/node-transformers/IVisitor';
 
 import { initializable } from '../decorators/Initializable';
 
+import { TransformationStage } from '../enums/node-transformers/TransformationStage';
+
 @injectable()
 export abstract class AbstractNodeTransformer implements INodeTransformer, IInitializable {
     /**
@@ -48,9 +50,10 @@ export abstract class AbstractNodeTransformer implements INodeTransformer, IInit
     }
 
     /**
-     * @returns {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public abstract getVisitor (): IVisitor;
+    public abstract getVisitor (transformationStage: TransformationStage): IVisitor | null;
 
     /**
      * @param {Node} node

+ 16 - 5
src/node-transformers/TransformersRunner.ts

@@ -13,6 +13,7 @@ import { ITransformersRunner } from '../interfaces/node-transformers/ITransforme
 import { IVisitor } from '../interfaces/node-transformers/IVisitor';
 
 import { NodeTransformer } from '../enums/node-transformers/NodeTransformer';
+import { TransformationStage } from '../enums/node-transformers/TransformationStage';
 import { VisitorDirection } from '../enums/node-transformers/VisitorDirection';
 
 import { NodeGuards } from '../node/NodeGuards';
@@ -36,11 +37,13 @@ export class TransformersRunner implements ITransformersRunner {
     /**
      * @param {T} astTree
      * @param {NodeTransformer[]} nodeTransformers
+     * @param {TransformationStage} transformationStage
      * @returns {T}
      */
     public transform <T extends ESTree.Node = ESTree.Program> (
         astTree: T,
-        nodeTransformers: NodeTransformer[]
+        nodeTransformers: NodeTransformer[],
+        transformationStage: TransformationStage
     ): T {
         if (!nodeTransformers.length) {
             return astTree;
@@ -50,20 +53,28 @@ export class TransformersRunner implements ITransformersRunner {
         const leaveVisitors: IVisitor[] = [];
         const nodeTransformersLength: number = nodeTransformers.length;
 
-        let visitor: IVisitor;
+        let visitor: IVisitor | null;
 
         for (let i: number = 0; i < nodeTransformersLength; i++) {
-            visitor = this.nodeTransformerFactory(nodeTransformers[i]).getVisitor();
+            visitor = this.nodeTransformerFactory(nodeTransformers[i]).getVisitor(transformationStage);
+
+            if (!visitor) {
+                continue;
+            }
 
             if (visitor.enter) {
-                enterVisitors.push(visitor);
+                enterVisitors.push({ enter: visitor.enter });
             }
 
             if (visitor.leave) {
-                leaveVisitors.push(visitor);
+                leaveVisitors.push({ leave: visitor.leave });
             }
         }
 
+        if (!enterVisitors.length && !leaveVisitors.length) {
+            return astTree;
+        }
+
         estraverse.replace(astTree, {
             enter: this.mergeVisitorsForDirection(enterVisitors, VisitorDirection.Enter),
             leave: this.mergeVisitorsForDirection(leaveVisitors, VisitorDirection.Leave)

+ 20 - 10
src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts

@@ -14,6 +14,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { ControlFlowCustomNode } from '../../enums/custom-nodes/ControlFlowCustomNode';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -60,10 +61,12 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme
                 || NodeGuards.isContinueStatementNode(statement);
             const isVariableDeclarationWithLetOrConstKind: boolean = NodeGuards.isVariableDeclarationNode(statement)
                 && (statement.kind === 'const' || statement.kind === 'let');
+            const isClassDeclaration: boolean = NodeGuards.isClassDeclarationNode(statement);
 
             return NodeGuards.isFunctionDeclarationNode(statement)
                 || isBreakOrContinueStatement
-                || isVariableDeclarationWithLetOrConstKind;
+                || isVariableDeclarationWithLetOrConstKind
+                || isClassDeclaration;
         });
     }
 
@@ -97,16 +100,23 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isBlockStatementNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.ControlFlowFlattening:
+                return {
+                    leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isBlockStatementNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 23 - 15
src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts

@@ -18,6 +18,7 @@ import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 import { ControlFlowCustomNode } from '../../enums/custom-nodes/ControlFlowCustomNode';
 import { ControlFlowReplacer } from '../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/ControlFlowReplacer';
 import { NodeType } from '../../enums/node/NodeType';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -101,22 +102,29 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (
-                    parentNode && (
-                        NodeGuards.isFunctionDeclarationNode(node) ||
-                        NodeGuards.isFunctionExpressionNode(node) ||
-                        NodeGuards.isArrowFunctionExpressionNode(node)
-                    )
-                ) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.ControlFlowFlattening:
+                return {
+                    leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (
+                            parentNode && (
+                                NodeGuards.isFunctionDeclarationNode(node) ||
+                                NodeGuards.isFunctionExpressionNode(node) ||
+                                NodeGuards.isArrowFunctionExpressionNode(node)
+                            )
+                        ) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 17 - 9
src/node-transformers/converting-transformers/MemberExpressionTransformer.ts

@@ -8,6 +8,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { NodeType } from '../../enums/node/NodeType';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -26,16 +27,23 @@ export class MemberExpressionTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isMemberExpressionNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Converting:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isMemberExpressionNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 17 - 9
src/node-transformers/converting-transformers/MethodDefinitionTransformer.ts

@@ -8,6 +8,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { NodeType } from '../../enums/node/NodeType';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -40,16 +41,23 @@ export class MethodDefinitionTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isMethodDefinitionNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Converting:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isMethodDefinitionNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 23 - 14
src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts

@@ -9,6 +9,8 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
+
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -62,21 +64,28 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (
-                    this.options.transformObjectKeys
-                    && parentNode
-                    && NodeGuards.isObjectExpressionNode(node)
-                    && NodeGuards.isVariableDeclaratorNode(parentNode)
-                ) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Converting:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (
+                            this.options.transformObjectKeys
+                            && parentNode
+                            && NodeGuards.isObjectExpressionNode(node)
+                            && NodeGuards.isVariableDeclaratorNode(parentNode)
+                        ) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 18 - 9
src/node-transformers/converting-transformers/TemplateLiteralTransformer.ts

@@ -7,6 +7,8 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
+
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
 import { Nodes } from '../../node/Nodes';
@@ -37,16 +39,23 @@ export class TemplateLiteralTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isTemplateLiteralNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Converting:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isTemplateLiteralNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 119 - 96
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -10,19 +10,24 @@ import { TNodeWithBlockScope } from '../../types/node/TNodeWithBlockScope';
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
-import { ITransformersRunner } from '../../interfaces/node-transformers/ITransformersRunner';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { DeadCodeInjectionCustomNode } from '../../enums/custom-nodes/DeadCodeInjectionCustomNode';
-import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
 import { NodeType } from '../../enums/node/NodeType';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
+import { Nodes } from '../../node/Nodes';
 import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
 export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {string}
+     */
+    private static deadCodeInjectionRootAstHostNodeName: string = 'deadCodeInjectionRootAstHostNode';
+
     /**
      * @type {number}
      */
@@ -34,16 +39,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     private static readonly minCollectedBlockStatementsCount: number = 5;
 
     /**
-     * @type {NodeTransformer[]}
+     * @type {Set <BlockStatement>}
      */
-    private static readonly transformersToRenameBlockScopeIdentifiers: NodeTransformer[] = [
-        NodeTransformer.CatchClauseTransformer,
-        NodeTransformer.ClassDeclarationTransformer,
-        NodeTransformer.FunctionDeclarationTransformer,
-        NodeTransformer.FunctionTransformer,
-        NodeTransformer.LabeledStatementTransformer,
-        NodeTransformer.VariableDeclarationTransformer
-    ];
+    private readonly deadCodeInjectionRootAstHostNodeSet: Set <ESTree.BlockStatement> = new Set();
 
     /**
      * @type {ESTree.BlockStatement[]}
@@ -53,66 +51,101 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     /**
      * @type {number}
      */
-    private collectedBlockStatementsLength: number;
+    private collectedBlockStatementsTotalLength: number;
 
     /**
      * @type {TDeadNodeInjectionCustomNodeFactory}
      */
     private readonly deadCodeInjectionCustomNodeFactory: TDeadNodeInjectionCustomNodeFactory;
 
-    /**
-     * @type {ITransformersRunner}
-     */
-    private readonly transformersRunner: ITransformersRunner;
-
     /**
      * @param {TControlFlowCustomNodeFactory} deadCodeInjectionCustomNodeFactory
-     * @param {ITransformersRunner} transformersRunner
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IDeadCodeInjectionCustomNode)
             deadCodeInjectionCustomNodeFactory: TDeadNodeInjectionCustomNodeFactory,
-        @inject(ServiceIdentifiers.ITransformersRunner) transformersRunner: ITransformersRunner,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(randomGenerator, options);
 
         this.deadCodeInjectionCustomNodeFactory = deadCodeInjectionCustomNodeFactory;
-        this.transformersRunner = transformersRunner;
     }
 
     /**
-     * @param {Node} node
+     * @param {Node} blockStatementNode
      * @returns {boolean}
      */
-    private static isValidBlockStatementNode (node: ESTree.Node): boolean {
-        return !NodeGuards.isBreakStatementNode(node) &&
-            !NodeGuards.isContinueStatementNode(node) &&
-            !NodeGuards.isAwaitExpressionNode(node) &&
-            !NodeGuards.isSuperNode(node);
+    private static isValidBlockStatementNode (blockStatementNode: ESTree.Node): boolean {
+        const isProhibitedNode: (node: ESTree.Node) => boolean =
+            (node: ESTree.Node): boolean => NodeGuards.isBreakStatementNode(node) ||
+                NodeGuards.isContinueStatementNode(node) ||
+                NodeGuards.isAwaitExpressionNode(node) ||
+                NodeGuards.isSuperNode(node);
+
+        let nestedBlockStatementsCount: number = 0,
+            isValidBlockStatementNode: boolean = true;
+
+        estraverse.traverse(blockStatementNode, {
+            enter: (node: ESTree.Node): any => {
+                if (NodeGuards.isBlockStatementNode(node)) {
+                    nestedBlockStatementsCount++;
+                }
+
+                if (
+                    nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
+                    isProhibitedNode(node)
+                ) {
+                    isValidBlockStatementNode = false;
+
+                    return estraverse.VisitorOption.Break;
+                }
+            }
+        });
+
+        return isValidBlockStatementNode;
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isProgramNode(node)) {
-                    this.analyzeNode(node, parentNode);
-
-                    return node;
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.DeadCodeInjection:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isProgramNode(node)) {
+                            this.analyzeNode(node, parentNode);
+
+                            return node;
+                        }
+                    },
+                    leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isBlockStatementNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            case TransformationStage.Finalizing:
+                if (!this.deadCodeInjectionRootAstHostNodeSet.size) {
+                    return null;
                 }
-            },
-            leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isBlockStatementNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && this.isDeadCodeInjectionRootAstHostNode(node)) {
+                            return this.restoreNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**
@@ -122,13 +155,21 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     public analyzeNode (programNode: ESTree.Node, parentNode: ESTree.Node): void {
         estraverse.traverse(programNode, {
             enter: (node: ESTree.Node): any => {
-                if (NodeGuards.isBlockStatementNode(node)) {
-                    this.collectBlockStatementNodes(node, this.collectedBlockStatements, parentNode);
+                if (!NodeGuards.isBlockStatementNode(node)) {
+                    return;
                 }
+
+                const clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(node);
+
+                if (!DeadCodeInjectionTransformer.isValidBlockStatementNode(clonedBlockStatementNode)) {
+                    return;
+                }
+
+                this.collectedBlockStatements.push(clonedBlockStatementNode);
             }
         });
 
-        this.collectedBlockStatementsLength = this.collectedBlockStatements.length;
+        this.collectedBlockStatementsTotalLength = this.collectedBlockStatements.length;
     }
 
     /**
@@ -137,7 +178,7 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      * @returns {NodeGuards | VisitorOption}
      */
     public transformNode (blockStatementNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node | estraverse.VisitorOption {
-        if (this.collectedBlockStatementsLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount) {
+        if (this.collectedBlockStatementsTotalLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount) {
             return estraverse.VisitorOption.Break;
         }
 
@@ -169,59 +210,26 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @param {BlockStatement} blockStatementNode
-     * @param {BlockStatement[]} collectedBlockStatements
+     * @param {FunctionExpression} deadCodeInjectionRootAstHostNode
      * @param {Node} parentNode
+     * @returns {Node}
      */
-    private collectBlockStatementNodes (
-        blockStatementNode: ESTree.BlockStatement,
-        collectedBlockStatements: ESTree.BlockStatement[],
-        parentNode: ESTree.Node
-    ): void {
-        let clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(blockStatementNode),
-            nestedBlockStatementsCount: number = 0,
-            isValidBlockStatementNode: boolean = true;
-
-        estraverse.replace(clonedBlockStatementNode, {
-            enter: (node: ESTree.Node): any => {
-                /**
-                 * Count nested block statements in current block statement
-                 */
-                if (NodeGuards.isBlockStatementNode(node)) {
-                    nestedBlockStatementsCount++;
-                }
+    public restoreNode (deadCodeInjectionRootAstHostNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node {
+        const hostNodeFirstStatement: ESTree.Statement = deadCodeInjectionRootAstHostNode.body[0];
 
-                /**
-                 * If nested block statements count bigger then specified amount or current block statement
-                 * contains prohibited nodes - we will stop traversing and leave method
-                 */
-                if (
-                    nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
-                    !DeadCodeInjectionTransformer.isValidBlockStatementNode(node)
-                ) {
-                    isValidBlockStatementNode = false;
-
-                    return estraverse.VisitorOption.Break;
-                }
-
-                return node;
-            }
-        });
-
-        if (!isValidBlockStatementNode) {
-            return;
+        if (!NodeGuards.isFunctionDeclarationNode(hostNodeFirstStatement)) {
+            throw new Error('Wrong dead code injection root AST host node. Host node should contain `FunctionDeclaration` node');
         }
 
-        /**
-         * We should transform identifiers in the dead code block statement to avoid conflicts with original code
-         */
-        NodeUtils.parentizeNode(clonedBlockStatementNode, parentNode);
-        clonedBlockStatementNode = this.transformersRunner.transform(
-            clonedBlockStatementNode,
-            DeadCodeInjectionTransformer.transformersToRenameBlockScopeIdentifiers
-        );
+        return hostNodeFirstStatement.body;
+    }
 
-        collectedBlockStatements.push(clonedBlockStatementNode);
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    private isDeadCodeInjectionRootAstHostNode (node: ESTree.Node): node is ESTree.BlockStatement {
+        return NodeGuards.isBlockStatementNode(node) && this.deadCodeInjectionRootAstHostNodeSet.has(node);
     }
 
     /**
@@ -235,14 +243,29 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
         randomBlockStatementNode: ESTree.BlockStatement,
         parentNode: ESTree.Node
     ): ESTree.BlockStatement {
+        /**
+         * we should wrap original random block statement node into the parent block statement node (ast root host node)
+         * with function declaration node. This function declaration node will create block scope for all identifiers
+         * inside random block statement node and this identifiers won't affect identifiers of the rest AST tree.
+         */
+        const deadCodeInjectionRootAstHostNode: ESTree.BlockStatement = Nodes.getBlockStatementNode([
+            Nodes.getFunctionDeclarationNode(
+                DeadCodeInjectionTransformer.deadCodeInjectionRootAstHostNodeName,
+                [],
+                randomBlockStatementNode
+            )
+        ]);
+
+        /**
+         * we should store that host node and then extract random block statement node on the `finalizing` stage
+         */
+        this.deadCodeInjectionRootAstHostNodeSet.add(deadCodeInjectionRootAstHostNode);
+
         const blockStatementDeadCodeInjectionCustomNode: ICustomNode = this.deadCodeInjectionCustomNodeFactory(
             DeadCodeInjectionCustomNode.BlockStatementDeadCodeInjectionNode
         );
 
-        blockStatementDeadCodeInjectionCustomNode.initialize(
-            blockStatementNode,
-            randomBlockStatementNode
-        );
+        blockStatementDeadCodeInjectionCustomNode.initialize(blockStatementNode, deadCodeInjectionRootAstHostNode);
 
         const newBlockStatementNode: ESTree.BlockStatement = <ESTree.BlockStatement>blockStatementDeadCodeInjectionCustomNode.getNode()[0];
 

+ 17 - 9
src/node-transformers/obfuscating-transformers/CatchClauseTransformer.ts

@@ -12,6 +12,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { IdentifierObfuscatingReplacer } from '../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -50,16 +51,23 @@ export class CatchClauseTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isCatchClauseNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Obfuscating:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isCatchClauseNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 17 - 9
src/node-transformers/obfuscating-transformers/ClassDeclarationTransformer.ts

@@ -14,6 +14,7 @@ import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { IdentifierObfuscatingReplacer } from "../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer";
 import { NodeType } from '../../enums/node/NodeType';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -59,16 +60,23 @@ export class ClassDeclarationTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isClassDeclarationNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Obfuscating:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isClassDeclarationNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 17 - 9
src/node-transformers/obfuscating-transformers/FunctionDeclarationTransformer.ts

@@ -16,6 +16,7 @@ import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { IdentifierObfuscatingReplacer } from "../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer";
 import { NodeType } from '../../enums/node/NodeType';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -61,16 +62,23 @@ export class FunctionDeclarationTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isFunctionDeclarationNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Obfuscating:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isFunctionDeclarationNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 23 - 15
src/node-transformers/obfuscating-transformers/FunctionTransformer.ts

@@ -12,6 +12,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { IdentifierObfuscatingReplacer } from '../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -50,22 +51,29 @@ export class FunctionTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (
-                    parentNode && (
-                        NodeGuards.isFunctionDeclarationNode(node) ||
-                        NodeGuards.isFunctionExpressionNode(node) ||
-                        NodeGuards.isArrowFunctionExpressionNode(node)
-                    )
-                ) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Obfuscating:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (
+                            parentNode && (
+                                NodeGuards.isFunctionDeclarationNode(node) ||
+                                NodeGuards.isFunctionExpressionNode(node) ||
+                                NodeGuards.isArrowFunctionExpressionNode(node)
+                            )
+                        ) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 17 - 9
src/node-transformers/obfuscating-transformers/LabeledStatementTransformer.ts

@@ -12,6 +12,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { IdentifierObfuscatingReplacer } from '../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -58,16 +59,23 @@ export class LabeledStatementTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isLabeledStatementNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Obfuscating:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isLabeledStatementNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 17 - 9
src/node-transformers/obfuscating-transformers/LiteralTransformer.ts

@@ -10,6 +10,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { LiteralObfuscatingReplacer } from '../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/LiteralObfuscatingReplacer';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -38,16 +39,23 @@ export class LiteralTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isLiteralNode(node) && !node.obfuscatedNode) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Obfuscating:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isLiteralNode(node) && !node.obfuscatedNode) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 17 - 9
src/node-transformers/obfuscating-transformers/ObjectExpressionTransformer.ts

@@ -8,6 +8,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { NodeType } from '../../enums/node/NodeType';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -45,16 +46,23 @@ export class ObjectExpressionTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isObjectExpressionNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Obfuscating:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isObjectExpressionNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 17 - 9
src/node-transformers/obfuscating-transformers/VariableDeclarationTransformer.ts

@@ -16,6 +16,7 @@ import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { IdentifierObfuscatingReplacer } from "../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer";
 import { NodeType } from '../../enums/node/NodeType';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
@@ -62,16 +63,23 @@ export class VariableDeclarationTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isVariableDeclarationNode(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Obfuscating:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isVariableDeclarationNode(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 18 - 9
src/node-transformers/preparing-transformers/CommentsTransformer.ts

@@ -7,6 +7,8 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
+
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
 
@@ -29,16 +31,23 @@ export class CommentsTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && NodeGuards.isNodeWithComments(node)) {
-                    return this.transformNode(node, parentNode);
-                }
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Preparing:
+                return {
+                    leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isNodeWithComments(node)) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 184 - 0
src/node-transformers/preparing-transformers/EvaCallExpressionTransformer.ts

@@ -0,0 +1,184 @@
+import { inject, injectable, } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import * as ESTree from 'estree';
+import jsStringEscape = require('js-string-escape');
+
+import { TStatement } from '../../types/node/TStatement';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
+
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
+
+import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { NodeGuards } from '../../node/NodeGuards';
+import { Nodes } from '../../node/Nodes';
+import { NodeUtils } from '../../node/NodeUtils';
+
+@injectable()
+export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {Set <FunctionExpression>}
+     */
+    private readonly evalRootAstHostNodeSet: Set <ESTree.FunctionExpression> = new Set();
+
+    /**
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     */
+    constructor (
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(randomGenerator, options);
+    }
+
+    /**
+     * @param {Expression | SpreadElement} node
+     * @returns {string | null}
+     */
+    private static extractEvalStringFromCallExpressionArgument (node: ESTree.Expression | ESTree.SpreadElement): string | null {
+        if (NodeGuards.isLiteralNode(node)) {
+            return EvalCallExpressionTransformer
+                .extractEvalStringFromLiteralNode(node);
+        }
+
+        if (NodeGuards.isTemplateLiteralNode(node)) {
+            return EvalCallExpressionTransformer
+                .extractEvalStringFromTemplateLiteralNode(node);
+        }
+
+        return null;
+    }
+
+    /**
+     * @param {Literal} node
+     * @returns {string | null}
+     */
+    private static extractEvalStringFromLiteralNode (node: ESTree.Literal): string | null {
+        return typeof node.value === 'string' ? node.value : null;
+    }
+
+    /**
+     * @param {TemplateLiteral} node
+     * @returns {string | null}
+     */
+    private static extractEvalStringFromTemplateLiteralNode (node: ESTree.TemplateLiteral): string | null {
+        const quasis: ESTree.TemplateElement[] = node.quasis;
+        const allowedQuasisLength: number = 1;
+
+        if (quasis.length !== allowedQuasisLength || node.expressions.length) {
+            return null;
+        }
+
+        return quasis[0].value.cooked;
+    }
+
+    /**
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
+     */
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Preparing:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (
+                            parentNode
+                            && NodeGuards.isCallExpressionNode(node)
+                            && NodeGuards.isIdentifierNode(node.callee)
+                            && node.callee.name === 'eval'
+                        ) {
+                            return this.transformNode(node, parentNode);
+                        }
+                    }
+                };
+
+            case TransformationStage.Finalizing:
+                if (!this.evalRootAstHostNodeSet.size) {
+                    return null;
+                }
+
+                return {
+                    leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && this.isEvalRootAstHostNode(node)) {
+                            return this.restoreNode(node, parentNode);
+                        }
+                    }
+                };
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * @param {CallExpression} callExpressionNode
+     * @param {Node} parentNode
+     * @returns {Node}
+     */
+    public transformNode (callExpressionNode: ESTree.CallExpression, parentNode: ESTree.Node): ESTree.Node {
+        const callExpressionFirstArgument: ESTree.Expression | ESTree.SpreadElement = callExpressionNode.arguments[0];
+
+        if (!callExpressionFirstArgument) {
+            return callExpressionNode;
+        }
+
+        const evalString: string | null = EvalCallExpressionTransformer
+            .extractEvalStringFromCallExpressionArgument(callExpressionFirstArgument);
+
+        if (!evalString) {
+            return callExpressionNode;
+        }
+
+        let ast: TStatement[];
+
+        // wrapping into try-catch to prevent parsing of incorrect `eval` string
+        try {
+            ast = NodeUtils.convertCodeToStructure(evalString);
+        } catch (e) {
+            return callExpressionNode;
+        }
+
+        /**
+         * we should wrap AST-tree into the parent function expression node (ast root host node).
+         * This function expression node will help to correctly transform AST-tree.
+         */
+        const evalRootAstHostNode: ESTree.FunctionExpression = Nodes
+            .getFunctionExpressionNode([], Nodes.getBlockStatementNode(<any>ast));
+
+        /**
+         * we should store that host node and then extract AST-tree on the `finalizing` stage
+         */
+        this.evalRootAstHostNodeSet.add(evalRootAstHostNode);
+
+        return evalRootAstHostNode;
+    }
+
+    /**
+     * @param {FunctionExpression} evalRootAstHostNode
+     * @param {Node} parentNode
+     * @returns {Node}
+     */
+    public restoreNode (evalRootAstHostNode: ESTree.FunctionExpression, parentNode: ESTree.Node): ESTree.Node {
+        const targetAst: ESTree.Statement[] = evalRootAstHostNode.body.body;
+        const obfuscatedCode: string = NodeUtils.convertStructureToCode(targetAst);
+
+        return Nodes.getCallExpressionNode(
+            Nodes.getIdentifierNode('eval'),
+            [
+                Nodes.getLiteralNode(jsStringEscape(obfuscatedCode))
+            ]
+        );
+    }
+
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    private isEvalRootAstHostNode (node: ESTree.Node): node is ESTree.FunctionExpression {
+        return NodeGuards.isFunctionExpressionNode(node) && this.evalRootAstHostNodeSet.has(node);
+    }
+}

+ 15 - 7
src/node-transformers/preparing-transformers/ObfuscatingGuardsTransformer.ts

@@ -11,6 +11,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { ObfuscatingGuard } from '../../enums/node-transformers/preparing-transformers/obfuscating-guards/ObfuscatingGuard';
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 
@@ -48,14 +49,21 @@ export class ObfuscatingGuardsTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                return this.transformNode(node, parentNode);
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Preparing:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        return this.transformNode(node, parentNode);
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 16 - 7
src/node-transformers/preparing-transformers/ParentificationTransformer.ts

@@ -7,6 +7,8 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
+import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
+
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeUtils } from '../../node/NodeUtils';
 
@@ -27,14 +29,21 @@ export class ParentificationTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @return {IVisitor}
+     * @param {TransformationStage} transformationStage
+     * @returns {IVisitor | null}
      */
-    public getVisitor (): IVisitor {
-        return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                return this.transformNode(node, parentNode);
-            }
-        };
+    public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        switch (transformationStage) {
+            case TransformationStage.Preparing:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        return this.transformNode(node, parentNode);
+                    }
+                };
+
+            default:
+                return null;
+        }
     }
 
     /**

+ 6 - 10
test/dev/dev.ts

@@ -7,20 +7,16 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
         (function(){
-            var object = {
-                foo: 'test1',
-                bar: {
-                    baz: 'test2'
-                }
-            };
+            function foo () {
+                eval('var s = 1;');
+            }
+        
+            foo();
         })();
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
-            compact: false,
-            transformObjectKeys: true,
-            stringArray: true,
-            stringArrayThreshold: 1
+            compact: false
         }
     ).getObfuscatedCode();
 

+ 26 - 3
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/BlockStatementControlFlowTransformer.spec.ts

@@ -36,7 +36,6 @@ describe('BlockStatementControlFlowTransformer', function () {
                 );
 
                 obfuscatedCode = obfuscationResult.getObfuscatedCode();
-                console.log(obfuscatedCode);
             });
 
             describe('`console.log` statements', ()=> {
@@ -455,7 +454,31 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('variant #13: `controlFlowFlatteningThreshold` chance', () => {
+        describe('variant #13: block statement contain class declaration', () => {
+            const statementRegExp: RegExp = /^\(function *\( *\) *{ * *class *_0x([a-f0-9]){4,6} *{.*?} *}.*class *_0x([a-f0-9]){4,6} *{.*?} *}.*class *_0x([a-f0-9]){4,6} *{.*?} *}/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/class-declaration.js');
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1
+                    }
+                );
+
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
+            });
+
+            it('shouldn\'t transform block statement', () => {
+                assert.match(obfuscatedCode, statementRegExp);
+            });
+        });
+
+        describe('variant #14: `controlFlowFlatteningThreshold` chance', () => {
             const samples: number = 1000;
             const delta: number = 0.1;
 
@@ -500,7 +523,7 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('variant #14: No `unreachable code after return statement` warning', () => {
+        describe('variant #15: No `unreachable code after return statement` warning', () => {
             const switchCaseRegExp: RegExp = /switch *\(_0x([a-f0-9]){4,6}\[_0x([a-f0-9]){4,6}\+\+\]\) *\{/;
             const switchCaseLengthRegExp: RegExp = /case *'[0-5]': *console\['log'\]\(0x[0-6]\);/g;
             const returnStatementRegExp: RegExp = /case *'[0-5]': *return; *(case|})/;

+ 29 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/class-declaration.js

@@ -0,0 +1,29 @@
+(function(){
+    class Test1 {
+        foo () {
+            var that = this;
+
+            console.log(that.foo);
+        }
+    }
+
+    class Test2 {
+        bar () {
+            var that = this;
+
+            console.log(that.bar);
+        }
+    }
+
+    class Test3 {
+        baz () {
+            var that = this;
+
+            console.log(that.baz);
+        }
+    }
+
+    console.log(new Test1().foo());
+    console.log(new Test2().bar());
+    console.log(new Test3().baz());
+})();

+ 326 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/EvalCallExpressionTransformer.spec.ts

@@ -0,0 +1,326 @@
+import { assert } from 'chai';
+
+import { IObfuscationResult } from '../../../../../src/interfaces/IObfuscationResult';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
+
+import { readFileAsString } from '../../../../helpers/readFileAsString';
+
+import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
+import { getRegExpMatch } from '../../../../helpers/getRegExpMatch';
+
+describe('EvalCallExpressionTransformer', () => {
+    describe('variant #1: identifier reference', () => {
+        const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
+        const evalExpressionRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/;
+
+        let functionIdentifierName: string | null,
+            obfuscatedCode: string,
+            variableReferenceIdentifierName: string | null;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/identifier-reference.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+
+            functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
+            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should correctly transform function parameter inside eval expression', () => {
+            assert.equal(functionIdentifierName, variableReferenceIdentifierName);
+        });
+    });
+
+    describe('variant #2: call expression with identifier reference', () => {
+        const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
+        const evalExpressionRegExp: RegExp = /eval *\('console\[\\'log\\']\((_0x(?:[a-f0-9]){4,6})\);'\);/;
+
+        let functionIdentifierName: string | null,
+            obfuscatedCode: string,
+            variableReferenceIdentifierName: string | null;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/call-expression-identifier-reference.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+
+            functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
+            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should correctly transform function parameter inside eval expression', () => {
+            assert.equal(functionIdentifierName, variableReferenceIdentifierName);
+        });
+    });
+
+    describe('variant #3: multiple statements in eval', () => {
+        const regExp: RegExp = /eval *\('_0x([a-f0-9]){4,6}; *_0x([a-f0-9]){4,6};'\);/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/multiple-statements-eval.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, regExp);
+        });
+    });
+
+    describe('variant #4: string array calls wrapper call', () => {
+        const stringArrayRegExp: RegExp = /var *_0x([a-f0-9]){4} *= *\['log', *'bar'];/;
+        const stringArrayCallsWrapperRegExp: RegExp = /eval *\('console\[_0x([a-f0-9]){4,6}\(\\'0x0\\'\)]\(_0x([a-f0-9]){4,6}\(\\'0x1\\'\)\);'\);/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/string-array-calls-wrapper-call.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    stringArray: true,
+                    stringArrayThreshold: 1
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('match #1: should add strings from eval expression to the string array', () => {
+            assert.match(obfuscatedCode, stringArrayRegExp);
+        });
+
+        it('match #1: should replace string with call to the string array calls wrapper', () => {
+            assert.match(obfuscatedCode, stringArrayCallsWrapperRegExp);
+        });
+    });
+
+    describe('variant #5: eval expression as argument', () => {
+        const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
+        const evalExpressionRegExp: RegExp = /console\['log']\(eval *\('(_0x(?:[a-f0-9]){4,6});'\)\);/;
+
+        let functionIdentifierName: string | null,
+            obfuscatedCode: string,
+            variableReferenceIdentifierName: string | null;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/eval-expression-as-argument.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+
+            functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
+            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should correctly transform function parameter inside eval expression', () => {
+            assert.equal(functionIdentifierName, variableReferenceIdentifierName);
+        });
+    });
+
+    describe('variant #6: nested eval expressions', () => {
+        const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6}), *(_0x(?:[a-f0-9]){4,6})\)/;
+        const evalExpressionMatch: string = `` +
+            `eval *\\('` +
+                `var *(_0x(?:[a-f0-9]){4,6}) *= *(_0x(?:[a-f0-9]){4,6}) *\\+ *(_0x(?:[a-f0-9]){4,6});` +
+                `eval\\(\\\\'` +
+                    `(_0x(?:[a-f0-9]){4,6}) *\\+ *(_0x(?:[a-f0-9]){4,6});` +
+                `\\\\'\\);` +
+            `'\\);` +
+        ``;
+        const evalExpressionRegExp: RegExp = new RegExp(evalExpressionMatch);
+        const expectedEvaluationResult: number = 4;
+
+        let evaluationResult: number,
+            functionIdentifierAName: string | null,
+            functionIdentifierBName: string | null,
+            obfuscatedCode: string,
+            variableReferenceIdentifierAName1: string | null,
+            variableReferenceIdentifierAName2: string | null,
+            variableReferenceIdentifierBName: string | null,
+            variableReferenceIdentifierCName1: string | null,
+            variableReferenceIdentifierCName2: string | null;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/nested-eval-expressions.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+
+            functionIdentifierAName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp, 0);
+            functionIdentifierBName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp, 1);
+
+            // parameter `a` reference inside parent eval expression
+            variableReferenceIdentifierAName1 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 1);
+            // parameter `a` reference inside nested eval expression
+            variableReferenceIdentifierAName2 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 3);
+            // parameter `b` reference inside parent eval expression
+            variableReferenceIdentifierBName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 2);
+            // variable declaration `c` inside parent eval expression
+            variableReferenceIdentifierCName1 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 0);
+            // variable `c` reference inside nested eval expression
+            variableReferenceIdentifierCName2 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 4);
+
+            evaluationResult = eval(obfuscatedCode);
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should generate correct code', () => {
+            assert.equal(evaluationResult, expectedEvaluationResult);
+        });
+
+        it('match #1: should correctly transform function parameter `a` inside eval expression', () => {
+            assert.equal(functionIdentifierAName, variableReferenceIdentifierAName1);
+        });
+
+        it('match #2: should correctly transform function parameter `a` inside nested eval expression', () => {
+            assert.equal(functionIdentifierAName, variableReferenceIdentifierAName2);
+        });
+
+        it('match #3: should correctly transform function parameter `b` inside eval expression', () => {
+            assert.equal(functionIdentifierBName, variableReferenceIdentifierBName);
+        });
+
+        it('match #4: should correctly transform variable declaration and variable reference inside eval and nested eval expressions', () => {
+            assert.equal(variableReferenceIdentifierCName1, variableReferenceIdentifierCName2);
+        });
+    });
+
+    describe('variant #7: wrong eval string', () => {
+        const evalExpressionRegExp: RegExp = /eval *\('~'\);/;
+
+        let obfuscatedCode: string
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/wrong-eval-string.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('should skip obfuscation of eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+    });
+
+    describe('variant #8: template literal inside eval expression', () => {
+        const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
+        const evalExpressionRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/;
+
+        let functionIdentifierName: string | null,
+            obfuscatedCode: string,
+            variableReferenceIdentifierName: string | null;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/eval-expression-template-literal.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+
+            functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
+            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should correctly transform function parameter inside eval expression', () => {
+            assert.equal(functionIdentifierName, variableReferenceIdentifierName);
+        });
+    });
+
+    describe('variant #9: integration with control flow flattening', () => {
+        const variableMatch: string = '_0x([a-f0-9]){4,6}';
+        const controlFlowStorageNodeMatch: string = `` +
+            `var *${variableMatch} *= *\\{` +
+                `'\\w{5}' *: *function *${variableMatch} *\\(${variableMatch}, *${variableMatch}\\) *\\{` +
+                    `return *${variableMatch} *\\+ *${variableMatch};` +
+                `\\}` +
+            `\\};` +
+        ``;
+        const controlFlowStorageNodeRegExp: RegExp = new RegExp(controlFlowStorageNodeMatch);
+        const evalExpressionRegExp: RegExp = /eval *\('_0x([a-f0-9]){4,6}\[\\'\w{5}\\']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\);'\);/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/control-flow-flattening-integration.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    controlFlowFlattening: true,
+                    controlFlowFlatteningThreshold: 1
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('should add control flow storage node', () => {
+            assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+    });
+});

+ 7 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/call-expression-identifier-reference.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo (bar) {
+        eval('console.log(bar);');
+    }
+
+    foo(1);
+})();

+ 5 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/control-flow-flattening-integration.js

@@ -0,0 +1,5 @@
+(function(){
+    function foo (a, b) {
+        eval('a + b');
+    }
+})();

+ 7 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/eval-expression-as-argument.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo (bar) {
+        console.log(eval('bar;'));
+    }
+
+    foo(1);
+})();

+ 7 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/eval-expression-template-literal.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo (bar) {
+        eval(`bar`);
+    }
+
+    foo(1);
+})();

+ 7 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/identifier-reference.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo (bar) {
+        eval('bar');
+    }
+
+    foo(1);
+})();

+ 7 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/multiple-statements-eval.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo (bar, baz) {
+        eval('bar;baz;');
+    }
+
+    foo(1, 2);
+})();

+ 7 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/nested-eval-expressions.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo (a, b) {
+        return eval('var c = a + b; eval(\'a + c\');');
+    }
+
+    return foo(1, 2);
+})();

+ 7 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/string-array-calls-wrapper-call.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo () {
+        eval('console.log(\'bar\')');
+    }
+
+    foo();
+})();

+ 7 - 0
test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/fixtures/wrong-eval-string.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo () {
+        eval('~');
+    }
+
+    foo();
+})();

+ 1 - 0
test/index.spec.ts

@@ -64,6 +64,7 @@ import './functional-tests/node-transformers/obfuscating-transformers/labeled-st
 import './functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec';
 import './functional-tests/node-transformers/obfuscating-transformers/object-expression-transformer/ObjectExpressionTransformer.spec';
 import './functional-tests/node-transformers/obfuscating-transformers/variable-declaration-transformer/VariableDeclarationTransformer.spec';
+import './functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/EvalCallExpressionTransformer.spec';
 import './functional-tests/node-transformers/preparing-transformers/comments-transformer/CommentsTransformer.spec';
 import './functional-tests/node-transformers/preparing-transformers/obfuscating-guards/black-list-obfuscating-guard/BlackListObfuscatingGuard.spec';
 import './functional-tests/node-transformers/preparing-transformers/obfuscating-guards/conditional-comment-obfuscating-guard/ConditionalCommentObfuscatingGuard.spec';

+ 0 - 1
tslint.json

@@ -181,7 +181,6 @@
         "variable-declaration": "space"
       }
     ],
-    "typeof-compare": true,
     "unified-signatures": true,
     "use-isnan": true,
     "valid-jsdoc": [false, {

+ 2 - 1
webpack.config.js

@@ -71,7 +71,8 @@ module.exports = {
         new CheckerPlugin(),
         new TSLintPlugin({
             files: ['./src/**/*.ts'],
-            project: './tsconfig.json'
+            project: './tsconfig.json',
+            exclude: []
         })
     ],
     output: {

+ 36 - 33
yarn.lock

@@ -10,12 +10,6 @@
   version "0.7.36"
   resolved "https://registry.yarnpkg.com/@types/chance/-/chance-0.7.36.tgz#1c3e14b9b11463d454182393ebcd8e977f1daff5"
 
-"@types/[email protected]":
-  version "2.12.2"
-  resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae"
-  dependencies:
-    commander "*"
-
 "@types/[email protected]":
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/@types/escodegen/-/escodegen-0.0.6.tgz#5230a9ce796e042cda6f086dbf19f22ea330659c"
@@ -72,20 +66,20 @@
   version "8.0.53"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.53.tgz#396b35af826fa66aad472c8cb7b8d5e277f4e6d8"
 
-"@types/node@8.5.7":
-  version "8.5.7"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.5.7.tgz#9c498c35af354dcfbca3790fb2e81129e93cf0e2"
+"@types/node@9.3.0":
+  version "9.3.0"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-9.3.0.tgz#3a129cda7c4e5df2409702626892cb4b96546dd5"
 
-"@types/rimraf@^2.0.2":
+"@types/[email protected]":
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e"
   dependencies:
     "@types/glob" "*"
     "@types/node" "*"
 
-"@types/[email protected].2":
-  version "4.1.2"
-  resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.1.2.tgz#9085db9cc3288b0f12daceee20a26c4afd9c2dcd"
+"@types/[email protected].3":
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.1.3.tgz#2ee25e0e302f31e78a945650a60029e08878eaf8"
 
 "@types/[email protected]":
   version "1.0.2"
@@ -1089,14 +1083,18 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
   dependencies:
     delayed-stream "~1.0.0"
 
-commander@*, [email protected], commander@^2.11.0, commander@^2.9.0:
-  version "2.12.2"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555"
-
 [email protected]:
   version "2.11.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
 
[email protected], commander@^2.12.1:
+  version "2.13.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
+
+commander@^2.11.0:
+  version "2.12.2"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555"
+
 commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -2242,6 +2240,10 @@ [email protected]:
     which "^1.1.1"
     wordwrap "^1.0.0"
 
[email protected]:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
+
 js-tokens@^3.0.0, js-tokens@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
@@ -2960,7 +2962,7 @@ pinkie@^2.0.0:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
 
-pjson@^1.0.9:
[email protected]:
   version "1.0.9"
   resolved "https://registry.yarnpkg.com/pjson/-/pjson-1.0.9.tgz#8a9520ce76a4739f8fee91679dad6b065b1c7938"
 
@@ -3235,7 +3237,7 @@ right-align@^0.1.1:
   dependencies:
     align-text "^0.1.1"
 
-rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1, rimraf@^2.6.2:
+rimraf@2, rimraf@2.6.2, rimraf@^2.5.1, rimraf@^2.6.1:
   version "2.6.2"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
   dependencies:
@@ -3327,9 +3329,9 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
 
[email protected].3:
-  version "4.1.3"
-  resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.3.tgz#fc599eda47ed9f1a694ce774b94ab44260bd7ac5"
[email protected].4:
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.4.tgz#36bb237bae38ddf9cc92dcc1b16c51e7785bbc9c"
   dependencies:
     diff "^3.1.0"
     formatio "1.2.0"
@@ -3609,7 +3611,7 @@ text-encoding@^0.6.4:
   version "0.6.4"
   resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
 
-threads@^0.10.0:
[email protected]:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/threads/-/threads-0.10.0.tgz#a6b0bc5d916fa75434b166c612769684b65fead5"
   dependencies:
@@ -3695,7 +3697,7 @@ tsconfig@^7.0.0:
     strip-bom "^3.0.0"
     strip-json-comments "^2.0.0"
 
[email protected], tslib@^1.0.0, tslib@^1.7.1:
[email protected], tslib@^1.0.0, tslib@^1.7.1, tslib@^1.8.0:
   version "1.8.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.1.tgz#6946af2d1d651a7b1863b531d6e5afa41aa44eac"
 
@@ -3707,9 +3709,9 @@ [email protected]:
     tslib "^1.0.0"
     tsutils "^1.4.0"
 
[email protected].7:
-  version "0.9.7"
-  resolved "https://registry.yarnpkg.com/tslint-language-service/-/tslint-language-service-0.9.7.tgz#94a35442bc1163e4629df8804e6fa694ced549ff"
[email protected].8:
+  version "0.9.8"
+  resolved "https://registry.yarnpkg.com/tslint-language-service/-/tslint-language-service-0.9.8.tgz#22a6f2f926b7c0a4cafed3ae1f65021e8008dc96"
 
 [email protected]:
   version "1.0.0"
@@ -3717,20 +3719,21 @@ [email protected]:
   dependencies:
     chalk "^2.1.0"
 
-tslint@5.8.0:
-  version "5.8.0"
-  resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.8.0.tgz#1f49ad5b2e77c76c3af4ddcae552ae4e3612eb13"
+tslint@5.9.1:
+  version "5.9.1"
+  resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae"
   dependencies:
     babel-code-frame "^6.22.0"
     builtin-modules "^1.1.1"
-    chalk "^2.1.0"
-    commander "^2.9.0"
+    chalk "^2.3.0"
+    commander "^2.12.1"
     diff "^3.2.0"
     glob "^7.1.1"
+    js-yaml "^3.7.0"
     minimatch "^3.0.4"
     resolve "^1.3.2"
     semver "^5.3.0"
-    tslib "^1.7.1"
+    tslib "^1.8.0"
     tsutils "^2.12.1"
 
 tsutils@^1.4.0:

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