Bladeren bron

Merge branch 'inversify' into dev

# Conflicts:
#	dist/index.js
#	src/node-transformers/node-control-flow-transformers/FunctionControlFlowTransformer.ts
sanex3339 8 jaren geleden
bovenliggende
commit
56db980d3f
100 gewijzigde bestanden met toevoegingen van 2344 en 1102 verwijderingen
  1. 377 332
      dist/index.js
  2. 0 4
      index.ts
  3. 2 0
      package.json
  4. 14 6
      src/JavaScriptObfuscator.ts
  5. 47 29
      src/JavaScriptObfuscatorInternal.ts
  6. 8 1
      src/ObfuscationResult.ts
  7. 71 35
      src/Obfuscator.ts
  8. 36 41
      src/SourceMapCorrector.ts
  9. 18 9
      src/Utils.ts
  10. 2 2
      src/cli/JavaScriptObfuscatorCLI.ts
  11. 106 0
      src/container/InversifyContainerFacade.ts
  12. 25 0
      src/container/ServiceIdentifiers.ts
  13. 131 0
      src/container/modules/custom-nodes/CustomNodesModule.ts
  14. 40 0
      src/container/modules/node-transformers/NodeControlFlowTransformersModule.ts
  15. 52 0
      src/container/modules/node-transformers/NodeObfuscatorsModule.ts
  16. 94 0
      src/container/modules/node-transformers/NodeTransformersModule.ts
  17. 55 0
      src/container/modules/stack-trace-analyzer/StackTraceAnalyzerModule.ts
  18. 18 5
      src/custom-nodes/AbstractCustomNode.ts
  19. 9 3
      src/custom-nodes/AbstractCustomNodesFactory.ts
  20. 22 19
      src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts
  21. 34 20
      src/custom-nodes/console-output-nodes/factory/ConsoleOutputCustomNodesFactory.ts
  22. 21 6
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionNode.ts
  23. 27 10
      src/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode.ts
  24. 22 10
      src/custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts
  25. 21 6
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts
  26. 21 6
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts
  27. 21 6
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts
  28. 38 15
      src/custom-nodes/debug-protection-nodes/factory/DebugProtectionCustomNodesFactory.ts
  29. 21 18
      src/custom-nodes/domain-lock-nodes/DomainLockNode.ts
  30. 34 20
      src/custom-nodes/domain-lock-nodes/factory/DomainLockCustomNodesFactory.ts
  31. 22 19
      src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts
  32. 21 18
      src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts
  33. 34 22
      src/custom-nodes/self-defending-nodes/factory/SelfDefendingCustomNodesFactory.ts
  34. 34 18
      src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts
  35. 29 13
      src/custom-nodes/string-array-nodes/StringArrayNode.ts
  36. 29 13
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  37. 40 42
      src/custom-nodes/string-array-nodes/factory/StringArrayCustomNodesFactory.ts
  38. 58 0
      src/decorators/Initializable.ts
  39. 1 1
      src/enums/ObfuscationEvents.ts
  40. 5 0
      src/enums/container/CalleeDataExtractors.ts
  41. 15 0
      src/enums/container/CustomNodes.ts
  42. 7 0
      src/enums/container/CustomNodesFactories.ts
  43. 3 0
      src/enums/container/NodeControlFlowTransformersReplacers.ts
  44. 6 0
      src/enums/container/NodeObfuscatorsReplacers.ts
  45. 12 0
      src/enums/container/NodeTransformers.ts
  46. 6 1
      src/event-emitters/ObfuscationEventEmitter.ts
  47. 6 0
      src/interfaces/IInitializable.d.ts
  48. 5 0
      src/interfaces/IJavaScriptObfsucator.d.ts
  49. 0 5
      src/interfaces/INodeTransformersFactory.d.ts
  50. 3 1
      src/interfaces/IObfuscationResult.d.ts
  51. 4 1
      src/interfaces/IObfuscator.d.ts
  52. 0 3
      src/interfaces/IReplacer.d.ts
  53. 1 1
      src/interfaces/ISourceMapCorrector.d.ts
  54. 26 0
      src/interfaces/container/IContainerServiceIdentifiers.d.ts
  55. 5 0
      src/interfaces/container/IInversifyContainerFacade.d.ts
  56. 8 4
      src/interfaces/custom-nodes/ICustomNode.d.ts
  57. 0 3
      src/interfaces/custom-nodes/ICustomNodeWithIdentifier.d.ts
  58. 1 1
      src/interfaces/event-emitters/IObfuscationEventEmitter.d.ts
  59. 2 2
      src/interfaces/node-transformers/IControlFlowReplacer.d.ts
  60. 0 0
      src/interfaces/node-transformers/INodeTransformer.d.ts
  61. 3 0
      src/interfaces/node-transformers/IReplacer.d.ts
  62. 2 2
      src/interfaces/options/IInputOptions.d.ts
  63. 2 2
      src/interfaces/options/IOptions.d.ts
  64. 3 1
      src/interfaces/stack-trace-analyzer/ICalleeDataExtractor.d.ts
  65. 3 1
      src/interfaces/storages/IStorage.d.ts
  66. 11 4
      src/node-transformers/AbstractNodeTransformer.ts
  67. 0 50
      src/node-transformers/AbstractNodeTransformersFactory.ts
  68. 15 13
      src/node-transformers/node-control-flow-transformers/FunctionControlFlowTransformer.ts
  69. 11 4
      src/node-transformers/node-control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts
  70. 30 7
      src/node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts
  71. 0 16
      src/node-transformers/node-control-flow-transformers/factory/NodeControlFlowTransformersFactory.ts
  72. 22 5
      src/node-transformers/node-obfuscators/CatchClauseObfuscator.ts
  73. 22 5
      src/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.ts
  74. 21 4
      src/node-transformers/node-obfuscators/FunctionObfuscator.ts
  75. 21 4
      src/node-transformers/node-obfuscators/LabeledStatementObfuscator.ts
  76. 34 6
      src/node-transformers/node-obfuscators/LiteralObfuscator.ts
  77. 32 3
      src/node-transformers/node-obfuscators/MemberExpressionObfuscator.ts
  78. 32 3
      src/node-transformers/node-obfuscators/MethodDefinitionObfuscator.ts
  79. 19 0
      src/node-transformers/node-obfuscators/ObjectExpressionObfuscator.ts
  80. 23 6
      src/node-transformers/node-obfuscators/VariableDeclarationObfuscator.ts
  81. 0 36
      src/node-transformers/node-obfuscators/factory/NodeObfuscatorsFactory.ts
  82. 12 6
      src/node-transformers/node-obfuscators/replacers/AbstractReplacer.ts
  83. 19 0
      src/node-transformers/node-obfuscators/replacers/BooleanLiteralReplacer.ts
  84. 37 2
      src/node-transformers/node-obfuscators/replacers/IdentifierReplacer.ts
  85. 20 3
      src/node-transformers/node-obfuscators/replacers/NumberLiteralReplacer.ts
  86. 20 4
      src/node-transformers/node-obfuscators/replacers/StringLiteralReplacer.ts
  87. 2 2
      src/node/Node.ts
  88. 2 2
      src/node/NodeAppender.ts
  89. 3 3
      src/node/NodeUtils.ts
  90. 19 16
      src/options/Options.ts
  91. 3 3
      src/options/OptionsNormalizer.ts
  92. 1 1
      src/preset-options/DefaultPreset.ts
  93. 1 1
      src/preset-options/NoCustomNodesPreset.ts
  94. 62 36
      src/stack-trace-analyzer/StackTraceAnalyzer.ts
  95. 16 0
      src/stack-trace-analyzer/callee-data-extractors/AbstractCalleeDataExtractor.ts
  96. 10 24
      src/stack-trace-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor.ts
  97. 12 26
      src/stack-trace-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor.ts
  98. 10 24
      src/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts
  99. 8 3
      src/storages/ArrayStorage.ts
  100. 11 3
      src/storages/MapStorage.ts

File diff suppressed because it is too large
+ 377 - 332
dist/index.js


+ 0 - 4
index.ts

@@ -2,8 +2,4 @@
 
 import { JavaScriptObfuscator } from './src/JavaScriptObfuscator';
 
-if (!(<any>global)._babelPolyfill) {
-    require('babel-polyfill');
-}
-
 module.exports = JavaScriptObfuscator;

+ 2 - 0
package.json

@@ -27,8 +27,10 @@
     "escodegen": "1.8.1",
     "esprima": "3.1.2",
     "estraverse": "4.2.0",
+    "inversify": "^3.0.0-beta.2",
     "is-equal": "^1.5.3",
     "mkdirp": "0.5.1",
+    "reflect-metadata": "^0.1.8",
     "source-map-support": "0.4.6",
     "string-template": "^1.0.0"
   },

+ 14 - 6
src/JavaScriptObfuscator.ts

@@ -1,9 +1,17 @@
-import { IInputOptions } from './interfaces/IInputOptions';
+import 'reflect-metadata';
+
+if (!(<any>global)._babelPolyfill) {
+    require('babel-polyfill');
+}
+
+import { IInputOptions } from './interfaces/options/IInputOptions';
+import { IInversifyContainerFacade } from './interfaces/container/IInversifyContainerFacade';
+import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
 
+import { InversifyContainerFacade } from './container/InversifyContainerFacade';
 import { JavaScriptObfuscatorCLI } from './cli/JavaScriptObfuscatorCLI';
-import { JavaScriptObfuscatorInternal } from './JavaScriptObfuscatorInternal';
-import { Options } from './options/Options';
+import { ServiceIdentifiers } from './container/ServiceIdentifiers';
 
 export class JavaScriptObfuscator {
     /**
@@ -12,9 +20,9 @@ export class JavaScriptObfuscator {
      * @returns {string}
      */
     public static obfuscate (sourceCode: string, inputOptions: IInputOptions = {}): IObfuscationResult {
-        const javaScriptObfuscator: JavaScriptObfuscatorInternal = new JavaScriptObfuscatorInternal(
-            new Options(inputOptions)
-        );
+        const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade(inputOptions);
+        const javaScriptObfuscator: IJavaScriptObfuscator = inversifyContainerFacade
+            .get<IJavaScriptObfuscator>(ServiceIdentifiers.IJavaScriptObfuscator);
 
         return javaScriptObfuscator.obfuscate(sourceCode);
     }

+ 47 - 29
src/JavaScriptObfuscatorInternal.ts

@@ -1,22 +1,23 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from './container/ServiceIdentifiers';
+
 import * as esprima from 'esprima';
 import * as escodegen from 'escodegen';
 import * as ESTree from 'estree';
 
-import { Chance } from 'chance';
-
-import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
+import { ICustomNode } from './interfaces/custom-nodes/ICustomNode';
+import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
-import { IOptions } from './interfaces/IOptions';
-
-import { CustomNodesStorage } from './storages/custom-nodes/CustomNodesStorage';
-import { ObfuscationEventEmitter } from './event-emitters/ObfuscationEventEmitter';
-import { ObfuscationResult } from './ObfuscationResult';
-import { Obfuscator } from './Obfuscator';
-import { SourceMapCorrector } from './SourceMapCorrector';
-import { StackTraceAnalyzer } from './stack-trace-analyzer/StackTraceAnalyzer';
+import { IObfuscator } from './interfaces/IObfuscator';
+import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
+import { IOptions } from './interfaces/options/IOptions';
+import { ISourceMapCorrector } from './interfaces/ISourceMapCorrector';
+import { IStorage } from './interfaces/storages/IStorage';
+
 import { Utils } from './Utils';
 
-export class JavaScriptObfuscatorInternal {
+@injectable()
+export class JavaScriptObfuscatorInternal implements IJavaScriptObfuscator {
     /**
      * @type {GenerateOptions}
      */
@@ -32,15 +33,41 @@ export class JavaScriptObfuscatorInternal {
         loc: true
     };
 
+    /**
+     * @type {IStorage<ICustomNode>}
+     */
+    private readonly customNodesStorage: IStorage<ICustomNode>;
+
+    /**
+     * @type {IObfuscator}
+     */
+    private readonly obfuscator: IObfuscator;
+
     /**
      * @type {IOptions}
      */
     private readonly options: IOptions;
 
     /**
+     * @type {ISourceMapCorrector}
+     */
+    private readonly sourceMapCorrector: ISourceMapCorrector;
+
+    /**
+     * @param obfuscator
+     * @param sourceMapCorrector
+     * @param customNodesStorage
      * @param options
      */
-    constructor (options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers.IObfuscator) obfuscator: IObfuscator,
+        @inject(ServiceIdentifiers.ISourceMapCorrector) sourceMapCorrector: ISourceMapCorrector,
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        this.obfuscator = obfuscator;
+        this.sourceMapCorrector = sourceMapCorrector;
+        this.customNodesStorage = customNodesStorage;
         this.options = options;
     }
 
@@ -74,15 +101,11 @@ export class JavaScriptObfuscatorInternal {
      * @param generatorOutput
      * @returns {IObfuscationResult}
      */
-    public getObfuscationResult (generatorOutput: IGeneratorOutput): IObfuscationResult {
-        return new SourceMapCorrector(
-            new ObfuscationResult(
-                generatorOutput.code,
-                generatorOutput.map
-            ),
-            this.options.sourceMapBaseUrl + this.options.sourceMapFileName,
-            this.options.sourceMapMode
-        ).correct();
+    private getObfuscationResult (generatorOutput: IGeneratorOutput): IObfuscationResult {
+        return this.sourceMapCorrector.correct(
+            generatorOutput.code,
+            generatorOutput.map
+        );
     }
 
     /**
@@ -91,19 +114,14 @@ export class JavaScriptObfuscatorInternal {
      */
     public obfuscate (sourceCode: string): IObfuscationResult {
         if (this.options.seed !== 0) {
-            Utils.setRandomGenerator(new Chance(this.options.seed));
+            Utils.setRandomGeneratorSeed(this.options.seed);
         }
 
         // parse AST tree
         const astTree: ESTree.Program = esprima.parse(sourceCode, JavaScriptObfuscatorInternal.esprimaParams);
 
         // obfuscate AST tree
-        const obfuscatedAstTree: ESTree.Program = new Obfuscator(
-            new ObfuscationEventEmitter(),
-            new StackTraceAnalyzer(),
-            new CustomNodesStorage(this.options),
-            this.options
-        ).obfuscateAstTree(astTree);
+        const obfuscatedAstTree: ESTree.Program = this.obfuscator.obfuscateAstTree(astTree, this.customNodesStorage);
 
         // generate code
         const generatorOutput: IGeneratorOutput = this.generateCode(sourceCode, obfuscatedAstTree);

+ 8 - 1
src/ObfuscationResult.ts

@@ -1,21 +1,28 @@
+import { injectable } from 'inversify';
+
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
 
+import { initializable } from './decorators/Initializable';
+
+@injectable()
 export class ObfuscationResult implements IObfuscationResult {
     /**
      * @type {string}
      */
+    @initializable()
     private obfuscatedCode: string;
 
     /**
      * @type {string}
      */
+    @initializable()
     private sourceMap: string;
 
     /**
      * @param obfuscatedCode
      * @param sourceMap
      */
-    constructor (obfuscatedCode: string, sourceMap: string) {
+    public initialize (obfuscatedCode: string, sourceMap: string): void {
         this.obfuscatedCode = obfuscatedCode;
         this.sourceMap = sourceMap;
     }

+ 71 - 35
src/Obfuscator.ts

@@ -1,31 +1,63 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from './container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
+import { TNodeTransformersFactory } from './types/container/TNodeTransformersFactory';
 import { TVisitorDirection } from './types/TVisitorDirection';
 
 import { ICustomNode } from './interfaces/custom-nodes/ICustomNode';
-import { IObfuscationEventEmitter } from './interfaces/IObfuscationEventEmitter';
+import { IObfuscationEventEmitter } from './interfaces/event-emitters/IObfuscationEventEmitter';
 import { IObfuscator } from './interfaces/IObfuscator';
-import { IOptions } from './interfaces/IOptions';
-import { INodeTransformer } from './interfaces/INodeTransformer';
-import { INodeTransformersFactory } from './interfaces/INodeTransformersFactory';
+import { IOptions } from './interfaces/options/IOptions';
+import { INodeTransformer } from './interfaces/node-transformers/INodeTransformer';
 import { IStackTraceAnalyzer } from './interfaces/stack-trace-analyzer/IStackTraceAnalyzer';
-import { IStorage } from './interfaces/IStorage';
+import { IStorage } from './interfaces/storages/IStorage';
 
+import { NodeTransformers } from './enums/container/NodeTransformers';
+import { NodeType } from './enums/NodeType';
 import { ObfuscationEvents } from './enums/ObfuscationEvents';
 import { VisitorDirection } from './enums/VisitorDirection';
 
-import { NodeControlFlowTransformersFactory } from './node-transformers/node-control-flow-transformers/factory/NodeControlFlowTransformersFactory';
-import { NodeObfuscatorsFactory } from './node-transformers/node-obfuscators/factory/NodeObfuscatorsFactory';
-
 import { Node } from './node/Node';
 import { NodeUtils } from './node/NodeUtils';
+import { IStackTraceData } from './interfaces/stack-trace-analyzer/IStackTraceData';
 
+@injectable()
 export class Obfuscator implements IObfuscator {
     /**
-     * @type {IStorage<ICustomNode>}
+     * @type {Map<string, NodeTransformers[]>}
+     */
+    private static readonly nodeControlFlowTransformersMap: Map <string, NodeTransformers[]> = new Map <string, NodeTransformers[]> ([
+        [NodeType.FunctionDeclaration, [NodeTransformers.FunctionControlFlowTransformer]],
+        [NodeType.FunctionExpression, [NodeTransformers.FunctionControlFlowTransformer]]
+    ]);
+
+    /**
+     * @type {Map<string, NodeTransformers[]>}
      */
-    private readonly customNodesStorage: IStorage<ICustomNode>;
+    private static readonly nodeObfuscatorsMap: Map <string, NodeTransformers[]> = new Map <string, NodeTransformers[]> ([
+        [NodeType.ArrowFunctionExpression, [NodeTransformers.FunctionObfuscator]],
+        [NodeType.ClassDeclaration, [NodeTransformers.FunctionDeclarationObfuscator]],
+        [NodeType.CatchClause, [NodeTransformers.CatchClauseObfuscator]],
+        [NodeType.FunctionDeclaration, [
+            NodeTransformers.FunctionDeclarationObfuscator,
+            NodeTransformers.FunctionObfuscator
+        ]],
+        [NodeType.FunctionExpression, [NodeTransformers.FunctionObfuscator]],
+        [NodeType.MemberExpression, [NodeTransformers.MemberExpressionObfuscator]],
+        [NodeType.MethodDefinition, [NodeTransformers.MethodDefinitionObfuscator]],
+        [NodeType.ObjectExpression, [NodeTransformers.ObjectExpressionObfuscator]],
+        [NodeType.VariableDeclaration, [NodeTransformers.VariableDeclarationObfuscator]],
+        [NodeType.LabeledStatement, [NodeTransformers.LabeledStatementObfuscator]],
+        [NodeType.Literal, [NodeTransformers.LiteralObfuscator]]
+    ]);
+
+    /**
+     * @type {TNodeTransformersFactory}
+     */
+    private readonly nodeTransformersFactory: TNodeTransformersFactory;
 
     /**
      * @type {IObfuscationEventEmitter}
@@ -43,60 +75,64 @@ export class Obfuscator implements IObfuscator {
     private readonly stackTraceAnalyzer: IStackTraceAnalyzer;
 
     /**
-     * @param obfuscationEventEmitter
      * @param stackTraceAnalyzer
-     * @param customNodesStorage
+     * @param obfuscationEventEmitter
+     * @param nodeTransformersFactory
      * @param options
      */
     constructor (
-        obfuscationEventEmitter: IObfuscationEventEmitter,
-        stackTraceAnalyzer: IStackTraceAnalyzer,
-        customNodesStorage: IStorage<ICustomNode>,
-        options: IOptions
+        @inject(ServiceIdentifiers.IStackTraceAnalyzer) stackTraceAnalyzer: IStackTraceAnalyzer,
+        @inject(ServiceIdentifiers.IObfuscationEventEmitter) obfuscationEventEmitter: IObfuscationEventEmitter,
+        @inject(ServiceIdentifiers['Factory<INodeTransformer[]>']) nodeTransformersFactory: TNodeTransformersFactory,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        this.obfuscationEventEmitter = obfuscationEventEmitter;
         this.stackTraceAnalyzer = stackTraceAnalyzer;
-        this.customNodesStorage = customNodesStorage;
+        this.obfuscationEventEmitter = obfuscationEventEmitter;
+        this.nodeTransformersFactory = nodeTransformersFactory;
         this.options = options;
     }
 
     /**
      * @param astTree
+     * @param customNodesStorage
      * @returns {ESTree.Program}
      */
-    public obfuscateAstTree (astTree: ESTree.Program): ESTree.Program {
+    public obfuscateAstTree (astTree: ESTree.Program, customNodesStorage: IStorage<ICustomNode>): ESTree.Program {
         if (Node.isProgramNode(astTree) && !astTree.body.length) {
             return astTree;
         }
 
-        // zero pass: parentize all nodes
         NodeUtils.parentize(astTree);
 
+        const stackTraceData: IStackTraceData[] = this.stackTraceAnalyzer.analyze(astTree.body);
+
         // prepare custom nodes
-        this.customNodesStorage.initialize(this.stackTraceAnalyzer.analyze(astTree.body));
-        this.customNodesStorage
+        customNodesStorage.initialize(stackTraceData);
+        customNodesStorage
             .getStorage()
             .forEach((customNode: ICustomNode) => {
                 this.obfuscationEventEmitter.once(customNode.getAppendEvent(), customNode.appendNode.bind(customNode));
             });
 
-        this.obfuscationEventEmitter.emit(ObfuscationEvents.BeforeObfuscation, astTree);
+        this.obfuscationEventEmitter.emit(ObfuscationEvents.BeforeObfuscation, astTree, stackTraceData);
 
         // first pass: control flow flattening
         if (this.options.controlFlowFlattening) {
-            this.transformAstTree(astTree, VisitorDirection.leave, new NodeControlFlowTransformersFactory(
-                this.customNodesStorage,
-                this.options
-            ));
+            this.transformAstTree(
+                astTree,
+                VisitorDirection.leave,
+                this.nodeTransformersFactory(Obfuscator.nodeControlFlowTransformersMap)
+            );
         }
 
         // second pass: nodes obfuscation
-        this.transformAstTree(astTree, VisitorDirection.enter, new NodeObfuscatorsFactory(
-            this.customNodesStorage,
-            this.options
-        ));
+        this.transformAstTree(
+            astTree,
+            VisitorDirection.enter,
+            this.nodeTransformersFactory(Obfuscator.nodeObfuscatorsMap)
+        );
 
-        this.obfuscationEventEmitter.emit(ObfuscationEvents.AfterObfuscation, astTree);
+        this.obfuscationEventEmitter.emit(ObfuscationEvents.AfterObfuscation, astTree, stackTraceData);
 
         return astTree;
     }
@@ -104,16 +140,16 @@ export class Obfuscator implements IObfuscator {
     /**
      * @param astTree
      * @param direction
-     * @param nodeTransformersFactory
+     * @param nodeTransformersConcreteFactory
      */
     private transformAstTree (
         astTree: ESTree.Program,
         direction: TVisitorDirection,
-        nodeTransformersFactory: INodeTransformersFactory
+        nodeTransformersConcreteFactory: (nodeType: string) => INodeTransformer[]
     ): void {
         estraverse.traverse(astTree, {
             [direction]: (node: ESTree.Node, parentNode: ESTree.Node): void => {
-                const nodeTransformers: INodeTransformer[] = nodeTransformersFactory.initializeNodeTransformers(node.type);
+                const nodeTransformers: INodeTransformer[] = nodeTransformersConcreteFactory(node.type);
 
                 nodeTransformers.forEach((nodeTransformer: INodeTransformer) => {
                     nodeTransformer.transformNode(node, parentNode);

+ 36 - 41
src/SourceMapCorrector.ts

@@ -1,88 +1,83 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from './container/ServiceIdentifiers';
+
+import { TObfuscationResultFactory } from './types/container/TObfuscationResultFactory';
+
 import { IObfuscationResult } from './interfaces/IObfuscationResult';
+import { IOptions } from './interfaces/options/IOptions';
 import { ISourceMapCorrector } from './interfaces/ISourceMapCorrector';
 
-import { TSourceMapMode } from './types/TSourceMapMode';
-
 import { SourceMapMode } from './enums/SourceMapMode';
 
-import { ObfuscationResult } from './ObfuscationResult';
 import { Utils } from './Utils';
 
+@injectable()
 export class SourceMapCorrector implements ISourceMapCorrector {
     /**
-     * @type {string}
-     */
-    private readonly obfuscatedCode: string;
-
-    /**
-     * @type {string}
+     * @type {TObfuscationResultFactory}
      */
-    private readonly sourceMap: string;
+    private readonly obfuscationResultFactory: TObfuscationResultFactory;
 
     /**
-     * @type {TSourceMapMode}
+     * @type {IOptions}
      */
-    private readonly sourceMapMode: TSourceMapMode;
+    private readonly options: IOptions;
 
     /**
-     * @type {string}
-     */
-    private readonly sourceMapUrl: string;
-
-    /**
-     * @param obfuscationResult
-     * @param sourceMapUrl
-     * @param sourceMapMode
+     * @param obfuscationResultFactory
+     * @param options
      */
     constructor (
-        obfuscationResult: IObfuscationResult,
-        sourceMapUrl: string,
-        sourceMapMode: TSourceMapMode
+        @inject(ServiceIdentifiers['Factory<IObfuscationResult>']) obfuscationResultFactory: TObfuscationResultFactory,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        this.obfuscatedCode = obfuscationResult.getObfuscatedCode();
-        this.sourceMap = obfuscationResult.getSourceMap();
-
-        this.sourceMapUrl = sourceMapUrl;
-        this.sourceMapMode = sourceMapMode;
+        this.obfuscationResultFactory = obfuscationResultFactory;
+        this.options = options;
     }
 
     /**
      * @returns {ObfuscationResult}
+     * @param obfuscatedCode
+     * @param sourceMap
      */
-    public correct (): IObfuscationResult {
-        return new ObfuscationResult(
-            this.correctObfuscatedCode(),
-            this.sourceMap
+    public correct (obfuscatedCode: string, sourceMap: string): IObfuscationResult {
+        return this.obfuscationResultFactory(
+            this.correctObfuscatedCode(obfuscatedCode, sourceMap),
+            sourceMap
         );
     }
 
     /**
+     * @param obfuscatedCode
+     * @param sourceMap
      * @returns {string}
      */
-    private correctObfuscatedCode (): string {
-        if (!this.sourceMap) {
-            return this.obfuscatedCode;
+    private correctObfuscatedCode (obfuscatedCode: string, sourceMap: string): string {
+        if (!sourceMap) {
+            return obfuscatedCode;
         }
 
+        const sourceMapUrl: string = this.options.sourceMapBaseUrl + this.options.sourceMapFileName;
+
         let sourceMappingUrl: string = '//# sourceMappingURL=';
 
-        switch (this.sourceMapMode) {
+        switch (this.options.sourceMapMode) {
             case SourceMapMode.Inline:
-                sourceMappingUrl += `data:application/json;base64,${Utils.btoa(this.sourceMap)}`;
+                sourceMappingUrl += `data:application/json;base64,${Utils.btoa(sourceMap)}`;
 
                 break;
 
             case SourceMapMode.Separate:
             default:
-                if (!this.sourceMapUrl) {
-                    return this.obfuscatedCode;
+                if (!sourceMapUrl) {
+                    return obfuscatedCode;
                 }
 
-                sourceMappingUrl += this.sourceMapUrl;
+                sourceMappingUrl += sourceMapUrl;
 
                 break;
         }
 
-        return `${this.obfuscatedCode}\n${sourceMappingUrl}`;
+        return `${obfuscatedCode}\n${sourceMappingUrl}`;
     };
 }

+ 18 - 9
src/Utils.ts

@@ -5,11 +5,21 @@ import { JSFuck } from './enums/JSFuck';
 const isEqual = require('is-equal');
 
 export class Utils {
+    /**
+     * @type {string}
+     */
+    public static readonly hexadecimalPrefix: string = '0x';
+
     /**
      * @type {string}
      */
     public static readonly randomGeneratorPool: string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 
+    /**
+     * @type {string}
+     */
+    public static readonly randomGeneratorPoolWithNumbers: string = `${Utils.randomGeneratorPool}0123456789`;
+
     /**
      * @type {Chance.Chance | Chance.SeededChance}
      */
@@ -58,7 +68,7 @@ export class Utils {
         let output: string = '';
 
         string = encodeURIComponent(string).replace(/%([0-9A-F]{2})/g, (match, p1) => {
-            return String.fromCharCode(parseInt('0x' + p1));
+            return String.fromCharCode(parseInt(`${Utils.hexadecimalPrefix}${p1}`));
         });
 
         for (
@@ -112,7 +122,7 @@ export class Utils {
      * @returns {number}
      */
     public static getRandomFloat (min: number, max: number): number {
-        return Utils.getRandomGenerator().floating({
+        return Utils.randomGenerator.floating({
             min: min,
             max: max,
             fixed: 7
@@ -138,7 +148,7 @@ export class Utils {
      * @returns {number}
      */
     public static getRandomInteger (min: number, max: number): number {
-        return Utils.getRandomGenerator().integer({
+        return Utils.randomGenerator.integer({
             min: min,
             max: max
         });
@@ -150,10 +160,9 @@ export class Utils {
      */
     public static getRandomVariableName (length: number = 6): string {
         const rangeMinInteger: number = 10000,
-            rangeMaxInteger: number = 99999999,
-            prefix: string = '_0x';
+            rangeMaxInteger: number = 99999999;
 
-        return `${prefix}${(
+        return `_${Utils.hexadecimalPrefix}${(
             Utils.decToHex(
                 Utils.getRandomInteger(rangeMinInteger, rangeMaxInteger)
             )
@@ -267,10 +276,10 @@ export class Utils {
     }
 
     /**
-     * @param randomGenerator
+     * @param randomGeneratorSeed
      */
-    public static setRandomGenerator (randomGenerator: Chance.Chance | Chance.SeededChance): void {
-        Utils.randomGenerator = randomGenerator;
+    public static setRandomGeneratorSeed (randomGeneratorSeed: number): void {
+        Utils.randomGenerator = new Chance(randomGeneratorSeed);
     }
 
     /**

+ 2 - 2
src/cli/JavaScriptObfuscatorCLI.ts

@@ -1,9 +1,9 @@
 import * as commander from 'commander';
 import * as path from 'path';
 
-import { TStringArrayEncoding } from '../types/TStringArrayEncoding';
+import { TStringArrayEncoding } from '../types/options/TStringArrayEncoding';
 
-import { IInputOptions } from '../interfaces/IInputOptions';
+import { IInputOptions } from '../interfaces/options/IInputOptions';
 import { IObfuscationResult } from '../interfaces/IObfuscationResult';
 
 import { SourceMapMode } from '../enums/SourceMapMode';

+ 106 - 0
src/container/InversifyContainerFacade.ts

@@ -0,0 +1,106 @@
+import { Container, interfaces } from 'inversify';
+import { ServiceIdentifiers } from './ServiceIdentifiers';
+
+import { customNodesModule } from './modules/custom-nodes/CustomNodesModule';
+import { nodeControlFlowTransformersModule } from './modules/node-transformers/NodeControlFlowTransformersModule';
+import { nodeObfuscatorsModule } from './modules/node-transformers/NodeObfuscatorsModule';
+import { nodeTransformersModule } from './modules/node-transformers/NodeTransformersModule';
+import { stackTraceAnalyzerModule } from './modules/stack-trace-analyzer/StackTraceAnalyzerModule';
+
+import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
+import { IInputOptions } from '../interfaces/options/IInputOptions';
+import { IInversifyContainerFacade } from '../interfaces/container/IInversifyContainerFacade';
+import { IJavaScriptObfuscator } from '../interfaces/IJavaScriptObfsucator';
+import { IObfuscationEventEmitter } from '../interfaces/event-emitters/IObfuscationEventEmitter';
+import { IObfuscationResult } from '../interfaces/IObfuscationResult';
+import { IObfuscator } from '../interfaces/IObfuscator';
+import { IOptions } from '../interfaces/options/IOptions';
+import { ISourceMapCorrector } from '../interfaces/ISourceMapCorrector';
+import { IStorage } from '../interfaces/storages/IStorage';
+
+import { CustomNodesStorage } from '../storages/custom-nodes/CustomNodesStorage';
+import { JavaScriptObfuscatorInternal } from '../JavaScriptObfuscatorInternal';
+import { ObfuscationEventEmitter } from '../event-emitters/ObfuscationEventEmitter';
+import { ObfuscationResult } from '../ObfuscationResult';
+import { Obfuscator } from '../Obfuscator';
+import { Options } from "../options/Options";
+import { SourceMapCorrector } from '../SourceMapCorrector';
+
+export class InversifyContainerFacade implements IInversifyContainerFacade {
+    /**
+     * @type {interfaces.Container}
+     */
+    private readonly container: interfaces.Container;
+
+    /**
+     * @param options
+     */
+    constructor (options: IInputOptions) {
+        this.container = new Container();
+
+        this.container
+            .bind<IOptions>(ServiceIdentifiers.IOptions)
+            .toDynamicValue(() => {
+                return new Options(options);
+            })
+            .inSingletonScope();
+
+        this.container
+            .bind<IJavaScriptObfuscator>(ServiceIdentifiers.IJavaScriptObfuscator)
+            .to(JavaScriptObfuscatorInternal)
+            .inSingletonScope();
+
+        this.container
+            .bind<IObfuscator>(ServiceIdentifiers.IObfuscator)
+            .to(Obfuscator)
+            .inSingletonScope();
+
+        this.container
+            .bind<IObfuscationResult>(ServiceIdentifiers.IObfuscationResult)
+            .to(ObfuscationResult)
+            .inSingletonScope();
+
+        this.container
+            .bind<IObfuscationResult>(ServiceIdentifiers['Factory<IObfuscationResult>'])
+            .toFactory<IObfuscationResult>((context: interfaces.Context) => {
+                return (obfuscatedCode: string, sourceMap: string) => {
+                    const obfuscationResult: IObfuscationResult = context.container
+                        .get<IObfuscationResult>(ServiceIdentifiers.IObfuscationResult);
+
+                    obfuscationResult.initialize(obfuscatedCode, sourceMap);
+
+                    return obfuscationResult;
+                };
+            });
+
+        this.container
+            .bind<ISourceMapCorrector>(ServiceIdentifiers.ISourceMapCorrector)
+            .to(SourceMapCorrector)
+            .inSingletonScope();
+
+        this.container
+            .bind<IObfuscationEventEmitter>(ServiceIdentifiers.IObfuscationEventEmitter)
+            .to(ObfuscationEventEmitter)
+            .inSingletonScope();
+
+        this.container
+            .bind<IStorage<ICustomNode>>(ServiceIdentifiers['IStorage<ICustomNode>'])
+            .to(CustomNodesStorage)
+            .inSingletonScope();
+
+        // modules
+        this.container.load(stackTraceAnalyzerModule);
+        this.container.load(customNodesModule);
+        this.container.load(nodeTransformersModule);
+        this.container.load(nodeControlFlowTransformersModule);
+        this.container.load(nodeObfuscatorsModule);
+    }
+
+    /**
+     * @param serviceIdentifier
+     * @returns {T}
+     */
+    public get <T> (serviceIdentifier: interfaces.ServiceIdentifier<T>): T {
+        return this.container.get<T>(serviceIdentifier);
+    }
+}

+ 25 - 0
src/container/ServiceIdentifiers.ts

@@ -0,0 +1,25 @@
+import { IContainerServiceIdentifiers } from '../interfaces/container/IContainerServiceIdentifiers';
+
+export const ServiceIdentifiers: IContainerServiceIdentifiers = {
+    'Factory<ICalleeDataExtractor>': Symbol('Factory<ICalleeDataExtractor>'),
+    'Factory<IControlFlowReplacer>': Symbol('Factory<IControlFlowReplacer>'),
+    'Factory<ICustomNode>': Symbol('Factory<ICustomNode>'),
+    'Factory<ICustomNodesFactory>': Symbol('Factory<ICustomNodesFactory>'),
+    'Factory<INodeTransformer[]>': Symbol('Factory<INodeTransformer[]>'),
+    'Factory<IObfuscationResult>': Symbol('Factory<IObfuscationResult>'),
+    'Factory<IReplacer>': Symbol('Factory<IReplacer>'),
+    ICalleeDataExtractor: Symbol('ICalleeDataExtractor'),
+    ICustomNode: Symbol('ICustomNode'),
+    ICustomNodesFactory: Symbol('ICustomNodesFactory'),
+    IControlFlowReplacer: Symbol('IControlFlowReplacer'),
+    IJavaScriptObfuscator: Symbol('IJavaScriptObfuscator'),
+    INodeTransformer: Symbol('INodeTransformer'),
+    IObfuscationEventEmitter: Symbol('IObfuscationEventEmitter'),
+    IObfuscationResult: Symbol('IObfuscationResult'),
+    IObfuscator: Symbol('IObfuscator'),
+    IOptions: Symbol('IOptions'),
+    IReplacer: Symbol('IReplacer'),
+    ISourceMapCorrector: Symbol('ISourceMapCorrector'),
+    IStackTraceAnalyzer: Symbol('IStackTraceAnalyzer'),
+    'IStorage<ICustomNode>': Symbol('IStorage<ICustomNode>')
+};

+ 131 - 0
src/container/modules/custom-nodes/CustomNodesModule.ts

@@ -0,0 +1,131 @@
+import { ContainerModule, interfaces } from 'inversify';
+import { ServiceIdentifiers } from '../../ServiceIdentifiers';
+
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { ICustomNodesFactory } from '../../../interfaces/custom-nodes/ICustomNodesFactory';
+
+import { CustomNodes } from '../../../enums/container/CustomNodes';
+import { CustomNodesFactories } from '../../../enums/container/CustomNodesFactories';
+
+import { ConsoleOutputCustomNodesFactory } from '../../../custom-nodes/console-output-nodes/factory/ConsoleOutputCustomNodesFactory';
+import { DebugProtectionCustomNodesFactory } from '../../../custom-nodes/debug-protection-nodes/factory/DebugProtectionCustomNodesFactory';
+import { DomainLockCustomNodesFactory } from '../../../custom-nodes/domain-lock-nodes/factory/DomainLockCustomNodesFactory';
+import { SelfDefendingCustomNodesFactory } from '../../../custom-nodes/self-defending-nodes/factory/SelfDefendingCustomNodesFactory';
+import { StringArrayCustomNodesFactory } from '../../../custom-nodes/string-array-nodes/factory/StringArrayCustomNodesFactory';
+
+import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionNode';
+import { ControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode';
+import { ControlFlowStorageNode } from '../../../custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode';
+import { ConsoleOutputDisableExpressionNode } from '../../../custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode';
+import { DebugProtectionFunctionCallNode } from '../../../custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode';
+import { DebugProtectionFunctionIntervalNode } from '../../../custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode';
+import { DebugProtectionFunctionNode } from '../../../custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode';
+import { DomainLockNode } from '../../../custom-nodes/domain-lock-nodes/DomainLockNode';
+import { NodeCallsControllerFunctionNode } from '../../../custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode';
+import { SelfDefendingUnicodeNode } from '../../../custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode';
+import { StringArrayCallsWrapper } from '../../../custom-nodes/string-array-nodes/StringArrayCallsWrapper';
+import { StringArrayNode } from '../../../custom-nodes/string-array-nodes/StringArrayNode';
+import { StringArrayRotateFunctionNode } from '../../../custom-nodes/string-array-nodes/StringArrayRotateFunctionNode';
+
+export const customNodesModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
+    const customNodesTag: string = 'customNodes';
+    const customNodesConcreteFactoriesTag: string = 'customNodesConcreteFactories';
+
+    // custom nodes
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(BinaryExpressionFunctionNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.BinaryExpressionFunctionNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(ControlFlowStorageCallNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.ControlFlowStorageCallNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(ControlFlowStorageNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.ControlFlowStorageNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(ConsoleOutputDisableExpressionNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.ConsoleOutputDisableExpressionNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(DebugProtectionFunctionCallNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.DebugProtectionFunctionCallNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(DebugProtectionFunctionIntervalNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.DebugProtectionFunctionIntervalNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(DebugProtectionFunctionNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.DebugProtectionFunctionNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(DomainLockNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.DomainLockNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(NodeCallsControllerFunctionNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.NodeCallsControllerFunctionNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(SelfDefendingUnicodeNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.SelfDefendingUnicodeNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(StringArrayCallsWrapper)
+        .whenTargetTagged(customNodesTag, CustomNodes.StringArrayCallsWrapper);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(StringArrayNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.StringArrayNode);
+
+    bind<ICustomNode>(ServiceIdentifiers.ICustomNode)
+        .to(StringArrayRotateFunctionNode)
+        .whenTargetTagged(customNodesTag, CustomNodes.StringArrayRotateFunctionNode);
+
+    // custom nodes concrete factories
+    bind<ICustomNodesFactory>(ServiceIdentifiers.ICustomNodesFactory)
+        .to(ConsoleOutputCustomNodesFactory)
+        .whenTargetTagged(customNodesConcreteFactoriesTag, CustomNodesFactories.ConsoleOutputCustomNodesFactory);
+
+    bind<ICustomNodesFactory>(ServiceIdentifiers.ICustomNodesFactory)
+        .to(DebugProtectionCustomNodesFactory)
+        .whenTargetTagged(customNodesConcreteFactoriesTag, CustomNodesFactories.DebugProtectionCustomNodesFactory);
+
+    bind<ICustomNodesFactory>(ServiceIdentifiers.ICustomNodesFactory)
+        .to(DomainLockCustomNodesFactory)
+        .whenTargetTagged(customNodesConcreteFactoriesTag, CustomNodesFactories.DomainLockCustomNodesFactory);
+
+    bind<ICustomNodesFactory>(ServiceIdentifiers.ICustomNodesFactory)
+        .to(SelfDefendingCustomNodesFactory)
+        .whenTargetTagged(customNodesConcreteFactoriesTag, CustomNodesFactories.SelfDefendingCustomNodesFactory);
+
+    bind<ICustomNodesFactory>(ServiceIdentifiers.ICustomNodesFactory)
+        .to(StringArrayCustomNodesFactory)
+        .whenTargetTagged(customNodesConcreteFactoriesTag, CustomNodesFactories.StringArrayCustomNodesFactory);
+
+    // customNode factory
+    bind<ICustomNode>(ServiceIdentifiers['Factory<ICustomNode>'])
+        .toFactory<ICustomNode>((context: interfaces.Context) => {
+            return (customNodeName: CustomNodes) => {
+                return context.container.getTagged<ICustomNode>(
+                    ServiceIdentifiers.ICustomNode,
+                    customNodesTag,
+                    customNodeName
+                );
+            };
+        });
+
+    // customNodesFactory factory
+    bind<ICustomNodesFactory>(ServiceIdentifiers['Factory<ICustomNodesFactory>'])
+        .toFactory<ICustomNodesFactory>((context: interfaces.Context) => {
+            return (customNodesFactoryName: CustomNodesFactories) => {
+                return context.container.getTagged<ICustomNodesFactory>(
+                    ServiceIdentifiers.ICustomNodesFactory,
+                    customNodesConcreteFactoriesTag,
+                    customNodesFactoryName
+                );
+            };
+        });
+});

+ 40 - 0
src/container/modules/node-transformers/NodeControlFlowTransformersModule.ts

@@ -0,0 +1,40 @@
+import { ContainerModule, interfaces } from 'inversify';
+import { ServiceIdentifiers } from '../../ServiceIdentifiers';
+
+import { IControlFlowReplacer } from '../../../interfaces/node-transformers/IControlFlowReplacer';
+
+import { NodeControlFlowTransformersReplacers } from '../../../enums/container/NodeControlFlowTransformersReplacers';
+
+import { BinaryExpressionControlFlowReplacer } from '../../../node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer';
+
+export const nodeControlFlowTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
+    const nodeControlFlowTransformersReplacersTag: string = 'nodeControlFlowTransformersReplacers';
+
+    bind<IControlFlowReplacer>(ServiceIdentifiers.IControlFlowReplacer)
+        .to(BinaryExpressionControlFlowReplacer)
+        .whenTargetTagged(
+            nodeControlFlowTransformersReplacersTag,
+            NodeControlFlowTransformersReplacers.BinaryExpressionControlFlowReplacer
+        );
+
+    bind<IControlFlowReplacer>(ServiceIdentifiers['Factory<IControlFlowReplacer>'])
+        .toFactory<IControlFlowReplacer>((context: interfaces.Context) => {
+            const cache: Map <NodeControlFlowTransformersReplacers, IControlFlowReplacer> = new Map <NodeControlFlowTransformersReplacers, IControlFlowReplacer> ();
+
+            return (replacer: NodeControlFlowTransformersReplacers) => {
+                if (cache.has(replacer)) {
+                    return <IControlFlowReplacer>cache.get(replacer);
+                }
+
+                const controlFlowReplacer: IControlFlowReplacer = context.container.getTagged<IControlFlowReplacer>(
+                    ServiceIdentifiers.IControlFlowReplacer,
+                    nodeControlFlowTransformersReplacersTag,
+                    replacer
+                );
+
+                cache.set(replacer, controlFlowReplacer);
+
+                return controlFlowReplacer;
+            };
+        });
+});

+ 52 - 0
src/container/modules/node-transformers/NodeObfuscatorsModule.ts

@@ -0,0 +1,52 @@
+import { ContainerModule, interfaces } from 'inversify';
+import { ServiceIdentifiers } from '../../ServiceIdentifiers';
+
+import { IReplacer } from '../../../interfaces/node-transformers/IReplacer';
+
+import { NodeObfuscatorsReplacers } from '../../../enums/container/NodeObfuscatorsReplacers';
+
+import { BooleanLiteralReplacer } from '../../../node-transformers/node-obfuscators/replacers/BooleanLiteralReplacer';
+import { IdentifierReplacer } from '../../../node-transformers/node-obfuscators/replacers/IdentifierReplacer';
+import { NumberLiteralReplacer } from '../../../node-transformers/node-obfuscators/replacers/NumberLiteralReplacer';
+import { StringLiteralReplacer } from '../../../node-transformers/node-obfuscators/replacers/StringLiteralReplacer';
+
+export const nodeObfuscatorsModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
+    const nodeObfuscatorsReplacersTag: string = 'nodeObfuscatorsReplacers';
+
+    bind<IReplacer>(ServiceIdentifiers.IReplacer)
+        .to(BooleanLiteralReplacer)
+        .whenTargetTagged(nodeObfuscatorsReplacersTag, NodeObfuscatorsReplacers.BooleanReplacer);
+
+    bind<IReplacer>(ServiceIdentifiers.IReplacer)
+        .to(IdentifierReplacer)
+        .whenTargetTagged(nodeObfuscatorsReplacersTag, NodeObfuscatorsReplacers.IdentifierReplacer);
+
+    bind<IReplacer>(ServiceIdentifiers.IReplacer)
+        .to(NumberLiteralReplacer)
+        .whenTargetTagged(nodeObfuscatorsReplacersTag, NodeObfuscatorsReplacers.NumberLiteralReplacer);
+
+    bind<IReplacer>(ServiceIdentifiers.IReplacer)
+        .to(StringLiteralReplacer)
+        .whenTargetTagged(nodeObfuscatorsReplacersTag, NodeObfuscatorsReplacers.StringLiteralReplacer);
+
+    bind<IReplacer>(ServiceIdentifiers['Factory<IReplacer>'])
+        .toFactory<IReplacer>((context: interfaces.Context) => {
+            const cache: Map <NodeObfuscatorsReplacers, IReplacer> = new Map <NodeObfuscatorsReplacers, IReplacer> ();
+
+            return (replacer: NodeObfuscatorsReplacers) => {
+                if (cache.has(replacer)) {
+                    return <IReplacer>cache.get(replacer);
+                }
+
+                const obfuscationReplacer: IReplacer = context.container.getTagged<IReplacer>(
+                    ServiceIdentifiers.IReplacer,
+                    nodeObfuscatorsReplacersTag,
+                    replacer
+                );
+
+                cache.set(replacer, obfuscationReplacer);
+
+                return obfuscationReplacer;
+            };
+        });
+});

+ 94 - 0
src/container/modules/node-transformers/NodeTransformersModule.ts

@@ -0,0 +1,94 @@
+import { ContainerModule, interfaces } from 'inversify';
+import { ServiceIdentifiers } from '../../ServiceIdentifiers';
+
+import { INodeTransformer } from '../../../interfaces/node-transformers/INodeTransformer';
+
+import { NodeTransformers } from '../../../enums/container/NodeTransformers';
+
+import { FunctionControlFlowTransformer } from '../../../node-transformers/node-control-flow-transformers/FunctionControlFlowTransformer';
+
+import { CatchClauseObfuscator } from '../../../node-transformers/node-obfuscators/CatchClauseObfuscator';
+import { FunctionDeclarationObfuscator } from '../../../node-transformers/node-obfuscators/FunctionDeclarationObfuscator';
+import { FunctionObfuscator } from '../../../node-transformers/node-obfuscators/FunctionObfuscator';
+import { LabeledStatementObfuscator } from '../../../node-transformers/node-obfuscators/LabeledStatementObfuscator';
+import { LiteralObfuscator } from '../../../node-transformers/node-obfuscators/LiteralObfuscator';
+import { MemberExpressionObfuscator } from '../../../node-transformers/node-obfuscators/MemberExpressionObfuscator';
+import { MethodDefinitionObfuscator } from '../../../node-transformers/node-obfuscators/MethodDefinitionObfuscator';
+import { ObjectExpressionObfuscator } from '../../../node-transformers/node-obfuscators/ObjectExpressionObfuscator';
+import { VariableDeclarationObfuscator } from '../../../node-transformers/node-obfuscators/VariableDeclarationObfuscator';
+
+export const nodeTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
+    const nodeTransformersTag: string = 'nodeTransformers';
+
+    // node control flow transformers
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(FunctionControlFlowTransformer)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.FunctionControlFlowTransformer);
+
+    // node obfuscators
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(CatchClauseObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.CatchClauseObfuscator);
+
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(FunctionDeclarationObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.FunctionDeclarationObfuscator);
+
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(FunctionObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.FunctionObfuscator);
+
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(LabeledStatementObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.LabeledStatementObfuscator);
+
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(LiteralObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.LiteralObfuscator);
+
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(MemberExpressionObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.MemberExpressionObfuscator);
+
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(MethodDefinitionObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.MethodDefinitionObfuscator);
+
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(ObjectExpressionObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.ObjectExpressionObfuscator);
+
+    bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
+        .to(VariableDeclarationObfuscator)
+        .whenTargetTagged(nodeTransformersTag, NodeTransformers.VariableDeclarationObfuscator);
+
+    // node transformers factory
+    bind<INodeTransformer[]>(ServiceIdentifiers['Factory<INodeTransformer[]>'])
+        .toFactory<INodeTransformer[]>((context: interfaces.Context) => {
+            const cache: Map <NodeTransformers, INodeTransformer> = new Map <NodeTransformers, INodeTransformer> ();
+
+            return (nodeTransformersMap: Map<string, NodeTransformers[]>) => (nodeType: string) => {
+                const nodeTransformers: NodeTransformers[] = nodeTransformersMap.get(nodeType) || [];
+                const instancesArray: INodeTransformer[] = [];
+
+                nodeTransformers.forEach((transformer: NodeTransformers) => {
+                    let nodeTransformer: INodeTransformer;
+
+                    if (cache.has(transformer)) {
+                        nodeTransformer = <INodeTransformer>cache.get(transformer);
+                    } else {
+                        nodeTransformer = context.container.getTagged<INodeTransformer>(
+                            ServiceIdentifiers.INodeTransformer,
+                            nodeTransformersTag,
+                            transformer
+                        );
+                        cache.set(transformer, nodeTransformer);
+                    }
+
+                    instancesArray.push(nodeTransformer);
+                });
+
+                return instancesArray;
+            };
+        });
+});

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

@@ -0,0 +1,55 @@
+import { ContainerModule, interfaces } from 'inversify';
+import { ServiceIdentifiers } from '../../ServiceIdentifiers';
+
+import { ICalleeDataExtractor } from '../../../interfaces/stack-trace-analyzer/ICalleeDataExtractor';
+import { IStackTraceAnalyzer } from '../../../interfaces/stack-trace-analyzer/IStackTraceAnalyzer';
+
+import { CalleeDataExtractors } from '../../../enums/container/CalleeDataExtractors';
+import { FunctionDeclarationCalleeDataExtractor } from '../../../stack-trace-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor';
+import { FunctionExpressionCalleeDataExtractor } from '../../../stack-trace-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor';
+import { ObjectExpressionCalleeDataExtractor } from '../../../stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor';
+import { StackTraceAnalyzer } from '../../../stack-trace-analyzer/StackTraceAnalyzer';
+
+export const stackTraceAnalyzerModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
+    const calleeDataExtractorsTag: string = 'calleeDataExtractors';
+
+    // stack trace analyzer
+    bind<IStackTraceAnalyzer>(ServiceIdentifiers.IStackTraceAnalyzer)
+        .to(StackTraceAnalyzer)
+        .inSingletonScope();
+
+    // callee data extractors
+    bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
+        .to(FunctionDeclarationCalleeDataExtractor)
+        .whenTargetTagged(calleeDataExtractorsTag, CalleeDataExtractors.FunctionDeclarationCalleeDataExtractor);
+
+    bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
+        .to(FunctionExpressionCalleeDataExtractor)
+        .whenTargetTagged(calleeDataExtractorsTag, CalleeDataExtractors.FunctionExpressionCalleeDataExtractor);
+
+    bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
+        .to(ObjectExpressionCalleeDataExtractor)
+        .whenTargetTagged(calleeDataExtractorsTag, CalleeDataExtractors.ObjectExpressionCalleeDataExtractor);
+
+    // node transformers factory
+    bind<ICalleeDataExtractor>(ServiceIdentifiers['Factory<ICalleeDataExtractor>'])
+        .toFactory<ICalleeDataExtractor>((context: interfaces.Context) => {
+            const cache: Map <CalleeDataExtractors, ICalleeDataExtractor> = new Map <CalleeDataExtractors, ICalleeDataExtractor> ();
+
+            return (calleeDataExtractorName: CalleeDataExtractors) => {
+                if (cache.has(calleeDataExtractorName)) {
+                    return <ICalleeDataExtractor>cache.get(calleeDataExtractorName);
+                }
+
+                const calleeDataExtractor: ICalleeDataExtractor = context.container.getTagged<ICalleeDataExtractor>(
+                    ServiceIdentifiers.ICalleeDataExtractor,
+                    calleeDataExtractorsTag,
+                    calleeDataExtractorName
+                );
+
+                cache.set(calleeDataExtractorName, calleeDataExtractor);
+
+                return calleeDataExtractor;
+            };
+        });
+});

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

@@ -1,13 +1,18 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
+
 import * as ESTree from 'estree';
 
-import { TObfuscationEvent } from '../types/TObfuscationEvent';
+import { TObfuscationEvent } from '../types/event-emitters/TObfuscationEvent';
 
 import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../interfaces/IOptions';
-import { TStatement } from '../types/TStatement';
+import { IOptions } from '../interfaces/options/IOptions';
+import { IStackTraceData } from '../interfaces/stack-trace-analyzer/IStackTraceData';
+import { TStatement } from '../types/node/TStatement';
 
 import { NodeUtils } from '../node/NodeUtils';
 
+@injectable()
 export abstract class AbstractCustomNode implements ICustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -22,14 +27,22 @@ export abstract class AbstractCustomNode implements ICustomNode {
     /**
      * @param options
      */
-    constructor (options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         this.options = options;
     }
 
+    /**
+     * @param args
+     */
+    public abstract initialize (...args: any[]): void;
+
     /**
      * @param astTree
+     * @param stackTraceData
      */
-    public abstract appendNode (astTree: ESTree.Node): void;
+    public abstract appendNode (astTree: ESTree.Node, stackTraceData: IStackTraceData[]): void;
 
     /**
      * @returns {TObfuscationEvent}

+ 9 - 3
src/custom-nodes/AbstractCustomNodesFactory.ts

@@ -1,12 +1,16 @@
-import { TObfuscationEvent } from '../types/TObfuscationEvent';
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
+
+import { TObfuscationEvent } from '../types/event-emitters/TObfuscationEvent';
 
 import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
 import { ICustomNodesFactory } from '../interfaces/custom-nodes/ICustomNodesFactory';
-import { IOptions } from '../interfaces/IOptions';
+import { IOptions } from '../interfaces/options/IOptions';
 import { IStackTraceData } from '../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../enums/ObfuscationEvents';
 
+@injectable()
 export abstract class AbstractCustomNodesFactory implements ICustomNodesFactory {
     /**
      * @type {TObfuscationEvent}
@@ -26,7 +30,9 @@ export abstract class AbstractCustomNodesFactory implements ICustomNodesFactory
     /**
      * @param options
      */
-    constructor (options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         this.options = options;
     }
 

+ 22 - 19
src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts

@@ -1,21 +1,27 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../interfaces/IOptions';
+import { IOptions } from '../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
 import { ConsoleOutputDisableExpressionTemplate } from '../../templates/custom-nodes/console-output-nodes/console-output-disable-expression-node/ConsoleOutputDisableExpressionTemplate';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { Utils } from '../../Utils';
 
+@injectable()
 export class ConsoleOutputDisableExpressionNode extends AbstractCustomNode {
-    /**
+/**
      * @type {TObfuscationEvent}
      */
     protected readonly appendEvent: TObfuscationEvent = ObfuscationEvents.BeforeObfuscation;
@@ -23,43 +29,40 @@ export class ConsoleOutputDisableExpressionNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     protected callsControllerFunctionName: string;
 
     /**
      * @type {number}
      */
+    @initializable()
     protected randomStackTraceIndex: number;
 
     /**
-     * @type {IStackTraceData[]}
-     */
-    protected stackTraceData: IStackTraceData[];
-
-    /**
-     * @param stackTraceData
-     * @param callsControllerFunctionName
-     * @param randomStackTraceIndex
      * @param options
      */
     constructor (
-        stackTraceData: IStackTraceData[],
-        callsControllerFunctionName: string,
-        randomStackTraceIndex: number,
-        options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
+    }
 
-        this.stackTraceData = stackTraceData;
+    /**
+     * @param callsControllerFunctionName
+     * @param randomStackTraceIndex
+     */
+    public initialize (callsControllerFunctionName: string, randomStackTraceIndex: number): void {
         this.callsControllerFunctionName = callsControllerFunctionName;
         this.randomStackTraceIndex = randomStackTraceIndex;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         NodeAppender.appendNodeToOptimalBlockScope(
-            this.stackTraceData,
+            stackTraceData,
             blockScopeNode,
             this.getNode(),
             this.randomStackTraceIndex

+ 34 - 20
src/custom-nodes/console-output-nodes/factory/ConsoleOutputCustomNodesFactory.ts

@@ -1,14 +1,38 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
+
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../../interfaces/stack-trace-analyzer/IStackTraceData';
 
-import { ConsoleOutputDisableExpressionNode } from '../ConsoleOutputDisableExpressionNode';
-import { NodeCallsControllerFunctionNode } from '../../node-calls-controller-nodes/NodeCallsControllerFunctionNode';
+import { CustomNodes } from '../../../enums/container/CustomNodes';
 
 import { AbstractCustomNodesFactory } from '../../AbstractCustomNodesFactory';
 import { NodeAppender } from '../../../node/NodeAppender';
 import { Utils } from '../../../Utils';
 
+@injectable()
 export class ConsoleOutputCustomNodesFactory extends AbstractCustomNodesFactory {
+    /**
+     * @type {TCustomNodeFactory}
+     */
+    private readonly customNodeFactory: TCustomNodeFactory;
+
+    /**
+     * @param customNodeFactory
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(options);
+
+        this.customNodeFactory = customNodeFactory;
+    }
+
     /**
      * @param stackTraceData
      * @returns {Map<string, ICustomNode>}
@@ -21,25 +45,15 @@ export class ConsoleOutputCustomNodesFactory extends AbstractCustomNodesFactory
         const callsControllerFunctionName: string = Utils.getRandomVariableName();
         const randomStackTraceIndex: number = NodeAppender.getRandomStackTraceIndex(stackTraceData.length);
 
+        const consoleOutputDisableExpressionNode: ICustomNode = this.customNodeFactory(CustomNodes.ConsoleOutputDisableExpressionNode);
+        const nodeCallsControllerFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.NodeCallsControllerFunctionNode);
+
+        consoleOutputDisableExpressionNode.initialize(callsControllerFunctionName, randomStackTraceIndex);
+        nodeCallsControllerFunctionNode.initialize(callsControllerFunctionName, randomStackTraceIndex);
+
         return this.syncCustomNodesWithNodesFactory(new Map <string, ICustomNode> ([
-            [
-                'consoleOutputDisableExpressionNode',
-                new ConsoleOutputDisableExpressionNode(
-                    stackTraceData,
-                    callsControllerFunctionName,
-                    randomStackTraceIndex,
-                    this.options
-                )
-            ],
-            [
-                'ConsoleOutputNodeCallsControllerFunctionNode',
-                new NodeCallsControllerFunctionNode(
-                    stackTraceData,
-                    callsControllerFunctionName,
-                    randomStackTraceIndex,
-                    this.options
-                )
-            ]
+            ['consoleOutputDisableExpressionNode', consoleOutputDisableExpressionNode],
+            ['ConsoleOutputNodeCallsControllerFunctionNode', nodeCallsControllerFunctionNode]
         ]));
     }
 }

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

@@ -1,17 +1,24 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../../interfaces/IOptions';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../../enums/ObfuscationEvents';
 
+import { initializable } from '../../../decorators/Initializable';
+
 import { BinaryExpressionFunctionTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionTemplate';
 
 import { AbstractCustomNode } from '../../AbstractCustomNode';
 import { Utils } from '../../../Utils';
 
+@injectable()
 export class BinaryExpressionFunctionNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -21,22 +28,30 @@ export class BinaryExpressionFunctionNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     private operator: string;
 
     /**
-     * @param operator
      * @param options
      */
-    constructor (operator: string, options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(options);
+    }
 
+    /**
+     * @param operator
+     */
+    initialize (operator: string): void {
         this.operator = operator;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {}
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {}
 
     /**
      * @returns {string}

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

@@ -1,17 +1,24 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../../interfaces/IOptions';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../../enums/ObfuscationEvents';
 
+import { initializable } from '../../../decorators/Initializable';
+
 import { ControlFlowStorageCallTemplate } from '../../../templates/custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallTemplate';
 
 import { AbstractCustomNode } from '../../AbstractCustomNode';
 import { NodeAppender } from '../../../node/NodeAppender';
 
+@injectable()
 export class ControlFlowStorageCallNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -21,39 +28,48 @@ export class ControlFlowStorageCallNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     private controlFlowStorageKey: string;
 
     /**
      * @type {string}
      */
+    @initializable()
     private controlFlowStorageName: string;
 
     /**
      * @type {string}
      */
+    @initializable()
     private leftValue: string;
 
     /**
      * @type {string}
      */
+    @initializable()
     private rightValue: string;
 
+    /**
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(options);
+    }
+
     /**
      * @param controlFlowStorageName
      * @param controlFlowStorageKey
      * @param leftValue
      * @param rightValue
-     * @param options
      */
-    constructor (
+    public initialize (
         controlFlowStorageName: string,
         controlFlowStorageKey: string,
         leftValue: string,
         rightValue: string,
-        options: IOptions
-    ) {
-        super(options);
-
+    ): void {
         this.controlFlowStorageName = controlFlowStorageName;
         this.controlFlowStorageKey = controlFlowStorageKey;
         this.leftValue = leftValue;
@@ -62,8 +78,9 @@ export class ControlFlowStorageCallNode extends AbstractCustomNode {
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         NodeAppender.prependNode(blockScopeNode, this.getNode());
     }
 

+ 22 - 10
src/custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts

@@ -1,19 +1,26 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { ControlFlowStorageTemplate } from '../../templates/custom-nodes/control-flow-storage-nodes/ControlFlowStorageTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 
+@injectable()
 export class ControlFlowStorageNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -23,33 +30,38 @@ export class ControlFlowStorageNode extends AbstractCustomNode {
     /**
      * @type {IStorage <ICustomNode>}
      */
+    @initializable()
     private controlFlowStorage: IStorage <ICustomNode>;
 
     /**
      * @type {string}
      */
+    @initializable()
     private controlFlowStorageName: string;
 
     /**
-     * @param controlFlowStorage
-     * @param controlFlowStorageName
      * @param options
      */
     constructor (
-        controlFlowStorage: IStorage <ICustomNode>,
-        controlFlowStorageName: string,
-        options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
+    }
 
+    /**
+     * @param controlFlowStorage
+     * @param controlFlowStorageName
+     */
+    public initialize (controlFlowStorage: IStorage <ICustomNode>, controlFlowStorageName: string): void {
         this.controlFlowStorage = controlFlowStorage;
         this.controlFlowStorageName = controlFlowStorageName;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         NodeAppender.prependNode(blockScopeNode, this.getNode());
     }
 

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

@@ -1,17 +1,24 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../interfaces/IOptions';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { DebugProtectionFunctionCallTemplate } from '../../templates/custom-nodes/debug-protection-nodes/debug-protection-function-call-node/DebufProtectionFunctionCallTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 
+@injectable()
 export class DebugProtectionFunctionCallNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -21,22 +28,30 @@ export class DebugProtectionFunctionCallNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     private debugProtectionFunctionName: string;
 
     /**
-     * @param debugProtectionFunctionName
      * @param options
      */
-    constructor (debugProtectionFunctionName: string, options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(options);
+    }
 
+    /**
+     * @param debugProtectionFunctionName
+     */
+    public initialize (debugProtectionFunctionName: string): void {
         this.debugProtectionFunctionName = debugProtectionFunctionName;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         NodeAppender.appendNode(blockScopeNode, this.getNode());
     }
 

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

@@ -1,17 +1,24 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../interfaces/IOptions';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { DebugProtectionFunctionIntervalTemplate } from '../../templates/custom-nodes/debug-protection-nodes/debug-protection-function-interval-node/DebugProtectionFunctionIntervalTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 
+@injectable()
 export class DebugProtectionFunctionIntervalNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -21,22 +28,30 @@ export class DebugProtectionFunctionIntervalNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     private debugProtectionFunctionName: string;
 
     /**
-     * @param debugProtectionFunctionName
      * @param options
      */
-    constructor (debugProtectionFunctionName: string, options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(options);
+    }
 
+    /**
+     * @param debugProtectionFunctionName
+     */
+    public initialize (debugProtectionFunctionName: string): void {
         this.debugProtectionFunctionName = debugProtectionFunctionName;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         NodeAppender.appendNode(blockScopeNode, this.getNode());
     }
 

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

@@ -1,18 +1,25 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../interfaces/IOptions';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { DebugProtectionFunctionTemplate } from '../../templates/custom-nodes/debug-protection-nodes/debug-protection-function-node/DebugProtectionFunctionTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { Utils } from '../../Utils';
 
+@injectable()
 export class DebugProtectionFunctionNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -22,22 +29,30 @@ export class DebugProtectionFunctionNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     private debugProtectionFunctionName: string;
 
     /**
-     * @param debugProtectionFunctionName
      * @param options
      */
-    constructor (debugProtectionFunctionName: string, options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(options);
+    }
 
+    /**
+     * @param debugProtectionFunctionName
+     */
+    public initialize (debugProtectionFunctionName: string): void {
         this.debugProtectionFunctionName = debugProtectionFunctionName;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         let programBodyLength: number = blockScopeNode.body.length,
             randomIndex: number = Utils.getRandomInteger(0, programBodyLength);
 

+ 38 - 15
src/custom-nodes/debug-protection-nodes/factory/DebugProtectionCustomNodesFactory.ts

@@ -1,14 +1,37 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
+
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../../interfaces/stack-trace-analyzer/IStackTraceData';
 
-import { DebugProtectionFunctionCallNode } from '../DebugProtectionFunctionCallNode';
-import { DebugProtectionFunctionIntervalNode } from '../DebugProtectionFunctionIntervalNode';
-import { DebugProtectionFunctionNode } from '../DebugProtectionFunctionNode';
+import { CustomNodes } from '../../../enums/container/CustomNodes';
 
 import { AbstractCustomNodesFactory } from '../../AbstractCustomNodesFactory';
 import { Utils } from '../../../Utils';
 
+@injectable()
 export class DebugProtectionCustomNodesFactory extends AbstractCustomNodesFactory {
+    /**
+     * @type {TCustomNodeFactory}
+     */
+    private readonly customNodeFactory: TCustomNodeFactory;
+
+    /**
+     * @param customNodeFactory
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(options);
+
+        this.customNodeFactory = customNodeFactory;
+    }
+
     /**
      * @param stackTraceData
      * @returns {Map<string, ICustomNode>}
@@ -19,22 +42,22 @@ export class DebugProtectionCustomNodesFactory extends AbstractCustomNodesFactor
         }
 
         const debugProtectionFunctionName: string = Utils.getRandomVariableName();
+
+        const debugProtectionFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.DebugProtectionFunctionNode);
+        const debugProtectionFunctionCallNode: ICustomNode = this.customNodeFactory(CustomNodes.DebugProtectionFunctionCallNode);
+        const debugProtectionFunctionIntervalNode: ICustomNode = this.customNodeFactory(CustomNodes.DebugProtectionFunctionIntervalNode);
+
+        debugProtectionFunctionNode.initialize(debugProtectionFunctionName);
+        debugProtectionFunctionCallNode.initialize(debugProtectionFunctionName);
+        debugProtectionFunctionIntervalNode.initialize(debugProtectionFunctionName);
+
         const customNodes: Map <string, ICustomNode> = new Map <string, ICustomNode> ([
-            [
-                'debugProtectionFunctionNode',
-                new DebugProtectionFunctionNode(debugProtectionFunctionName, this.options)
-            ],
-            [
-                'debugProtectionFunctionCallNode',
-                new DebugProtectionFunctionCallNode(debugProtectionFunctionName, this.options)
-            ]
+            ['debugProtectionFunctionNode', debugProtectionFunctionNode],
+            ['debugProtectionFunctionCallNode', debugProtectionFunctionCallNode]
         ]);
 
         if (this.options.debugProtectionInterval) {
-            customNodes.set(
-                'debugProtectionFunctionIntervalNode',
-                new DebugProtectionFunctionIntervalNode(debugProtectionFunctionName, this.options)
-            );
+            customNodes.set('debugProtectionFunctionIntervalNode', debugProtectionFunctionIntervalNode);
         }
 
         return this.syncCustomNodesWithNodesFactory(customNodes);

+ 21 - 18
src/custom-nodes/domain-lock-nodes/DomainLockNode.ts

@@ -1,19 +1,25 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../interfaces/IOptions';
+import { IOptions } from '../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { DomainLockNodeTemplate } from '../../templates/custom-nodes/domain-lock-nodes/domain-lock-node/DomainLockNodeTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { Utils } from '../../Utils';
 
+@injectable()
 export class DomainLockNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -23,43 +29,40 @@ export class DomainLockNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     protected callsControllerFunctionName: string;
 
     /**
      * @type {number}
      */
+    @initializable()
     protected randomStackTraceIndex: number;
 
     /**
-     * @type {IStackTraceData[]}
-     */
-    protected stackTraceData: IStackTraceData[];
-
-    /**
-     * @param stackTraceData
-     * @param callsControllerFunctionName
-     * @param randomStackTraceIndex
      * @param options
      */
     constructor (
-        stackTraceData: IStackTraceData[],
-        callsControllerFunctionName: string,
-        randomStackTraceIndex: number,
-        options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
+    }
 
-        this.stackTraceData = stackTraceData;
+    /**
+     * @param callsControllerFunctionName
+     * @param randomStackTraceIndex
+     */
+    public initialize (callsControllerFunctionName: string, randomStackTraceIndex: number): void {
         this.callsControllerFunctionName = callsControllerFunctionName;
         this.randomStackTraceIndex = randomStackTraceIndex;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         NodeAppender.appendNodeToOptimalBlockScope(
-            this.stackTraceData,
+            stackTraceData,
             blockScopeNode,
             this.getNode(),
             this.randomStackTraceIndex

+ 34 - 20
src/custom-nodes/domain-lock-nodes/factory/DomainLockCustomNodesFactory.ts

@@ -1,14 +1,38 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
+
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../../interfaces/stack-trace-analyzer/IStackTraceData';
 
-import { DomainLockNode } from '../DomainLockNode';
-import { NodeCallsControllerFunctionNode } from '../../node-calls-controller-nodes/NodeCallsControllerFunctionNode';
+import { CustomNodes } from '../../../enums/container/CustomNodes';
 
 import { AbstractCustomNodesFactory } from '../../AbstractCustomNodesFactory';
 import { NodeAppender } from '../../../node/NodeAppender';
 import { Utils } from '../../../Utils';
 
+@injectable()
 export class DomainLockCustomNodesFactory extends AbstractCustomNodesFactory {
+    /**
+     * @type {TCustomNodeFactory}
+     */
+    private readonly customNodeFactory: TCustomNodeFactory;
+
+    /**
+     * @param customNodeFactory
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(options);
+
+        this.customNodeFactory = customNodeFactory;
+    }
+
     /**
      * @param stackTraceData
      * @returns {Map<string, ICustomNode>}
@@ -21,25 +45,15 @@ export class DomainLockCustomNodesFactory extends AbstractCustomNodesFactory {
         const callsControllerFunctionName: string = Utils.getRandomVariableName();
         const randomStackTraceIndex: number = NodeAppender.getRandomStackTraceIndex(stackTraceData.length);
 
+        const domainLockNode: ICustomNode = this.customNodeFactory(CustomNodes.DomainLockNode);
+        const nodeCallsControllerFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.NodeCallsControllerFunctionNode);
+
+        domainLockNode.initialize(callsControllerFunctionName, randomStackTraceIndex);
+        nodeCallsControllerFunctionNode.initialize(callsControllerFunctionName, randomStackTraceIndex);
+
         return this.syncCustomNodesWithNodesFactory(new Map <string, ICustomNode> ([
-            [
-                'DomainLockNode',
-                new DomainLockNode(
-                    stackTraceData,
-                    callsControllerFunctionName,
-                    randomStackTraceIndex,
-                    this.options
-                )
-            ],
-            [
-                'DomainLockNodeCallsControllerFunctionNode',
-                new NodeCallsControllerFunctionNode(
-                    stackTraceData,
-                    callsControllerFunctionName,
-                    randomStackTraceIndex,
-                    this.options
-                )
-            ]
+            ['DomainLockNode', domainLockNode],
+            ['DomainLockNodeCallsControllerFunctionNode', nodeCallsControllerFunctionNode]
         ]));
     }
 }

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

@@ -1,13 +1,18 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../interfaces/IOptions';
+import { IOptions } from '../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { SingleNodeCallControllerTemplate } from '../../templates/custom-nodes/SingleNodeCallControllerTemplate';
 
 import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset';
@@ -16,6 +21,7 @@ import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
 
+@injectable()
 export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -25,46 +31,43 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     protected callsControllerFunctionName: string;
 
     /**
      * @type {number}
      */
+    @initializable()
     protected randomStackTraceIndex: number;
 
     /**
-     * @type {IStackTraceData[]}
-     */
-    protected stackTraceData: IStackTraceData[];
-
-    /**
-     * @param stackTraceData
-     * @param callsControllerFunctionName
-     * @param randomStackTraceIndex
      * @param options
      */
     constructor (
-        stackTraceData: IStackTraceData[],
-        callsControllerFunctionName: string,
-        randomStackTraceIndex: number,
-        options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
+    }
 
-        this.stackTraceData = stackTraceData;
+    /**
+     * @param callsControllerFunctionName
+     * @param randomStackTraceIndex
+     */
+    public initialize (callsControllerFunctionName: string, randomStackTraceIndex: number): void {
         this.callsControllerFunctionName = callsControllerFunctionName;
         this.randomStackTraceIndex = randomStackTraceIndex;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         let targetBlockScope: TNodeWithBlockStatement;
 
-        if (this.stackTraceData.length) {
+        if (stackTraceData.length) {
             targetBlockScope = NodeAppender
-                .getOptimalBlockScope(this.stackTraceData, this.randomStackTraceIndex, 1);
+                .getOptimalBlockScope(stackTraceData, this.randomStackTraceIndex, 1);
         } else {
             targetBlockScope = blockScopeNode;
         }

+ 21 - 18
src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts

@@ -1,13 +1,18 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../interfaces/IOptions';
+import { IOptions } from '../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset';
 
 import { SelfDefendingTemplate } from '../../templates/custom-nodes/self-defending-nodes/self-defending-unicode-node/SelfDefendingTemplate';
@@ -17,6 +22,7 @@ import { NodeAppender } from '../../node/NodeAppender';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { Utils } from '../../Utils';
 
+@injectable()
 export class SelfDefendingUnicodeNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -26,43 +32,40 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode {
     /**
      * @type {string}
      */
+    @initializable()
     protected callsControllerFunctionName: string;
 
     /**
      * @type {number}
      */
+    @initializable()
     protected randomStackTraceIndex: number;
 
     /**
-     * @type {IStackTraceData[]}
-     */
-    protected stackTraceData: IStackTraceData[];
-
-    /**
-     * @param stackTraceData
-     * @param callsControllerFunctionName
-     * @param randomStackTraceIndex
      * @param options
      */
     constructor (
-        stackTraceData: IStackTraceData[],
-        callsControllerFunctionName: string,
-        randomStackTraceIndex: number,
-        options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
+    }
 
-        this.stackTraceData = stackTraceData;
+    /**
+     * @param callsControllerFunctionName
+     * @param randomStackTraceIndex
+     */
+    public initialize (callsControllerFunctionName: string, randomStackTraceIndex: number): void {
         this.callsControllerFunctionName = callsControllerFunctionName;
         this.randomStackTraceIndex = randomStackTraceIndex;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         NodeAppender.appendNodeToOptimalBlockScope(
-            this.stackTraceData,
+            stackTraceData,
             blockScopeNode,
             this.getNode(),
             this.randomStackTraceIndex

+ 34 - 22
src/custom-nodes/self-defending-nodes/factory/SelfDefendingCustomNodesFactory.ts

@@ -1,23 +1,45 @@
-import { TObfuscationEvent } from '../../../types/TObfuscationEvent';
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
+import { TObfuscationEvent } from '../../../types/event-emitters/TObfuscationEvent';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../../interfaces/stack-trace-analyzer/IStackTraceData';
 
+import { CustomNodes } from '../../../enums/container/CustomNodes';
 import { ObfuscationEvents } from '../../../enums/ObfuscationEvents';
 
-import { NodeCallsControllerFunctionNode } from '../../node-calls-controller-nodes/NodeCallsControllerFunctionNode';
-import { SelfDefendingUnicodeNode } from '../SelfDefendingUnicodeNode';
-
 import { AbstractCustomNodesFactory } from '../../AbstractCustomNodesFactory';
 import { NodeAppender } from '../../../node/NodeAppender';
 import { Utils } from '../../../Utils';
 
+@injectable()
 export class SelfDefendingCustomNodesFactory extends AbstractCustomNodesFactory {
     /**
      * @type {TObfuscationEvent}
      */
     protected appendEvent: TObfuscationEvent = ObfuscationEvents.AfterObfuscation;
 
+    /**
+     * @type {TCustomNodeFactory}
+     */
+    private readonly customNodeFactory: TCustomNodeFactory;
+
+    /**
+     * @param customNodeFactory
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(options);
+
+        this.customNodeFactory = customNodeFactory;
+    }
+
     /**
      * @param stackTraceData
      * @returns {Map<string, ICustomNode>}
@@ -30,25 +52,15 @@ export class SelfDefendingCustomNodesFactory extends AbstractCustomNodesFactory
         const callsControllerFunctionName: string = Utils.getRandomVariableName();
         const randomStackTraceIndex: number = NodeAppender.getRandomStackTraceIndex(stackTraceData.length);
 
+        const selfDefendingUnicodeNode: ICustomNode = this.customNodeFactory(CustomNodes.SelfDefendingUnicodeNode);
+        const nodeCallsControllerFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.NodeCallsControllerFunctionNode);
+
+        selfDefendingUnicodeNode.initialize(callsControllerFunctionName, randomStackTraceIndex);
+        nodeCallsControllerFunctionNode.initialize(callsControllerFunctionName, randomStackTraceIndex);
+
         return this.syncCustomNodesWithNodesFactory(new Map <string, ICustomNode> ([
-            [
-                'selfDefendingUnicodeNode',
-                new SelfDefendingUnicodeNode(
-                    stackTraceData,
-                    callsControllerFunctionName,
-                    randomStackTraceIndex,
-                    this.options
-                )
-            ],
-            [
-                'SelfDefendingNodeCallsControllerFunctionNode',
-                new NodeCallsControllerFunctionNode(
-                    stackTraceData,
-                    callsControllerFunctionName,
-                    randomStackTraceIndex,
-                    this.options
-                )
-            ]
+            ['selfDefendingUnicodeNode', selfDefendingUnicodeNode],
+            ['SelfDefendingNodeCallsControllerFunctionNode', nodeCallsControllerFunctionNode]
         ]));
     }
 }

+ 34 - 18
src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts

@@ -1,16 +1,22 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
-import { TStatement } from '../../types/TStatement';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
+import { TStatement } from '../../types/node/TStatement';
 
 import { ICustomNodeWithIdentifier } from '../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset';
 
 import { AtobTemplate } from '../../templates/custom-nodes/AtobTemplate';
@@ -24,6 +30,7 @@ import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
 
+@injectable()
 export class StringArrayCallsWrapper extends AbstractCustomNode implements ICustomNodeWithIdentifier {
     /**
      * @type {TObfuscationEvent}
@@ -33,41 +40,50 @@ export class StringArrayCallsWrapper extends AbstractCustomNode implements ICust
     /**
      * @type {IStorage <string>}
      */
+    @initializable()
     private stringArray: IStorage <string>;
 
     /**
      * @type {string}
      */
-    private stringArrayName: string;
+    @initializable()
+    private stringArrayCallsWrapperName: string;
 
     /**
      * @type {string}
      */
-    private stringArrayCallsWrapperName: string;
+    @initializable()
+    private stringArrayName: string;
 
     /**
-     * @param stringArrayCallsWrapperName
-     * @param stringArrayName
-     * @param stringArray
      * @param options
      */
     constructor (
-        stringArrayCallsWrapperName: string,
-        stringArrayName: string,
-        stringArray: IStorage <string>,
-        options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
+    }
 
-        this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
-        this.stringArrayName = stringArrayName;
+    /**
+     * @param stringArray
+     * @param stringArrayName
+     * @param stringArrayCallsWrapperName
+     */
+    public initialize (
+        stringArray: IStorage <string>,
+        stringArrayName: string,
+        stringArrayCallsWrapperName: string
+    ): void {
         this.stringArray = stringArray;
+        this.stringArrayName = stringArrayName;
+        this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         if (!this.stringArray.getLength()) {
             return;
         }
@@ -110,7 +126,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode implements ICust
     /**
      * @returns {string}
      */
-    protected getDecodeStringArrayTemplate (): string {
+    private getDecodeStringArrayTemplate (): string {
         let decodeStringArrayTemplate: string = '',
             selfDefendingCode: string = '';
 

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

@@ -1,21 +1,28 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
-import { TStatement } from '../../types/TStatement';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
+import { TStatement } from '../../types/node/TStatement';
 
 import { ICustomNodeWithData } from '../../interfaces/custom-nodes/ICustomNodeWithData';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { StringArrayTemplate } from '../../templates/custom-nodes/string-array-nodes/string-array-node/StringArrayTemplate';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { StringArrayStorage } from '../../storages/string-array/StringArrayStorage';
 
+@injectable()
 export class StringArrayNode extends AbstractCustomNode implements ICustomNodeWithData {
     /**
      * @type {number}
@@ -30,32 +37,40 @@ export class StringArrayNode extends AbstractCustomNode implements ICustomNodeWi
     /**
      * @type {IStorage <string>}
      */
+    @initializable()
     private stringArray: IStorage <string>;
 
     /**
      * @type {string}
      */
+    @initializable()
     private stringArrayName: string;
 
     /**
      * @type {number}
      */
+    @initializable()
     private stringArrayRotateValue: number;
 
     /**
-     * @param stringArray
-     * @param stringArrayName
-     * @param stringArrayRotateValue
      * @param options
      */
     constructor (
-        stringArray: IStorage <string>,
-        stringArrayName: string,
-        stringArrayRotateValue: number = 0,
-        options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
+    }
 
+    /**
+     * @param stringArray
+     * @param stringArrayName
+     * @param stringArrayRotateValue
+     */
+    public initialize (
+        stringArray: IStorage <string>,
+        stringArrayName: string,
+        stringArrayRotateValue: number
+    ): void {
         this.stringArray = stringArray;
         this.stringArrayName = stringArrayName;
         this.stringArrayRotateValue = stringArrayRotateValue;
@@ -63,8 +78,9 @@ export class StringArrayNode extends AbstractCustomNode implements ICustomNodeWi
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         if (!this.stringArray.getLength()) {
             return;
         }

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

@@ -1,13 +1,19 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as format from 'string-template';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
 
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStackTraceData } from '../../interfaces/stack-trace-analyzer/IStackTraceData';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
 import { ObfuscationEvents } from '../../enums/ObfuscationEvents';
 
+import { initializable } from '../../decorators/Initializable';
+
 import { NO_CUSTOM_NODES_PRESET } from '../../preset-options/NoCustomNodesPreset';
 
 import { SelfDefendingTemplate } from '../../templates/custom-nodes/string-array-nodes/string-array-rotate-function-node/SelfDefendingTemplate';
@@ -18,6 +24,7 @@ import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 import { NodeAppender } from '../../node/NodeAppender';
 import { Utils } from '../../Utils';
 
+@injectable()
 export class StringArrayRotateFunctionNode extends AbstractCustomNode {
     /**
      * @type {TObfuscationEvent}
@@ -27,41 +34,50 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
     /**
      * @type {IStorage <string>}
      */
+    @initializable()
     private stringArray: IStorage <string>;
 
     /**
      * @type {string}
      */
+    @initializable()
     private stringArrayName: string;
 
     /**
      * @param {number}
      */
+    @initializable()
     private stringArrayRotateValue: number;
 
     /**
-     * @param stringArrayName
-     * @param stringArray
-     * @param stringArrayRotateValue
      * @param options
      */
     constructor (
-        stringArrayName: string,
-        stringArray: IStorage <string>,
-        stringArrayRotateValue: number,
-        options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
+    }
 
-        this.stringArrayName = stringArrayName;
+    /**
+     * @param stringArray
+     * @param stringArrayName
+     * @param stringArrayRotateValue
+     */
+    public initialize (
+        stringArray: IStorage <string>,
+        stringArrayName: string,
+        stringArrayRotateValue: number
+    ): void {
         this.stringArray = stringArray;
+        this.stringArrayName = stringArrayName;
         this.stringArrayRotateValue = stringArrayRotateValue;
     }
 
     /**
      * @param blockScopeNode
+     * @param stackTraceData
      */
-    public appendNode (blockScopeNode: TNodeWithBlockStatement): void {
+    public appendNode (blockScopeNode: TNodeWithBlockStatement, stackTraceData: IStackTraceData[]): void {
         if (!this.stringArray.getLength()) {
             return;
         }

+ 40 - 42
src/custom-nodes/string-array-nodes/factory/StringArrayCustomNodesFactory.ts

@@ -1,19 +1,24 @@
-import { TObfuscationEvent } from '../../../types/TObfuscationEvent';
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
+import { TObfuscationEvent } from '../../../types/event-emitters/TObfuscationEvent';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
 import { IStackTraceData } from '../../../interfaces/stack-trace-analyzer/IStackTraceData';
+import { IStorage } from '../../../interfaces/storages/IStorage';
 
+import { CustomNodes } from '../../../enums/container/CustomNodes';
 import { ObfuscationEvents } from '../../../enums/ObfuscationEvents';
 
-import { StringArrayCallsWrapper } from '../StringArrayCallsWrapper';
 import { StringArrayNode } from '../StringArrayNode';
-import { StringArrayRotateFunctionNode } from '../StringArrayRotateFunctionNode';
 
 import { AbstractCustomNodesFactory } from '../../AbstractCustomNodesFactory';
 import { StringArrayStorage } from '../../../storages/string-array/StringArrayStorage';
 import { Utils } from '../../../Utils';
-import { IStorage } from '../../../interfaces/IStorage';
 
+@injectable()
 export class StringArrayCustomNodesFactory extends AbstractCustomNodesFactory {
     /**
      * @type {TObfuscationEvent}
@@ -21,19 +26,22 @@ export class StringArrayCustomNodesFactory extends AbstractCustomNodesFactory {
     protected appendEvent: TObfuscationEvent = ObfuscationEvents.AfterObfuscation;
 
     /**
-     * @type {string}
+     * @type {TCustomNodeFactory}
      */
-    private stringArrayName: string = Utils.getRandomVariableName(StringArrayNode.ARRAY_RANDOM_LENGTH);
+    private readonly customNodeFactory: TCustomNodeFactory;
 
     /**
-     * @type {string}
+     * @param customNodeFactory
+     * @param options
      */
-    private stringArrayCallsWrapper: string = Utils.getRandomVariableName(StringArrayNode.ARRAY_RANDOM_LENGTH);
+    constructor (
+        @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(options);
 
-    /**
-     * @type {number}
-     */
-    private stringArrayRotateValue: number;
+        this.customNodeFactory = customNodeFactory;
+    }
 
     /**
      * @param stackTraceData
@@ -44,44 +52,34 @@ export class StringArrayCustomNodesFactory extends AbstractCustomNodesFactory {
             return;
         }
 
+        const stringArray: IStorage <string> = new StringArrayStorage();
+
+        const stringArrayNode: ICustomNode = this.customNodeFactory(CustomNodes.StringArrayNode);
+        const stringArrayCallsWrapper: ICustomNode = this.customNodeFactory(CustomNodes.StringArrayCallsWrapper);
+        const stringArrayRotateFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.StringArrayRotateFunctionNode);
+
+        const stringArrayName: string = Utils.getRandomVariableName(StringArrayNode.ARRAY_RANDOM_LENGTH);
+        const stringArrayCallsWrapperName: string = Utils.getRandomVariableName(StringArrayNode.ARRAY_RANDOM_LENGTH);
+
+        let stringArrayRotateValue: number;
+
         if (this.options.rotateStringArray) {
-            this.stringArrayRotateValue = Utils.getRandomInteger(100, 500);
+            stringArrayRotateValue = Utils.getRandomInteger(100, 500);
         } else {
-            this.stringArrayRotateValue = 0;
+            stringArrayRotateValue = 0;
         }
 
-        const stringArray: IStorage <string> = new StringArrayStorage();
-        const stringArrayNode: ICustomNode = new StringArrayNode(
-            stringArray,
-            this.stringArrayName,
-            this.stringArrayRotateValue,
-            this.options
-        );
+        stringArrayNode.initialize(stringArray, stringArrayName, stringArrayRotateValue);
+        stringArrayCallsWrapper.initialize(stringArray, stringArrayName, stringArrayCallsWrapperName);
+        stringArrayRotateFunctionNode.initialize(stringArray, stringArrayName, stringArrayRotateValue);
+
         const customNodes: Map <string, ICustomNode> = new Map <string, ICustomNode> ([
-            [
-                'stringArrayNode', stringArrayNode,
-            ],
-            [
-                'stringArrayCallsWrapper',
-                new StringArrayCallsWrapper(
-                    this.stringArrayCallsWrapper,
-                    this.stringArrayName,
-                    stringArray,
-                    this.options
-                )
-            ]
+            ['stringArrayNode', stringArrayNode],
+            ['stringArrayCallsWrapper', stringArrayCallsWrapper]
         ]);
 
         if (this.options.rotateStringArray) {
-            customNodes.set(
-                'stringArrayRotateFunctionNode',
-                new StringArrayRotateFunctionNode(
-                    this.stringArrayName,
-                    stringArray,
-                    this.stringArrayRotateValue,
-                    this.options
-                )
-            );
+            customNodes.set('stringArrayRotateFunctionNode', stringArrayRotateFunctionNode);
         }
 
         return this.syncCustomNodesWithNodesFactory(customNodes);

+ 58 - 0
src/decorators/Initializable.ts

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

+ 1 - 1
src/enums/ObfuscationEvents.ts

@@ -1,4 +1,4 @@
-import { TObfuscationEvent } from '../types/TObfuscationEvent';
+import { TObfuscationEvent } from '../types/event-emitters/TObfuscationEvent';
 
 import { Utils } from '../Utils';
 

+ 5 - 0
src/enums/container/CalleeDataExtractors.ts

@@ -0,0 +1,5 @@
+export enum CalleeDataExtractors {
+    FunctionDeclarationCalleeDataExtractor,
+    FunctionExpressionCalleeDataExtractor,
+    ObjectExpressionCalleeDataExtractor,
+}

+ 15 - 0
src/enums/container/CustomNodes.ts

@@ -0,0 +1,15 @@
+export enum CustomNodes {
+    BinaryExpressionFunctionNode,
+    ControlFlowStorageCallNode,
+    ControlFlowStorageNode,
+    ConsoleOutputDisableExpressionNode,
+    DebugProtectionFunctionCallNode,
+    DebugProtectionFunctionIntervalNode,
+    DebugProtectionFunctionNode,
+    DomainLockNode,
+    NodeCallsControllerFunctionNode,
+    SelfDefendingUnicodeNode,
+    StringArrayCallsWrapper,
+    StringArrayNode,
+    StringArrayRotateFunctionNode
+}

+ 7 - 0
src/enums/container/CustomNodesFactories.ts

@@ -0,0 +1,7 @@
+export enum CustomNodesFactories {
+    ConsoleOutputCustomNodesFactory,
+    DebugProtectionCustomNodesFactory,
+    DomainLockCustomNodesFactory,
+    SelfDefendingCustomNodesFactory,
+    StringArrayCustomNodesFactory
+}

+ 3 - 0
src/enums/container/NodeControlFlowTransformersReplacers.ts

@@ -0,0 +1,3 @@
+export enum NodeControlFlowTransformersReplacers {
+    BinaryExpressionControlFlowReplacer
+}

+ 6 - 0
src/enums/container/NodeObfuscatorsReplacers.ts

@@ -0,0 +1,6 @@
+export enum NodeObfuscatorsReplacers {
+    BooleanReplacer,
+    IdentifierReplacer,
+    NumberLiteralReplacer,
+    StringLiteralReplacer
+}

+ 12 - 0
src/enums/container/NodeTransformers.ts

@@ -0,0 +1,12 @@
+export enum NodeTransformers {
+    FunctionControlFlowTransformer,
+    CatchClauseObfuscator,
+    FunctionDeclarationObfuscator,
+    FunctionObfuscator,
+    LabeledStatementObfuscator,
+    LiteralObfuscator,
+    MemberExpressionObfuscator,
+    MethodDefinitionObfuscator,
+    ObjectExpressionObfuscator,
+    VariableDeclarationObfuscator
+}

+ 6 - 1
src/event-emitters/ObfuscationEventEmitter.ts

@@ -1,5 +1,10 @@
-import { IObfuscationEventEmitter } from '../interfaces/IObfuscationEventEmitter';
+import { decorate, injectable } from 'inversify';
+
+import { IObfuscationEventEmitter } from '../interfaces/event-emitters/IObfuscationEventEmitter';
 
 import { EventEmitter } from 'events';
 
+decorate(injectable(), EventEmitter);
+
+@injectable()
 export class ObfuscationEventEmitter extends EventEmitter implements IObfuscationEventEmitter {}

+ 6 - 0
src/interfaces/IInitializable.d.ts

@@ -0,0 +1,6 @@
+export interface IInitializable {
+    /**
+     * @param args
+     */
+    initialize (...args: any[]): void;
+}

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

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

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

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

+ 3 - 1
src/interfaces/IObfuscationResult.d.ts

@@ -1,4 +1,6 @@
-export interface IObfuscationResult {
+import { IInitializable } from './IInitializable';
+
+export interface IObfuscationResult extends IInitializable {
     /**
      * @return {string}
      */

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

@@ -1,5 +1,8 @@
 import * as ESTree from 'estree';
 
+import { ICustomNode } from './custom-nodes/ICustomNode';
+import { IStorage } from './storages/IStorage';
+
 export interface IObfuscator {
-    obfuscateAstTree (astTree: ESTree.Program): ESTree.Program;
+    obfuscateAstTree (astTree: ESTree.Program, customNodesStorage: IStorage<ICustomNode>): ESTree.Program;
 }

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

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

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

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

+ 26 - 0
src/interfaces/container/IContainerServiceIdentifiers.d.ts

@@ -0,0 +1,26 @@
+import { interfaces } from 'inversify';
+
+export interface IContainerServiceIdentifiers {
+    'Factory<ICalleeDataExtractor>': interfaces.ServiceIdentifier<any>;
+    'Factory<IControlFlowReplacer>': interfaces.ServiceIdentifier<any>;
+    'Factory<ICustomNode>': interfaces.ServiceIdentifier<any>;
+    'Factory<ICustomNodesFactory>': interfaces.ServiceIdentifier<any>;
+    'Factory<INodeTransformer[]>': interfaces.ServiceIdentifier<any>;
+    'Factory<IObfuscationResult>': interfaces.ServiceIdentifier<any>;
+    'Factory<IReplacer>': interfaces.ServiceIdentifier<any>;
+    ICalleeDataExtractor: interfaces.ServiceIdentifier<any>;
+    IControlFlowReplacer: interfaces.ServiceIdentifier<any>;
+    ICustomNode: interfaces.ServiceIdentifier<any>;
+    ICustomNodesFactory: interfaces.ServiceIdentifier<any>;
+    IJavaScriptObfuscator: interfaces.ServiceIdentifier<any>;
+    INodeTransformer: interfaces.ServiceIdentifier<any>;
+    IObfuscationEventEmitter: interfaces.ServiceIdentifier<any>;
+    IObfuscationResult: interfaces.ServiceIdentifier<any>;
+    IObfuscator: interfaces.ServiceIdentifier<any>;
+    IOptions: interfaces.ServiceIdentifier<any>;
+    IReplacer: interfaces.ServiceIdentifier<any>;
+    ISourceMapCorrector: interfaces.ServiceIdentifier<any>;
+    IStackTraceAnalyzer: interfaces.ServiceIdentifier<any>;
+    'IStorage<ICustomNode>': interfaces.ServiceIdentifier<any>;
+    [key: string]: interfaces.ServiceIdentifier<any>;
+}

+ 5 - 0
src/interfaces/container/IInversifyContainerFacade.d.ts

@@ -0,0 +1,5 @@
+import { interfaces } from 'inversify';
+
+export interface IInversifyContainerFacade {
+    get <T> (serviceIdentifier: interfaces.ServiceIdentifier<T>): T;
+}

+ 8 - 4
src/interfaces/custom-nodes/ICustomNode.d.ts

@@ -1,13 +1,17 @@
 import * as ESTree from 'estree';
 
-import { TObfuscationEvent } from '../../types/TObfuscationEvent';
-import { TStatement } from '../../types/TStatement';
+import { TObfuscationEvent } from '../../types/event-emitters/TObfuscationEvent';
+import { IStackTraceData } from '../stack-trace-analyzer/IStackTraceData';
+import { TStatement } from '../../types/node/TStatement';
 
-export interface ICustomNode {
+import { IInitializable } from '../IInitializable';
+
+export interface ICustomNode extends IInitializable {
     /**
      * @param astTree
+     * @param stackTraceData
      */
-    appendNode (astTree: ESTree.Node): void;
+    appendNode (astTree: ESTree.Node, stackTraceData: IStackTraceData[]): void;
 
     /**
      * @returns {TObfuscationEvent}

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

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

+ 1 - 1
src/interfaces/IObfuscationEventEmitter.d.ts → src/interfaces/event-emitters/IObfuscationEventEmitter.d.ts

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

+ 2 - 2
src/interfaces/IControlFlowReplacer.d.ts → src/interfaces/node-transformers/IControlFlowReplacer.d.ts

@@ -1,7 +1,7 @@
 import * as ESTree from 'estree';
 
-import { ICustomNode } from './custom-nodes/ICustomNode';
-import { IStorage } from './IStorage';
+import { ICustomNode } from '../custom-nodes/ICustomNode';
+import { IStorage } from '../storages/IStorage';
 
 export interface IControlFlowReplacer {
     replace (

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


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

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

+ 2 - 2
src/interfaces/IInputOptions.d.ts → src/interfaces/options/IInputOptions.d.ts

@@ -1,5 +1,5 @@
-import { TSourceMapMode } from '../types/TSourceMapMode';
-import { TStringArrayEncoding } from '../types/TStringArrayEncoding';
+import { TSourceMapMode } from '../../types/TSourceMapMode';
+import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
 
 export interface IInputOptions {
     compact?: boolean;

+ 2 - 2
src/interfaces/IOptions.d.ts → src/interfaces/options/IOptions.d.ts

@@ -1,5 +1,5 @@
-import { TSourceMapMode } from '../types/TSourceMapMode';
-import { TStringArrayEncoding } from '../types/TStringArrayEncoding';
+import { TSourceMapMode } from '../../types/TSourceMapMode';
+import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
 
 export interface IOptions {
     readonly compact: boolean;

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

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

+ 3 - 1
src/interfaces/IStorage.d.ts → src/interfaces/storages/IStorage.d.ts

@@ -1,4 +1,6 @@
-export interface IStorage <T> {
+import { IInitializable } from '../IInitializable';
+
+export interface IStorage <T> extends IInitializable {
     get (key: string | number): T;
     getKeyOf (value: T): string | number | null;
     getLength (): number;

+ 11 - 4
src/node-transformers/AbstractNodeTransformer.ts

@@ -1,10 +1,14 @@
+import { injectable, inject } from 'inversify';
+
 import * as ESTree from 'estree';
 
 import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
-import { INodeTransformer } from '../interfaces/INodeTransformer';
-import { IOptions } from '../interfaces/IOptions';
-import { IStorage } from '../interfaces/IStorage';
+import { INodeTransformer } from '../interfaces/node-transformers/INodeTransformer';
+import { IOptions } from '../interfaces/options/IOptions';
+import { IStorage } from '../interfaces/storages/IStorage';
+import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
 
+@injectable()
 export abstract class AbstractNodeTransformer implements INodeTransformer {
     /**
      * @type IStorage<ICustomNode>
@@ -20,7 +24,10 @@ export abstract class AbstractNodeTransformer implements INodeTransformer {
      * @param customNodesStorage
      * @param options
      */
-    constructor (customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         this.customNodesStorage = customNodesStorage;
         this.options = options;
     }

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

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

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

@@ -1,12 +1,15 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
-import { TControlFlowReplacer } from '../../types/TControlFlowReplacer';
-import { TStatement } from '../../types/TStatement';
+import { TControlFlowReplacer } from '../../types/node-transformers/TControlFlowReplacer';
+import { TStatement } from '../../types/node/TStatement';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
 import { NodeType } from '../../enums/NodeType';
 
@@ -17,8 +20,8 @@ import { ControlFlowStorageNode } from '../../custom-nodes/control-flow-storage-
 import { Node } from '../../node/Node';
 import { NodeAppender } from '../../node/NodeAppender';
 import { Utils } from '../../Utils';
-import { NodeUtils } from '../../node/NodeUtils';
 
+@injectable()
 export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
     /**
      * @type {Map <string, IReplacer>}
@@ -31,7 +34,10 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
      * @param customNodesStorage
      * @param options
      */
-    constructor (customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(customNodesStorage, options);
     }
 
@@ -53,8 +59,6 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
         const controlFlowStorage: IStorage <ICustomNode> = new ControlFlowStorage();
         const controlFlowStorageCustomNodeName: string = Utils.getRandomVariableName(6);
 
-        console.log(NodeUtils.getNodeBlockScopeDepth(functionNode.body));
-
         estraverse.replace(functionNode.body, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                 const controlFlowReplacer: TControlFlowReplacer | undefined = FunctionControlFlowTransformer
@@ -91,11 +95,9 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
             return;
         }
 
-        const controlFlowStorageCustomNode: ICustomNode = new ControlFlowStorageNode(
-            controlFlowStorage,
-            controlFlowStorageCustomNodeName,
-            this.options
-        );
+        const controlFlowStorageCustomNode: ICustomNode = new ControlFlowStorageNode(this.options);
+
+        controlFlowStorageCustomNode.initialize(controlFlowStorage, controlFlowStorageCustomNodeName);
 
         NodeAppender.prependNode(functionNode.body, controlFlowStorageCustomNode.getNode());
     }

+ 11 - 4
src/node-transformers/node-control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts

@@ -1,12 +1,16 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
 import * as ESTree from 'estree';
 
-import { IControlFlowReplacer } from '../../../interfaces/IControlFlowReplacer';
+import { IControlFlowReplacer } from '../../../interfaces/node-transformers/IControlFlowReplacer';
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../../interfaces/IOptions';
-import { IStorage } from '../../../interfaces/IStorage';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IStorage } from '../../../interfaces/storages/IStorage';
 
 import { Utils } from '../../../Utils';
 
+@injectable()
 export abstract class AbstractControlFlowReplacer implements IControlFlowReplacer {
     /**
      * @type IStorage<ICustomNode>
@@ -22,7 +26,10 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace
      * @param customNodesStorage
      * @param options
      */
-    constructor (customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         this.customNodesStorage = customNodesStorage;
         this.options = options;
     }

+ 30 - 7
src/node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts

@@ -1,14 +1,30 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
 import * as escodegen from 'escodegen';
 import * as ESTree from 'estree';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
-import { IStorage } from '../../../interfaces/IStorage';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IStorage } from '../../../interfaces/storages/IStorage';
 
 import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
 import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionNode';
 import { ControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode';
 
+@injectable()
 export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowReplacer {
+    /**
+     * @param customNodesStorage
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+    }
+
     /**
      * @param expressionNode
      * @returns {string}
@@ -24,24 +40,31 @@ export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowRepl
      * @param parentNode
      * @param controlFlowStorage
      * @param controlFlowStorageCustomNodeName
-     * @returns {ICustomNode | undefined}
+     * @returns {ICustomNode}
      */
     public replace (
         binaryExpressionNode: ESTree.BinaryExpression,
         parentNode: ESTree.Node,
         controlFlowStorage: IStorage <ICustomNode>,
         controlFlowStorageCustomNodeName: string
-    ): ICustomNode | undefined {
+    ): ICustomNode {
         const key: string = AbstractControlFlowReplacer.getStorageKey();
+        const binaryExpressionFunctionNode = new BinaryExpressionFunctionNode(this.options);
+
+        // TODO: pass through real stackTraceData
+        binaryExpressionFunctionNode.initialize(binaryExpressionNode.operator);
 
-        controlFlowStorage.set(key, new BinaryExpressionFunctionNode(binaryExpressionNode.operator, this.options));
+        controlFlowStorage.set(key, binaryExpressionFunctionNode);
 
-        return new ControlFlowStorageCallNode(
+        const controlFlowStorageCallNode: ICustomNode = new ControlFlowStorageCallNode(this.options);
+
+        controlFlowStorageCallNode.initialize(
             controlFlowStorageCustomNodeName,
             key,
             BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.left),
-            BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.right),
-            this.options
+            BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.right)
         );
+
+        return controlFlowStorageCallNode;
     }
 }

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

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

+ 22 - 5
src/node-transformers/node-obfuscators/CatchClauseObfuscator.ts

@@ -1,16 +1,22 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
+import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
+import { Utils } from '../../Utils';
 
 /**
  * replaces:
@@ -20,26 +26,37 @@ import { NodeUtils } from '../../node/NodeUtils';
  *     try {} catch (_0x12d45f) { console.log(_0x12d45f); };
  *
  */
+@injectable()
 export class CatchClauseObfuscator extends AbstractNodeTransformer {
     /**
      * @type {IdentifierReplacer}
      */
-    private readonly identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IReplacer & IdentifierReplacer;
 
     /**
      * @param customNodesStorage
+     * @param replacersFactory
      * @param options
      */
-    constructor(customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(customNodesStorage, options);
 
-        this.identifierReplacer = new IdentifierReplacer(this.customNodesStorage, this.options);
+        this.identifierReplacer = <IdentifierReplacer>replacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
 
     /**
      * @param catchClauseNode
      */
     public transformNode (catchClauseNode: ESTree.CatchClause): void {
+        this.identifierReplacer.setPrefix(Utils.getRandomGenerator().string({
+            length: 5,
+            pool: Utils.randomGeneratorPool
+        }));
+
         this.storeCatchClauseParam(catchClauseNode);
         this.replaceCatchClauseParam(catchClauseNode);
     }

+ 22 - 5
src/node-transformers/node-obfuscators/FunctionDeclarationObfuscator.ts

@@ -1,18 +1,24 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
+import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
+import { Utils } from '../../Utils';
 
 /**
  * replaces:
@@ -23,6 +29,7 @@ import { NodeUtils } from '../../node/NodeUtils';
  *     function _0x12d45f () { //... };
  *     _0x12d45f();
  */
+@injectable()
 export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
     /**
      * @type {IdentifierReplacer}
@@ -31,12 +38,17 @@ export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
 
     /**
      * @param customNodesStorage
+     * @param nodeObfuscatorsReplacersFactory
      * @param options
      */
-    constructor(customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers['Factory<IReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(customNodesStorage, options);
 
-        this.identifierReplacer = new IdentifierReplacer(this.customNodesStorage, this.options);
+        this.identifierReplacer = <IdentifierReplacer>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
 
     /**
@@ -44,6 +56,11 @@ export class FunctionDeclarationObfuscator extends AbstractNodeTransformer {
      * @param parentNode
      */
     public transformNode (functionDeclarationNode: ESTree.FunctionDeclaration, parentNode: ESTree.Node): void {
+        this.identifierReplacer.setPrefix(Utils.getRandomGenerator().string({
+            length: 5,
+            pool: Utils.randomGeneratorPool
+        }));
+
         const blockScopeOfFunctionDeclarationNode: TNodeWithBlockStatement = NodeUtils
             .getBlockScopeOfNode(functionDeclarationNode);
 

+ 21 - 4
src/node-transformers/node-obfuscators/FunctionObfuscator.ts

@@ -1,16 +1,22 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
+import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
+import { Utils } from '../../Utils';
 
 /**
  * replaces:
@@ -20,6 +26,7 @@ import { NodeUtils } from '../../node/NodeUtils';
  *     function foo (_0x12d45f) { return _0x12d45f; };
  *
  */
+@injectable()
 export class FunctionObfuscator extends AbstractNodeTransformer {
     /**
      * @type {IdentifierReplacer}
@@ -28,18 +35,28 @@ export class FunctionObfuscator extends AbstractNodeTransformer {
 
     /**
      * @param customNodesStorage
+     * @param nodeObfuscatorsReplacersFactory
      * @param options
      */
-    constructor(customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers['Factory<IReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(customNodesStorage, options);
 
-        this.identifierReplacer = new IdentifierReplacer(this.customNodesStorage, this.options);
+        this.identifierReplacer = <IdentifierReplacer>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
 
     /**
      * @param functionNode
      */
     public transformNode (functionNode: ESTree.Function): void {
+        this.identifierReplacer.setPrefix(Utils.getRandomGenerator().string({
+            length: 5,
+            pool: Utils.randomGeneratorPool
+        }));
+
         this.storeFunctionParams(functionNode);
         this.replaceFunctionParams(functionNode);
     }

+ 21 - 4
src/node-transformers/node-obfuscators/LabeledStatementObfuscator.ts

@@ -1,16 +1,22 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
+import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
+import { Utils } from '../../Utils';
 
 /**
  * replaces:
@@ -28,6 +34,7 @@ import { NodeUtils } from '../../node/NodeUtils';
  *     }
  *
  */
+@injectable()
 export class LabeledStatementObfuscator extends AbstractNodeTransformer {
     /**
      * @type {IdentifierReplacer}
@@ -36,18 +43,28 @@ export class LabeledStatementObfuscator extends AbstractNodeTransformer {
 
     /**
      * @param customNodesStorage
+     * @param nodeObfuscatorsReplacersFactory
      * @param options
      */
-    constructor(customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers['Factory<IReplacer>']) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(customNodesStorage, options);
 
-        this.identifierReplacer = new IdentifierReplacer(this.customNodesStorage, this.options);
+        this.identifierReplacer = <IdentifierReplacer>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
 
     /**
      * @param labeledStatementNode
      */
     public transformNode (labeledStatementNode: ESTree.LabeledStatement): void {
+        this.identifierReplacer.setPrefix(Utils.getRandomGenerator().string({
+            length: 5,
+            pool: Utils.randomGeneratorPool
+        }));
+
         this.storeLabeledStatementName(labeledStatementNode);
         this.replaceLabeledStatementName(labeledStatementNode);
     }

+ 34 - 6
src/node-transformers/node-obfuscators/LiteralObfuscator.ts

@@ -1,13 +1,41 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as escodegen from 'escodegen';
 import * as ESTree from 'estree';
 
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../interfaces/storages/IStorage';
+
+import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
+
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
-import { BooleanLiteralReplacer } from './replacers/BooleanLiteralReplacer';
 import { Node } from '../../node/Node';
-import { NumberLiteralReplacer } from './replacers/NumberLiteralReplacer';
-import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 
+@injectable()
 export class LiteralObfuscator extends AbstractNodeTransformer {
+    /**
+     * @type {(replacer: NodeObfuscatorsReplacers) => IReplacer}
+     */
+    private readonly replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer;
+
+    /**
+     * @param customNodesStorage
+     * @param replacersFactory
+     * @param options
+     */
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+
+        this.replacersFactory = replacersFactory;
+    }
+
     /**
      * @param literalNode
      * @param parentNode
@@ -21,19 +49,19 @@ export class LiteralObfuscator extends AbstractNodeTransformer {
 
         switch (typeof literalNode.value) {
             case 'boolean':
-                content = new BooleanLiteralReplacer(this.customNodesStorage, this.options)
+                content = this.replacersFactory(NodeObfuscatorsReplacers.BooleanReplacer)
                     .replace(<boolean>literalNode.value);
 
                 break;
 
             case 'number':
-                content = new NumberLiteralReplacer(this.customNodesStorage, this.options)
+                content = this.replacersFactory(NodeObfuscatorsReplacers.NumberLiteralReplacer)
                     .replace(<number>literalNode.value);
 
                 break;
 
             case 'string':
-                content = new StringLiteralReplacer(this.customNodesStorage, this.options)
+                content = this.replacersFactory(NodeObfuscatorsReplacers.StringLiteralReplacer)
                     .replace(<string>literalNode.value);
 
                 break;

+ 32 - 3
src/node-transformers/node-obfuscators/MemberExpressionObfuscator.ts

@@ -1,14 +1,43 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as escodegen from 'escodegen';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../interfaces/storages/IStorage';
+
+import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
-import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 
+@injectable()
 export class MemberExpressionObfuscator extends AbstractNodeTransformer {
+    /**
+     * @type {IReplacer}
+     */
+    private readonly stringLiteralReplacer: IReplacer;
+
+    /**
+     * @param customNodesStorage
+     * @param replacersFactory
+     * @param options
+     */
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+
+        this.stringLiteralReplacer = replacersFactory(NodeObfuscatorsReplacers.StringLiteralReplacer);
+    }
+
     /**
      * @param memberExpressionNode
      */
@@ -50,7 +79,7 @@ export class MemberExpressionObfuscator extends AbstractNodeTransformer {
         const literalNode: ESTree.Literal = {
             raw: `'${nodeValue}'`,
             'x-verbatim-property': {
-                content : new StringLiteralReplacer(this.customNodesStorage, this.options).replace(nodeValue),
+                content: this.stringLiteralReplacer.replace(nodeValue),
                 precedence: escodegen.Precedence.Primary
             },
             type: NodeType.Literal,
@@ -74,7 +103,7 @@ export class MemberExpressionObfuscator extends AbstractNodeTransformer {
     private obfuscateLiteralProperty (node: ESTree.Literal): void {
         if (typeof node.value === 'string' && !node['x-verbatim-property']) {
             node['x-verbatim-property'] = {
-                content : new StringLiteralReplacer(this.customNodesStorage, this.options).replace(node.value),
+                content : this.stringLiteralReplacer.replace(node.value),
                 precedence: escodegen.Precedence.Primary
             };
         }

+ 32 - 3
src/node-transformers/node-obfuscators/MethodDefinitionObfuscator.ts

@@ -1,10 +1,19 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../interfaces/storages/IStorage';
+
+import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
+
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { Node } from '../../node/Node';
 import { Utils } from '../../Utils';
-import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
 
 /**
  * replaces:
@@ -13,12 +22,33 @@ import { StringLiteralReplacer } from './replacers/StringLiteralReplacer';
  * on:
  *     [_0x9a4e('0x0')] { //... };
  */
+@injectable()
 export class MethodDefinitionObfuscator extends AbstractNodeTransformer {
+    /**
+     * @type {IReplacer}
+     */
+    private readonly stringLiteralReplacer: IReplacer;
+
     /**
      * @type {string[]}
      */
     private static readonly ignoredNames: string[] = ['constructor'];
 
+    /**
+     * @param customNodesStorage
+     * @param replacersFactory
+     * @param options
+     */
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+
+        this.stringLiteralReplacer = replacersFactory(NodeObfuscatorsReplacers.StringLiteralReplacer);
+    }
+
     /**
      * @param methodDefinitionNode
      * @param parentNode
@@ -39,8 +69,7 @@ export class MethodDefinitionObfuscator extends AbstractNodeTransformer {
                     methodDefinitionNode.computed === false
                 ) {
                     methodDefinitionNode.computed = true;
-                    node.name = new StringLiteralReplacer(this.customNodesStorage, this.options)
-                        .replace(node.name);
+                    node.name = this.stringLiteralReplacer.replace(node.name);
 
                     return;
                 }

+ 19 - 0
src/node-transformers/node-obfuscators/ObjectExpressionObfuscator.ts

@@ -1,7 +1,14 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as escodegen from 'escodegen';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
+import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IStorage } from '../../interfaces/storages/IStorage';
+
 import { NodeType } from '../../enums/NodeType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
@@ -18,7 +25,19 @@ import { Utils } from '../../Utils';
  * on:
  *     var object = { '\u0050\u0053\u0045\u0055\u0044\u004f': 1 };
  */
+@injectable()
 export class ObjectExpressionObfuscator extends AbstractNodeTransformer {
+    /**
+     * @param customNodesStorage
+     * @param options
+     */
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+    }
+
     /**
      * @param objectExpressionNode
      */

+ 23 - 6
src/node-transformers/node-obfuscators/VariableDeclarationObfuscator.ts

@@ -1,18 +1,24 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
-import { TNodeWithBlockStatement } from '../../types/TNodeWithBlockStatement';
+import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../interfaces/IOptions';
-import { IStorage } from '../../interfaces/IStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IReplacer } from '../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../interfaces/storages/IStorage';
 
+import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
 import { NodeType } from '../../enums/NodeType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { IdentifierReplacer } from './replacers/IdentifierReplacer';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
+import { Utils } from '../../Utils';
 
 /**
  * replaces:
@@ -24,20 +30,26 @@ import { NodeUtils } from '../../node/NodeUtils';
  *     _0x12d45f++;
  *
  */
+@injectable()
 export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
     /**
      * @type {IdentifierReplacer}
      */
-    private readonly identifierReplacer: IdentifierReplacer;
+    private readonly identifierReplacer: IReplacer & IdentifierReplacer;
 
     /**
      * @param customNodesStorage
+     * @param replacersFactory
      * @param options
      */
-    constructor(customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor(
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers['Factory<IReplacer>']) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IReplacer,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         super(customNodesStorage, options);
 
-        this.identifierReplacer = new IdentifierReplacer(this.customNodesStorage, this.options);
+        this.identifierReplacer = <IdentifierReplacer>replacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
     }
 
     /**
@@ -45,6 +57,11 @@ export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
      * @param parentNode
      */
     public transformNode (variableDeclarationNode: ESTree.VariableDeclaration, parentNode: ESTree.Node): void {
+        this.identifierReplacer.setPrefix(Utils.getRandomGenerator().string({
+            length: 5,
+            pool: Utils.randomGeneratorPool
+        }));
+
         const blockScopeOfVariableDeclarationNode: TNodeWithBlockStatement = NodeUtils
             .getBlockScopeOfNode(variableDeclarationNode);
 

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

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

+ 12 - 6
src/node-transformers/node-obfuscators/replacers/AbstractReplacer.ts

@@ -1,8 +1,12 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
-import { IOptions } from '../../../interfaces/IOptions';
-import { IReplacer } from '../../../interfaces/IReplacer';
-import { IStorage } from '../../../interfaces/IStorage';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IReplacer } from '../../../interfaces/node-transformers/IReplacer';
+import { IStorage } from '../../../interfaces/storages/IStorage';
 
+@injectable()
 export abstract class AbstractReplacer implements IReplacer {
     /**
      * @type IStorage<ICustomNode>
@@ -18,15 +22,17 @@ export abstract class AbstractReplacer implements IReplacer {
      * @param customNodesStorage
      * @param options
      */
-    constructor (customNodesStorage: IStorage<ICustomNode>, options: IOptions) {
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
         this.customNodesStorage = customNodesStorage;
         this.options = options;
     }
 
     /**
      * @param nodeValue
-     * @param namesMap
      * @returns {string}
      */
-    public abstract replace (nodeValue: any, namesMap?: Map <string, string>): string;
+    public abstract replace (nodeValue: any): string;
 }

+ 19 - 0
src/node-transformers/node-obfuscators/replacers/BooleanLiteralReplacer.ts

@@ -1,8 +1,27 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IStorage } from '../../../interfaces/storages/IStorage';
+
 import { JSFuck } from '../../../enums/JSFuck';
 
 import { AbstractReplacer } from './AbstractReplacer';
 
+@injectable()
 export class BooleanLiteralReplacer extends AbstractReplacer {
+    /**
+     * @param customNodesStorage
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+    }
+
     /**
      * @param nodeValue
      * @returns {string}

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

@@ -1,18 +1,42 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IStorage } from '../../../interfaces/storages/IStorage';
+
 import { AbstractReplacer } from './AbstractReplacer';
 import { Utils } from '../../../Utils';
 
+@injectable()
 export class IdentifierReplacer extends AbstractReplacer {
     /**
      * @type {Map<string, string>}
      */
     private readonly namesMap: Map<string, string> = new Map<string, string>();
 
+    /**
+     * @type {string}
+     */
+    private uniquePrefix: string;
+
+    /**
+     * @param customNodesStorage
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+    }
+
     /**
      * @param nodeValue
      * @returns {string}
      */
     public replace (nodeValue: string): string {
-        const obfuscatedIdentifierName: string|undefined = this.namesMap.get(nodeValue);
+        const obfuscatedIdentifierName: string|undefined = this.namesMap.get(`${nodeValue}-${this.uniquePrefix}`);
 
         if (!obfuscatedIdentifierName) {
             return nodeValue;
@@ -21,6 +45,13 @@ export class IdentifierReplacer extends AbstractReplacer {
         return obfuscatedIdentifierName;
     }
 
+    /**
+     * @param uniquePrefix
+     */
+    public setPrefix (uniquePrefix: string): void {
+        this.uniquePrefix = uniquePrefix
+    }
+
     /**
      * Store all identifiers names as keys in given `namesMap` with random names as value.
      * Reserved names will be ignored.
@@ -28,8 +59,12 @@ export class IdentifierReplacer extends AbstractReplacer {
      * @param nodeName
      */
     public storeNames (nodeName: string): void {
+        if (!this.uniquePrefix) {
+            throw new Error('`uniquePrefix` is `undefined`. Set it before `storeNames`');
+        }
+
         if (!this.isReservedName(nodeName)) {
-            this.namesMap.set(nodeName, Utils.getRandomVariableName());
+            this.namesMap.set(`${nodeName}-${this.uniquePrefix}`, Utils.getRandomVariableName());
         }
     }
 

+ 20 - 3
src/node-transformers/node-obfuscators/replacers/NumberLiteralReplacer.ts

@@ -1,18 +1,35 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IStorage } from '../../../interfaces/storages/IStorage';
+
 import { AbstractReplacer } from './AbstractReplacer';
 import { Utils } from '../../../Utils';
 
+@injectable()
 export class NumberLiteralReplacer extends AbstractReplacer {
+    /**
+     * @param customNodesStorage
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+    }
+
     /**
      * @param nodeValue
      * @returns {string}
      */
     public replace (nodeValue: number): string {
-        const prefix: string = '0x';
-
         if (!Utils.isInteger(nodeValue)) {
             return String(nodeValue);
         }
 
-        return `${prefix}${Utils.decToHex(nodeValue)}`;
+        return `${Utils.hexadecimalPrefix}${Utils.decToHex(nodeValue)}`;
     }
 }

+ 20 - 4
src/node-transformers/node-obfuscators/replacers/StringLiteralReplacer.ts

@@ -1,13 +1,18 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { ICustomNodeWithData } from '../../../interfaces/custom-nodes/ICustomNodeWithData';
-import { IStorage } from '../../../interfaces/IStorage';
+import { ICustomNodeWithIdentifier } from '../../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IStorage } from '../../../interfaces/storages/IStorage';
 
 import { StringArrayEncoding } from '../../../enums/StringArrayEncoding';
 
 import { AbstractReplacer } from './AbstractReplacer';
-import { NumberLiteralReplacer } from './NumberLiteralReplacer';
 import { Utils } from '../../../Utils';
-import { ICustomNodeWithIdentifier } from '../../../interfaces/custom-nodes/ICustomNodeWithIdentifier';
 
+@injectable()
 export class StringLiteralReplacer extends AbstractReplacer {
     /**
      * @type {number}
@@ -20,6 +25,17 @@ export class StringLiteralReplacer extends AbstractReplacer {
     private static readonly rc4Keys: string[] = Utils.getRandomGenerator()
         .n(() => Utils.getRandomGenerator().string({length: 4}), 50);
 
+    /**
+     * @param customNodesStorage
+     * @param options
+     */
+    constructor (
+        @inject(ServiceIdentifiers['IStorage<ICustomNode>']) customNodesStorage: IStorage<ICustomNode>,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(customNodesStorage, options);
+    }
+
     /**
      * @param nodeValue
      * @returns {string}
@@ -76,7 +92,7 @@ export class StringLiteralReplacer extends AbstractReplacer {
         }
 
         const stringArrayCallsWrapper: ICustomNodeWithIdentifier = <ICustomNodeWithIdentifier>this.customNodesStorage.get('stringArrayCallsWrapper');
-        const hexadecimalIndex: string = new NumberLiteralReplacer(this.customNodesStorage, this.options).replace(indexOfValue);
+        const hexadecimalIndex: string = `${Utils.hexadecimalPrefix}${Utils.decToHex(indexOfValue)}`;
 
         if (this.options.stringArrayEncoding === StringArrayEncoding.rc4) {
             return `${stringArrayCallsWrapper.getNodeIdentifier()}('${hexadecimalIndex}', '${Utils.stringToUnicodeEscapeSequence(rc4Key)}')`;

+ 2 - 2
src/node/Node.ts

@@ -1,7 +1,7 @@
 import * as ESTree from 'estree';
 
-import { TNodeWithBlockStatement } from '../types/TNodeWithBlockStatement';
-import { TStatement } from '../types/TStatement';
+import { TNodeWithBlockStatement } from '../types/node/TNodeWithBlockStatement';
+import { TStatement } from '../types/node/TStatement';
 
 import { NodeType } from '../enums/NodeType';
 

+ 2 - 2
src/node/NodeAppender.ts

@@ -1,7 +1,7 @@
 import * as ESTree from 'estree';
 
-import { TNodeWithBlockStatement } from '../types/TNodeWithBlockStatement';
-import { TStatement } from '../types/TStatement';
+import { TNodeWithBlockStatement } from '../types/node/TNodeWithBlockStatement';
+import { TStatement } from '../types/node/TStatement';
 
 import { IStackTraceData } from '../interfaces/stack-trace-analyzer/IStackTraceData';
 

+ 3 - 3
src/node/NodeUtils.ts

@@ -3,8 +3,8 @@ import * as esprima from 'esprima';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
-import { TNodeWithBlockStatement } from '../types/TNodeWithBlockStatement';
-import { TStatement } from '../types/TStatement';
+import { TNodeWithBlockStatement } from '../types/node/TNodeWithBlockStatement';
+import { TStatement } from '../types/node/TStatement';
 
 import { NodeType } from '../enums/NodeType';
 
@@ -116,7 +116,7 @@ export class NodeUtils {
             return depth;
         }
 
-        if (Node.isBlockStatementNode(node)) {
+        if (Node.isBlockStatementNode(node) && Utils.arrayContains(NodeUtils.nodesWithBlockScope, parentNode.type)) {
             return NodeUtils.getNodeBlockScopeDepth(parentNode, ++depth);
         }
 

+ 19 - 16
src/options/Options.ts

@@ -1,30 +1,33 @@
+import { injectable } from 'inversify';
+
 import {
-    ArrayUnique,
-    IsBoolean,
-    IsArray,
-    IsIn,
-    IsNumber,
-    IsString,
-    IsUrl,
-    Min,
-    Max,
-    ValidateIf,
-    validateSync,
-    ValidationError,
-    ValidatorOptions
+ArrayUnique,
+IsBoolean,
+IsArray,
+IsIn,
+IsNumber,
+IsString,
+IsUrl,
+Min,
+Max,
+ValidateIf,
+validateSync,
+ValidationError,
+ValidatorOptions
 } from 'class-validator';
 
-import { IInputOptions } from '../interfaces/IInputOptions';
-import { IOptions } from '../interfaces/IOptions';
+import { IInputOptions } from '../interfaces/options/IInputOptions';
+import { IOptions } from '../interfaces/options/IOptions';
 
 import { TSourceMapMode } from '../types/TSourceMapMode';
-import { TStringArrayEncoding } from '../types/TStringArrayEncoding';
+import { TStringArrayEncoding } from '../types/options/TStringArrayEncoding';
 
 import { DEFAULT_PRESET } from '../preset-options/DefaultPreset';
 
 import { OptionsNormalizer } from './OptionsNormalizer';
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 
+@injectable()
 export class Options implements IOptions {
     /**
      * @type {ValidatorOptions}

+ 3 - 3
src/options/OptionsNormalizer.ts

@@ -1,7 +1,7 @@
-import { IInputOptions } from '../interfaces/IInputOptions';
-import { IOptions } from '../interfaces/IOptions';
+import { IInputOptions } from '../interfaces/options/IInputOptions';
+import { IOptions } from '../interfaces/options/IOptions';
 
-import { TOptionsNormalizerRule } from '../types/TOptionsNormalizerRule';
+import { TOptionsNormalizerRule } from '../types/options/TOptionsNormalizerRule';
 
 import { Utils } from '../Utils';
 

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

@@ -1,4 +1,4 @@
-import { IInputOptions } from '../interfaces/IInputOptions';
+import { IInputOptions } from '../interfaces/options/IInputOptions';
 
 import { SourceMapMode } from '../enums/SourceMapMode';
 

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

@@ -1,4 +1,4 @@
-import { IInputOptions } from '../interfaces/IInputOptions';
+import { IInputOptions } from '../interfaces/options/IInputOptions';
 
 import { SourceMapMode } from '../enums/SourceMapMode';
 

+ 62 - 36
src/stack-trace-analyzer/StackTraceAnalyzer.ts

@@ -1,17 +1,17 @@
+import { injectable, inject } from 'inversify';
+import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
-import { TCalleeDataExtractor } from '../types/TCalleeDataExtractor';
+import { TCalleeDataExtractorsFactory } from '../types/container/TCalleeDataExtractorsFactory';
 
 import { ICalleeData } from '../interfaces/stack-trace-analyzer/ICalleeData';
+import { ICalleeDataExtractor } from '../interfaces/stack-trace-analyzer/ICalleeDataExtractor';
 import { IStackTraceAnalyzer } from '../interfaces/stack-trace-analyzer/IStackTraceAnalyzer';
 import { IStackTraceData } from '../interfaces/stack-trace-analyzer/IStackTraceData';
 
-import { NodeType } from '../enums/NodeType';
-
-import { FunctionDeclarationCalleeDataExtractor } from './callee-data-extractors/FunctionDeclarationCalleeDataExtractor';
-import { FunctionExpressionCalleeDataExtractor } from './callee-data-extractors/FunctionExpressionCalleeDataExtractor';
-import { ObjectExpressionCalleeDataExtractor } from './callee-data-extractors/ObjectExpressionCalleeDataExtractor';
+import { CalleeDataExtractors } from '../enums/container/CalleeDataExtractors';
 
 import { Node } from '../node/Node';
 import { NodeUtils } from '../node/NodeUtils';
@@ -47,7 +47,17 @@ import { NodeUtils } from '../node/NodeUtils';
  *      }
  * ]
  */
+@injectable()
 export class StackTraceAnalyzer implements IStackTraceAnalyzer {
+    /**
+     * @type {CalleeDataExtractors[]}
+     */
+    private static readonly calleeDataExtractorsList: CalleeDataExtractors[] = [
+        CalleeDataExtractors.FunctionDeclarationCalleeDataExtractor,
+        CalleeDataExtractors.FunctionExpressionCalleeDataExtractor,
+        CalleeDataExtractors.ObjectExpressionCalleeDataExtractor
+    ];
+
     /**
      * @type {number}
      */
@@ -59,13 +69,15 @@ export class StackTraceAnalyzer implements IStackTraceAnalyzer {
     private static readonly limitThreshold: number = 0.002;
 
     /**
-     * @type {Map<string, TCalleeDataExtractor>}
+     * @type {(calleeDataExtractorName: CalleeDataExtractors) => ICalleeDataExtractor}
      */
-    private readonly calleeDataExtractors: Map <string, TCalleeDataExtractor> = new Map <string, TCalleeDataExtractor> ([
-        [NodeType.FunctionDeclaration, FunctionDeclarationCalleeDataExtractor],
-        [NodeType.FunctionExpression, FunctionExpressionCalleeDataExtractor],
-        [NodeType.ObjectExpression, ObjectExpressionCalleeDataExtractor]
-    ]);
+    private calleeDataExtractorsFactory: (calleeDataExtractorName: CalleeDataExtractors) => ICalleeDataExtractor;
+
+    constructor (
+        @inject(ServiceIdentifiers['Factory<ICalleeDataExtractor>']) calleeDataExtractorsFactory: TCalleeDataExtractorsFactory
+    ) {
+        this.calleeDataExtractorsFactory = calleeDataExtractorsFactory;
+    }
 
     /**
      * @param blockScopeBodyLength
@@ -105,42 +117,56 @@ export class StackTraceAnalyzer implements IStackTraceAnalyzer {
     private analyzeRecursive (blockScopeBody: ESTree.Node[]): IStackTraceData[] {
         const limitIndex: number = StackTraceAnalyzer.getLimitIndex(blockScopeBody.length);
         const stackTraceData: IStackTraceData[] = [];
+        const blockScopeBodyLength: number = blockScopeBody.length;
 
-        for (
-            let index: number = 0, blockScopeBodyLength: number = blockScopeBody.length;
-            index < blockScopeBodyLength;
-            index++
-        ) {
-            const rootNode: ESTree.Node = blockScopeBody[index];
-
+        for (let index: number = 0; index < blockScopeBodyLength; index++) {
             if (index > limitIndex) {
                 break;
             }
 
-            estraverse.traverse(rootNode, {
-                enter: (node: ESTree.Node): any => {
-                    if (!Node.isCallExpressionNode(node) || rootNode.parentNode !== NodeUtils.getBlockScopeOfNode(node)) {
+            const blockScopeBodyNode: ESTree.Node = blockScopeBody[index];
+
+            estraverse.traverse(blockScopeBodyNode, {
+                enter: (node: ESTree.Node): void => {
+                    if (
+                        !Node.isCallExpressionNode(node) ||
+                        blockScopeBodyNode.parentNode !== NodeUtils.getBlockScopeOfNode(node)
+                    ) {
                         return;
                     }
 
-                    this.calleeDataExtractors.forEach((calleeDataExtractor: TCalleeDataExtractor) => {
-                        const calleeData: ICalleeData|null = new calleeDataExtractor(
-                            blockScopeBody,
-                            node.callee
-                        ).extract();
-
-                        if (!calleeData) {
-                            return;
-                        }
-
-                        stackTraceData.push(Object.assign({}, calleeData, {
-                            stackTrace: this.analyzeRecursive(calleeData.callee.body)
-                        }));
-                    });
+                    this.analyzeCallExpressionNode(stackTraceData, blockScopeBody, node);
                 }
             });
         }
 
         return stackTraceData;
     }
+
+    /**
+     * @param stackTraceData
+     * @param blockScopeBody
+     * @param callExpressionNode
+     * @returns {IStackTraceData[]}
+     */
+    private analyzeCallExpressionNode (
+        stackTraceData: IStackTraceData[],
+        blockScopeBody: ESTree.Node[],
+        callExpressionNode: ESTree.CallExpression
+    ): void {
+        StackTraceAnalyzer.calleeDataExtractorsList.forEach((calleeDataExtractorName: CalleeDataExtractors) => {
+            const calleeData: ICalleeData | null = this.calleeDataExtractorsFactory(calleeDataExtractorName)
+                .extract(blockScopeBody, callExpressionNode.callee);
+
+            if (!calleeData) {
+                return;
+            }
+
+            stackTraceData.push(
+                Object.assign({}, calleeData, {
+                    stackTrace: this.analyzeRecursive(calleeData.callee.body)
+                })
+            );
+        });
+    }
 }

+ 16 - 0
src/stack-trace-analyzer/callee-data-extractors/AbstractCalleeDataExtractor.ts

@@ -0,0 +1,16 @@
+import { injectable } from 'inversify';
+
+import * as ESTree from 'estree';
+
+import { ICalleeData } from '../../interfaces/stack-trace-analyzer/ICalleeData';
+import { ICalleeDataExtractor } from '../../interfaces/stack-trace-analyzer/ICalleeDataExtractor';
+
+@injectable()
+export abstract class AbstractCalleeDataExtractor implements ICalleeDataExtractor {
+    /**
+     * @param blockScopeBody
+     * @param callee
+     * @returns {ICalleeData|null}
+     */
+    public abstract extract (blockScopeBody: ESTree.Node[], callee: ESTree.Node): ICalleeData|null;
+}

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

@@ -1,42 +1,28 @@
+import { injectable } from 'inversify';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { ICalleeData } from '../../interfaces/stack-trace-analyzer/ICalleeData';
-import { ICalleeDataExtractor } from '../../interfaces/stack-trace-analyzer/ICalleeDataExtractor';
 
+import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
 
-export class FunctionDeclarationCalleeDataExtractor implements ICalleeDataExtractor {
-    /**
-     * @type {ESTree.Node[]}
-     */
-    private blockScopeBody: ESTree.Node[];
-
-    /**
-     * @type {ESTree.Identifier}
-     */
-    private callee: ESTree.Identifier;
-
+@injectable()
+export class FunctionDeclarationCalleeDataExtractor extends AbstractCalleeDataExtractor {
     /**
      * @param blockScopeBody
      * @param callee
-     */
-    constructor (blockScopeBody: ESTree.Node[], callee: ESTree.Identifier) {
-        this.blockScopeBody = blockScopeBody;
-        this.callee = callee;
-    }
-
-    /**
      * @returns {ICalleeData|null}
      */
-    public extract (): ICalleeData|null {
+    public extract (blockScopeBody: ESTree.Node[], callee: ESTree.Identifier): ICalleeData|null {
         let calleeBlockStatement: ESTree.BlockStatement|null = null;
 
-        if (Node.isIdentifierNode(this.callee)) {
+        if (Node.isIdentifierNode(callee)) {
             calleeBlockStatement = this.getCalleeBlockStatement(
-                NodeUtils.getBlockScopeOfNode(this.blockScopeBody[0]),
-                this.callee.name
+                NodeUtils.getBlockScopeOfNode(blockScopeBody[0]),
+                callee.name
             );
         }
 
@@ -46,7 +32,7 @@ export class FunctionDeclarationCalleeDataExtractor implements ICalleeDataExtrac
 
         return {
             callee: calleeBlockStatement,
-            name: this.callee.name
+            name: callee.name
         };
     }
 

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

@@ -1,47 +1,33 @@
+import { injectable } from 'inversify';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { ICalleeData } from '../../interfaces/stack-trace-analyzer/ICalleeData';
-import { ICalleeDataExtractor } from '../../interfaces/stack-trace-analyzer/ICalleeDataExtractor';
 
+import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
 
-export class FunctionExpressionCalleeDataExtractor implements ICalleeDataExtractor {
-    /**
-     * @type {ESTree.Node[]}
-     */
-    private blockScopeBody: ESTree.Node[];
-
-    /**
-     * @type {ESTree.Identifier}
-     */
-    private callee: ESTree.Identifier;
-
+@injectable()
+export class FunctionExpressionCalleeDataExtractor extends AbstractCalleeDataExtractor {
     /**
      * @param blockScopeBody
      * @param callee
-     */
-    constructor (blockScopeBody: ESTree.Node[], callee: ESTree.Identifier) {
-        this.blockScopeBody = blockScopeBody;
-        this.callee = callee;
-    }
-
-    /**
      * @returns {ICalleeData|null}
      */
-    public extract (): ICalleeData|null {
+    public extract (blockScopeBody: ESTree.Node[], callee: ESTree.Identifier): ICalleeData|null {
         let calleeBlockStatement: ESTree.BlockStatement|null = null;
 
-        if (Node.isIdentifierNode(this.callee)) {
+        if (Node.isIdentifierNode(callee)) {
             calleeBlockStatement = this.getCalleeBlockStatement(
-                NodeUtils.getBlockScopeOfNode(this.blockScopeBody[0]),
-                this.callee.name
+                NodeUtils.getBlockScopeOfNode(blockScopeBody[0]),
+                callee.name
             );
         }
 
-        if (Node.isFunctionExpressionNode(this.callee)) {
-            calleeBlockStatement = this.callee.body;
+        if (Node.isFunctionExpressionNode(callee)) {
+            calleeBlockStatement = callee.body;
         }
 
         if (!calleeBlockStatement) {
@@ -50,7 +36,7 @@ export class FunctionExpressionCalleeDataExtractor implements ICalleeDataExtract
 
         return {
             callee: calleeBlockStatement,
-            name: this.callee.name || null
+            name: callee.name || null
         };
     }
 

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

@@ -1,45 +1,31 @@
+import { injectable } from 'inversify';
+
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
-import { TObjectMembersCallsChain } from '../../types/TObjectMembersCallsChain';
+import { TObjectMembersCallsChain } from '../../types/stack-trace-analyzer/TObjectMembersCallsChain';
 
 import { ICalleeData } from '../../interfaces/stack-trace-analyzer/ICalleeData';
-import { ICalleeDataExtractor } from '../../interfaces/stack-trace-analyzer/ICalleeDataExtractor';
 
 import { Node } from '../../node/Node';
 import { NodeUtils } from '../../node/NodeUtils';
+import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
 
-export class ObjectExpressionCalleeDataExtractor implements ICalleeDataExtractor {
-    /**
-     * @type {ESTree.Node[]}
-     */
-    private blockScopeBody: ESTree.Node[];
-
-    /**
-     * @type {ESTree.MemberExpression}
-     */
-    private callee: ESTree.MemberExpression;
-
+@injectable()
+export class ObjectExpressionCalleeDataExtractor extends AbstractCalleeDataExtractor {
     /**
      * @param blockScopeBody
      * @param callee
-     */
-    constructor (blockScopeBody: ESTree.Node[], callee: ESTree.MemberExpression) {
-        this.blockScopeBody = blockScopeBody;
-        this.callee = callee;
-    }
-
-    /**
      * @returns {ICalleeData|null}
      */
-    public extract (): ICalleeData|null {
+    public extract (blockScopeBody: ESTree.Node[], callee: ESTree.MemberExpression): ICalleeData|null {
         let calleeBlockStatement: ESTree.BlockStatement|null = null,
             functionExpressionName: string|number|null = null;
 
-        if (Node.isMemberExpressionNode(this.callee)) {
+        if (Node.isMemberExpressionNode(callee)) {
             const objectMembersCallsChain: TObjectMembersCallsChain = this.createObjectMembersCallsChain(
                 [],
-                this.callee
+                callee
             );
 
             if (!objectMembersCallsChain.length) {
@@ -48,7 +34,7 @@ export class ObjectExpressionCalleeDataExtractor implements ICalleeDataExtractor
 
             functionExpressionName = objectMembersCallsChain[objectMembersCallsChain.length - 1];
             calleeBlockStatement = this.getCalleeBlockStatement(
-                NodeUtils.getBlockScopeOfNode(this.blockScopeBody[0]),
+                NodeUtils.getBlockScopeOfNode(blockScopeBody[0]),
                 objectMembersCallsChain
             );
         }

+ 8 - 3
src/storages/ArrayStorage.ts

@@ -1,10 +1,13 @@
-import { IStorage } from '../interfaces/IStorage';
+import { IStorage } from '../interfaces/storages/IStorage';
+
+import { initializable } from '../decorators/Initializable';
 
 export abstract class ArrayStorage <T> implements IStorage <T> {
     /**
      * @type {T[]}
      */
-    protected storage: T[] = [];
+    @initializable()
+    protected storage: T[];
 
     /**
      * @param key
@@ -45,7 +48,9 @@ export abstract class ArrayStorage <T> implements IStorage <T> {
     /**
      * @param args
      */
-    public initialize (...args: any[]): void {}
+    public initialize (...args: any[]): void {
+        this.storage = [];
+    }
 
     /**
      * @param key

+ 11 - 3
src/storages/MapStorage.ts

@@ -1,12 +1,18 @@
-import { IStorage } from '../interfaces/IStorage';
+import { injectable } from 'inversify';
+
+import { IStorage } from '../interfaces/storages/IStorage';
+
+import { initializable } from '../decorators/Initializable';
 
 import { Utils } from '../Utils';
 
+@injectable()
 export abstract class MapStorage <T> implements IStorage <T> {
     /**
      * @type {Map <string | number, T>}
      */
-    protected storage: Map <string | number, T> = new Map <string | number, T> ();
+    @initializable()
+    protected storage: Map <string | number, T>;
 
     /**
      * @param key
@@ -47,7 +53,9 @@ export abstract class MapStorage <T> implements IStorage <T> {
     /**
      * @param args
      */
-    public initialize (...args: any[]): void {}
+    public initialize (...args: any[]): void {
+        this.storage = new Map <string | number, T> ();
+    }
 
     /**
      * @param key

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