瀏覽代碼

Merge pull request #487 from javascript-obfuscator/declaration-kind-analyzer

Auto detection of prevailing kind of variables
Timofey Kachalov 5 年之前
父節點
當前提交
dcc489a148
共有 100 個文件被更改,包括 1219 次插入280 次删除
  1. 4 0
      CHANGELOG.md
  2. 8 0
      README.md
  3. 0 0
      dist/index.browser.js
  4. 0 0
      dist/index.cli.js
  5. 0 0
      dist/index.js
  6. 1 1
      package.json
  7. 61 0
      src/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.ts
  8. 2 0
      src/container/ServiceIdentifiers.ts
  9. 7 0
      src/container/modules/analyzers/AnalyzersModule.ts
  10. 7 2
      src/container/modules/custom-nodes/CustomNodesModule.ts
  11. 7 0
      src/container/modules/utils/UtilsModule.ts
  12. 15 4
      src/custom-nodes/AbstractCustomNode.ts
  13. 5 4
      src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts
  14. 7 2
      src/custom-nodes/console-output-nodes/group/ConsoleOutputCustomNodeGroup.ts
  15. 4 1
      src/custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode.ts
  16. 37 20
      src/custom-nodes/control-flow-flattening-nodes/BlockStatementControlFlowFlatteningNode.ts
  17. 4 1
      src/custom-nodes/control-flow-flattening-nodes/CallExpressionFunctionNode.ts
  18. 4 1
      src/custom-nodes/control-flow-flattening-nodes/LogicalExpressionFunctionNode.ts
  19. 4 1
      src/custom-nodes/control-flow-flattening-nodes/StringLiteralNode.ts
  20. 4 1
      src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/CallExpressionControlFlowStorageCallNode.ts
  21. 25 8
      src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts
  22. 4 1
      src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ExpressionWithOperatorControlFlowStorageCallNode.ts
  23. 4 1
      src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode.ts
  24. 4 1
      src/custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode.ts
  25. 5 4
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts
  26. 5 4
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts
  27. 5 4
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts
  28. 13 4
      src/custom-nodes/debug-protection-nodes/group/DebugProtectionCustomNodeGroup.ts
  29. 8 7
      src/custom-nodes/domain-lock-nodes/DomainLockNode.ts
  30. 7 2
      src/custom-nodes/domain-lock-nodes/group/DomainLockCustomNodeGroup.ts
  31. 6 5
      src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts
  32. 29 10
      src/custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode.ts
  33. 8 7
      src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts
  34. 7 2
      src/custom-nodes/self-defending-nodes/group/SelfDefendingCustomNodeGroup.ts
  35. 12 11
      src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts
  36. 5 4
      src/custom-nodes/string-array-nodes/StringArrayNode.ts
  37. 9 8
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  38. 10 3
      src/custom-nodes/string-array-nodes/group/StringArrayCustomNodeGroup.ts
  39. 2 2
      src/interfaces/analyzers/IAnalyzer.d.ts
  40. 1 1
      src/interfaces/analyzers/calls-graph-analyzer/ICallsGraphAnalyzer.d.ts
  41. 15 0
      src/interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer.d.ts
  42. 3 1
      src/interfaces/custom-nodes/ICustomNode.d.ts
  43. 6 0
      src/interfaces/utils/IArrayUtils.d.ts
  44. 13 0
      src/interfaces/utils/ITemplateFormatter.d.ts
  45. 4 3
      src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts
  46. 4 3
      src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts
  47. 7 6
      src/node-transformers/control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts
  48. 7 6
      src/node-transformers/control-flow-transformers/control-flow-replacers/CallExpressionControlFlowReplacer.ts
  49. 4 3
      src/node-transformers/control-flow-transformers/control-flow-replacers/ExpressionWithOperatorControlFlowReplacer.ts
  50. 7 6
      src/node-transformers/control-flow-transformers/control-flow-replacers/LogicalExpressionControlFlowReplacer.ts
  51. 7 6
      src/node-transformers/control-flow-transformers/control-flow-replacers/StringLiteralControlFlowReplacer.ts
  52. 6 3
      src/node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor.ts
  53. 4 3
      src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts
  54. 11 0
      src/node-transformers/preparing-transformers/CustomNodesTransformer.ts
  55. 4 1
      src/node/NodeUtils.ts
  56. 7 5
      src/templates/AtobTemplate.ts
  57. 1 1
      src/templates/GlobalVariableNoEvalTemplate.ts
  58. 2 2
      src/templates/GlobalVariableTemplate1.ts
  59. 3 3
      src/templates/GlobalVariableTemplate2.ts
  60. 7 5
      src/templates/Rc4Template.ts
  61. 4 4
      src/templates/SingleNodeCallControllerTemplate.ts
  62. 3 3
      src/templates/console-output-nodes/console-output-disable-expression-node/ConsoleOutputDisableExpressionTemplate.ts
  63. 14 11
      src/templates/debug-protection-nodes/debug-protection-function-call-node/DebugProtectionFunctionCallTemplate.ts
  64. 1 1
      src/templates/debug-protection-nodes/debug-protection-function-node/DebuggerTemplateNoEval.ts
  65. 23 23
      src/templates/domain-lock-nodes/domain-lock-node/DomainLockNodeTemplate.ts
  66. 10 10
      src/templates/self-defending-nodes/self-defending-unicode-node/SelfDefendingTemplate.ts
  67. 4 4
      src/templates/string-array-nodes/string-array-calls-wrapper/SelfDefendingTemplate.ts
  68. 4 4
      src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayBase64DecodeNodeTemplate.ts
  69. 2 2
      src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayCallsWrapperTemplate.ts
  70. 1 1
      src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayRC4DecodeNodeTemplate.ts
  71. 1 1
      src/templates/string-array-nodes/string-array-node/StringArrayTemplate.ts
  72. 13 14
      src/templates/string-array-nodes/string-array-rotate-function-node/SelfDefendingTemplate.ts
  73. 1 1
      src/templates/string-array-nodes/string-array-rotate-function-node/StringArrayRotateFunctionTemplate.ts
  74. 3 0
      src/types/TInitialData.d.ts
  75. 3 1
      src/types/container/custom-nodes/TControlFlowCustomNodeFactory.d.ts
  76. 3 1
      src/types/container/custom-nodes/TCustomNodeFactory.d.ts
  77. 3 1
      src/types/container/custom-nodes/TDeadNodeInjectionCustomNodeFactory.d.ts
  78. 3 1
      src/types/container/custom-nodes/TObjectExpressionKeysTransformerCustomNodeFactory.d.ts
  79. 31 0
      src/utils/ArrayUtils.ts
  80. 53 0
      src/utils/TemplateFormatter.ts
  81. 4 7
      test/dev/dev.ts
  82. 134 0
      test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts
  83. 32 0
      test/functional-tests/javascript-obfuscator/fixtures/prevailing-kind-of-variables-const.js
  84. 32 0
      test/functional-tests/javascript-obfuscator/fixtures/prevailing-kind-of-variables-let.js
  85. 32 0
      test/functional-tests/javascript-obfuscator/fixtures/prevailing-kind-of-variables-var.js
  86. 119 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/BlockStatementControlFlowTransformer.spec.ts
  87. 9 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/prevailing-kind-of-variables-const.js
  88. 9 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/prevailing-kind-of-variables-let.js
  89. 9 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/prevailing-kind-of-variables-var.js
  90. 71 0
      test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/FunctionControlFlowTransformer.spec.ts
  91. 3 0
      test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/fixtures/prevailing-kind-of-variables-const.js
  92. 3 0
      test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/fixtures/prevailing-kind-of-variables-let.js
  93. 3 0
      test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/fixtures/prevailing-kind-of-variables-var.js
  94. 86 3
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts
  95. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/prevailing-kind-of-variables-const.js
  96. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/prevailing-kind-of-variables-let.js
  97. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/prevailing-kind-of-variables-var.js
  98. 1 1
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-3.js
  99. 1 1
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-4.js
  100. 1 1
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-5.js

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 
+v0.22.0
+---
+* **Breaking:** auto-detection of kind of variables of inserted nodes, based on most prevailing kind of variables of source code
+
 v0.21.1
 ---
 * Fixed conditional comments in some rare cases

+ 8 - 0
README.md

@@ -263,6 +263,10 @@ For example:
 * Obfuscation of the variable's name at its declaration is called direct transformation;
 * Obfuscation of the variable's name beyond its declaration is called child transformation.
 
+## Kind of variables
+
+Kind of variables of inserted nodes will auto-detected, based on most prevailing kind of variables of source code.
+
 ## Antiviruses false positive virus alerts
 
 Some input source code that will obfuscated with some obfuscation options can trigger false positive alerts in a few antiviruses. If you will get this false positive triggers, try to play with obfuscation options.
@@ -952,6 +956,10 @@ Likely this is `selfDefending` mechanism. Something is changing source code afte
 
 No. JSX support isn't planned.
 
+### How to change kind of variables of inserted nodes (`var`, `let` or `const`)?
+
+See: [`Kind of variables`](#kind-of-variables)
+
 ## Backers
 
 Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/javascript-obfuscator#backer)]

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


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


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


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.21.1",
+  "version": "0.22.0",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",

+ 61 - 0
src/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.ts

@@ -0,0 +1,61 @@
+import { inject, injectable, } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
+import { IPrevailingKindOfVariablesAnalyzer } from '../../interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
+
+import { NodeGuards } from '../../node/NodeGuards';
+
+@injectable()
+export class PrevailingKindOfVariablesAnalyzer implements IPrevailingKindOfVariablesAnalyzer {
+    /**
+     * @type {ESTree.VariableDeclaration['kind']}
+     */
+    private static readonly defaultKindOfVariables: ESTree.VariableDeclaration['kind'] = 'var';
+
+    /**
+     * @type {IArrayUtils}
+     */
+    private readonly arrayUtils: IArrayUtils;
+
+    /**
+     * @type {ESTree.VariableDeclaration['kind']}
+     */
+    private prevailingKindOfVariables: ESTree.VariableDeclaration['kind'] = PrevailingKindOfVariablesAnalyzer.defaultKindOfVariables;
+
+    constructor (
+        @inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils
+    ) {
+        this.arrayUtils = arrayUtils;
+    }
+
+    /**
+     * @param {Program} astTree
+     */
+    public analyze (astTree: ESTree.Program): void {
+        const variableKinds: ESTree.VariableDeclaration['kind'][] = [];
+
+        estraverse.traverse(astTree, {
+            enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+                if (!NodeGuards.isVariableDeclarationNode(node)) {
+                    return;
+                }
+
+                variableKinds.push(node.kind);
+            }
+        });
+
+        this.prevailingKindOfVariables = this.arrayUtils.findMostOccurringElement(variableKinds)
+            || PrevailingKindOfVariablesAnalyzer.defaultKindOfVariables;
+    }
+
+    /**
+     * @returns {VariableDeclaration["kind"]}
+     */
+    public getPrevailingKind (): ESTree.VariableDeclaration['kind'] {
+        return this.prevailingKindOfVariables;
+    }
+}

+ 2 - 0
src/container/ServiceIdentifiers.ts

@@ -35,10 +35,12 @@ export enum ServiceIdentifiers {
     IOptions = 'IOptions',
     IOptionsNormalizer = 'IOptionsNormalizer',
     IObfuscatingReplacer = 'IObfuscatingReplacer',
+    IPrevailingKindOfVariablesAnalyzer = 'IPrevailingKindOfVariablesAnalyzer',
     IPropertiesExtractor = 'IPropertiesExtractor',
     IRandomGenerator = 'IRandomGenerator',
     ISourceCode = 'ISourceCode',
     ISourceMapCorrector = 'ISourceMapCorrector',
+    ITemplateFormatter = 'ITemplateFormatter',
     ITransformersRunner = 'ITransformersRunner',
     Newable__ICustomNode = 'Newable<ICustomNode>',
     Newable__TControlFlowStorage = 'Newable<TControlFlowStorage>',

+ 7 - 0
src/container/modules/analyzers/AnalyzersModule.ts

@@ -4,12 +4,14 @@ import { ServiceIdentifiers } from '../../ServiceIdentifiers';
 
 import { ICalleeDataExtractor } from '../../../interfaces/analyzers/calls-graph-analyzer/ICalleeDataExtractor';
 import { ICallsGraphAnalyzer } from '../../../interfaces/analyzers/calls-graph-analyzer/ICallsGraphAnalyzer';
+import { IPrevailingKindOfVariablesAnalyzer } from '../../../interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
 
 import { CalleeDataExtractor } from '../../../enums/analyzers/calls-graph-analyzer/CalleeDataExtractor';
 import { CallsGraphAnalyzer } from '../../../analyzers/calls-graph-analyzer/CallsGraphAnalyzer';
 import { FunctionDeclarationCalleeDataExtractor } from '../../../analyzers/calls-graph-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor';
 import { FunctionExpressionCalleeDataExtractor } from '../../../analyzers/calls-graph-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor';
 import { ObjectExpressionCalleeDataExtractor } from '../../../analyzers/calls-graph-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor';
+import { PrevailingKindOfVariablesAnalyzer } from '../../../analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer';
 
 export const analyzersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
     // calls graph analyzer
@@ -17,6 +19,11 @@ export const analyzersModule: interfaces.ContainerModule = new ContainerModule((
         .to(CallsGraphAnalyzer)
         .inSingletonScope();
 
+    // prevailing kind of variables analyzer
+    bind<IPrevailingKindOfVariablesAnalyzer>(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer)
+        .to(PrevailingKindOfVariablesAnalyzer)
+        .inSingletonScope();
+
     // callee data extractors
     bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
         .to(FunctionDeclarationCalleeDataExtractor)

+ 7 - 2
src/container/modules/custom-nodes/CustomNodesModule.ts

@@ -160,8 +160,10 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
             .getConstructorFactory<ControlFlowCustomNode, ICustomNode>(
                 ServiceIdentifiers.Newable__ICustomNode,
                 ServiceIdentifiers.Factory__IIdentifierNamesGenerator,
+                ServiceIdentifiers.ITemplateFormatter,
                 ServiceIdentifiers.IRandomGenerator,
-                ServiceIdentifiers.IOptions
+                ServiceIdentifiers.IOptions,
+                ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer
             ));
 
     // dead code injection customNode constructor factory
@@ -170,6 +172,7 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
             .getConstructorFactory<DeadCodeInjectionCustomNode, ICustomNode>(
                 ServiceIdentifiers.Newable__ICustomNode,
                 ServiceIdentifiers.Factory__IIdentifierNamesGenerator,
+                ServiceIdentifiers.ITemplateFormatter,
                 ServiceIdentifiers.IRandomGenerator,
                 ServiceIdentifiers.IOptions
             ));
@@ -180,8 +183,10 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
             .getConstructorFactory<ObjectExpressionKeysTransformerCustomNode, ICustomNode>(
                 ServiceIdentifiers.Newable__ICustomNode,
                 ServiceIdentifiers.Factory__IIdentifierNamesGenerator,
+                ServiceIdentifiers.ITemplateFormatter,
                 ServiceIdentifiers.IRandomGenerator,
-                ServiceIdentifiers.IOptions
+                ServiceIdentifiers.IOptions,
+                ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer
             ));
 
     // customNodeGroup factory

+ 7 - 0
src/container/modules/utils/UtilsModule.ts

@@ -7,6 +7,7 @@ import { IEscapeSequenceEncoder } from '../../../interfaces/utils/IEscapeSequenc
 import { ILevelledTopologicalSorter } from '../../../interfaces/utils/ILevelledTopologicalSorter';
 import { INodeTransformerNamesGroupsBuilder } from '../../../interfaces/utils/INodeTransformerNamesGroupsBuilder';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../../interfaces/utils/ITemplateFormatter';
 
 import { ArrayUtils } from '../../../utils/ArrayUtils';
 import { CryptUtils } from '../../../utils/CryptUtils';
@@ -14,6 +15,7 @@ import { EscapeSequenceEncoder } from '../../../utils/EscapeSequenceEncoder';
 import { LevelledTopologicalSorter } from '../../../utils/LevelledTopologicalSorter';
 import { NodeTransformerNamesGroupsBuilder } from '../../../utils/NodeTransformerNamesGroupsBuilder';
 import { RandomGenerator } from '../../../utils/RandomGenerator';
+import { TemplateFormatter } from '../../../utils/TemplateFormatter';
 
 export const utilsModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
     // array utils
@@ -44,4 +46,9 @@ export const utilsModule: interfaces.ContainerModule = new ContainerModule((bind
     bind<INodeTransformerNamesGroupsBuilder>(ServiceIdentifiers.INodeTransformerNamesGroupsBuilder)
         .to(NodeTransformerNamesGroupsBuilder)
         .inSingletonScope();
+
+    // template formatter
+    bind<ITemplateFormatter>(ServiceIdentifiers.ITemplateFormatter)
+        .to(TemplateFormatter)
+        .inSingletonScope();
 });

+ 15 - 4
src/custom-nodes/AbstractCustomNode.ts

@@ -8,12 +8,15 @@ import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
 import { IIdentifierNamesGenerator } from '../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
 import { IOptions } from '../interfaces/options/IOptions';
 import { IRandomGenerator } from '../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../interfaces/utils/ITemplateFormatter';
 
 import { GlobalVariableTemplate1 } from '../templates/GlobalVariableTemplate1';
 import { GlobalVariableTemplate2 } from '../templates/GlobalVariableTemplate2';
 
 @injectable()
-export abstract class AbstractCustomNode implements ICustomNode {
+export abstract class AbstractCustomNode <
+    TInitialData extends any[] = any[]
+> implements ICustomNode <TInitialData> {
     /**
      * @type {string[]}
      */
@@ -42,26 +45,34 @@ export abstract class AbstractCustomNode implements ICustomNode {
      */
     protected readonly randomGenerator: IRandomGenerator;
 
+    /**
+     * @type {ITemplateFormatter}
+     */
+    protected readonly templateFormatter: ITemplateFormatter;
+
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
-    constructor (
+    protected constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
+        this.templateFormatter = templateFormatter;
         this.randomGenerator = randomGenerator;
         this.options = options;
     }
 
     /**
-     * @param {unknown[]} args
+     * @param {TInitialData} args
      */
-    public abstract initialize (...args: unknown[]): void;
+    public abstract initialize (...args: TInitialData): void;
 
     /**
      * @returns {TStatement[]}

+ 5 - 4
src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts

@@ -1,13 +1,12 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 
@@ -29,16 +28,18 @@ export class ConsoleOutputDisableExpressionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**
@@ -63,7 +64,7 @@ export class ConsoleOutputDisableExpressionNode extends AbstractCustomNode {
             ? this.getGlobalVariableTemplate()
             : GlobalVariableNoEvalTemplate();
 
-        return format(ConsoleOutputDisableExpressionTemplate(), {
+        return this.templateFormatter.format(ConsoleOutputDisableExpressionTemplate(), {
             consoleLogDisableFunctionName: this.identifierNamesGenerator.generate(),
             globalVariableTemplate,
             singleNodeCallControllerFunctionName: this.callsControllerFunctionName

+ 7 - 2
src/custom-nodes/console-output-nodes/group/ConsoleOutputCustomNodeGroup.ts

@@ -3,6 +3,7 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 import { TCustomNodeFactory } from '../../../types/container/custom-nodes/TCustomNodeFactory';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TInitialData } from '../../../types/TInitialData';
 import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
@@ -16,7 +17,9 @@ import { CustomNode } from '../../../enums/custom-nodes/CustomNode';
 import { ObfuscationEvent } from '../../../enums/event-emitters/ObfuscationEvent';
 
 import { AbstractCustomNodeGroup } from '../../AbstractCustomNodeGroup';
+import { ConsoleOutputDisableExpressionNode } from '../ConsoleOutputDisableExpressionNode';
 import { NodeAppender } from '../../../node/NodeAppender';
+import { NodeCallsControllerFunctionNode } from '../../node-calls-controller-nodes/NodeCallsControllerFunctionNode';
 
 @injectable()
 export class ConsoleOutputCustomNodeGroup extends AbstractCustomNodeGroup {
@@ -90,8 +93,10 @@ export class ConsoleOutputCustomNodeGroup extends AbstractCustomNodeGroup {
 
         const callsControllerFunctionName: string = this.identifierNamesGenerator.generate();
 
-        const consoleOutputDisableExpressionNode: ICustomNode = this.customNodeFactory(CustomNode.ConsoleOutputDisableExpressionNode);
-        const nodeCallsControllerFunctionNode: ICustomNode = this.customNodeFactory(CustomNode.NodeCallsControllerFunctionNode);
+        const consoleOutputDisableExpressionNode: ICustomNode<TInitialData<ConsoleOutputDisableExpressionNode>> =
+            this.customNodeFactory(CustomNode.ConsoleOutputDisableExpressionNode);
+        const nodeCallsControllerFunctionNode: ICustomNode<TInitialData<NodeCallsControllerFunctionNode>> =
+            this.customNodeFactory(CustomNode.NodeCallsControllerFunctionNode);
 
         consoleOutputDisableExpressionNode.initialize(callsControllerFunctionName);
         nodeCallsControllerFunctionNode.initialize(this.appendEvent, callsControllerFunctionName);

+ 4 - 1
src/custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode.ts

@@ -8,6 +8,7 @@ import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -25,16 +26,18 @@ export class BinaryExpressionFunctionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**

+ 37 - 20
src/custom-nodes/control-flow-flattening-nodes/BlockStatementControlFlowFlatteningNode.ts

@@ -7,7 +7,9 @@ import { TIdentifierNamesGeneratorFactory } from '../../types/container/generato
 import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
+import { IPrevailingKindOfVariablesAnalyzer } from '../../interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -30,6 +32,11 @@ export class BlockStatementControlFlowFlatteningNode extends AbstractCustomNode
     @initializable()
     private originalKeysIndexesInShuffledArray!: number[];
 
+    /**
+     * @type {ESTree.VariableDeclaration['kind']}
+     */
+    private readonly prevailingKindOfVariables: ESTree.VariableDeclaration['kind'];
+
     /**
      * @type {number[]}
      */
@@ -38,16 +45,23 @@ export class BlockStatementControlFlowFlatteningNode extends AbstractCustomNode
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
+     * @param {IPrevailingKindOfVariablesAnalyzer} prevailingKindOfVariablesAnalyzer
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer)
+            prevailingKindOfVariablesAnalyzer: IPrevailingKindOfVariablesAnalyzer,
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
+
+        this.prevailingKindOfVariables = prevailingKindOfVariablesAnalyzer.getPrevailingKind();
     }
 
     /**
@@ -72,26 +86,29 @@ export class BlockStatementControlFlowFlatteningNode extends AbstractCustomNode
         const controllerIdentifierName: string = this.randomGenerator.getRandomString(6);
         const indexIdentifierName: string = this.randomGenerator.getRandomString(6);
         const structure: ESTree.BlockStatement = NodeFactory.blockStatementNode([
-            NodeFactory.variableDeclarationNode([
-                NodeFactory.variableDeclaratorNode(
-                    NodeFactory.identifierNode(controllerIdentifierName),
-                    NodeFactory.callExpressionNode(
-                        NodeFactory.memberExpressionNode(
-                            NodeFactory.literalNode(
-                                this.originalKeysIndexesInShuffledArray.join('|')
+            NodeFactory.variableDeclarationNode(
+                [
+                    NodeFactory.variableDeclaratorNode(
+                        NodeFactory.identifierNode(controllerIdentifierName),
+                        NodeFactory.callExpressionNode(
+                            NodeFactory.memberExpressionNode(
+                                NodeFactory.literalNode(
+                                    this.originalKeysIndexesInShuffledArray.join('|')
+                                ),
+                                NodeFactory.identifierNode('split')
                             ),
-                            NodeFactory.identifierNode('split')
-                        ),
-                        [
-                            NodeFactory.literalNode('|')
-                        ]
+                            [
+                                NodeFactory.literalNode('|')
+                            ]
+                        )
+                    ),
+                    NodeFactory.variableDeclaratorNode(
+                        NodeFactory.identifierNode(indexIdentifierName),
+                        NodeFactory.literalNode(0)
                     )
-                ),
-                NodeFactory.variableDeclaratorNode(
-                    NodeFactory.identifierNode(indexIdentifierName),
-                    NodeFactory.literalNode(0)
-                )
-            ]),
+                ],
+                this.prevailingKindOfVariables
+            ),
             NodeFactory.whileStatementNode(
                 NodeFactory.literalNode(true),
                 NodeFactory.blockStatementNode([

+ 4 - 1
src/custom-nodes/control-flow-flattening-nodes/CallExpressionFunctionNode.ts

@@ -8,6 +8,7 @@ import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -25,16 +26,18 @@ export class CallExpressionFunctionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**

+ 4 - 1
src/custom-nodes/control-flow-flattening-nodes/LogicalExpressionFunctionNode.ts

@@ -8,6 +8,7 @@ import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -25,16 +26,18 @@ export class LogicalExpressionFunctionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**

+ 4 - 1
src/custom-nodes/control-flow-flattening-nodes/StringLiteralNode.ts

@@ -6,6 +6,7 @@ import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -22,16 +23,18 @@ export class StringLiteralNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**

+ 4 - 1
src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/CallExpressionControlFlowStorageCallNode.ts

@@ -12,6 +12,7 @@ import { TStatement } from '../../../types/node/TStatement';
 
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IRandomGenerator } from "../../../interfaces/utils/IRandomGenerator";
+import { ITemplateFormatter } from '../../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../../decorators/Initializable';
 
@@ -47,16 +48,18 @@ export class CallExpressionControlFlowStorageCallNode extends AbstractCustomNode
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**

+ 25 - 8
src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts

@@ -9,7 +9,9 @@ import { TStatement } from '../../../types/node/TStatement';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../../interfaces/options/IOptions';
+import { IPrevailingKindOfVariablesAnalyzer } from '../../../interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../../decorators/Initializable';
 
@@ -26,18 +28,30 @@ export class ControlFlowStorageNode extends AbstractCustomNode {
     @initializable()
     private controlFlowStorage!: TControlFlowStorage;
 
+    /**
+     * @type {ESTree.VariableDeclaration['kind']}
+     */
+    private readonly prevailingKindOfVariables: ESTree.VariableDeclaration['kind'];
+
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
+     * @param {IPrevailingKindOfVariablesAnalyzer} prevailingKindOfVariablesAnalyzer
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer)
+            prevailingKindOfVariablesAnalyzer: IPrevailingKindOfVariablesAnalyzer
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
+
+        this.prevailingKindOfVariables = prevailingKindOfVariablesAnalyzer.getPrevailingKind();
     }
 
     /**
@@ -66,12 +80,15 @@ export class ControlFlowStorageNode extends AbstractCustomNode {
                 );
             });
 
-        let structure: ESTree.Node = NodeFactory.variableDeclarationNode([
-            NodeFactory.variableDeclaratorNode(
-                NodeFactory.identifierNode(this.controlFlowStorage.getStorageId()),
-                NodeFactory.objectExpressionNode(propertyNodes)
-            )
-        ]);
+        let structure: ESTree.Node = NodeFactory.variableDeclarationNode(
+            [
+                NodeFactory.variableDeclaratorNode(
+                    NodeFactory.identifierNode(this.controlFlowStorage.getStorageId()),
+                    NodeFactory.objectExpressionNode(propertyNodes)
+                )
+            ],
+            this.prevailingKindOfVariables
+        );
 
         structure = NodeUtils.parentizeAst(structure);
 

+ 4 - 1
src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ExpressionWithOperatorControlFlowStorageCallNode.ts

@@ -8,6 +8,7 @@ import { TStatement } from '../../../types/node/TStatement';
 
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../../decorators/Initializable';
 
@@ -43,16 +44,18 @@ export class ExpressionWithOperatorControlFlowStorageCallNode extends AbstractCu
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**

+ 4 - 1
src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode.ts

@@ -6,6 +6,7 @@ import { TStatement } from '../../../types/node/TStatement';
 
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../../decorators/Initializable';
 
@@ -29,16 +30,18 @@ export class StringLiteralControlFlowStorageCallNode extends AbstractCustomNode
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**

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

@@ -8,6 +8,7 @@ import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -31,16 +32,18 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**

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

@@ -1,13 +1,12 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -32,16 +31,18 @@ export class DebugProtectionFunctionCallNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**
@@ -64,7 +65,7 @@ export class DebugProtectionFunctionCallNode extends AbstractCustomNode {
      * @returns {string}
      */
     protected getTemplate (): string {
-        return format(DebugProtectionFunctionCallTemplate(), {
+        return this.templateFormatter.format(DebugProtectionFunctionCallTemplate(), {
             debugProtectionFunctionName: this.debugProtectionFunctionName,
             singleNodeCallControllerFunctionName: this.callsControllerFunctionName
         });

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

@@ -1,13 +1,12 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -26,16 +25,18 @@ export class DebugProtectionFunctionIntervalNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**
@@ -56,7 +57,7 @@ export class DebugProtectionFunctionIntervalNode extends AbstractCustomNode {
      * @returns {string}
      */
     protected getTemplate (): string {
-        return format(DebugProtectionFunctionIntervalTemplate(), {
+        return this.templateFormatter.format(DebugProtectionFunctionIntervalTemplate(), {
             debugProtectionFunctionName: this.debugProtectionFunctionName
         });
     }

+ 5 - 4
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts

@@ -1,13 +1,12 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 
@@ -30,16 +29,18 @@ export class DebugProtectionFunctionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**
@@ -64,7 +65,7 @@ export class DebugProtectionFunctionNode extends AbstractCustomNode {
             ? DebuggerTemplate()
             : DebuggerTemplateNoEval();
 
-        return format(DebugProtectionFunctionTemplate(), {
+        return this.templateFormatter.format(DebugProtectionFunctionTemplate(), {
             debuggerTemplate,
             debugProtectionFunctionName: this.debugProtectionFunctionName
         });

+ 13 - 4
src/custom-nodes/debug-protection-nodes/group/DebugProtectionCustomNodeGroup.ts

@@ -3,6 +3,7 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 import { TCustomNodeFactory } from '../../../types/container/custom-nodes/TCustomNodeFactory';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TInitialData } from '../../../types/TInitialData';
 import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
@@ -16,7 +17,11 @@ import { CustomNode } from '../../../enums/custom-nodes/CustomNode';
 import { ObfuscationEvent } from '../../../enums/event-emitters/ObfuscationEvent';
 
 import { AbstractCustomNodeGroup } from '../../AbstractCustomNodeGroup';
+import { DebugProtectionFunctionNode } from '../DebugProtectionFunctionNode';
+import { DebugProtectionFunctionCallNode } from '../DebugProtectionFunctionCallNode';
+import { DebugProtectionFunctionIntervalNode } from '../DebugProtectionFunctionIntervalNode';
 import { NodeAppender } from '../../../node/NodeAppender';
+import { NodeCallsControllerFunctionNode } from '../../node-calls-controller-nodes/NodeCallsControllerFunctionNode';
 import { NodeGuards } from '../../../node/NodeGuards';
 
 @injectable()
@@ -107,10 +112,14 @@ export class DebugProtectionCustomNodeGroup extends AbstractCustomNodeGroup {
         const debugProtectionFunctionName: string = this.identifierNamesGenerator.generate();
         const callsControllerFunctionName: string = this.identifierNamesGenerator.generate();
 
-        const debugProtectionFunctionNode: ICustomNode = this.customNodeFactory(CustomNode.DebugProtectionFunctionNode);
-        const debugProtectionFunctionCallNode: ICustomNode = this.customNodeFactory(CustomNode.DebugProtectionFunctionCallNode);
-        const debugProtectionFunctionIntervalNode: ICustomNode = this.customNodeFactory(CustomNode.DebugProtectionFunctionIntervalNode);
-        const nodeCallsControllerFunctionNode: ICustomNode = this.customNodeFactory(CustomNode.NodeCallsControllerFunctionNode);
+        const debugProtectionFunctionNode: ICustomNode<TInitialData<DebugProtectionFunctionNode>> =
+            this.customNodeFactory(CustomNode.DebugProtectionFunctionNode);
+        const debugProtectionFunctionCallNode: ICustomNode<TInitialData<DebugProtectionFunctionCallNode>> =
+            this.customNodeFactory(CustomNode.DebugProtectionFunctionCallNode);
+        const debugProtectionFunctionIntervalNode: ICustomNode<TInitialData<DebugProtectionFunctionIntervalNode>> =
+            this.customNodeFactory(CustomNode.DebugProtectionFunctionIntervalNode);
+        const nodeCallsControllerFunctionNode: ICustomNode<TInitialData<NodeCallsControllerFunctionNode>> =
+            this.customNodeFactory(CustomNode.NodeCallsControllerFunctionNode);
 
         debugProtectionFunctionNode.initialize(debugProtectionFunctionName);
         debugProtectionFunctionCallNode.initialize(debugProtectionFunctionName, callsControllerFunctionName);

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

@@ -1,14 +1,13 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { ICryptUtils } from '../../interfaces/utils/ICryptUtils';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 
@@ -35,18 +34,20 @@ export class DomainLockNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
-     * @param {ICryptUtils} cryptUtils
      * @param {IOptions} options
+     * @param {ICryptUtils} cryptUtils
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.ICryptUtils) cryptUtils: ICryptUtils,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.ICryptUtils) cryptUtils: ICryptUtils
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
 
         this.cryptUtils = cryptUtils;
     }
@@ -78,7 +79,7 @@ export class DomainLockNode extends AbstractCustomNode {
             ? this.getGlobalVariableTemplate()
             : GlobalVariableNoEvalTemplate();
 
-        return format(DomainLockNodeTemplate(), {
+        return this.templateFormatter.format(DomainLockNodeTemplate(), {
             domainLockFunctionName: this.identifierNamesGenerator.generate(),
             diff: diff,
             domains: hiddenDomainsString,

+ 7 - 2
src/custom-nodes/domain-lock-nodes/group/DomainLockCustomNodeGroup.ts

@@ -3,6 +3,7 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 import { TCustomNodeFactory } from '../../../types/container/custom-nodes/TCustomNodeFactory';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TInitialData } from '../../../types/TInitialData';
 import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
@@ -16,7 +17,9 @@ import { CustomNode } from '../../../enums/custom-nodes/CustomNode';
 import { ObfuscationEvent } from '../../../enums/event-emitters/ObfuscationEvent';
 
 import { AbstractCustomNodeGroup } from '../../AbstractCustomNodeGroup';
+import { DomainLockNode } from '../DomainLockNode';
 import { NodeAppender } from '../../../node/NodeAppender';
+import { NodeCallsControllerFunctionNode } from '../../node-calls-controller-nodes/NodeCallsControllerFunctionNode';
 
 @injectable()
 export class DomainLockCustomNodeGroup extends AbstractCustomNodeGroup {
@@ -90,8 +93,10 @@ export class DomainLockCustomNodeGroup extends AbstractCustomNodeGroup {
 
         const callsControllerFunctionName: string = this.identifierNamesGenerator.generate();
 
-        const domainLockNode: ICustomNode = this.customNodeFactory(CustomNode.DomainLockNode);
-        const nodeCallsControllerFunctionNode: ICustomNode = this.customNodeFactory(CustomNode.NodeCallsControllerFunctionNode);
+        const domainLockNode: ICustomNode<TInitialData<DomainLockNode>> =
+            this.customNodeFactory(CustomNode.DomainLockNode);
+        const nodeCallsControllerFunctionNode: ICustomNode<TInitialData<NodeCallsControllerFunctionNode>> =
+            this.customNodeFactory(CustomNode.NodeCallsControllerFunctionNode);
 
         domainLockNode.initialize(callsControllerFunctionName);
         nodeCallsControllerFunctionNode.initialize(this.appendEvent, callsControllerFunctionName);

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

@@ -1,8 +1,6 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
@@ -20,6 +18,7 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../options/presets/NoCustomNodes'
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscatorFacade';
 import { NodeUtils } from '../../node/NodeUtils';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 @injectable()
 export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
@@ -37,16 +36,18 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**
@@ -71,7 +72,7 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
     protected getTemplate (): string {
         if (this.appendEvent === ObfuscationEvent.AfterObfuscation) {
             return JavaScriptObfuscator.obfuscate(
-                format(SingleNodeCallControllerTemplate(), {
+                this.templateFormatter.format(SingleNodeCallControllerTemplate(), {
                     singleNodeCallControllerFunctionName: this.callsControllerFunctionName
                 }),
                 {
@@ -83,7 +84,7 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode {
             ).getObfuscatedCode();
         }
 
-        return format(SingleNodeCallControllerTemplate(), {
+        return this.templateFormatter.format(SingleNodeCallControllerTemplate(), {
             singleNodeCallControllerFunctionName: this.callsControllerFunctionName
         });
     }

+ 29 - 10
src/custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode.ts

@@ -1,29 +1,45 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
+import * as ESTree from 'estree';
+
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IOptions } from '../../interfaces/options/IOptions';
+import { IPrevailingKindOfVariablesAnalyzer } from '../../interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeFactory } from '../../node/NodeFactory';
 
 @injectable()
 export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCustomNode {
+    /**
+     * @type {ESTree.VariableDeclaration['kind']}
+     */
+    private readonly prevailingKindOfVariables: ESTree.VariableDeclaration['kind'];
+
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
+     * @param {IPrevailingKindOfVariablesAnalyzer} prevailingKindOfVariablesAnalyzer
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer)
+            prevailingKindOfVariablesAnalyzer: IPrevailingKindOfVariablesAnalyzer,
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
+
+        this.prevailingKindOfVariables = prevailingKindOfVariablesAnalyzer.getPrevailingKind();
     }
 
     public initialize (): void {}
@@ -32,14 +48,17 @@ export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCus
      * @returns {TStatement[]}
      */
     protected getNodeStructure (): TStatement[] {
-        const structure: TStatement = NodeFactory.variableDeclarationNode([
-            NodeFactory.variableDeclaratorNode(
-                NodeFactory.identifierNode(
-                    this.identifierNamesGenerator.generate()
-                ),
-                NodeFactory.objectExpressionNode([])
-            )
-        ]);
+        const structure: TStatement = NodeFactory.variableDeclarationNode(
+            [
+                NodeFactory.variableDeclaratorNode(
+                    NodeFactory.identifierNode(
+                        this.identifierNamesGenerator.generate()
+                    ),
+                    NodeFactory.objectExpressionNode([])
+                )
+            ],
+            this.prevailingKindOfVariables
+        );
 
         return [structure];
     }

+ 8 - 7
src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts

@@ -1,14 +1,13 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -35,18 +34,20 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
-     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
+     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
 
         this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
@@ -70,7 +71,7 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode {
      */
     protected getTemplate (): string {
         return JavaScriptObfuscator.obfuscate(
-            format(SelfDefendingTemplate(this.escapeSequenceEncoder), {
+            this.templateFormatter.format(SelfDefendingTemplate(this.escapeSequenceEncoder), {
                 selfDefendingFunctionName: this.identifierNamesGenerator.generate(),
                 singleNodeCallControllerFunctionName: this.callsControllerFunctionName
             }),

+ 7 - 2
src/custom-nodes/self-defending-nodes/group/SelfDefendingCustomNodeGroup.ts

@@ -3,6 +3,7 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 import { TCustomNodeFactory } from '../../../types/container/custom-nodes/TCustomNodeFactory';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TInitialData } from '../../../types/TInitialData';
 import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
@@ -17,6 +18,8 @@ import { ObfuscationEvent } from '../../../enums/event-emitters/ObfuscationEvent
 
 import { AbstractCustomNodeGroup } from '../../AbstractCustomNodeGroup';
 import { NodeAppender } from '../../../node/NodeAppender';
+import { NodeCallsControllerFunctionNode } from '../../node-calls-controller-nodes/NodeCallsControllerFunctionNode';
+import { SelfDefendingUnicodeNode } from '../SelfDefendingUnicodeNode';
 
 @injectable()
 export class SelfDefendingCustomNodeGroup extends AbstractCustomNodeGroup {
@@ -90,8 +93,10 @@ export class SelfDefendingCustomNodeGroup extends AbstractCustomNodeGroup {
 
         const callsControllerFunctionName: string = this.identifierNamesGenerator.generate();
 
-        const selfDefendingUnicodeNode: ICustomNode = this.customNodeFactory(CustomNode.SelfDefendingUnicodeNode);
-        const nodeCallsControllerFunctionNode: ICustomNode = this.customNodeFactory(CustomNode.NodeCallsControllerFunctionNode);
+        const selfDefendingUnicodeNode: ICustomNode<TInitialData<SelfDefendingUnicodeNode>> =
+            this.customNodeFactory(CustomNode.SelfDefendingUnicodeNode);
+        const nodeCallsControllerFunctionNode: ICustomNode<TInitialData<NodeCallsControllerFunctionNode>> =
+            this.customNodeFactory(CustomNode.NodeCallsControllerFunctionNode);
 
         selfDefendingUnicodeNode.initialize(callsControllerFunctionName);
         nodeCallsControllerFunctionNode.initialize(this.appendEvent, callsControllerFunctionName);

+ 12 - 11
src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts

@@ -1,14 +1,13 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
@@ -50,18 +49,20 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
-     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
+     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
 
         this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
@@ -92,7 +93,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
         const decodeNodeTemplate: string = this.getDecodeStringArrayTemplate();
 
         return JavaScriptObfuscator.obfuscate(
-            format(StringArrayCallsWrapperTemplate(), {
+            this.templateFormatter.format(StringArrayCallsWrapperTemplate(), {
                 decodeNodeTemplate,
                 stringArrayCallsWrapperName: this.stringArrayCallsWrapperName,
                 stringArrayName: this.stringArrayName
@@ -113,13 +114,13 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
         const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
             ? this.getGlobalVariableTemplate()
             : GlobalVariableNoEvalTemplate();
-        const atobPolyfill: string = format(AtobTemplate(), { globalVariableTemplate });
+        const atobPolyfill: string = this.templateFormatter.format(AtobTemplate(), { globalVariableTemplate });
 
         let decodeStringArrayTemplate: string = '';
         let selfDefendingCode: string = '';
 
         if (this.options.selfDefending) {
-            selfDefendingCode = format(
+            selfDefendingCode = this.templateFormatter.format(
                 SelfDefendingTemplate(
                     this.randomGenerator,
                     this.escapeSequenceEncoder
@@ -133,7 +134,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
 
         switch (this.options.stringArrayEncoding) {
             case StringArrayEncoding.Rc4:
-                decodeStringArrayTemplate = format(
+                decodeStringArrayTemplate = this.templateFormatter.format(
                     StringArrayRc4DecodeNodeTemplate(this.randomGenerator),
                     {
                         atobPolyfill,
@@ -146,7 +147,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
                 break;
 
             case StringArrayEncoding.Base64:
-                decodeStringArrayTemplate = format(
+                decodeStringArrayTemplate = this.templateFormatter.format(
                     StringArrayBase64DecodeNodeTemplate(this.randomGenerator),
                     {
                         atobPolyfill,

+ 5 - 4
src/custom-nodes/string-array-nodes/StringArrayNode.ts

@@ -1,14 +1,13 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 import { TStringArrayStorage } from '../../types/storages/TStringArrayStorage';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -40,16 +39,18 @@ export class StringArrayNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
     }
 
     /**
@@ -87,7 +88,7 @@ export class StringArrayNode extends AbstractCustomNode {
      * @returns {string}
      */
     protected getTemplate (): string {
-        return format(StringArrayTemplate(), {
+        return this.templateFormatter.format(StringArrayTemplate(), {
             stringArrayName: this.stringArrayName,
             stringArray: this.stringArrayStorage.toString()
         });

+ 9 - 8
src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts

@@ -1,14 +1,13 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
-import format from 'string-template';
-
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITemplateFormatter } from '../../interfaces/utils/ITemplateFormatter';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -43,18 +42,20 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
-     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
+     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.ITemplateFormatter) templateFormatter: ITemplateFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
+        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder
     ) {
-        super(identifierNamesGeneratorFactory, randomGenerator, options);
+        super(identifierNamesGeneratorFactory, templateFormatter, randomGenerator, options);
 
         this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
@@ -88,7 +89,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
         let code: string = '';
 
         if (this.options.selfDefending) {
-            code = format(SelfDefendingTemplate(this.escapeSequenceEncoder), {
+            code = this.templateFormatter.format(SelfDefendingTemplate(this.escapeSequenceEncoder), {
                 timesName,
                 whileFunctionName
             });
@@ -97,7 +98,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
         }
 
         return JavaScriptObfuscator.obfuscate(
-            format(StringArrayRotateFunctionTemplate(), {
+            this.templateFormatter.format(StringArrayRotateFunctionTemplate(), {
                 code,
                 timesName,
                 stringArrayName: this.stringArrayName,

+ 10 - 3
src/custom-nodes/string-array-nodes/group/StringArrayCustomNodeGroup.ts

@@ -3,6 +3,7 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
 import { TCustomNodeFactory } from '../../../types/container/custom-nodes/TCustomNodeFactory';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TInitialData } from '../../../types/TInitialData';
 import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
 import { TStringArrayStorage } from '../../../types/storages/TStringArrayStorage';
 
@@ -18,6 +19,9 @@ import { ObfuscationEvent } from '../../../enums/event-emitters/ObfuscationEvent
 
 import { AbstractCustomNodeGroup } from '../../AbstractCustomNodeGroup';
 import { NodeAppender } from '../../../node/NodeAppender';
+import { StringArrayNode } from '../StringArrayNode';
+import { StringArrayCallsWrapper } from '../StringArrayCallsWrapper';
+import { StringArrayRotateFunctionNode } from '../StringArrayRotateFunctionNode';
 
 @injectable()
 export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
@@ -95,9 +99,12 @@ export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
             return;
         }
 
-        const stringArrayNode: ICustomNode = this.customNodeFactory(CustomNode.StringArrayNode);
-        const stringArrayCallsWrapper: ICustomNode = this.customNodeFactory(CustomNode.StringArrayCallsWrapper);
-        const stringArrayRotateFunctionNode: ICustomNode = this.customNodeFactory(CustomNode.StringArrayRotateFunctionNode);
+        const stringArrayNode: ICustomNode<TInitialData<StringArrayNode>> =
+            this.customNodeFactory(CustomNode.StringArrayNode);
+        const stringArrayCallsWrapper: ICustomNode<TInitialData<StringArrayCallsWrapper>> =
+            this.customNodeFactory(CustomNode.StringArrayCallsWrapper);
+        const stringArrayRotateFunctionNode: ICustomNode<TInitialData<StringArrayRotateFunctionNode>> =
+            this.customNodeFactory(CustomNode.StringArrayRotateFunctionNode);
 
         const stringArrayStorageId: string = this.stringArrayStorage.getStorageId();
 

+ 2 - 2
src/interfaces/analyzers/IAnalyzer.d.ts

@@ -3,7 +3,7 @@ import * as ESTree from 'estree';
 export interface IAnalyzer <T> {
     /**
      * @param {Program} astTree
-     * @returns {T[]}
+     * @returns {T}
      */
-    analyze (astTree: ESTree.Program): T[];
+    analyze (astTree: ESTree.Program): T;
 }

+ 1 - 1
src/interfaces/analyzers/calls-graph-analyzer/ICallsGraphAnalyzer.d.ts

@@ -3,7 +3,7 @@ import * as ESTree from 'estree';
 import { IAnalyzer } from '../IAnalyzer';
 import { ICallsGraphData } from './ICallsGraphData';
 
-export interface ICallsGraphAnalyzer extends IAnalyzer<ICallsGraphData> {
+export interface ICallsGraphAnalyzer extends IAnalyzer<ICallsGraphData[]> {
     /**
      * @param {Program} astTree
      * @returns {ICallsGraphData[]}

+ 15 - 0
src/interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer.d.ts

@@ -0,0 +1,15 @@
+import * as ESTree from 'estree';
+
+import { IAnalyzer } from '../IAnalyzer';
+
+export interface IPrevailingKindOfVariablesAnalyzer extends IAnalyzer<void> {
+    /**
+     * @param {Program} astTree
+     */
+    analyze (astTree: ESTree.Program): void;
+
+    /**
+     * @returns {ESTree.VariableDeclaration['kind']}
+     */
+    getPrevailingKind (): ESTree.VariableDeclaration['kind'];
+}

+ 3 - 1
src/interfaces/custom-nodes/ICustomNode.d.ts

@@ -2,7 +2,9 @@ import { TStatement } from '../../types/node/TStatement';
 
 import { IInitializable } from '../IInitializable';
 
-export interface ICustomNode extends IInitializable<any[]> {
+export interface ICustomNode <
+    TInitialData extends any[] = any[]
+> extends IInitializable<TInitialData> {
     /**
      * @returns ESTree.Node[]
      */

+ 6 - 0
src/interfaces/utils/IArrayUtils.d.ts

@@ -5,6 +5,12 @@ export interface IArrayUtils {
      */
     createWithRange (length: number): number[];
 
+    /**
+     * @param {T[]} array
+     * @returns {T | null}
+     */
+    findMostOccurringElement <T extends string | number> (array: T[]): T | null;
+
     /**
      * @param array
      * @param times

+ 13 - 0
src/interfaces/utils/ITemplateFormatter.d.ts

@@ -0,0 +1,13 @@
+import { TObject } from '../../types/TObject';
+
+export interface ITemplateFormatter {
+    /**
+     * @param {string} template
+     * @param {TMapping} mapping
+     * @returns {string}
+     */
+    format <TMapping extends TObject> (
+        template: string,
+        mapping: TMapping
+    ): string;
+}

+ 4 - 3
src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts

@@ -5,6 +5,7 @@ import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { TControlFlowCustomNodeFactory } from '../../types/container/custom-nodes/TControlFlowCustomNodeFactory';
+import { TInitialData } from '../../types/TInitialData';
 import { TStatement } from '../../types/node/TStatement';
 
 import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
@@ -17,6 +18,7 @@ import { ControlFlowCustomNode } from '../../enums/custom-nodes/ControlFlowCusto
 import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { BlockStatementControlFlowFlatteningNode } from '../../custom-nodes/control-flow-flattening-nodes/BlockStatementControlFlowFlatteningNode';
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeUtils } from '../../node/NodeUtils';
 
@@ -131,9 +133,8 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme
         const originalKeys: number[] = this.arrayUtils.createWithRange(blockStatementBody.length);
         const shuffledKeys: number[] = this.arrayUtils.shuffle(originalKeys);
         const originalKeysIndexesInShuffledArray: number[] = originalKeys.map((key: number) => shuffledKeys.indexOf(key));
-        const blockStatementControlFlowFlatteningCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.BlockStatementControlFlowFlatteningNode
-        );
+        const blockStatementControlFlowFlatteningCustomNode: ICustomNode<TInitialData<BlockStatementControlFlowFlatteningNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.BlockStatementControlFlowFlatteningNode);
 
         blockStatementControlFlowFlatteningCustomNode.initialize(
             blockStatementBody,

+ 4 - 3
src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts

@@ -8,6 +8,7 @@ import { TControlFlowCustomNodeFactory } from '../../types/container/custom-node
 import { TControlFlowReplacerFactory } from '../../types/container/node-transformers/TControlFlowReplacerFactory';
 import { TControlFlowStorage } from '../../types/storages/TControlFlowStorage';
 import { TControlFlowStorageFactory } from '../../types/container/node-transformers/TControlFlowStorageFactory';
+import { TInitialData } from '../../types/TInitialData';
 import { TNodeWithStatements } from '../../types/node/TNodeWithStatements';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
@@ -21,6 +22,7 @@ import { NodeType } from '../../enums/node/NodeType';
 import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { ControlFlowStorageNode } from '../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode';
 import { NodeAppender } from '../../node/NodeAppender';
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeMetadata } from '../../node/NodeMetadata';
@@ -151,9 +153,8 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
             return functionNode;
         }
 
-        const controlFlowStorageCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.ControlFlowStorageNode
-        );
+        const controlFlowStorageCustomNode: ICustomNode<TInitialData<ControlFlowStorageNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.ControlFlowStorageNode);
 
         controlFlowStorageCustomNode.initialize(controlFlowStorage);
         NodeAppender.prepend(hostNode, controlFlowStorageCustomNode.getNode());

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

@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
 
 import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory';
 import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
+import { TInitialData } from '../../../types/TInitialData';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../../interfaces/options/IOptions';
@@ -12,6 +13,7 @@ import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 
 import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCustomNode';
 
+import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode';
 import { ExpressionWithOperatorControlFlowReplacer } from './ExpressionWithOperatorControlFlowReplacer';
 
 @injectable()
@@ -46,17 +48,16 @@ export class BinaryExpressionControlFlowReplacer extends ExpressionWithOperatorC
         parentNode: ESTree.Node,
         controlFlowStorage: TControlFlowStorage
     ): ESTree.Node {
-        const replacerId: string = binaryExpressionNode.operator;
-        const binaryExpressionFunctionCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.BinaryExpressionFunctionNode
-        );
+        const operator: ESTree.BinaryOperator = binaryExpressionNode.operator;
+        const binaryExpressionFunctionCustomNode: ICustomNode<TInitialData<BinaryExpressionFunctionNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.BinaryExpressionFunctionNode);
 
-        binaryExpressionFunctionCustomNode.initialize(replacerId);
+        binaryExpressionFunctionCustomNode.initialize(operator);
 
         const storageKey: string = this.insertCustomNodeToControlFlowStorage(
             binaryExpressionFunctionCustomNode,
             controlFlowStorage,
-            replacerId,
+            operator,
             BinaryExpressionControlFlowReplacer.usingExistingIdentifierChance
         );
 

+ 7 - 6
src/node-transformers/control-flow-transformers/control-flow-replacers/CallExpressionControlFlowReplacer.ts

@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
 
 import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory';
 import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
+import { TInitialData } from '../../../types/TInitialData';
 import { TStatement } from '../../../types/node/TStatement';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
@@ -14,6 +15,8 @@ import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCustomNode';
 
 import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
+import { CallExpressionFunctionNode } from '../../../custom-nodes/control-flow-flattening-nodes/CallExpressionFunctionNode';
+import { CallExpressionControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/CallExpressionControlFlowStorageCallNode';
 import { NodeGuards } from '../../../node/NodeGuards';
 
 @injectable()
@@ -55,9 +58,8 @@ export class CallExpressionControlFlowReplacer extends AbstractControlFlowReplac
         }
 
         const replacerId: string = String(callExpressionNode.arguments.length);
-        const callExpressionFunctionCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.CallExpressionFunctionNode
-        );
+        const callExpressionFunctionCustomNode: ICustomNode<TInitialData<CallExpressionFunctionNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.CallExpressionFunctionNode);
         const expressionArguments: (ESTree.Expression | ESTree.SpreadElement)[] = callExpressionNode.arguments;
 
         callExpressionFunctionCustomNode.initialize(expressionArguments);
@@ -90,9 +92,8 @@ export class CallExpressionControlFlowReplacer extends AbstractControlFlowReplac
         callee: ESTree.Expression,
         expressionArguments: (ESTree.Expression | ESTree.SpreadElement)[]
     ): ESTree.Node {
-        const controlFlowStorageCallCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.CallExpressionControlFlowStorageCallNode
-        );
+        const controlFlowStorageCallCustomNode: ICustomNode<TInitialData<CallExpressionControlFlowStorageCallNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.CallExpressionControlFlowStorageCallNode);
 
         controlFlowStorageCallCustomNode.initialize(controlFlowStorageId, storageKey, callee, expressionArguments);
 

+ 4 - 3
src/node-transformers/control-flow-transformers/control-flow-replacers/ExpressionWithOperatorControlFlowReplacer.ts

@@ -4,6 +4,7 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 import * as ESTree from 'estree';
 
 import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory';
+import { TInitialData } from '../../../types/TInitialData';
 import { TStatement } from '../../../types/node/TStatement';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
@@ -13,6 +14,7 @@ import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCustomNode';
 
 import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
+import { ExpressionWithOperatorControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ExpressionWithOperatorControlFlowStorageCallNode';
 import { NodeGuards } from '../../../node/NodeGuards';
 
 @injectable()
@@ -44,9 +46,8 @@ export abstract class ExpressionWithOperatorControlFlowReplacer extends Abstract
         leftExpression: ESTree.Expression,
         rightExpression: ESTree.Expression
     ): ESTree.Node {
-        const controlFlowStorageCallCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.ExpressionWithOperatorControlFlowStorageCallNode
-        );
+        const controlFlowStorageCallCustomNode: ICustomNode<TInitialData<ExpressionWithOperatorControlFlowStorageCallNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.ExpressionWithOperatorControlFlowStorageCallNode);
 
         controlFlowStorageCallCustomNode.initialize(controlFlowStorageId, storageKey, leftExpression, rightExpression);
 

+ 7 - 6
src/node-transformers/control-flow-transformers/control-flow-replacers/LogicalExpressionControlFlowReplacer.ts

@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
 
 import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory';
 import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
+import { TInitialData } from '../../../types/TInitialData';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../../interfaces/options/IOptions';
@@ -13,6 +14,7 @@ import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCustomNode';
 
 import { ExpressionWithOperatorControlFlowReplacer } from './ExpressionWithOperatorControlFlowReplacer';
+import { LogicalExpressionFunctionNode } from '../../../custom-nodes/control-flow-flattening-nodes/LogicalExpressionFunctionNode';
 import { NodeGuards } from '../../../node/NodeGuards';
 import { NodeUtils } from '../../../node/NodeUtils';
 
@@ -52,17 +54,16 @@ export class LogicalExpressionControlFlowReplacer extends ExpressionWithOperator
             return logicalExpressionNode;
         }
 
-        const replacerId: string = logicalExpressionNode.operator;
-        const logicalExpressionFunctionCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.LogicalExpressionFunctionNode
-        );
+        const operator: ESTree.LogicalOperator = logicalExpressionNode.operator;
+        const logicalExpressionFunctionCustomNode: ICustomNode<TInitialData<LogicalExpressionFunctionNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.LogicalExpressionFunctionNode);
 
-        logicalExpressionFunctionCustomNode.initialize(replacerId);
+        logicalExpressionFunctionCustomNode.initialize(operator);
 
         const storageKey: string = this.insertCustomNodeToControlFlowStorage(
             logicalExpressionFunctionCustomNode,
             controlFlowStorage,
-            replacerId,
+            operator,
             LogicalExpressionControlFlowReplacer.usingExistingIdentifierChance
         );
 

+ 7 - 6
src/node-transformers/control-flow-transformers/control-flow-replacers/StringLiteralControlFlowReplacer.ts

@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
 
 import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory';
 import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
+import { TInitialData } from '../../../types/TInitialData';
 import { TStatement } from '../../../types/node/TStatement';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
@@ -15,6 +16,8 @@ import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCu
 
 import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
 import { NodeGuards } from '../../../node/NodeGuards';
+import { StringLiteralControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode';
+import { StringLiteralNode } from '../../../custom-nodes/control-flow-flattening-nodes/StringLiteralNode';
 
 @injectable()
 export class StringLiteralControlFlowReplacer extends AbstractControlFlowReplacer {
@@ -57,9 +60,8 @@ export class StringLiteralControlFlowReplacer extends AbstractControlFlowReplace
         }
 
         const replacerId: string = String(literalNode.value);
-        const literalFunctionCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.StringLiteralNode
-        );
+        const literalFunctionCustomNode: ICustomNode<TInitialData<StringLiteralNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.StringLiteralNode);
 
         literalFunctionCustomNode.initialize(literalNode.value);
 
@@ -82,9 +84,8 @@ export class StringLiteralControlFlowReplacer extends AbstractControlFlowReplace
         controlFlowStorageId: string,
         storageKey: string
     ): ESTree.Node {
-        const controlFlowStorageCallCustomNode: ICustomNode = this.controlFlowCustomNodeFactory(
-            ControlFlowCustomNode.StringLiteralControlFlowStorageCallNode
-        );
+        const controlFlowStorageCallCustomNode: ICustomNode<TInitialData<StringLiteralControlFlowStorageCallNode>> =
+            this.controlFlowCustomNodeFactory(ControlFlowCustomNode.StringLiteralControlFlowStorageCallNode);
 
         controlFlowStorageCallCustomNode.initialize(controlFlowStorageId, storageKey);
 

+ 6 - 3
src/node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor.ts

@@ -8,12 +8,14 @@ import { TObjectExpressionKeysTransformerCustomNodeFactory } from '../../../type
 import { TStatement } from '../../../types/node/TStatement';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { TInitialData } from '../../../types/TInitialData';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 
 import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
 
 import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
+import { BasePropertiesExtractorObjectExpressionHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode';
 import { NodeAppender } from '../../../node/NodeAppender';
 import { NodeGuards } from '../../../node/NodeGuards';
 
@@ -92,9 +94,10 @@ export class BasePropertiesExtractor extends AbstractPropertiesExtractor {
      * @returns {VariableDeclaration}
      */
     private getObjectExpressionHostNode (): ESTree.VariableDeclaration {
-        const objectExpressionHostCustomNode: ICustomNode = this.objectExpressionKeysTransformerCustomNodeFactory(
-            ObjectExpressionKeysTransformerCustomNode.BasePropertiesExtractorObjectExpressionHostNode
-        );
+        const objectExpressionHostCustomNode: ICustomNode<TInitialData<BasePropertiesExtractorObjectExpressionHostNode>> =
+            this.objectExpressionKeysTransformerCustomNodeFactory(
+                ObjectExpressionKeysTransformerCustomNode.BasePropertiesExtractorObjectExpressionHostNode
+            );
 
         objectExpressionHostCustomNode.initialize();
 

+ 4 - 3
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -5,6 +5,7 @@ import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { TDeadNodeInjectionCustomNodeFactory } from '../../types/container/custom-nodes/TDeadNodeInjectionCustomNodeFactory';
+import { TInitialData } from '../../types/TInitialData';
 import { TNodeWithStatements } from '../../types/node/TNodeWithStatements';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
@@ -19,6 +20,7 @@ import { NodeType } from '../../enums/node/NodeType';
 import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { BlockStatementDeadCodeInjectionNode } from '../../custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeStatementUtils } from '../../node/NodeStatementUtils';
@@ -389,9 +391,8 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
          */
         this.deadCodeInjectionRootAstHostNodeSet.add(deadCodeInjectionRootAstHostNode);
 
-        const blockStatementDeadCodeInjectionCustomNode: ICustomNode = this.deadCodeInjectionCustomNodeFactory(
-            DeadCodeInjectionCustomNode.BlockStatementDeadCodeInjectionNode
-        );
+        const blockStatementDeadCodeInjectionCustomNode: ICustomNode<TInitialData<BlockStatementDeadCodeInjectionNode>> =
+            this.deadCodeInjectionCustomNodeFactory(DeadCodeInjectionCustomNode.BlockStatementDeadCodeInjectionNode);
 
         blockStatementDeadCodeInjectionCustomNode.initialize(blockStatementNode, deadCodeInjectionRootAstHostNode);
 

+ 11 - 0
src/node-transformers/preparing-transformers/CustomNodesTransformer.ts

@@ -11,6 +11,7 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { ICallsGraphAnalyzer } from '../../interfaces/analyzers/calls-graph-analyzer/ICallsGraphAnalyzer';
 import { ICallsGraphData } from '../../interfaces/analyzers/calls-graph-analyzer/ICallsGraphData';
+import { IPrevailingKindOfVariablesAnalyzer } from '../../interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { ObfuscationEvent } from '../../enums/event-emitters/ObfuscationEvent';
@@ -44,8 +45,14 @@ export class CustomNodesTransformer extends AbstractNodeTransformer {
      */
     private callsGraphData: ICallsGraphData[] = [];
 
+    /**
+     * @type {IPrevailingKindOfVariablesAnalyzer}
+     */
+    private readonly prevailingKindOfVariablesAnalyzer: IPrevailingKindOfVariablesAnalyzer;
+
     /**
      * @param {ICallsGraphAnalyzer} callsGraphAnalyzer
+     * @param {IPrevailingKindOfVariablesAnalyzer} prevailingKindOfVariablesAnalyzer
      * @param {IObfuscationEventEmitter} obfuscationEventEmitter
      * @param {TCustomNodeGroupStorage} customNodeGroupStorage
      * @param {IRandomGenerator} randomGenerator
@@ -53,6 +60,8 @@ export class CustomNodesTransformer extends AbstractNodeTransformer {
      */
     constructor (
         @inject(ServiceIdentifiers.ICallsGraphAnalyzer) callsGraphAnalyzer: ICallsGraphAnalyzer,
+        @inject(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer)
+            prevailingKindOfVariablesAnalyzer: IPrevailingKindOfVariablesAnalyzer,
         @inject(ServiceIdentifiers.IObfuscationEventEmitter) obfuscationEventEmitter: IObfuscationEventEmitter,
         @inject(ServiceIdentifiers.TCustomNodeGroupStorage) customNodeGroupStorage: TCustomNodeGroupStorage,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@@ -61,6 +70,7 @@ export class CustomNodesTransformer extends AbstractNodeTransformer {
         super(randomGenerator, options);
 
         this.callsGraphAnalyzer = callsGraphAnalyzer;
+        this.prevailingKindOfVariablesAnalyzer = prevailingKindOfVariablesAnalyzer;
         this.obfuscationEventEmitter = obfuscationEventEmitter;
         this.customNodeGroupStorage = customNodeGroupStorage;
     }
@@ -103,6 +113,7 @@ export class CustomNodesTransformer extends AbstractNodeTransformer {
      */
     public analyzeNode (node: ESTree.Program, parentNode: ESTree.Node | null): void {
         this.callsGraphData = this.callsGraphAnalyzer.analyze(node);
+        this.prevailingKindOfVariablesAnalyzer.analyze(node);
     }
 
     /**

+ 4 - 1
src/node/NodeUtils.ts

@@ -33,7 +33,10 @@ export class NodeUtils {
      * @returns {Statement[]}
      */
     public static convertCodeToStructure (code: string): ESTree.Statement[] {
-        const structure: ESTree.Program = espree.parse(code, { sourceType: 'script' });
+        const structure: ESTree.Program = espree.parse(code, {
+            ecmaVersion: 10,
+            sourceType: 'script'
+        });
 
         estraverse.replace(structure, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node => {

+ 7 - 5
src/templates/AtobTemplate.ts

@@ -6,21 +6,23 @@ export function AtobTemplate (): string {
         (function () {
             {globalVariableTemplate}
             
-            var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+            const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
 
             that.atob || (
                 that.atob = function(input) {
-                    var str = String(input).replace(/=+$/, '');
+                    const str = String(input).replace(/=+$/, '');
+                    let output = '';
                     for (
-                        var bc = 0, bs, buffer, idx = 0, output = '';
+                        let bc = 0, bs, buffer, idx = 0;
                         buffer = str.charAt(idx++);
                         ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
                             bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
                     ) {
                         buffer = chars.indexOf(buffer);
                     }
-                return output;
-            });
+                    return output;
+                }
+            );
         })();
     `;
 }

+ 1 - 1
src/templates/GlobalVariableNoEvalTemplate.ts

@@ -3,7 +3,7 @@
  */
 export function GlobalVariableNoEvalTemplate (): string {
     return `
-        var that = (typeof window !== 'undefined'
+        const that = (typeof window !== 'undefined'
            ? window
            : (typeof process === 'object' &&
               typeof require === 'function' &&

+ 2 - 2
src/templates/GlobalVariableTemplate1.ts

@@ -3,10 +3,10 @@
  */
 export function GlobalVariableTemplate1 (): string {
     return `
-        var that;
+        let that;
         
         try {
-            var getGlobal = Function('return (function() ' + '{}.constructor("return this")( )' + ');');
+            const getGlobal = Function('return (function() ' + '{}.constructor("return this")( )' + ');');
             
             that = getGlobal();
         } catch (e) {

+ 3 - 3
src/templates/GlobalVariableTemplate2.ts

@@ -3,8 +3,8 @@
  */
 export function GlobalVariableTemplate2 (): string {
     return `
-        var getGlobal = function () {
-            var globalObject;
+        const getGlobal = function () {
+            let globalObject;
         
             try {
                 globalObject = Function('return (function() ' + '{}.constructor("return this")( )' + ');')();
@@ -14,6 +14,6 @@ export function GlobalVariableTemplate2 (): string {
             
             return globalObject;
         };
-        var that = getGlobal();
+        const that = getGlobal();
     `;
 }

+ 7 - 5
src/templates/Rc4Template.ts

@@ -3,18 +3,20 @@
  */
 export function Rc4Template (): string {
     return `
-        var rc4 = function (str, key) {
-            var s = [], j = 0, x, res = '', newStr = '';
+        const rc4 = function (str, key) {
+            let s = [], j = 0, x, res = '', newStr = '';
            
             str = atob(str);
                 
-            for (var k = 0, length = str.length; k < length; k++) {
+            for (let k = 0, length = str.length; k < length; k++) {
                 newStr += '%' + ('00' + str.charCodeAt(k).toString(16)).slice(-2);
             }
         
             str = decodeURIComponent(newStr);
+                    	     
+            let i;
                     	        
-	        for (var i = 0; i < 256; i++) {
+	        for (i = 0; i < 256; i++) {
                 s[i] = i;
             }
  
@@ -28,7 +30,7 @@ export function Rc4Template (): string {
             i = 0;
             j = 0;
             
-            for (var y = 0; y < str.length; y++) {
+            for (let y = 0; y < str.length; y++) {
                 i = (i + 1) % 256;
                 j = (j + s[i]) % 256;
                 x = s[i];

+ 4 - 4
src/templates/SingleNodeCallControllerTemplate.ts

@@ -3,13 +3,13 @@
  */
 export function SingleNodeCallControllerTemplate (): string {
     return `
-        var {singleNodeCallControllerFunctionName} = (function(){
-            var firstCall = true;
+        const {singleNodeCallControllerFunctionName} = (function(){
+            let firstCall = true;
             
             return function (context, fn){
-                var rfn = firstCall ? function(){
+                const rfn = firstCall ? function(){
                     if(fn){
-                        var res = fn.apply(context, arguments);
+                        const res = fn.apply(context, arguments);
                         fn = null;
                         return res;
                     }

+ 3 - 3
src/templates/console-output-nodes/console-output-disable-expression-node/ConsoleOutputDisableExpressionTemplate.ts

@@ -3,14 +3,14 @@
  */
 export function ConsoleOutputDisableExpressionTemplate (): string {
     return `
-        var {consoleLogDisableFunctionName} = {singleNodeCallControllerFunctionName}(this, function () {
-            var func = function () {};
+        const {consoleLogDisableFunctionName} = {singleNodeCallControllerFunctionName}(this, function () {
+            const func = function () {};
             
             {globalVariableTemplate}
                         
             if (!that.console) {
                 that.console = (function (func){
-                    var c = {};
+                    const c = {};
                     
                     c.log = func;
                     c.warn = func;

+ 14 - 11
src/templates/debug-protection-nodes/debug-protection-function-call-node/DebugProtectionFunctionCallTemplate.ts

@@ -4,18 +4,21 @@
 export function DebugProtectionFunctionCallTemplate (): string {
     return `
         (function () {
-            {singleNodeCallControllerFunctionName}(this, function () {
-                var regExp1 = new RegExp('function *\\\\( *\\\\)');
-                var regExp2 = new RegExp('\\\\+\\\\+ *\\(?:[a-zA-Z_$][0-9a-zA-Z_$]*\\)', 'i');
-       
-                var result = {debugProtectionFunctionName}('init');
-                
-                if (!regExp1.test(result + 'chain') || !regExp2.test(result + 'input')) {
-                    result('0');
-                } else {
-                    {debugProtectionFunctionName}();
+            {singleNodeCallControllerFunctionName}(
+                this,
+                function () {
+                    const regExp1 = new RegExp('function *\\\\( *\\\\)');
+                    const regExp2 = new RegExp('\\\\+\\\\+ *\\(?:[a-zA-Z_$][0-9a-zA-Z_$]*\\)', 'i');
+           
+                    const result = {debugProtectionFunctionName}('init');
+                    
+                    if (!regExp1.test(result + 'chain') || !regExp2.test(result + 'input')) {
+                        result('0');
+                    } else {
+                        {debugProtectionFunctionName}();
+                    }
                 }
-            })();
+            )();
         })();
     `;
 }

+ 1 - 1
src/templates/debug-protection-nodes/debug-protection-function-node/DebuggerTemplateNoEval.ts

@@ -4,7 +4,7 @@
 export function DebuggerTemplateNoEval (): string {
     return `
         if (typeof counter === 'string') {
-            var func = function () {
+            const func = function () {
                 while (true) {}
             };
             

+ 23 - 23
src/templates/domain-lock-nodes/domain-lock-node/DomainLockNodeTemplate.ts

@@ -3,17 +3,17 @@
  */
 export function DomainLockNodeTemplate (): string {
     return `
-        var {domainLockFunctionName} = {singleNodeCallControllerFunctionName}(this, function () {
+        const {domainLockFunctionName} = {singleNodeCallControllerFunctionName}(this, function () {
             
             {globalVariableTemplate}
             
-            var func = function () {
+            const func = function () {
                 return {
                     key: 'item',
                     value: 'attribute',
                     getAttribute: function () {
-                        for (var i = 0; i < 1000; i--) {
-                            var isPositive = i > 0;
+                        for (let i = 0; i < 1000; i--) {
+                            const isPositive = i > 0;
                             
                             switch (isPositive) {
                                 case true:
@@ -26,14 +26,14 @@ export function DomainLockNodeTemplate (): string {
                 };
             };
                         
-            var regExp = new RegExp("[{diff}]", "g");
-            var domains = "{domains}".replace(regExp, "").split(";");
-            var document;
-            var domain;
-            var location;
-            var hostname;
+            const regExp = new RegExp("[{diff}]", "g");
+            const domains = "{domains}".replace(regExp, "").split(";");
+            let document;
+            let domain;
+            let location;
+            let hostname;
 
-            for (var d in that) {
+            for (let d in that) {
                 if (d.length == 8 && d.charCodeAt(7) == 116 && d.charCodeAt(5) == 101 && d.charCodeAt(3) == 117 && d.charCodeAt(0) == 100) {
                     document = d;
                 
@@ -41,7 +41,7 @@ export function DomainLockNodeTemplate (): string {
                 }
             }
 
-            for (var d1 in that[document]) {
+            for (let d1 in that[document]) {
                 if (d1.length == 6 && d1.charCodeAt(5) == 110 && d1.charCodeAt(0) == 100) {
                     domain = d1;
                     
@@ -50,7 +50,7 @@ export function DomainLockNodeTemplate (): string {
             }
 
             if (!("~" > domain)) {
-                for (var d2 in that[document]) {
+                for (let d2 in that[document]) {
                     if (d2.length == 8 && d2.charCodeAt(7) == 110 && d2.charCodeAt(0) == 108) {
                         location = d2;
                         
@@ -58,7 +58,7 @@ export function DomainLockNodeTemplate (): string {
                     }
                 }
 
-                for (var d3 in that[document][location]) {
+                for (let d3 in that[document][location]) {
                     if (d3.length == 8 && d3.charCodeAt(7) == 101 && d3.charCodeAt(0) == 104) {
                         hostname = d3;
                         
@@ -71,21 +71,21 @@ export function DomainLockNodeTemplate (): string {
                 return;
             }
             
-            var documentDomain = that[document][domain];
-            var documentLocationHostName = !!that[document][location] && that[document][location][hostname];
-            var currentDomain = documentDomain || documentLocationHostName;
+            const documentDomain = that[document][domain];
+            const documentLocationHostName = !!that[document][location] && that[document][location][hostname];
+            const currentDomain = documentDomain || documentLocationHostName;
           
             if (!currentDomain) {
                 return;
             }
           
-            var ok = false;
+            let ok = false;
                         
-            for (var i = 0; i < domains.length; i++) {
-                var domain = domains[i];
-                var position = currentDomain.length - domain.length;
-                var lastIndex = currentDomain.indexOf(domain, position);
-                var endsWith = lastIndex !== -1 && lastIndex === position;
+            for (let i = 0; i < domains.length; i++) {
+                const domain = domains[i];
+                const position = currentDomain.length - domain.length;
+                const lastIndex = currentDomain.indexOf(domain, position);
+                const endsWith = lastIndex !== -1 && lastIndex === position;
                 
                 if (endsWith) {
                     if (currentDomain.length == domain.length || domain.indexOf(".") === 0) {

+ 10 - 10
src/templates/self-defending-nodes/self-defending-unicode-node/SelfDefendingTemplate.ts

@@ -9,38 +9,38 @@ import { IEscapeSequenceEncoder } from '../../../interfaces/utils/IEscapeSequenc
  */
 export function SelfDefendingTemplate (escapeSequenceEncoder: IEscapeSequenceEncoder): string {
     return `
-        var {selfDefendingFunctionName} = {singleNodeCallControllerFunctionName}(this, function () {
-            var func1 = function(){return 'dev';},
+        const {selfDefendingFunctionName} = {singleNodeCallControllerFunctionName}(this, function () {
+            const func1 = function(){return 'dev';},
                 func2 = function () {
                     return 'window';
                 };
                 
-            var test1 = function () {
-                var regExp = new RegExp('${
+            const test1 = function () {
+                const regExp = new RegExp('${
                     escapeSequenceEncoder.encode(`\\w+ *\\(\\) *{\\w+ *['|"].+['|"];? *}`, true)
                 }');
                 
                 return !regExp.test(func1.toString());
             };
             
-            var test2 = function () {
-                var regExp = new RegExp('${
+            const test2 = function () {
+                const regExp = new RegExp('${
                     escapeSequenceEncoder.encode(`(\\\\[x|u](\\w){2,4})+`, true)
                 }');
                 
                 return regExp.test(func2.toString());
             };
             
-            var recursiveFunc1 = function (string) {
-                var i = ~-1 >> 1 + 255 % 0;
+            const recursiveFunc1 = function (string) {
+                const i = ~-1 >> 1 + 255 % 0;
                                 
                 if (string.indexOf('i' === i)) {
                     recursiveFunc2(string)
                 }
             };
             
-            var recursiveFunc2 = function (string) {
-                var i = ~-4 >> 1 + 255 % 0;
+            const recursiveFunc2 = function (string) {
+                const i = ~-4 >> 1 + 255 % 0;
                 
                 if (string.indexOf((true+"")[3]) !== i) {
                     recursiveFunc1(string)

+ 4 - 4
src/templates/string-array-nodes/string-array-calls-wrapper/SelfDefendingTemplate.ts

@@ -23,7 +23,7 @@ export function SelfDefendingTemplate (
     const stateResultIdentifier: string = randomGenerator.getRandomString(identifierLength);
     
     return `
-        var StatesClass = function (${rc4BytesIdentifier}) {
+        const StatesClass = function (${rc4BytesIdentifier}) {
             this.${rc4BytesIdentifier} = ${rc4BytesIdentifier};
             this.${statesIdentifier} = [1, 0, 0];
             this.${newStateIdentifier} = function(){return 'newState';};
@@ -36,8 +36,8 @@ export function SelfDefendingTemplate (
         };
         
         StatesClass.prototype.${checkStateIdentifier} = function () {
-            var regExp = new RegExp(this.${firstStateIdentifier} + this.${secondStateIdentifier});
-            var expression = regExp.test(this.${newStateIdentifier}.toString())
+            const regExp = new RegExp(this.${firstStateIdentifier} + this.${secondStateIdentifier});
+            const expression = regExp.test(this.${newStateIdentifier}.toString())
                 ? --this.${statesIdentifier}[1]
                 : --this.${statesIdentifier}[0];
             
@@ -53,7 +53,7 @@ export function SelfDefendingTemplate (
         };
 
         StatesClass.prototype.${getStateIdentifier} = function (${rc4BytesIdentifier}) {
-            for (var i = 0, len = this.${statesIdentifier}.length; i < len; i++) {
+            for (let i = 0, len = this.${statesIdentifier}.length; i < len; i++) {
                 this.${statesIdentifier}.push(Math.round(Math.random()));
                 len = this.${statesIdentifier}.length;
             }

+ 4 - 4
src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayBase64DecodeNodeTemplate.ts

@@ -18,10 +18,10 @@ export function StringArrayBase64DecodeNodeTemplate (
             {atobPolyfill}
             
             {stringArrayCallsWrapperName}.${base64DecodeFunctionIdentifier} = function (str) {
-                var string = atob(str);
-                var newStringChars = [];
+                const string = atob(str);
+                let newStringChars = [];
                 
-                for (var i = 0, length = string.length; i < length; i++) {
+                for (let i = 0, length = string.length; i < length; i++) {
                     newStringChars += '%' + ('00' + string.charCodeAt(i).toString(16)).slice(-2);
                 }
                 
@@ -33,7 +33,7 @@ export function StringArrayBase64DecodeNodeTemplate (
             {stringArrayCallsWrapperName}.${initializedIdentifier} = true;
         }
                   
-        var cachedValue = {stringArrayCallsWrapperName}.${dataIdentifier}[index];
+        const cachedValue = {stringArrayCallsWrapperName}.${dataIdentifier}[index];
                         
         if (cachedValue === undefined) {
             {selfDefendingCode}

+ 2 - 2
src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayCallsWrapperTemplate.ts

@@ -3,10 +3,10 @@
  */
 export function StringArrayCallsWrapperTemplate (): string {
     return `
-        var {stringArrayCallsWrapperName} = function (index, key) {
+        const {stringArrayCallsWrapperName} = function (index, key) {
             index = index - 0;
             
-            var value = {stringArrayName}[index];
+            let value = {stringArrayName}[index];
             
             {decodeNodeTemplate}
         

+ 1 - 1
src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayRC4DecodeNodeTemplate.ts

@@ -26,7 +26,7 @@ export function StringArrayRc4DecodeNodeTemplate (
             {stringArrayCallsWrapperName}.${initializedIdentifier} = true;
         }
   
-        var cachedValue = {stringArrayCallsWrapperName}.${dataIdentifier}[index];
+        const cachedValue = {stringArrayCallsWrapperName}.${dataIdentifier}[index];
 
         if (cachedValue === undefined) {
             if ({stringArrayCallsWrapperName}.${onceIdentifier} === undefined) {

+ 1 - 1
src/templates/string-array-nodes/string-array-node/StringArrayTemplate.ts

@@ -3,6 +3,6 @@
  */
 export function StringArrayTemplate (): string {
     return `
-        var {stringArrayName} = [{stringArray}];
+        const {stringArrayName} = [{stringArray}];
     `;
 }

+ 13 - 14
src/templates/string-array-nodes/string-array-rotate-function-node/SelfDefendingTemplate.ts

@@ -8,8 +8,8 @@ import { IEscapeSequenceEncoder } from '../../../interfaces/utils/IEscapeSequenc
  */
 export function SelfDefendingTemplate (escapeSequenceEncoder: IEscapeSequenceEncoder): string {
     return `
-        var selfDefendingFunc = function () {
-            var object = {
+        const selfDefendingFunc = function () {
+            const object = {
                 data: {
                     key: 'cookie',
                     value: 'timeout'
@@ -17,16 +17,15 @@ export function SelfDefendingTemplate (escapeSequenceEncoder: IEscapeSequenceEnc
                 setCookie: function (options, name, value, document) {
                     document = document || {};
                     
-                    var updatedCookie = name + "=" + value;
-
-                    var i = 0;
+                    let updatedCookie = name + "=" + value;
+                    let i = 0;
                                                             
-                    for (var i = 0, len = options.length; i < len; i++) {
-                        var propName = options[i];
+                    for (let i = 0, len = options.length; i < len; i++) {
+                        const propName = options[i];
                                      
                         updatedCookie += "; " + propName;
                         
-                        var propValue = options[propName];
+                        const propValue = options[propName];
                         
                         options.push(propValue);
                         len = options.length;
@@ -41,11 +40,11 @@ export function SelfDefendingTemplate (escapeSequenceEncoder: IEscapeSequenceEnc
                 removeCookie: function(){return 'dev';},
                 getCookie: function (document, name) {
                     document = document || function (value) { return value };
-                    var matches = document(new RegExp(
+                    const matches = document(new RegExp(
                         "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
                     ));
                     
-                    var func = function (param1, param2) {
+                    const func = function (param1, param2) {
                         param1(++param2);
                     };
                     
@@ -55,8 +54,8 @@ export function SelfDefendingTemplate (escapeSequenceEncoder: IEscapeSequenceEnc
                 }
             };
             
-            var test1 = function () {
-                var regExp = new RegExp('${
+            const test1 = function () {
+                const regExp = new RegExp('${
                     escapeSequenceEncoder.encode(`\\w+ *\\(\\) *{\\w+ *['|"].+['|"];? *}`, true)
                 }');
                 
@@ -65,8 +64,8 @@ export function SelfDefendingTemplate (escapeSequenceEncoder: IEscapeSequenceEnc
             
             object['updateCookie'] = test1;
             
-            var cookie = '';
-            var result = object['updateCookie']();
+            let cookie = '';
+            const result = object['updateCookie']();
                                     
             if (!result) {
                 object['setCookie'](['*'], 'counter', 1);

+ 1 - 1
src/templates/string-array-nodes/string-array-rotate-function-node/StringArrayRotateFunctionTemplate.ts

@@ -4,7 +4,7 @@
 export function StringArrayRotateFunctionTemplate (): string {
     return `
         (function (array, {timesName}) {
-            var {whileFunctionName} = function (times) {
+            const {whileFunctionName} = function (times) {
                 while (--times) {
                     array['push'](array['shift']());
                 }

+ 3 - 0
src/types/TInitialData.d.ts

@@ -0,0 +1,3 @@
+import { IInitializable } from '../interfaces/IInitializable';
+
+export type TInitialData <TClass extends IInitializable> = Parameters<TClass['initialize']>;

+ 3 - 1
src/types/container/custom-nodes/TControlFlowCustomNodeFactory.d.ts

@@ -2,4 +2,6 @@ import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 
 import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCustomNode';
 
-export type TControlFlowCustomNodeFactory = (controlFlowCustomNodeName: ControlFlowCustomNode) => ICustomNode;
+export type TControlFlowCustomNodeFactory = <
+    TInitialData extends any[] = any[]
+> (controlFlowCustomNodeName: ControlFlowCustomNode) => ICustomNode<TInitialData>;

+ 3 - 1
src/types/container/custom-nodes/TCustomNodeFactory.d.ts

@@ -2,4 +2,6 @@ import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 
 import { CustomNode } from '../../../enums/custom-nodes/CustomNode';
 
-export type TCustomNodeFactory = (customNodeName: CustomNode) => ICustomNode;
+export type TCustomNodeFactory = <
+    TInitialData extends any[] = any[]
+> (customNodeName: CustomNode) => ICustomNode<TInitialData>;

+ 3 - 1
src/types/container/custom-nodes/TDeadNodeInjectionCustomNodeFactory.d.ts

@@ -2,4 +2,6 @@ import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 
 import { DeadCodeInjectionCustomNode } from '../../../enums/custom-nodes/DeadCodeInjectionCustomNode';
 
-export type TDeadNodeInjectionCustomNodeFactory = (deadCodeInjectionCustomNodeName: DeadCodeInjectionCustomNode) => ICustomNode;
+export type TDeadNodeInjectionCustomNodeFactory = <
+    TInitialData extends any[] = any[]
+> (deadCodeInjectionCustomNodeName: DeadCodeInjectionCustomNode) => ICustomNode <TInitialData>;

+ 3 - 1
src/types/container/custom-nodes/TObjectExpressionKeysTransformerCustomNodeFactory.d.ts

@@ -2,4 +2,6 @@ import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 
 import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
 
-export type TObjectExpressionKeysTransformerCustomNodeFactory = (objectExpressionKeysTransformerNodeName: ObjectExpressionKeysTransformerCustomNode) => ICustomNode;
+export type TObjectExpressionKeysTransformerCustomNodeFactory = <
+    TInitialData extends any[] = any[]
+> (objectExpressionKeysTransformerNodeName: ObjectExpressionKeysTransformerCustomNode) => ICustomNode <TInitialData>;

+ 31 - 0
src/utils/ArrayUtils.ts

@@ -34,6 +34,37 @@ export class ArrayUtils implements IArrayUtils {
         return range;
     }
 
+    /**
+     * @param {T[]} array
+     * @returns {T | null}
+     */
+    public findMostOccurringElement <T extends string | number> (array: T[]): T | null {
+        const arrayLength: number = array.length;
+
+        if (!arrayLength) {
+            return null;
+        }
+
+        const elementsMap: Partial<{[key in T]: number}> = {};
+
+        let mostOccurringElement: T = array[0];
+        let mostOccurringElementCount: number = 1;
+
+        for (const element of array) {
+            const currentElementCount: number = elementsMap[element] ?? 0;
+            const updatedElementCount: number = currentElementCount + 1;
+
+            if (updatedElementCount > mostOccurringElementCount) {
+                mostOccurringElement = element;
+                mostOccurringElementCount = updatedElementCount;
+            }
+
+            elementsMap[element] = updatedElementCount;
+        }
+
+        return mostOccurringElement;
+    }
+
     /**
      * @param {T[]} array
      * @param {number} times

+ 53 - 0
src/utils/TemplateFormatter.ts

@@ -0,0 +1,53 @@
+import { inject, injectable } from 'inversify';
+import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
+
+import * as ESTree from 'estree';
+import format from 'string-template';
+
+import { TObject } from '../types/TObject';
+
+import { IPrevailingKindOfVariablesAnalyzer } from '../interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
+import { ITemplateFormatter } from '../interfaces/utils/ITemplateFormatter';
+
+@injectable()
+export class TemplateFormatter implements ITemplateFormatter {
+    /**
+     * @type {IPrevailingKindOfVariablesAnalyzer}
+     */
+    private readonly prevailingKindOfVariablesAnalyzer: IPrevailingKindOfVariablesAnalyzer;
+
+    /**
+     * @param {IPrevailingKindOfVariablesAnalyzer} prevailingKindOfVariablesAnalyzer
+     */
+    constructor (
+        @inject(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer)
+            prevailingKindOfVariablesAnalyzer: IPrevailingKindOfVariablesAnalyzer,
+    ) {
+        this.prevailingKindOfVariablesAnalyzer = prevailingKindOfVariablesAnalyzer;
+    }
+
+    public format <TMapping extends TObject> (
+        template: string,
+        mapping: TMapping
+    ): string {
+        const formattedTemplate: string = format(template, mapping);
+        const prevailingKindOfVariables: ESTree.VariableDeclaration['kind'] =
+            this.prevailingKindOfVariablesAnalyzer.getPrevailingKind();
+
+        return this.replaceKindOfVariablesToPrevailingKind(formattedTemplate, prevailingKindOfVariables);
+    }
+
+    /**
+     * @param {string} template
+     * @param {ESTree.VariableDeclaration['kind']} prevailingKindOfVariables
+     * @returns {string}
+     */
+    private replaceKindOfVariablesToPrevailingKind (
+        template: string,
+        prevailingKindOfVariables: ESTree.VariableDeclaration['kind']
+    ): string {
+        return prevailingKindOfVariables === 'var'
+            ? template.replace(/(?<!\w)(?:let|const)(?!\w)/g, prevailingKindOfVariables)
+            : template;
+    }
+}

+ 4 - 7
test/dev/dev.ts

@@ -7,16 +7,13 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            (function () {
-                var a = 'a', b = 'b';
-                ({[a]: a, [b]: b} = {a: 1, b: 2});
-            
-                console.log(a, b);
-            })();
+            var foo = 'abcd'
+            var bar = 'abcde'
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
-            transformObjectKeys: true
+            stringArray: true,
+            stringArrayThreshold: 1
         }
     ).getObfuscatedCode();
 

+ 134 - 0
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -15,6 +15,7 @@ import { IdentifierNamesGenerator } from '../../../src/enums/generators/identifi
 import { buildLargeCode } from '../../helpers/buildLargeCode';
 import { getRegExpMatch } from '../../helpers/getRegExpMatch';
 import { readFileAsString } from '../../helpers/readFileAsString';
+import { TInputOptions } from '../../../src/types/options/TInputOptions';
 
 describe('JavaScriptObfuscator', () => {
     describe('obfuscate', () => {
@@ -710,5 +711,138 @@ describe('JavaScriptObfuscator', () => {
                 assert.equal(areCollisionsExists, false);
             });
         });
+
+        describe('Prevailing kind of variables', () => {
+            const baseParams: TInputOptions = {
+                compact: true,
+                controlFlowFlattening: true,
+                controlFlowFlatteningThreshold: 1,
+                deadCodeInjection: true,
+                deadCodeInjectionThreshold: 1,
+                debugProtection: true,
+                debugProtectionInterval: true,
+                disableConsoleOutput: false,
+                rotateStringArray: true,
+                selfDefending: true,
+                stringArray: true,
+                stringArrayThreshold: 1,
+                transformObjectKeys: true,
+                unicodeEscapeSequence: false
+            };
+
+            describe('`var` kind', function () {
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            ...baseParams,
+                            stringArrayEncoding: StringArrayEncoding.Rc4
+                        }
+                    ).getObfuscatedCode();
+
+                });
+
+                it('does not break on run', () => {
+                    assert.doesNotThrow(() => eval(obfuscatedCode));
+                });
+            });
+
+            describe('`const` kind', function () {
+                describe('Variant #1: StringArrayEncoding: rc4', () => {
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                ...baseParams,
+                                stringArrayEncoding: StringArrayEncoding.Rc4
+                            }
+                        ).getObfuscatedCode();
+
+                    });
+
+                    it('does not break on run', () => {
+                        assert.doesNotThrow(() => eval(obfuscatedCode));
+                    });
+                });
+
+                describe('Variant #2: StringArrayEncoding: base64', () => {
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                ...baseParams,
+                                stringArrayEncoding: StringArrayEncoding.Rc4
+                            }
+                        ).getObfuscatedCode();
+
+                    });
+
+                    it('does not break on run', () => {
+                        assert.doesNotThrow(() => eval(obfuscatedCode));
+                    });
+                });
+            });
+
+            describe('`let` kind', function () {
+                describe('Variant #1: StringArrayEncoding: rc4', () => {
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                ...baseParams,
+                                stringArrayEncoding: StringArrayEncoding.Rc4
+                            }
+                        ).getObfuscatedCode();
+
+                    });
+
+                    it('does not break on run', () => {
+                        assert.doesNotThrow(() => eval(obfuscatedCode));
+                    });
+                });
+
+                describe('Variant #2: StringArrayEncoding: base64', () => {
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                ...baseParams,
+                                stringArrayEncoding: StringArrayEncoding.Rc4
+                            }
+                        ).getObfuscatedCode();
+
+                    });
+
+                    it('does not break on run', () => {
+                        assert.doesNotThrow(() => eval(obfuscatedCode));
+                    });
+                });
+            });
+        });
     });
 });

+ 32 - 0
test/functional-tests/javascript-obfuscator/fixtures/prevailing-kind-of-variables-const.js

@@ -0,0 +1,32 @@
+const foo = 'foo';
+const bar = 'bar';
+const baz = 'baz';
+const bark = 'bark';
+const hawk = 'hawk';
+
+function test() {
+    var foo = 'foo';
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    return foo;
+}
+test();

+ 32 - 0
test/functional-tests/javascript-obfuscator/fixtures/prevailing-kind-of-variables-let.js

@@ -0,0 +1,32 @@
+let foo = 'foo';
+let bar = 'bar';
+let baz = 'baz';
+let bark = 'bark';
+let hawk = 'hawk';
+
+function test() {
+    var foo = 'foo';
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    return foo;
+}
+test();

+ 32 - 0
test/functional-tests/javascript-obfuscator/fixtures/prevailing-kind-of-variables-var.js

@@ -0,0 +1,32 @@
+var foo = 'foo';
+var bar = 'bar';
+var baz = 'baz';
+var bark = 'bark';
+var hawk = 'hawk';
+
+function test() {
+    var foo = 'foo';
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    if (true) {
+        foo += 'o';
+    }
+
+    return foo;
+}
+test();

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

@@ -650,5 +650,124 @@ describe('BlockStatementControlFlowTransformer', function () {
                 assert.match(obfuscatedCode, returnStatementRegExp);
             });
         });
+
+        describe('Prevailing kind of variables', () => {
+            describe('`var` kind', () => {
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                describe('switch-case map', () => {
+                    const switchCaseMapVariableRegExp: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4,6}\['.*'\]\['split'\]\('\|'\)/;
+                    const switchCaseMapStringRegExp: RegExp = /var *_0x(?:[a-f0-9]){4,6} *= *\{'.*' *: *'(.*)'\};/;
+                    const expectedSwitchCasesSequence: string[] = ['0', '1', '2', '3', '4'];
+
+                    let switchCaseMap: string[];
+
+                    before(() => {
+                        const switchCaseMapMatch: string = getRegExpMatch(obfuscatedCode, switchCaseMapStringRegExp);
+
+                        switchCaseMap = switchCaseMapMatch.split('|').sort();
+                    });
+
+                    it('should use correct kind of variable for switch-case map', () => {
+                        assert.match(obfuscatedCode, switchCaseMapVariableRegExp);
+                    });
+
+                    it('should use correct kind of variable for switch cases sequence', () => {
+                        assert.deepEqual(switchCaseMap, expectedSwitchCasesSequence);
+                    });
+                });
+            });
+
+            describe('`const` kind', () => {
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                describe('switch-case map', () => {
+                    const switchCaseMapVariableRegExp: RegExp = /const *_0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4,6}\['.*'\]\['split'\]\('\|'\)/;
+                    const switchCaseMapStringRegExp: RegExp = /const *_0x(?:[a-f0-9]){4,6} *= *\{'.*' *: *'(.*)'\};/;
+                    const expectedSwitchCasesSequence: string[] = ['0', '1', '2', '3', '4'];
+
+                    let switchCaseMap: string[];
+
+                    before(() => {
+                        const switchCaseMapMatch: string = getRegExpMatch(obfuscatedCode, switchCaseMapStringRegExp);
+
+                        switchCaseMap = switchCaseMapMatch.split('|').sort();
+                    });
+
+                    it('should use correct kind of variable for switch-case map', () => {
+                        assert.match(obfuscatedCode, switchCaseMapVariableRegExp);
+                    });
+
+                    it('should use correct kind of variable for switch cases sequence', () => {
+                        assert.deepEqual(switchCaseMap, expectedSwitchCasesSequence);
+                    });
+                });
+            });
+
+            describe('`let` kind', () => {
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                describe('switch-case map', () => {
+                    const switchCaseMapVariableRegExp: RegExp = /let *_0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4,6}\['.*'\]\['split'\]\('\|'\)/;
+                    const switchCaseMapStringRegExp: RegExp = /let *_0x(?:[a-f0-9]){4,6} *= *\{'.*' *: *'(.*)'\};/;
+                    const expectedSwitchCasesSequence: string[] = ['0', '1', '2', '3', '4'];
+
+                    let switchCaseMap: string[];
+
+                    before(() => {
+                        const switchCaseMapMatch: string = getRegExpMatch(obfuscatedCode, switchCaseMapStringRegExp);
+
+                        switchCaseMap = switchCaseMapMatch.split('|').sort();
+                    });
+
+                    it('should use correct kind of variable for switch-case map', () => {
+                        assert.match(obfuscatedCode, switchCaseMapVariableRegExp);
+                    });
+
+                    it('should use correct kind of variable for switch cases sequence', () => {
+                        assert.deepEqual(switchCaseMap, expectedSwitchCasesSequence);
+                    });
+                });
+            });
+        });
     });
 });

+ 9 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/prevailing-kind-of-variables-const.js

@@ -0,0 +1,9 @@
+const test = 0;
+
+(function () {
+    console.log(1);
+    console.log(2);
+    console.log(3);
+    console.log(4);
+    console.log(5);
+})();

+ 9 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/prevailing-kind-of-variables-let.js

@@ -0,0 +1,9 @@
+let test = 0;
+
+(function () {
+    console.log(1);
+    console.log(2);
+    console.log(3);
+    console.log(4);
+    console.log(5);
+})();

+ 9 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/prevailing-kind-of-variables-var.js

@@ -0,0 +1,9 @@
+var test = 0;
+
+(function () {
+    console.log(1);
+    console.log(2);
+    console.log(3);
+    console.log(4);
+    console.log(5);
+})();

+ 71 - 0
test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/FunctionControlFlowTransformer.spec.ts

@@ -268,5 +268,76 @@ describe('FunctionControlFlowTransformer', function () {
                 });
             });
         });
+
+        describe('prevailing kind of variables', () => {
+            describe('Variant #1 - `var` kind', () => {
+                const regexp: RegExp = new RegExp(`var *${variableMatch} *= *\\{`);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should use correct kind of variables for `control flow storage`', () => {
+                    assert.match(obfuscatedCode, regexp);
+                });
+            });
+
+            describe('Variant #2 - `const` kind', () => {
+                const regexp: RegExp = new RegExp(`const *${variableMatch} *= *\\{`);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should use correct kind of variables for `control flow storage`', () => {
+                    assert.match(obfuscatedCode, regexp);
+                });
+            });
+
+            describe('Variant #3 - `let` kind', () => {
+                const regexp: RegExp = new RegExp(`let *${variableMatch} *= *\\{`);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should use correct kind of variables for `control flow storage`', () => {
+                    assert.match(obfuscatedCode, regexp);
+                });
+            });
+        });
     });
 });

+ 3 - 0
test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/fixtures/prevailing-kind-of-variables-const.js

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

+ 3 - 0
test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/fixtures/prevailing-kind-of-variables-let.js

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

+ 3 - 0
test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/fixtures/prevailing-kind-of-variables-var.js

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

+ 86 - 3
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts

@@ -316,7 +316,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 const match: string = `` +
                     `var *${variableMatch} *= *{};` +
                     `${variableMatch}\\['foo'] *= *'foo';` +
-                    `const *${variableMatch} *= *${variableMatch}, *` +
+                    `var *${variableMatch} *= *${variableMatch}, *` +
                     `${variableMatch} *= *${variableMatch}\\['foo'];` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -344,7 +344,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 const match: string = `` +
                     `var *${variableMatch} *= *{};` +
                     `${variableMatch}\\['foo'] *= *'foo';` +
-                    `const *${variableMatch} *= *${variableMatch}, *` +
+                    `var *${variableMatch} *= *${variableMatch}, *` +
                     `${variableMatch} *= *\\[${variableMatch}\\['foo']];` +
                 ``;
                 const regExp: RegExp = new RegExp(match);
@@ -959,7 +959,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 const match: string = `` +
                     `var *${variableMatch} *= *{};` +
                     `${variableMatch}\\['bar'] *= *'bar';` +
-                    `const *${variableMatch} *= *{}, *` +
+                    `var *${variableMatch} *= *{}, *` +
                         `${variableMatch} *= *${variableMatch}, *` +
                         `${variableMatch} *= *${variableMatch}\\['bar']; *` +
                     `${variableMatch}\\['foo'] *= *'foo';` +
@@ -988,6 +988,89 @@ describe('ObjectExpressionKeysTransformer', () => {
         });
     });
 
+    describe('prevailing kind of variables', () => {
+        describe('Variant #1: `var` kind`', () => {
+            const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should use correct kind of variables', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #2: `const` kind`', () => {
+            const match: string = `` +
+                `const *${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should use correct kind of variables', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #3: `let` kind`', () => {
+            const match: string = `` +
+                `let *${variableMatch} *= *{};` +
+                `${variableMatch}\\['foo'] *= *'bar';` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should use correct kind of variables', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+    });
+
     describe('Ignore transformation', () => {
         describe('Variant #1: disabled option', () => {
             const match: string = `` +

+ 6 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/prevailing-kind-of-variables-const.js

@@ -0,0 +1,6 @@
+(function(){
+    const object = {
+        foo: 'bar',
+        baz: 'bark'
+    };
+})();

+ 6 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/prevailing-kind-of-variables-let.js

@@ -0,0 +1,6 @@
+(function(){
+    let object = {
+        foo: 'bar',
+        baz: 'bark'
+    };
+})();

+ 6 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/prevailing-kind-of-variables-var.js

@@ -0,0 +1,6 @@
+(function(){
+    var object = {
+        foo: 'bar',
+        baz: 'bark'
+    };
+})();

+ 1 - 1
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-3.js

@@ -1,5 +1,5 @@
 (function () {
-    const object1 = {foo: 'foo'},
+    var object1 = {foo: 'foo'},
         object2 = {bar: 'bar'},
         variable = object2.bar;
     console.log(variable);

+ 1 - 1
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-4.js

@@ -1,4 +1,4 @@
 (function () {
-    const object = {foo: 'foo'},
+    var object = {foo: 'foo'},
         variable = object.foo;
 })();

+ 1 - 1
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-5.js

@@ -1,4 +1,4 @@
 (function () {
-    const object = {foo: 'foo'},
+    var object = {foo: 'foo'},
         variable = [object.foo];
 })();

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