Przeglądaj źródła

Auto detection of prevailing kind of variables

sanex3339 5 lat temu
rodzic
commit
3a1cc4e4f5
87 zmienionych plików z 1257 dodań i 223 usunięć
  1. 0 0
      dist/index.cli.js
  2. 0 0
      dist/index.js
  3. 61 0
      src/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.ts
  4. 2 0
      src/container/ServiceIdentifiers.ts
  5. 7 0
      src/container/modules/analyzers/AnalyzersModule.ts
  6. 5 0
      src/container/modules/custom-nodes/CustomNodesModule.ts
  7. 7 0
      src/container/modules/utils/UtilsModule.ts
  8. 10 1
      src/custom-nodes/AbstractCustomNode.ts
  9. 5 4
      src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts
  10. 4 1
      src/custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode.ts
  11. 37 20
      src/custom-nodes/control-flow-flattening-nodes/BlockStatementControlFlowFlatteningNode.ts
  12. 4 1
      src/custom-nodes/control-flow-flattening-nodes/CallExpressionFunctionNode.ts
  13. 4 1
      src/custom-nodes/control-flow-flattening-nodes/LogicalExpressionFunctionNode.ts
  14. 4 1
      src/custom-nodes/control-flow-flattening-nodes/StringLiteralNode.ts
  15. 4 1
      src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/CallExpressionControlFlowStorageCallNode.ts
  16. 25 8
      src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts
  17. 4 1
      src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ExpressionWithOperatorControlFlowStorageCallNode.ts
  18. 4 1
      src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode.ts
  19. 4 1
      src/custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode.ts
  20. 5 4
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts
  21. 5 4
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts
  22. 5 4
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts
  23. 5 4
      src/custom-nodes/domain-lock-nodes/DomainLockNode.ts
  24. 6 5
      src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts
  25. 29 10
      src/custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode.ts
  26. 5 4
      src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts
  27. 9 8
      src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts
  28. 5 4
      src/custom-nodes/string-array-nodes/StringArrayNode.ts
  29. 6 5
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  30. 2 2
      src/interfaces/analyzers/IAnalyzer.d.ts
  31. 1 1
      src/interfaces/analyzers/calls-graph-analyzer/ICallsGraphAnalyzer.d.ts
  32. 15 0
      src/interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer.d.ts
  33. 6 0
      src/interfaces/utils/IArrayUtils.d.ts
  34. 13 0
      src/interfaces/utils/ITemplateFormatter.d.ts
  35. 11 0
      src/node-transformers/preparing-transformers/CustomNodesTransformer.ts
  36. 4 1
      src/node/NodeUtils.ts
  37. 7 5
      src/templates/AtobTemplate.ts
  38. 1 1
      src/templates/GlobalVariableNoEvalTemplate.ts
  39. 2 2
      src/templates/GlobalVariableTemplate1.ts
  40. 3 3
      src/templates/GlobalVariableTemplate2.ts
  41. 7 5
      src/templates/Rc4Template.ts
  42. 4 4
      src/templates/SingleNodeCallControllerTemplate.ts
  43. 3 3
      src/templates/console-output-nodes/console-output-disable-expression-node/ConsoleOutputDisableExpressionTemplate.ts
  44. 14 11
      src/templates/debug-protection-nodes/debug-protection-function-call-node/DebugProtectionFunctionCallTemplate.ts
  45. 1 1
      src/templates/debug-protection-nodes/debug-protection-function-node/DebuggerTemplateNoEval.ts
  46. 23 23
      src/templates/domain-lock-nodes/domain-lock-node/DomainLockNodeTemplate.ts
  47. 10 10
      src/templates/self-defending-nodes/self-defending-unicode-node/SelfDefendingTemplate.ts
  48. 4 4
      src/templates/string-array-nodes/string-array-calls-wrapper/SelfDefendingTemplate.ts
  49. 4 4
      src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayBase64DecodeNodeTemplate.ts
  50. 2 2
      src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayCallsWrapperTemplate.ts
  51. 1 1
      src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayRC4DecodeNodeTemplate.ts
  52. 1 1
      src/templates/string-array-nodes/string-array-node/StringArrayTemplate.ts
  53. 13 14
      src/templates/string-array-nodes/string-array-rotate-function-node/SelfDefendingTemplate.ts
  54. 1 1
      src/templates/string-array-nodes/string-array-rotate-function-node/StringArrayRotateFunctionTemplate.ts
  55. 31 0
      src/utils/ArrayUtils.ts
  56. 53 0
      src/utils/TemplateFormatter.ts
  57. 4 7
      test/dev/dev.ts
  58. 88 0
      test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts
  59. 5 0
      test/functional-tests/javascript-obfuscator/fixtures/prevailing-kind-of-variables-const.js
  60. 5 0
      test/functional-tests/javascript-obfuscator/fixtures/prevailing-kind-of-variables-var.js
  61. 80 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/BlockStatementControlFlowTransformer.spec.ts
  62. 9 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/prevailing-kind-of-variables-const.js
  63. 9 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/prevailing-kind-of-variables-var.js
  64. 48 0
      test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/FunctionControlFlowTransformer.spec.ts
  65. 3 0
      test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/fixtures/prevailing-kind-of-variables-1.js
  66. 3 0
      test/functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/fixtures/prevailing-kind-of-variables-2.js
  67. 59 3
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts
  68. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/prevailing-kind-of-variables-1.js
  69. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/prevailing-kind-of-variables-2.js
  70. 1 1
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-3.js
  71. 1 1
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-4.js
  72. 1 1
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-with-object-call-5.js
  73. 3 3
      test/functional-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/identifier-obfuscating-replacers/BaseIdentifierObfuscatingReplacer.spec.ts
  74. 0 0
      test/functional-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/identifier-obfuscating-replacers/fixtures/simple-input.js
  75. 0 2
      test/functional-tests/templates/debug-protection-nodes/DebugProtectionFunctionCallTemplate.spec.ts
  76. 64 11
      test/functional-tests/templates/string-array-nodes/string-array-calls-wrapper-node-template/StringArrayCallsWrapperNodeTemplate.spec.ts
  77. 1 0
      test/functional-tests/templates/string-array-nodes/string-array-calls-wrapper-node-template/fixtures/prevailing-kind-of-variables-const.js
  78. 1 0
      test/functional-tests/templates/string-array-nodes/string-array-calls-wrapper-node-template/fixtures/prevailing-kind-of-variables-var.js
  79. 62 0
      test/functional-tests/templates/string-array-nodes/string-array-rotate-function-template/StringArrayRotateFunctionTemplate.spec.ts
  80. 1 0
      test/functional-tests/templates/string-array-nodes/string-array-rotate-function-template/fixtures/prevailing-kind-of-variables-const.js
  81. 1 0
      test/functional-tests/templates/string-array-nodes/string-array-rotate-function-template/fixtures/prevailing-kind-of-variables-var.js
  82. 60 0
      test/functional-tests/templates/string-array-nodes/string-array-template/StringArrayTemplate.spec.ts
  83. 1 0
      test/functional-tests/templates/string-array-nodes/string-array-template/fixtures/prevailing-kind-of-variables-const.js
  84. 1 0
      test/functional-tests/templates/string-array-nodes/string-array-template/fixtures/prevailing-kind-of-variables-var.js
  85. 5 2
      test/index.spec.ts
  86. 172 0
      test/unit-tests/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.spec.ts
  87. 49 0
      test/unit-tests/utils/ArrayUtils.spec.ts

Plik diff jest za duży
+ 0 - 0
dist/index.cli.js


Plik diff jest za duży
+ 0 - 0
dist/index.js


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

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

@@ -160,7 +160,9 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
             .getConstructorFactory<ControlFlowCustomNode, ICustomNode>(
                 ServiceIdentifiers.Newable__ICustomNode,
                 ServiceIdentifiers.Factory__IIdentifierNamesGenerator,
+                ServiceIdentifiers.ITemplateFormatter,
                 ServiceIdentifiers.IRandomGenerator,
+                ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer,
                 ServiceIdentifiers.IOptions
             ));
 
@@ -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,7 +183,9 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
             .getConstructorFactory<ObjectExpressionKeysTransformerCustomNode, ICustomNode>(
                 ServiceIdentifiers.Newable__ICustomNode,
                 ServiceIdentifiers.Factory__IIdentifierNamesGenerator,
+                ServiceIdentifiers.ITemplateFormatter,
                 ServiceIdentifiers.IRandomGenerator,
+                ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer,
                 ServiceIdentifiers.IOptions
             ));
 

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

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

@@ -8,6 +8,7 @@ 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';
@@ -42,18 +43,26 @@ 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;
     }

+ 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

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

+ 5 - 4
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,6 +34,7 @@ export class DomainLockNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {ICryptUtils} cryptUtils
      * @param {IOptions} options
@@ -42,11 +42,12 @@ export class DomainLockNode extends AbstractCustomNode {
     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
     ) {
-        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,

+ 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];
     }

+ 5 - 4
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,6 +34,7 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
@@ -42,11 +42,12 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode {
     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
     ) {
-        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
             }),

+ 9 - 8
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,6 +49,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
@@ -57,11 +57,12 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
     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
     ) {
-        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()
         });

+ 6 - 5
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,6 +42,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {ITemplateFormatter} templateFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
@@ -50,11 +50,12 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
     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
     ) {
-        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,

+ 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'];
+}

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

+ 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']());
                 }

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

+ 88 - 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,92 @@ 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));
+                    });
+                });
+            });
+        });
     });
 });

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

@@ -0,0 +1,5 @@
+function test() {
+    const foo = 'foo';
+    return foo;
+}
+test();

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

@@ -0,0 +1,5 @@
+function test() {
+    var foo = 'foo';
+    return foo;
+}
+test();

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

@@ -650,5 +650,85 @@ 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 create switch-case map variable', () => {
+                        assert.match(obfuscatedCode, switchCaseMapVariableRegExp);
+                    });
+
+                    it('should create valid switch-case map variable with order of 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 create switch-case map variable', () => {
+                        assert.match(obfuscatedCode, switchCaseMapVariableRegExp);
+                    });
+
+                    it('should create valid switch-case map variable with order of 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-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);
+})();

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

@@ -268,5 +268,53 @@ 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-1.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should add `control flow storage` node to the obfuscated code', () => {
+                    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-2.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            controlFlowFlattening: true,
+                            controlFlowFlatteningThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should add `control flow storage` node to the obfuscated code', () => {
+                    assert.match(obfuscatedCode, regexp);
+                });
+            });
+        });
     });
 });

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

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

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

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

+ 59 - 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,62 @@ 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-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should correctly transform object keys', () => {
+                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-2.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should correctly transform object keys', () => {
+                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-1.js

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

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

@@ -0,0 +1,6 @@
+(function(){
+    const 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];
 })();

+ 3 - 3
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/obfuscating-replacers/identifier-obfuscating-replacers/BaseIdentifierObfuscatingReplacer.spec.ts → test/functional-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/identifier-obfuscating-replacers/BaseIdentifierObfuscatingReplacer.spec.ts

@@ -1,10 +1,10 @@
 import { assert } from 'chai';
 
-import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../../src/options/presets/NoCustomNodes';
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
 
-import { readFileAsString } from '../../../../../../helpers/readFileAsString';
+import { readFileAsString } from '../../../../../helpers/readFileAsString';
 
-import { JavaScriptObfuscator } from '../../../../../../../src/JavaScriptObfuscatorFacade';
+import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscatorFacade';
 
 describe('BaseIdentifierObfuscatingReplacer', () => {
     describe('Base rule', () => {

+ 0 - 0
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/obfuscating-replacers/identifier-obfuscating-replacers/fixtures/simple-input.js → test/functional-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/identifier-obfuscating-replacers/fixtures/simple-input.js


+ 0 - 2
test/functional-tests/templates/debug-protection-nodes/DebugProtectionFunctionCallTemplate.spec.ts

@@ -10,8 +10,6 @@ import { ObfuscationTarget } from '../../../../src/enums/ObfuscationTarget';
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
 
-
-
 async function spawnThread(obfuscatedCode: string, threadCallback: Function, timeoutCallback: Function): Promise<void> {
     const evaluationWorker = await spawn(new Worker('./workers/evaluation-worker'));
 

+ 64 - 11
test/functional-tests/templates/string-array-nodes/StringArrayCallsWrapperNodeTemplate.spec.ts → test/functional-tests/templates/string-array-nodes/string-array-calls-wrapper-node-template/StringArrayCallsWrapperNodeTemplate.spec.ts

@@ -4,20 +4,25 @@ import format from 'string-template';
 
 import { assert } from 'chai';
 
-import { ServiceIdentifiers } from '../../../../src/container/ServiceIdentifiers';
+import { ServiceIdentifiers } from '../../../../../src/container/ServiceIdentifiers';
 
-import { ICryptUtils } from '../../../../src/interfaces/utils/ICryptUtils';
-import { IInversifyContainerFacade } from '../../../../src/interfaces/container/IInversifyContainerFacade';
-import { IRandomGenerator } from '../../../../src/interfaces/utils/IRandomGenerator';
+import { ICryptUtils } from '../../../../../src/interfaces/utils/ICryptUtils';
+import { IInversifyContainerFacade } from '../../../../../src/interfaces/container/IInversifyContainerFacade';
+import { IObfuscatedCode } from '../../../../../src/interfaces/source-code/IObfuscatedCode';
+import { IRandomGenerator } from '../../../../../src/interfaces/utils/IRandomGenerator';
 
-import { AtobTemplate } from '../../../../src/templates/AtobTemplate';
-import { GlobalVariableTemplate1 } from '../../../../src/templates/GlobalVariableTemplate1';
-import { Rc4Template } from '../../../../src/templates/Rc4Template';
-import { StringArrayBase64DecodeNodeTemplate } from '../../../../src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayBase64DecodeNodeTemplate';
-import { StringArrayCallsWrapperTemplate } from '../../../../src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayCallsWrapperTemplate';
-import { StringArrayRc4DecodeNodeTemplate } from '../../../../src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayRC4DecodeNodeTemplate';
+import { AtobTemplate } from '../../../../../src/templates/AtobTemplate';
+import { GlobalVariableTemplate1 } from '../../../../../src/templates/GlobalVariableTemplate1';
+import { Rc4Template } from '../../../../../src/templates/Rc4Template';
+import { StringArrayBase64DecodeNodeTemplate } from '../../../../../src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayBase64DecodeNodeTemplate';
+import { StringArrayCallsWrapperTemplate } from '../../../../../src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayCallsWrapperTemplate';
+import { StringArrayRc4DecodeNodeTemplate } from '../../../../../src/templates/string-array-nodes/string-array-calls-wrapper/StringArrayRC4DecodeNodeTemplate';
 
-import { InversifyContainerFacade } from '../../../../src/container/InversifyContainerFacade';
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
+
+import { InversifyContainerFacade } from '../../../../../src/container/InversifyContainerFacade';
+import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
+import { readFileAsString } from '../../../../helpers/readFileAsString';
 
 describe('StringArrayCallsWrapperNodeTemplate', () => {
     const stringArrayName: string = 'stringArrayName';
@@ -111,4 +116,52 @@ describe('StringArrayCallsWrapperNodeTemplate', () => {
             assert.deepEqual(decodedValue, expectedDecodedValue);
         });
     });
+
+    describe('Prevailing kind of variables', () => {
+        describe('`var` kind', () => {
+            let obfuscatedCode: string,
+                stringArrayCallsWrapperRegExp: RegExp = /var (_0x(\w){4}) *= *function/;
+
+            beforeEach(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
+                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                );
+
+                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+            });
+
+            it('Should return correct variable kind for string array calls wrapper', () => {
+                assert.match(obfuscatedCode, stringArrayCallsWrapperRegExp);
+            });
+        });
+
+        describe('`const` kind', () => {
+            let obfuscatedCode: string,
+                stringArrayCallsWrapperRegExp: RegExp = /const (_0x(\w){4}) *= *function/;
+
+            beforeEach(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
+                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                );
+
+                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+            });
+
+            it('Should return correct variable kind for string array calls wrapper', () => {
+                assert.match(obfuscatedCode, stringArrayCallsWrapperRegExp);
+            });
+        });
+    });
 });

+ 1 - 0
test/functional-tests/templates/string-array-nodes/string-array-calls-wrapper-node-template/fixtures/prevailing-kind-of-variables-const.js

@@ -0,0 +1 @@
+const foo = 'foo';

+ 1 - 0
test/functional-tests/templates/string-array-nodes/string-array-calls-wrapper-node-template/fixtures/prevailing-kind-of-variables-var.js

@@ -0,0 +1 @@
+var foo = 'foo';

+ 62 - 0
test/functional-tests/templates/string-array-nodes/string-array-rotate-function-template/StringArrayRotateFunctionTemplate.spec.ts

@@ -0,0 +1,62 @@
+import 'reflect-metadata';
+
+import { assert } from 'chai';
+
+import { IObfuscatedCode } from '../../../../../src/interfaces/source-code/IObfuscatedCode';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
+
+import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
+import { readFileAsString } from '../../../../helpers/readFileAsString';
+
+describe('StringArrayRotateFunctionTemplate', () => {
+    describe('Prevailing kind of variables', () => {
+        describe('`var` kind', () => {
+            let obfuscatedCode: string,
+                stringArrayRotateFunctionRegExp: RegExp = /function\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\){var _0x([a-f0-9]){4,6} *= *function/;
+
+            beforeEach(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
+                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1,
+                        rotateStringArray: true
+                    }
+                );
+
+                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+            });
+
+            it('Should return correct variable kind for string array rotate function', () => {
+                assert.match(obfuscatedCode, stringArrayRotateFunctionRegExp);
+            });
+        });
+
+        describe('`const` kind', () => {
+            let obfuscatedCode: string,
+                stringArrayRotateFunctionRegExp: RegExp = /function\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\){const _0x([a-f0-9]){4,6} *= *function/;
+
+            beforeEach(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
+                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1,
+                        rotateStringArray: true
+                    }
+                );
+
+                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+            });
+
+            it('Should return correct variable kind for string array rotate function', () => {
+                assert.match(obfuscatedCode, stringArrayRotateFunctionRegExp);
+            });
+        });
+    });
+});

+ 1 - 0
test/functional-tests/templates/string-array-nodes/string-array-rotate-function-template/fixtures/prevailing-kind-of-variables-const.js

@@ -0,0 +1 @@
+const foo = 'foo';

+ 1 - 0
test/functional-tests/templates/string-array-nodes/string-array-rotate-function-template/fixtures/prevailing-kind-of-variables-var.js

@@ -0,0 +1 @@
+var foo = 'foo';

+ 60 - 0
test/functional-tests/templates/string-array-nodes/string-array-template/StringArrayTemplate.spec.ts

@@ -0,0 +1,60 @@
+import 'reflect-metadata';
+
+import { assert } from 'chai';
+
+import { IObfuscatedCode } from '../../../../../src/interfaces/source-code/IObfuscatedCode';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
+
+import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
+import { readFileAsString } from '../../../../helpers/readFileAsString';
+
+describe('StringArrayTemplate', () => {
+    describe('Prevailing kind of variables', () => {
+        describe('`var` kind', () => {
+            let obfuscatedCode: string,
+                stringArrayRegExp: RegExp = /var (_0x(\w){4}) *= *\['.*'];/;
+
+            beforeEach(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
+                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                );
+
+                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+            });
+
+            it('Should return correct variable kind for string array', () => {
+                assert.match(obfuscatedCode, stringArrayRegExp);
+            });
+        });
+
+        describe('`const` kind', () => {
+            let obfuscatedCode: string,
+                stringArrayRegExp: RegExp = /const (_0x(\w){4}) *= *\['.*'];/;
+
+            beforeEach(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
+                const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                );
+
+                obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
+            });
+
+            it('Should return correct variable kind for string array', () => {
+                assert.match(obfuscatedCode, stringArrayRegExp);
+            });
+        });
+    });
+});

+ 1 - 0
test/functional-tests/templates/string-array-nodes/string-array-template/fixtures/prevailing-kind-of-variables-const.js

@@ -0,0 +1 @@
+const foo = 'foo';

+ 1 - 0
test/functional-tests/templates/string-array-nodes/string-array-template/fixtures/prevailing-kind-of-variables-var.js

@@ -0,0 +1 @@
+var foo = 'foo';

+ 5 - 2
test/index.spec.ts

@@ -6,6 +6,7 @@ require('source-map-support').install();
  * Unit tests
  */
 import './unit-tests/analyzers/calls-graph-analyzer/CallsGraphAnalyzer.spec';
+import './unit-tests/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.spec';
 import './unit-tests/cli/sanitizers/ArraySanitizer.spec';
 import './unit-tests/cli/sanitizers/BooleanSanitizer.spec';
 import './unit-tests/cli/sanitizers/IdentifierNamesGeneratorSanitizer.spec';
@@ -75,7 +76,7 @@ import './functional-tests/node-transformers/obfuscating-transformers/function-t
 import './functional-tests/node-transformers/obfuscating-transformers/impot-declaration-transformer/ImportDeclarationTransformer.spec';
 import './functional-tests/node-transformers/obfuscating-transformers/labeled-statement-transformer/LabeledStatementTransformer.spec';
 import './functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec';
-import './functional-tests/node-transformers/obfuscating-transformers/literal-transformer/obfuscating-replacers/identifier-obfuscating-replacers/BaseIdentifierObfuscatingReplacer.spec';
+import './functional-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/identifier-obfuscating-replacers/BaseIdentifierObfuscatingReplacer.spec';
 import './functional-tests/node-transformers/obfuscating-transformers/variable-declaration-transformer/VariableDeclarationTransformer.spec';
 import './functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/EvalCallExpressionTransformer.spec';
 import './functional-tests/node-transformers/preparing-transformers/comments-transformer/CommentsTransformer.spec';
@@ -86,7 +87,9 @@ import './functional-tests/options/OptionsNormalizer.spec';
 import './functional-tests/templates/debug-protection-nodes/DebugProtectionFunctionCallTemplate.spec';
 import './functional-tests/templates/domain-lock-nodes/DomainLockNodeTemplate.spec';
 import './functional-tests/templates/GlobalVariableNoEvalTemplate.spec';
-import './functional-tests/templates/string-array-nodes/StringArrayCallsWrapperNodeTemplate.spec';
+import './functional-tests/templates/string-array-nodes/string-array-template/StringArrayTemplate.spec';
+import './functional-tests/templates/string-array-nodes/string-array-calls-wrapper-node-template/StringArrayCallsWrapperNodeTemplate.spec';
+import './functional-tests/templates/string-array-nodes/string-array-rotate-function-template/StringArrayRotateFunctionTemplate.spec';
 
 /**
  * Performance tests

+ 172 - 0
test/unit-tests/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.spec.ts

@@ -0,0 +1,172 @@
+import 'reflect-metadata';
+
+import { assert } from 'chai';
+import * as ESTree from 'estree';
+
+import { ServiceIdentifiers } from '../../../../src/container/ServiceIdentifiers';
+
+import { IInversifyContainerFacade } from '../../../../src/interfaces/container/IInversifyContainerFacade';
+import { IPrevailingKindOfVariablesAnalyzer } from '../../../../src/interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
+
+import { InversifyContainerFacade } from '../../../../src/container/InversifyContainerFacade';
+import { NodeFactory } from '../../../../src/node/NodeFactory';
+
+describe('PrevailingKindOfVariablesAnalyzer', () => {
+    let prevailingKindOfVariablesAnalyzer: IPrevailingKindOfVariablesAnalyzer;
+
+    before(() => {
+        const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
+
+        inversifyContainerFacade.load('', '', {});
+        prevailingKindOfVariablesAnalyzer = inversifyContainerFacade
+            .get<IPrevailingKindOfVariablesAnalyzer>(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer);
+    });
+
+    describe('analyze', () => {
+        let prevailingKindOfVariables: ESTree.VariableDeclaration['kind'];
+
+        describe('Prevailing `var` kind', () => {
+            const expectedPrevailingKind: ESTree.VariableDeclaration['kind'] = 'var';
+
+            before(() => {
+                const astTree: ESTree.Program = NodeFactory.programNode([
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('foo'),
+                                null
+                            )
+                        ],
+                        'var'
+                    ),
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('bar'),
+                                null
+                            )
+                        ],
+                        'const'
+                    ),
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('baz'),
+                                null
+                            )
+                        ],
+                        'var'
+                    )
+                ]);
+
+                prevailingKindOfVariablesAnalyzer.analyze(astTree);
+                prevailingKindOfVariables = prevailingKindOfVariablesAnalyzer.getPrevailingKind();
+            });
+
+            it('should return correct prevailing kind of variables', () => {
+                assert.equal(prevailingKindOfVariables, expectedPrevailingKind);
+            });
+        });
+
+        describe('Prevailing `let` kind', () => {
+            const expectedPrevailingKind: ESTree.VariableDeclaration['kind'] = 'let';
+
+            before(() => {
+                const astTree: ESTree.Program = NodeFactory.programNode([
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('foo'),
+                                null
+                            )
+                        ],
+                        'let'
+                    ),
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('bar'),
+                                null
+                            )
+                        ],
+                        'var'
+                    ),
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('baz'),
+                                null
+                            )
+                        ],
+                        'let'
+                    )
+                ]);
+
+                prevailingKindOfVariablesAnalyzer.analyze(astTree);
+                prevailingKindOfVariables = prevailingKindOfVariablesAnalyzer.getPrevailingKind();
+            });
+
+            it('should return correct prevailing kind of variables', () => {
+                assert.equal(prevailingKindOfVariables, expectedPrevailingKind);
+            });
+        });
+
+        describe('Prevailing `const` kind', () => {
+            const expectedPrevailingKind: ESTree.VariableDeclaration['kind'] = 'const';
+
+            before(() => {
+                const astTree: ESTree.Program = NodeFactory.programNode([
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('foo'),
+                                null
+                            )
+                        ],
+                        'let'
+                    ),
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('bar'),
+                                null
+                            )
+                        ],
+                        'const'
+                    ),
+                    NodeFactory.variableDeclarationNode(
+                        [
+                            NodeFactory.variableDeclaratorNode(
+                                NodeFactory.identifierNode('baz'),
+                                null
+                            )
+                        ],
+                        'const'
+                    )
+                ]);
+
+                prevailingKindOfVariablesAnalyzer.analyze(astTree);
+                prevailingKindOfVariables = prevailingKindOfVariablesAnalyzer.getPrevailingKind();
+            });
+
+            it('should return correct prevailing kind of variables', () => {
+                assert.equal(prevailingKindOfVariables, expectedPrevailingKind);
+            });
+        });
+
+        describe('No variables', () => {
+            const expectedPrevailingKind: ESTree.VariableDeclaration['kind'] = 'var';
+
+            before(() => {
+                const astTree: ESTree.Program = NodeFactory.programNode();
+
+                prevailingKindOfVariablesAnalyzer.analyze(astTree);
+                prevailingKindOfVariables = prevailingKindOfVariablesAnalyzer.getPrevailingKind();
+            });
+
+            it('should return correct prevailing kind of variables', () => {
+                assert.equal(prevailingKindOfVariables, expectedPrevailingKind);
+            });
+        });
+    });
+});

+ 49 - 0
test/unit-tests/utils/ArrayUtils.spec.ts

@@ -1,3 +1,5 @@
+import 'reflect-metadata';
+
 import { assert } from 'chai';
 
 import { ServiceIdentifiers } from '../../../src/container/ServiceIdentifiers';
@@ -64,6 +66,53 @@ describe('ArrayUtils', () => {
         });
     });
 
+    describe('findMostOccurringElement', () => {
+        describe('empty array', () => {
+            const array: string[] = [];
+            const expectedMostOccurringElement: null = null;
+
+            let mostOccurringElement: string | null;
+
+            before(() => {
+                mostOccurringElement = arrayUtils.findMostOccurringElement(array);
+            });
+
+            it('should return null if array is empty', () => {
+                assert.equal(mostOccurringElement, expectedMostOccurringElement);
+            });
+        });
+
+        describe('one elements is most occurring', () => {
+            const array: string[] = ['foo', 'bar', 'bar', 'baz', 'bar', 'foo'];
+            const expectedMostOccurringElement: string = 'bar';
+
+            let mostOccurringElement: string | null;
+
+            before(() => {
+                mostOccurringElement = arrayUtils.findMostOccurringElement(array);
+            });
+
+            it('should return most occurring element', () => {
+                assert.equal(mostOccurringElement, expectedMostOccurringElement);
+            });
+        });
+
+        describe('few elements are most occurring', () => {
+            const array: string[] = ['foo', 'bar', 'bar', 'baz', 'bar'];
+            const expectedMostOccurringElement: string = 'bar';
+
+            let mostOccurringElement: string | null;
+
+            before(() => {
+                mostOccurringElement = arrayUtils.findMostOccurringElement(array);
+            });
+
+            it('should return first most occurring element', () => {
+                assert.equal(mostOccurringElement, expectedMostOccurringElement);
+            });
+        });
+    });
+
     describe('rotate', () => {
         let array: number[],
             rotatedArray: number[];

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików