Просмотр исходного кода

Merge pull request #811 from javascript-obfuscator/string-array-call-index-type

New option: `stringArrayIndexesType` accepts an array of types of string array call indexes
Timofey Kachalov 4 лет назад
Родитель
Сommit
962cbc250e
43 измененных файлов с 690 добавлено и 208 удалено
  1. 2 0
      CHANGELOG.md
  2. 24 1
      README.md
  3. 0 0
      dist/index.browser.js
  4. 0 0
      dist/index.cli.js
  5. 0 0
      dist/index.js
  6. 8 0
      src/cli/JavaScriptObfuscatorCLI.ts
  7. 2 0
      src/container/ServiceIdentifiers.ts
  8. 22 1
      src/container/modules/custom-nodes/CustomNodesModule.ts
  9. 38 7
      src/custom-nodes/string-array-nodes/AbstractStringArrayCallNode.ts
  10. 6 1
      src/custom-nodes/string-array-nodes/StringArrayCallNode.ts
  11. 6 1
      src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode.ts
  12. 6 1
      src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode.ts
  13. 40 0
      src/custom-nodes/string-array-nodes/string-array-index-nodes/AbstractStringArrayIndexNode.ts
  14. 36 0
      src/custom-nodes/string-array-nodes/string-array-index-nodes/StringArrayHexadecimalNumberIndexNode.ts
  15. 36 0
      src/custom-nodes/string-array-nodes/string-array-index-nodes/StringArrayHexadecimalNumericStringIndexNode.ts
  16. 4 0
      src/enums/custom-nodes/string-array-index-nodes/StringArrayIndexNode.ts
  17. 9 0
      src/enums/node-transformers/string-array-transformers/StringArrayIndexesType.ts
  18. 5 0
      src/interfaces/custom-nodes/string-array-nodes/IStringArrayIndexNode.ts
  19. 2 0
      src/interfaces/options/IOptions.ts
  20. 1 1
      src/node-transformers/preparing-transformers/EvalCallExpressionTransformer.ts
  21. 12 12
      src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts
  22. 11 0
      src/options/Options.ts
  23. 4 0
      src/options/presets/Default.ts
  24. 4 0
      src/options/presets/NoCustomNodes.ts
  25. 5 0
      src/types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory.ts
  26. 5 0
      src/types/options/TStringArrayIndexesType.ts
  27. 5 0
      test/dev/dev.ts
  28. 4 4
      test/functional-tests/generators/identifier-names-generators/dictionary-identifier-names-generator/DictionaryIdentifierNamesGenerator.spec.ts
  29. 6 6
      test/functional-tests/generators/identifier-names-generators/mangled-identifier-names-generator/MangledIdentifierNamesGenerator.spec.ts
  30. 8 3
      test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts
  31. 2 2
      test/functional-tests/node-transformers/converting-transformers/member-expression-transformer/MemberExpressionTransformer.spec.ts
  32. 2 2
      test/functional-tests/node-transformers/converting-transformers/method-definition-transformer/MethodDefinitionTransformer.spec.ts
  33. 14 14
      test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts
  34. 7 0
      test/functional-tests/node-transformers/finalizing-transformers/escape-sequence-transformer/EscapeSequenceTransformer.spec.ts
  35. 60 22
      test/functional-tests/node-transformers/preparing-transformers/eval-call-expression-transformer/EvalCallExpressionTransformer.spec.ts
  36. 1 1
      test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/black-list-obfuscating-guard/BlackListObfuscatingGuard.spec.ts
  37. 1 1
      test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/force-transform-string-obfuscating-guard/ForceTransformStringObfuscatingGuard.spec.ts
  38. 3 3
      test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/ignored-require-import-obfuscating-guard/IgnoredRequireImportObfuscatingGuard.spec.ts
  39. 2 2
      test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/VariablePreserveTransformer.spec.ts
  40. 108 57
      test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/StringArrayScopeCallsWrapperTransformer.spec.ts
  41. 151 43
      test/functional-tests/node-transformers/string-array-transformers/string-array-transformer/StringArrayTransformer.spec.ts
  42. 23 23
      test/functional-tests/storages/string-array-transformers/string-array-storage/StringArrayStorage.spec.ts
  43. 5 0
      test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts

+ 2 - 0
CHANGELOG.md

@@ -2,6 +2,8 @@ Change Log
 
 v2.9.0
 ---
+* New option: `stringArrayIndexesType` accepts an array of types of string array call indexes
+* Changed default type of all string array call indexes from `hexadecimal-numeric-string` to `hexadecimal-number`
 * New option: `stringArrayIndexShift` enables additional index shift for all string array calls
 
 v2.8.1

+ 24 - 1
README.md

@@ -364,6 +364,9 @@ Following options are available for the JS Obfuscator:
     splitStrings: false,
     splitStringsChunkLength: 10,
     stringArray: true,
+    stringArrayCallsIndexType: [
+        'hexadecimal-number'
+    ],
     stringArrayEncoding: [],
     stringArrayIndexShift: true,
     stringArrayWrappersCount: 1,
@@ -418,6 +421,7 @@ Following options are available for the JS Obfuscator:
     --split-strings <boolean>
     --split-strings-chunk-length <number>
     --string-array <boolean>
+    --string-array-calls-index-type '<list>' (comma separated) [hexadecimal-number, hexadecimal-numeric-string]
     --string-array-encoding '<list>' (comma separated) [none, base64, rc4]
     --string-array-index-shift <boolean>
     --string-array-wrappers-count <number>
@@ -962,7 +966,7 @@ Sets chunk length of [`splitStrings`](#splitstrings) option.
 Type: `boolean` Default: `true`
 
 Removes string literals and place them in a special array. For instance, the string `"Hello World"` in `var m = "Hello World";` will be replaced with something like `var m = _0x12c456[0x1];`
-    
+
 ### `stringArrayEncoding`
 Type: `string[]` Default: `[]`
 
@@ -989,6 +993,25 @@ stringArrayEncoding: [
 ]
 ```
 
+### `stringArrayIndexesType`
+Type: `string[]` Default: `['hexadecimal-number']`
+
+##### :warning: `stringArray` option must be enabled
+
+Allows to control the type of string array call indexes.
+
+Each `stringArray` call index will be transformed by the randomly picked type from the passed list. This makes possible to use multiple types.
+
+Available values:
+* `'hexadecimal-number'` (`default`): transforms string array call indexes as hexadecimal numbers
+* `'hexadecimal-numeric-string'`: transforms string array call indexes as hexadecimal numeric string
+
+Before `2.9.0` release `javascript-obfuscator` transformed all string array call indexes with `hexadecimal-numeric-string` type. This makes some manual deobfuscation slightly harder but it allows easy detection of these calls by automatic deobfuscators.
+
+The new `hexadecimal-number` type approaches to make harder auto-detect of string array call patterns in the code.
+
+More types will be added in the future.
+
 ### `stringArrayIndexShift`
 Type: `boolean` Default: `true`
 

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/index.browser.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/index.cli.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/index.js


+ 8 - 0
src/cli/JavaScriptObfuscatorCLI.ts

@@ -30,6 +30,7 @@ import { Logger } from '../logger/Logger';
 import { ObfuscatedCodeWriter } from './utils/ObfuscatedCodeWriter';
 import { SourceCodeReader } from './utils/SourceCodeReader';
 import { Utils } from '../utils/Utils';
+import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 
 export class JavaScriptObfuscatorCLI implements IInitializable {
     /**
@@ -350,6 +351,13 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 `Default: ${StringArrayEncoding.None}`,
                 ArraySanitizer
             )
+            .option(
+                '--string-array-indexes-type <list> (comma separated, without whitespaces)',
+                'Encodes each string in strings array using base64 or rc4 (this option can slow down your code speed). ' +
+                `Values: ${CLIUtils.stringifyOptionAvailableValues(StringArrayIndexesType)}. ` +
+                `Default: ${StringArrayIndexesType.HexadecimalNumber}`,
+                ArraySanitizer
+            )
             .option(
                 '--string-array-index-shift <boolean>',
                 'Enables additional index shift for all string array calls',

+ 2 - 0
src/container/ServiceIdentifiers.ts

@@ -13,6 +13,7 @@ export enum ServiceIdentifiers {
     Factory__IObjectExpressionKeysTransformerCustomNode = 'Factory<IObjectExpressionKeysTransformerCustomNode>',
     Factory__IObjectExpressionExtractor = 'Factory<IObjectExpressionExtractor>',
     Factory__IStringArrayCustomNode = 'Factory<IStringArrayCustomNode>',
+    Factory__IStringArrayIndexNode = 'Factory<IStringArrayIndexNode>',
     Factory__TControlFlowStorage = 'Factory<TControlFlowStorage>',
     IArrayUtils = 'IArrayUtils',
     ICalleeDataExtractor = 'ICalleeDataExtractor',
@@ -50,6 +51,7 @@ export enum ServiceIdentifiers {
     IScopeIdentifiersTraverser = 'IScopeIdentifiersTraverser',
     ISourceCode = 'ISourceCode',
     IScopeAnalyzer = 'IScopeAnalyzer',
+    IStringArrayIndexNode = 'IStringArrayIndexNode',
     IStringArrayScopeCallsWrapperLexicalScopeDataStorage = 'IStringArrayScopeCallsWrapperLexicalScopeDataStorage',
     IStringArrayScopeCallsWrapperNamesDataStorage = 'IStringArrayScopeCallsWrapperNamesDataStorage',
     IStringArrayStorage = 'IStringArrayStorage',

+ 22 - 1
src/container/modules/custom-nodes/CustomNodesModule.ts

@@ -3,13 +3,13 @@ import { ContainerModule, interfaces } from 'inversify';
 import { ServiceIdentifiers } from '../../ServiceIdentifiers';
 
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
+import { IStringArrayIndexNode } from '../../../interfaces/custom-nodes/string-array-nodes/IStringArrayIndexNode';
 
 import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCustomNode';
 import { DeadCodeInjectionCustomNode } from '../../../enums/custom-nodes/DeadCodeInjectionCustomNode';
 import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
 import { StringArrayCustomNode } from '../../../enums/custom-nodes/StringArrayCustomNode';
 
-import { ObjectExpressionVariableDeclarationHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode';
 import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode';
 import { BlockStatementControlFlowFlatteningNode } from '../../../custom-nodes/control-flow-flattening-nodes/BlockStatementControlFlowFlatteningNode';
 import { BlockStatementDeadCodeInjectionNode } from '../../../custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode';
@@ -18,7 +18,11 @@ import { CallExpressionFunctionNode } from '../../../custom-nodes/control-flow-f
 import { ControlFlowStorageNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode';
 import { ExpressionWithOperatorControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ExpressionWithOperatorControlFlowStorageCallNode';
 import { LogicalExpressionFunctionNode } from '../../../custom-nodes/control-flow-flattening-nodes/LogicalExpressionFunctionNode';
+import { ObjectExpressionVariableDeclarationHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode';
 import { StringArrayCallNode } from '../../../custom-nodes/string-array-nodes/StringArrayCallNode';
+import { StringArrayHexadecimalNumberIndexNode } from '../../../custom-nodes/string-array-nodes/string-array-index-nodes/StringArrayHexadecimalNumberIndexNode';
+import { StringArrayHexadecimalNumericStringIndexNode } from '../../../custom-nodes/string-array-nodes/string-array-index-nodes/StringArrayHexadecimalNumericStringIndexNode';
+import { StringArrayIndexNode } from '../../../enums/custom-nodes/string-array-index-nodes/StringArrayIndexNode';
 import { StringArrayScopeCallsWrapperFunctionNode } from '../../../custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode';
 import { StringArrayScopeCallsWrapperVariableNode } from '../../../custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode';
 import { StringLiteralControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode';
@@ -85,6 +89,17 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
         .toConstructor(StringArrayScopeCallsWrapperVariableNode)
         .whenTargetNamed(StringArrayCustomNode.StringArrayScopeCallsWrapperVariableNode);
 
+    // string array index nodes
+    bind<IStringArrayIndexNode>(ServiceIdentifiers.IStringArrayIndexNode)
+        .to(StringArrayHexadecimalNumberIndexNode)
+        .inSingletonScope()
+        .whenTargetNamed(StringArrayIndexNode.StringArrayHexadecimalNumberIndexNode);
+
+    bind<IStringArrayIndexNode>(ServiceIdentifiers.IStringArrayIndexNode)
+        .to(StringArrayHexadecimalNumericStringIndexNode)
+        .inSingletonScope()
+        .whenTargetNamed(StringArrayIndexNode.StringArrayHexadecimalNumericStringIndexNode);
+
     // control flow customNode constructor factory
     bind<ICustomNode>(ServiceIdentifiers.Factory__IControlFlowCustomNode)
         .toFactory<ICustomNode>(InversifyContainerFacade
@@ -124,8 +139,14 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
             .getConstructorFactory<StringArrayCustomNode, ICustomNode>(
                 ServiceIdentifiers.Newable__ICustomNode,
                 ServiceIdentifiers.Factory__IIdentifierNamesGenerator,
+                ServiceIdentifiers.Factory__IStringArrayIndexNode,
                 ServiceIdentifiers.ICustomCodeHelperFormatter,
                 ServiceIdentifiers.IRandomGenerator,
                 ServiceIdentifiers.IOptions
             ));
+
+    // string array index node factory
+    bind<IStringArrayIndexNode>(ServiceIdentifiers.Factory__IStringArrayIndexNode)
+        .toFactory<IStringArrayIndexNode>(InversifyContainerFacade
+            .getCacheFactory<StringArrayIndexNode, IStringArrayIndexNode>(ServiceIdentifiers.IStringArrayIndexNode));
 });

+ 38 - 7
src/custom-nodes/string-array-nodes/AbstractStringArrayCallNode.ts

@@ -4,21 +4,39 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 import * as ESTree from 'estree';
 
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
+import { TStringArrayIndexesType } from '../../types/options/TStringArrayIndexesType';
+import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory';
 
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
+import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
+import { StringArrayIndexNode } from '../../enums/custom-nodes/string-array-index-nodes/StringArrayIndexNode';
+
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeMetadata } from '../../node/NodeMetadata';
 import { NodeUtils } from '../../node/NodeUtils';
-import { NumberUtils } from '../../utils/NumberUtils';
 
 @injectable()
 export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
+    /**
+     * @type {Map<TStringArrayIndexesType, StringArrayIndexNode>}
+     */
+    private static readonly stringArrayIndexNodesMap: Map<TStringArrayIndexesType, StringArrayIndexNode> = new Map([
+        [StringArrayIndexesType.HexadecimalNumber, StringArrayIndexNode.StringArrayHexadecimalNumberIndexNode],
+        [StringArrayIndexesType.HexadecimalNumericString, StringArrayIndexNode.StringArrayHexadecimalNumericStringIndexNode]
+    ]);
+
+    /**
+     * @type {TStringArrayIndexNodeFactory}
+     */
+    private readonly stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory;
+
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
@@ -26,6 +44,8 @@ export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
     protected constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
+            stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
@@ -36,26 +56,37 @@ export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
             randomGenerator,
             options
         );
+
+        this.stringArrayIndexNodeFactory = stringArrayIndexNodeFactory;
     }
 
     /**
      * @param {number} index
      * @returns {Expression}
      */
-    protected getHexadecimalNode (index: number): ESTree.Expression {
+    protected getStringArrayIndexNode (index: number): ESTree.Expression {
         const isPositive: boolean = index >= 0;
         const normalizedIndex: number = Math.abs(index);
 
-        const hexadecimalIndex: string = NumberUtils.toHex(normalizedIndex);
-        const hexadecimalLiteralNode: ESTree.Literal = NodeFactory.literalNode(hexadecimalIndex);
+        const stringArrayCallsIndexType: TStringArrayIndexesType = this.randomGenerator
+            .getRandomGenerator()
+            .pickone(this.options.stringArrayIndexesType);
+        const stringArrayIndexNodeName: StringArrayIndexNode | null = AbstractStringArrayCallNode.stringArrayIndexNodesMap.get(stringArrayCallsIndexType) ?? null;
+
+        if (!stringArrayIndexNodeName) {
+            throw new Error('Invalid string array index node name');
+        }
+
+        const stringArrayCallIndexNode: ESTree.Expression = this.stringArrayIndexNodeFactory(stringArrayIndexNodeName)
+            .getNode(normalizedIndex);
 
-        NodeMetadata.set(hexadecimalLiteralNode, { replacedLiteral: true });
+        NodeMetadata.set(stringArrayCallIndexNode, { replacedLiteral: true });
 
         const hexadecimalNode: ESTree.Expression = isPositive
-            ? hexadecimalLiteralNode
+            ? stringArrayCallIndexNode
             : NodeFactory.unaryExpressionNode(
                 '-',
-                hexadecimalLiteralNode
+                stringArrayCallIndexNode
             );
 
         NodeUtils.parentizeAst(hexadecimalNode);

+ 6 - 1
src/custom-nodes/string-array-nodes/StringArrayCallNode.ts

@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
 
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
+import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory';
 
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
@@ -44,6 +45,7 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
@@ -51,12 +53,15 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
     public constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
+            stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(
             identifierNamesGeneratorFactory,
+            stringArrayIndexNodeFactory,
             customCodeHelperFormatter,
             randomGenerator,
             options
@@ -86,7 +91,7 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
      */
     protected getNodeStructure (): TStatement[] {
         const callExpressionArgs: ESTree.Expression[] = [
-            this.getHexadecimalNode(this.indexShiftAmount + this.index)
+            this.getStringArrayIndexNode(this.indexShiftAmount + this.index)
         ];
 
         if (this.decodeKey) {

+ 6 - 1
src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode.ts

@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
 
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
+import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory';
 
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
@@ -39,6 +40,7 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
@@ -46,12 +48,15 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
     public constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
+            stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(
             identifierNamesGeneratorFactory,
+            stringArrayIndexNodeFactory,
             customCodeHelperFormatter,
             randomGenerator,
             options
@@ -101,7 +106,7 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
                             NodeFactory.binaryExpressionNode(
                                 '-',
                                 firstCallArgumentIdentifierNode,
-                                this.getHexadecimalNode(this.shiftedIndex)
+                                this.getStringArrayIndexNode(this.shiftedIndex)
                             ),
                             secondCallArgumentIdentifierNode
                         ]

+ 6 - 1
src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode.ts

@@ -3,6 +3,7 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
+import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory';
 
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
@@ -31,6 +32,7 @@ export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArra
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
@@ -38,12 +40,15 @@ export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArra
     public constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
+            stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
+        @inject(ServiceIdentifiers.IOptions) options: IOptions,
     ) {
         super(
             identifierNamesGeneratorFactory,
+            stringArrayIndexNodeFactory,
             customCodeHelperFormatter,
             randomGenerator,
             options

+ 40 - 0
src/custom-nodes/string-array-nodes/string-array-index-nodes/AbstractStringArrayIndexNode.ts

@@ -0,0 +1,40 @@
+import { inject, injectable } from 'inversify';
+
+import * as ESTree from 'estree';
+
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+import { IStringArrayIndexNode } from '../../../interfaces/custom-nodes/string-array-nodes/IStringArrayIndexNode';
+
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+@injectable()
+export abstract class AbstractStringArrayIndexNode implements IStringArrayIndexNode {
+    /**
+     * @type {IOptions}
+     */
+    protected readonly options: IOptions;
+
+    /**
+     * @type {IRandomGenerator}
+     */
+    protected readonly randomGenerator: IRandomGenerator;
+
+    /**
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     */
+    protected constructor (
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        this.randomGenerator = randomGenerator;
+        this.options = options;
+    }
+
+    /**
+     * @param {number} index
+     * @returns {Expression}
+     */
+    public abstract getNode (index: number): ESTree.Expression;
+}

+ 36 - 0
src/custom-nodes/string-array-nodes/string-array-index-nodes/StringArrayHexadecimalNumberIndexNode.ts

@@ -0,0 +1,36 @@
+import { inject, injectable } from 'inversify';
+
+import * as ESTree from 'estree';
+
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { AbstractStringArrayIndexNode } from './AbstractStringArrayIndexNode';
+import { NodeFactory } from '../../../node/NodeFactory';
+import { NumberUtils } from '../../../utils/NumberUtils';
+
+@injectable()
+export class StringArrayHexadecimalNumberIndexNode extends AbstractStringArrayIndexNode {
+    /**
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     */
+    public constructor (
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(randomGenerator, options);
+    }
+
+    /**
+     * @param {number} index
+     * @returns {Expression}
+     */
+    public getNode (index: number): ESTree.Expression {
+        const hexadecimalIndex: string = NumberUtils.toHex(index);
+
+        return NodeFactory.literalNode(index, hexadecimalIndex);
+    }
+}

+ 36 - 0
src/custom-nodes/string-array-nodes/string-array-index-nodes/StringArrayHexadecimalNumericStringIndexNode.ts

@@ -0,0 +1,36 @@
+import { inject, injectable } from 'inversify';
+
+import * as ESTree from 'estree';
+
+import { IOptions } from '../../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+
+import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
+
+import { AbstractStringArrayIndexNode } from './AbstractStringArrayIndexNode';
+import { NodeFactory } from '../../../node/NodeFactory';
+import { NumberUtils } from '../../../utils/NumberUtils';
+
+@injectable()
+export class StringArrayHexadecimalNumericStringIndexNode extends AbstractStringArrayIndexNode {
+    /**
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     */
+    public constructor (
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(randomGenerator, options);
+    }
+
+    /**
+     * @param {number} index
+     * @returns {Expression}
+     */
+    public getNode (index: number): ESTree.Expression {
+        const hexadecimalIndex: string = NumberUtils.toHex(index);
+
+        return NodeFactory.literalNode(hexadecimalIndex);
+    }
+}

+ 4 - 0
src/enums/custom-nodes/string-array-index-nodes/StringArrayIndexNode.ts

@@ -0,0 +1,4 @@
+export enum StringArrayIndexNode {
+    StringArrayHexadecimalNumberIndexNode = 'StringArrayHexadecimalNumberIndexNode',
+    StringArrayHexadecimalNumericStringIndexNode = 'StringArrayHexadecimalNumericStringIndexNode'
+}

+ 9 - 0
src/enums/node-transformers/string-array-transformers/StringArrayIndexesType.ts

@@ -0,0 +1,9 @@
+import { Utils } from '../../../utils/Utils';
+
+export const StringArrayIndexesType: Readonly<{
+    HexadecimalNumber: 'hexadecimal-number';
+    HexadecimalNumericString: 'hexadecimal-numeric-string';
+}> = Utils.makeEnum({
+    HexadecimalNumber: 'hexadecimal-number',
+    HexadecimalNumericString: 'hexadecimal-numeric-string'
+});

+ 5 - 0
src/interfaces/custom-nodes/string-array-nodes/IStringArrayIndexNode.ts

@@ -0,0 +1,5 @@
+import * as ESTree from 'estree';
+
+export interface IStringArrayIndexNode {
+    getNode: (index: number) => ESTree.Expression;
+}

+ 2 - 0
src/interfaces/options/IOptions.ts

@@ -1,4 +1,5 @@
 import { TOptionsPreset } from '../../types/options/TOptionsPreset';
+import { TStringArrayIndexesType } from '../../types/options/TStringArrayIndexesType';
 import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
 import { TStringArrayWrappersType } from '../../types/options/TStringArrayWrappersType';
 import { TTypeFromEnum } from '../../types/utils/TTypeFromEnum';
@@ -43,6 +44,7 @@ export interface IOptions {
     readonly splitStringsChunkLength: number;
     readonly stringArray: boolean;
     readonly stringArrayEncoding: TStringArrayEncoding[];
+    readonly stringArrayIndexesType: TStringArrayIndexesType[];
     readonly stringArrayIndexShift: boolean;
     readonly stringArrayWrappersChainedCalls: boolean;
     readonly stringArrayWrappersCount: number;

+ 1 - 1
src/node-transformers/preparing-transformers/EvalCallExpressionTransformer.ts

@@ -19,7 +19,7 @@ import { StringUtils } from '../../utils/StringUtils';
 @injectable()
 export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
     /**
-     * @type {NodeTransformer.ParentificationTransformer[]}
+     * @type {NodeTransformer.NodeTransformer[]}
      */
     public readonly runAfter: NodeTransformer[] = [
         NodeTransformer.EscapeSequenceTransformer,

+ 12 - 12
src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts

@@ -125,10 +125,10 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     ): TNodeWithLexicalScopeStatements {
         const stringArrayScopeCallsWrapperNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding | null =
             this.stringArrayScopeCallsWrapperNamesDataStorage.get(lexicalScopeBodyNode) ?? null;
-        const stringArrayScopeCallsWrapperNamesLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData | null =
+        const stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData | null =
             this.stringArrayScopeCallsWrapperLexicalScopeDataStorage.get(lexicalScopeBodyNode) ?? null;
 
-        if (!stringArrayScopeCallsWrapperNamesDataByEncoding || !stringArrayScopeCallsWrapperNamesLexicalScopeData) {
+        if (!stringArrayScopeCallsWrapperNamesDataByEncoding || !stringArrayScopeCallsWrapperLexicalScopeData) {
             return lexicalScopeBodyNode;
         }
 
@@ -153,9 +153,9 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
                 const [
                     upperStringArrayCallsWrapperName,
                     upperStringArrayCallsWrapperShiftedIndex,
-                ] = this.getUpperStringArrayCallsWrapperName(
+                ] = this.getUpperStringArrayCallsWrapperData(
                     stringArrayScopeCallsWrapperNamesData,
-                    stringArrayScopeCallsWrapperNamesLexicalScopeData,
+                    stringArrayScopeCallsWrapperLexicalScopeData,
                 );
 
                 const stringArrayScopeCallsWrapperNode: TStatement[] = this.getStringArrayScopeCallsWrapperNode(
@@ -176,15 +176,15 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
     /**
      * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperNamesLexicalScopeData
+     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperLexicalScopeData
      * @returns {[name: string, index: number]}
      */
     private getRootStringArrayCallsWrapperData (
         stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
-        stringArrayScopeCallsWrapperNamesLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
+        stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
     ): [name: string, index: number] {
         const {encoding} = stringArrayScopeCallsWrapperNamesData;
-        const {resultShiftedIndex} = stringArrayScopeCallsWrapperNamesLexicalScopeData;
+        const {resultShiftedIndex} = stringArrayScopeCallsWrapperLexicalScopeData;
 
         return [
             this.stringArrayStorage.getStorageCallsWrapperName(encoding),
@@ -194,19 +194,19 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
     /**
      * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperNamesLexicalScopeData
+     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperLexicalScopeData
      * @returns {[name: string, index: number]}
      */
-    private getUpperStringArrayCallsWrapperName (
+    private getUpperStringArrayCallsWrapperData (
         stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
-        stringArrayScopeCallsWrapperNamesLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
+        stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
     ): [name: string, index: number] {
         const {encoding} = stringArrayScopeCallsWrapperNamesData;
-        const {scopeShiftedIndex} = stringArrayScopeCallsWrapperNamesLexicalScopeData;
+        const {scopeShiftedIndex} = stringArrayScopeCallsWrapperLexicalScopeData;
 
         const rootStringArrayCallsWrapperData = this.getRootStringArrayCallsWrapperData(
             stringArrayScopeCallsWrapperNamesData,
-            stringArrayScopeCallsWrapperNamesLexicalScopeData
+            stringArrayScopeCallsWrapperLexicalScopeData
         );
 
         if (!this.options.stringArrayWrappersChainedCalls) {

+ 11 - 0
src/options/Options.ts

@@ -20,6 +20,7 @@ import {
 
 import { TInputOptions } from '../types/options/TInputOptions';
 import { TOptionsPreset } from '../types/options/TOptionsPreset';
+import { TStringArrayIndexesType } from '../types/options/TStringArrayIndexesType';
 import { TStringArrayEncoding } from '../types/options/TStringArrayEncoding';
 import { TStringArrayWrappersType } from '../types/options/TStringArrayWrappersType';
 import { TTypeFromEnum } from '../types/utils/TTypeFromEnum';
@@ -31,6 +32,7 @@ import { IdentifierNamesGenerator } from '../enums/generators/identifier-names-g
 import { ObfuscationTarget } from '../enums/ObfuscationTarget';
 import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
+import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
@@ -314,6 +316,15 @@ export class Options implements IOptions {
     @IsIn([StringArrayEncoding.None, StringArrayEncoding.Base64, StringArrayEncoding.Rc4], { each: true })
     public readonly stringArrayEncoding!: TStringArrayEncoding[];
 
+    /**
+     * @type {TStringArrayIndexesType[]}
+     */
+    @IsArray()
+    @ArrayNotEmpty()
+    @ArrayUnique()
+    @IsIn([StringArrayIndexesType.HexadecimalNumber, StringArrayIndexesType.HexadecimalNumericString], { each: true })
+    public readonly stringArrayIndexesType!: TStringArrayIndexesType[];
+
     /**
      * @type {boolean}
      */

+ 4 - 0
src/options/presets/Default.ts

@@ -4,6 +4,7 @@ import { IdentifierNamesGenerator } from '../../enums/generators/identifier-name
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { OptionsPreset } from '../../enums/options/presets/OptionsPreset';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
+import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
@@ -47,6 +48,9 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     stringArrayEncoding: [
         StringArrayEncoding.None
     ],
+    stringArrayIndexesType: [
+        StringArrayIndexesType.HexadecimalNumber
+    ],
     stringArrayIndexShift: true,
     stringArrayWrappersChainedCalls: true,
     stringArrayWrappersCount: 1,

+ 4 - 0
src/options/presets/NoCustomNodes.ts

@@ -5,6 +5,7 @@ import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
 import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
+import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 
 export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     compact: true,
@@ -44,6 +45,9 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     stringArrayEncoding: [
         StringArrayEncoding.None
     ],
+    stringArrayIndexesType: [
+        StringArrayIndexesType.HexadecimalNumber
+    ],
     stringArrayIndexShift: false,
     stringArrayWrappersChainedCalls: false,
     stringArrayWrappersCount: 0,

+ 5 - 0
src/types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory.ts

@@ -0,0 +1,5 @@
+import { IStringArrayIndexNode } from '../../../../interfaces/custom-nodes/string-array-nodes/IStringArrayIndexNode';
+
+import { StringArrayIndexNode } from '../../../../enums/custom-nodes/string-array-index-nodes/StringArrayIndexNode';
+
+export type TStringArrayIndexNodeFactory = (stringArrayIndexNodeName: StringArrayIndexNode) => IStringArrayIndexNode;

+ 5 - 0
src/types/options/TStringArrayIndexesType.ts

@@ -0,0 +1,5 @@
+import { TTypeFromEnum } from '../utils/TTypeFromEnum';
+
+import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
+
+export type TStringArrayIndexesType = TTypeFromEnum<typeof StringArrayIndexesType>;

+ 5 - 0
test/dev/dev.ts

@@ -1,6 +1,7 @@
 'use strict';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
+import { StringArrayIndexesType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
@@ -34,6 +35,10 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
             rotateStringArray: true,
             shuffleStringArray: true,
             stringArray: true,
+            stringArrayIndexesType: [
+                StringArrayIndexesType.HexadecimalNumericString,
+                StringArrayIndexesType.HexadecimalNumber
+            ],
             stringArrayIndexShift: true,
             stringArrayThreshold: 1,
             stringArrayWrappersCount: 2,

+ 4 - 4
test/functional-tests/generators/identifier-names-generators/dictionary-identifier-names-generator/DictionaryIdentifierNamesGenerator.spec.ts

@@ -14,7 +14,7 @@ describe('DictionaryIdentifierNamesGenerator', () => {
         describe('Variant #1: should not generate same name for string array as existing name in code', () => {
             describe('Variant #1: `renameGlobals` option is disabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const a[aB] *= *\['abc'];/;
-                const variableDeclarationIdentifierNameRegExp: RegExp = /const ab *= *a[abAB]\('0x0'\);/;
+                const variableDeclarationIdentifierNameRegExp: RegExp = /const ab *= *a[abAB]\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -46,7 +46,7 @@ describe('DictionaryIdentifierNamesGenerator', () => {
 
             describe('Variant #2: `renameGlobals` option is enabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const a[aB] *= *\['abc'];/;
-                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const a[AB] *= *a[AB]\('0x0'\);/;
+                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const a[AB] *= *a[AB]\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -85,7 +85,7 @@ describe('DictionaryIdentifierNamesGenerator', () => {
 
             describe('Variant #1: `renameGlobals` option is disabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const ([abAB]{1,3}) *= *\['first', *'abc'];/;
-                const variableDeclarationIdentifierNameRegExp: RegExp = /const ([abAB]{1,3}){1,2} *= *[abAB]{1,3}\('0x0'\);/;
+                const variableDeclarationIdentifierNameRegExp: RegExp = /const ([abAB]{1,3}){1,2} *= *[abAB]{1,3}\(0x0\);/;
 
                 let isIdentifiersAreConflicted: boolean = false;
 
@@ -124,7 +124,7 @@ describe('DictionaryIdentifierNamesGenerator', () => {
 
             describe('Variant #2: `renameGlobals` option is enabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const ([abAB]{1,3}) *= *\['first', *'abc'];/;
-                const variableDeclarationIdentifierNameRegExp: RegExp = /const ([abAB]{1,3}){1,2} *= *[abAB]{1,3}\('0x0'\);/;
+                const variableDeclarationIdentifierNameRegExp: RegExp = /const ([abAB]{1,3}){1,2} *= *[abAB]{1,3}\(0x0\);/;
 
                 let isIdentifiersAreConflicted: boolean = false;
 

+ 6 - 6
test/functional-tests/generators/identifier-names-generators/mangled-identifier-names-generator/MangledIdentifierNamesGenerator.spec.ts

@@ -13,7 +13,7 @@ describe('MangledIdentifierNamesGenerator', () => {
         describe('Variant #1: should not generate same name for string array as existing name in code', () => {
             describe('Variant #1: `renameGlobals` option is disabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const ab *= *\['abc'];/;
-                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const aa *= *ac\('0x0'\);/;
+                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const aa *= *ac\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -44,7 +44,7 @@ describe('MangledIdentifierNamesGenerator', () => {
 
             describe('Variant #2: `renameGlobals` option is enabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const ab *= *\['abc'];/;
-                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const aB *= *ac\('0x0'\);/;
+                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const aB *= *ac\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -79,7 +79,7 @@ describe('MangledIdentifierNamesGenerator', () => {
             describe('Variant #1: `renameGlobals` option is disabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const aa *= *\['abc', *'last'];/;
                 const functionDeclarationIdentifierNameRegExp: RegExp = /function foo *\(\) *{/;
-                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const ac *= *ab\('0x1'\);/;
+                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const ac *= *ab\(0x1\);/;
 
                 let obfuscatedCode: string;
 
@@ -115,7 +115,7 @@ describe('MangledIdentifierNamesGenerator', () => {
             describe('Variant #2: `renameGlobals` option is enabled', () => {
                 const stringArrayStorageRegExp: RegExp = /const aa *= *\['abc', *'last'];/;
                 const functionDeclarationIdentifierNameRegExp: RegExp = /function ac *\(\) *{/;
-                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const ad *= *ab\('0x1'\);/;
+                const lastVariableDeclarationIdentifierNameRegExp: RegExp = /const ad *= *ab\(0x1\);/;
 
                 let obfuscatedCode: string;
 
@@ -222,7 +222,7 @@ describe('MangledIdentifierNamesGenerator', () => {
         describe('Variant #2: Should generate different names set for different lexical scopes when string array is enabled', () => {
             describe('Variant #1: `renameGlobals` option is disabled', () => {
                 const stringArrayIdentifierRegExp: RegExp = /var a *= *\['abc'];/;
-                const variableIdentifierRegExp: RegExp = /var foo *= *b\('0x0'\);/;
+                const variableIdentifierRegExp: RegExp = /var foo *= *b\(0x0\);/;
                 const functionDeclarationRegExp1: RegExp = /function bar *\(c, *d\) *{}/;
                 const functionDeclarationRegExp2: RegExp = /function baz *\(c, *d\) *{}/;
 
@@ -261,7 +261,7 @@ describe('MangledIdentifierNamesGenerator', () => {
 
             describe('Variant #2: `renameGlobals` option is enabled', () => {
                 const stringArrayIdentifierRegExp: RegExp = /var a *= *\['abc'];/;
-                const variableIdentifierRegExp: RegExp = /var c *= *b\('0x0'\);/;
+                const variableIdentifierRegExp: RegExp = /var c *= *b\(0x0\);/;
                 const functionDeclarationRegExp1: RegExp = /function d *\(f, *g\) *{}/;
                 const functionDeclarationRegExp2: RegExp = /function e *\(f, *g\) *{}/;
 

+ 8 - 3
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -9,6 +9,7 @@ import { IObfuscatedCode } from '../../../src/interfaces/source-code/IObfuscated
 
 import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
 import { StringArrayEncoding } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
+import { StringArrayIndexesType } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayWrappersType } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
 import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscatorFacade';
@@ -276,7 +277,7 @@ describe('JavaScriptObfuscator', () => {
 
             describe('Variant #4: with `stringArray`, `renameGlobals` and `identifiersPrefix` options', () => {
                 const stringArrayRegExp: RegExp = /^var foo_0x(\w){4} *= *\['abc'\];/;
-                const stringArrayCallRegExp: RegExp = /var foo_0x(\w){4,6} *= *foo_0x(\w){4}\('0x0'\);$/;
+                const stringArrayCallRegExp: RegExp = /var foo_0x(\w){4,6} *= *foo_0x(\w){4}\(0x0\);$/;
 
                 let obfuscatedCode: string;
 
@@ -368,7 +369,7 @@ describe('JavaScriptObfuscator', () => {
 
         describe('latin literal variable value', () => {
             const stringArrayLatinRegExp: RegExp = /^var _0x(\w){4} *= *\['abc'\];/;
-            const stringArrayCallRegExp: RegExp = /var test *= *_0x(\w){4}\('0x0'\);$/;
+            const stringArrayCallRegExp: RegExp = /var test *= *_0x(\w){4}\(0x0\);$/;
 
             let obfuscatedCode: string;
 
@@ -396,7 +397,7 @@ describe('JavaScriptObfuscator', () => {
 
         describe('cyrillic literal variable value', () => {
             const stringArrayCyrillicRegExp: RegExp = /^var _0x(\w){4} *= *\['абц'\];/;
-            const stringArrayCallRegExp: RegExp = /var test *= *_0x(\w){4}\('0x0'\);$/;
+            const stringArrayCallRegExp: RegExp = /var test *= *_0x(\w){4}\(0x0\);$/;
 
             let obfuscatedCode: string;
 
@@ -918,6 +919,10 @@ describe('JavaScriptObfuscator', () => {
                             StringArrayEncoding.Base64,
                             StringArrayEncoding.Rc4
                         ],
+                        stringArrayIndexesType: [
+                            StringArrayIndexesType.HexadecimalNumber,
+                            StringArrayIndexesType.HexadecimalNumericString
+                        ],
                         stringArrayIndexShift: true,
                         stringArrayWrappersChainedCalls: true,
                         stringArrayWrappersCount: 10,

+ 2 - 2
test/functional-tests/node-transformers/converting-transformers/member-expression-transformer/MemberExpressionTransformer.spec.ts

@@ -31,7 +31,7 @@ describe('MemberExpressionTransformer', () => {
 
         describe('`stringArray` option is enabled', () => {
             const stringArrayRegExp: RegExp = /var _0x([a-f0-9]){4} *= *\['log'\];/;
-            const stringArrayCallRegExp: RegExp = /var test *= *console\[_0x([a-f0-9]){4}\('0x0'\)\];/;
+            const stringArrayCallRegExp: RegExp = /var test *= *console\[_0x([a-f0-9]){4}\(0x0\)\];/;
 
             let obfuscatedCode: string;
 
@@ -61,7 +61,7 @@ describe('MemberExpressionTransformer', () => {
     describe('transformation of member expression node with square brackets', () => {
         describe('Variant #1: square brackets literal ', () => {
             const stringArrayRegExp: RegExp = /var _0x([a-f0-9]){4} *= *\['log'\];/;
-            const stringArrayCallRegExp: RegExp = /var test *= *console\[_0x([a-f0-9]){4}\('0x0'\)\];/;
+            const stringArrayCallRegExp: RegExp = /var test *= *console\[_0x([a-f0-9]){4}\(0x0\)\];/;
 
             let obfuscatedCode: string;
 

+ 2 - 2
test/functional-tests/node-transformers/converting-transformers/method-definition-transformer/MethodDefinitionTransformer.spec.ts

@@ -31,7 +31,7 @@ describe('MethodDefinitionTransformer', () => {
 
         describe('Variant #2: `stringArray` option is enabled', () => {
             const stringArrayRegExp: RegExp = /var _0x([a-f0-9]){4} *= *\['bar'\];/;
-            const stringArrayCallRegExp: RegExp = /\[_0x([a-f0-9]){4}\('0x0'\)\]\(\)\{\}/;
+            const stringArrayCallRegExp: RegExp = /\[_0x([a-f0-9]){4}\(0x0\)\]\(\)\{\}/;
 
             let obfuscatedCode: string;
 
@@ -103,7 +103,7 @@ describe('MethodDefinitionTransformer', () => {
 
         describe('Variant #2: `stringArray` option is enabled', () => {
             const stringArrayRegExp: RegExp = /var _0x([a-f0-9]){4} *= *\['constructor', *'bar'];/;
-            const stringArrayCallRegExp: RegExp = /\[_0x([a-f0-9]){4}\('0x1'\)\]\(\)\{\}/;
+            const stringArrayCallRegExp: RegExp = /\[_0x([a-f0-9]){4}\(0x1\)\]\(\)\{\}/;
 
             let obfuscatedCode: string;
 

+ 14 - 14
test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts

@@ -12,17 +12,17 @@ import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade
 describe('DeadCodeInjectionTransformer', () => {
     const variableMatch: string = '_0x([a-f0-9]){4,6}';
     const hexMatch: string = '0x[a-f0-9]';
-    const stringArrayCallMatch: string = `${variableMatch}\\('${hexMatch}'\\)`;
+    const stringArrayCallMatch: string = `${variableMatch}\\(${hexMatch}\\)`;
 
     describe('transformNode', function () {
         this.timeout(100000);
 
         describe('Variant #1 - 5 simple block statements', () => {
             const regExp: RegExp = new RegExp(
-                `if *\\(${variableMatch}\\('${hexMatch}'\\) *[=|!]== *${variableMatch}\\('${hexMatch}'\\)\\) *\\{`+
-                    `(?:console|${variableMatch})\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                `if *\\(${variableMatch}\\(${hexMatch}\\) *[=|!]== *${variableMatch}\\(${hexMatch}\\)\\) *\\{`+
+                    `(?:console|${variableMatch})\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                 `\\} *else *\\{`+
-                    `(?:console|${variableMatch})\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                    `(?:console|${variableMatch})\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                 `\\}`,
                 'g'
             );
@@ -58,7 +58,7 @@ describe('DeadCodeInjectionTransformer', () => {
         describe('Variant #2 - block statements count is less than `5`', () => {
             const regexp: RegExp = new RegExp(
                 `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                    `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                    `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                 `\\};`,
                 'g'
             );
@@ -94,7 +94,7 @@ describe('DeadCodeInjectionTransformer', () => {
         describe('Variant #3 - deadCodeInjectionThreshold: 0', () => {
             const regexp: RegExp = new RegExp(
                 `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                    `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                    `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                 `\\};`,
                 'g'
             );
@@ -131,7 +131,7 @@ describe('DeadCodeInjectionTransformer', () => {
             describe('Variant #1 - function declaration in block statement', () => {
                 const functionRegExp: RegExp = new RegExp(
                     `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                        `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                        `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                     `\\};`,
                     'g'
                 );
@@ -183,7 +183,7 @@ describe('DeadCodeInjectionTransformer', () => {
                 describe('Variant #1', () => {
                     const functionRegExp: RegExp = new RegExp(
                         `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                            `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                            `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                         `\\};`,
                         'g'
                     );
@@ -236,7 +236,7 @@ describe('DeadCodeInjectionTransformer', () => {
                 describe('Variant #2', () => {
                     const functionRegExp: RegExp = new RegExp(
                         `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                            `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                            `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                         `\\};`,
                         'g'
                     );
@@ -289,7 +289,7 @@ describe('DeadCodeInjectionTransformer', () => {
             describe('Variant #3 - await expression in block statement', () => {
                 const functionRegExp: RegExp = new RegExp(
                     `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                        `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                        `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                     `\\};`,
                     'g'
                 );
@@ -340,7 +340,7 @@ describe('DeadCodeInjectionTransformer', () => {
             describe('Variant #4 - yield expression in block statement', () => {
                 const functionRegExp: RegExp = new RegExp(
                     `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                        `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                        `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                     `\\};`,
                     'g'
                 );
@@ -391,7 +391,7 @@ describe('DeadCodeInjectionTransformer', () => {
             describe('Variant #5 - super expression in block statement', () => {
                 const functionRegExp: RegExp = new RegExp(
                     `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                        `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                        `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                     `\\};`,
                     'g'
                 );
@@ -442,7 +442,7 @@ describe('DeadCodeInjectionTransformer', () => {
             describe('Variant #6 - for-await expression in block statement', () => {
                 const functionRegExp: RegExp = new RegExp(
                     `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                        `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                        `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                     `\\};`,
                     'g'
                 );
@@ -596,7 +596,7 @@ describe('DeadCodeInjectionTransformer', () => {
         describe('Variant #6 - block scope of block statement is `ProgramNode`', () => {
             const regExp: RegExp = new RegExp(
                 `if *\\(!!\\[\\]\\) *{` +
-                    `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                    `console\\[${variableMatch}\\(${hexMatch}\\)\\]\\(${variableMatch}\\(${hexMatch}\\)\\);` +
                 `\\}`
             );
 

+ 7 - 0
test/functional-tests/node-transformers/finalizing-transformers/escape-sequence-transformer/EscapeSequenceTransformer.spec.ts

@@ -3,6 +3,7 @@ import { assert } from 'chai';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
 import { IdentifierNamesGenerator } from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
+import { StringArrayIndexesType } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 
@@ -69,6 +70,9 @@ describe('EscapeSequenceTransformer', function () {
                 {
                     ...NO_ADDITIONAL_NODES_PRESET,
                     stringArray: true,
+                    stringArrayIndexesType: [
+                        StringArrayIndexesType.HexadecimalNumericString
+                    ],
                     stringArrayThreshold: 1,
                     unicodeEscapeSequence: true
                 }
@@ -191,6 +195,9 @@ describe('EscapeSequenceTransformer', function () {
                     ...NO_ADDITIONAL_NODES_PRESET,
                     identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
                     stringArray: true,
+                    stringArrayIndexesType: [
+                        StringArrayIndexesType.HexadecimalNumericString
+                    ],
                     stringArrayThreshold: 1,
                     stringArrayWrappersChainedCalls: true,
                     stringArrayWrappersCount: 1,

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

@@ -2,6 +2,8 @@ import { assert } from 'chai';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
+import { StringArrayIndexesType } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
+
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
@@ -92,30 +94,66 @@ describe('EvalCallExpressionTransformer', () => {
     });
 
     describe('Variant #4: string array calls wrapper call', () => {
-        const stringArrayRegExp: RegExp = /var _0x([a-f0-9]){4} *= *\['log', *'bar'];/;
-        const stringArrayCallsWrapperRegExp: RegExp = /eval *\('console\[_0x([a-f0-9]){4,6}\(\\'0x0\\'\)]\(_0x([a-f0-9]){4,6}\(\\'0x1\\'\)\);'\);/;
-
-        let obfuscatedCode: string;
-
-        before(() => {
-            const code: string = readFileAsString(__dirname + '/fixtures/string-array-calls-wrapper-call.js');
-
-            obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                code,
-                {
-                    ...NO_ADDITIONAL_NODES_PRESET,
-                    stringArray: true,
-                    stringArrayThreshold: 1
-                }
-            ).getObfuscatedCode();
-        });
-
-        it('match #1: should add strings from eval expression to the string array', () => {
-            assert.match(obfuscatedCode, stringArrayRegExp);
+        describe('Variant #1: hexadecimal number indexes type', () => {
+            const stringArrayRegExp: RegExp = /var _0x([a-f0-9]){4} *= *\['log', *'bar'];/;
+            const stringArrayCallsWrapperRegExp: RegExp = /eval *\('console\[_0x([a-f0-9]){4,6}\(0\)]\(_0x([a-f0-9]){4,6}\(1\)\);'\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/string-array-calls-wrapper-call.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayIndexesType: [
+                            StringArrayIndexesType.HexadecimalNumber
+                        ],
+                        stringArrayThreshold: 1
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('match #1: should add strings from eval expression to the string array', () => {
+                assert.match(obfuscatedCode, stringArrayRegExp);
+            });
+
+            it('match #1: should replace string with call to the string array calls wrapper', () => {
+                assert.match(obfuscatedCode, stringArrayCallsWrapperRegExp);
+            });
         });
 
-        it('match #1: should replace string with call to the string array calls wrapper', () => {
-            assert.match(obfuscatedCode, stringArrayCallsWrapperRegExp);
+        describe('Variant #1: hexadecimal numeric string indexes type', () => {
+            const stringArrayRegExp: RegExp = /var _0x([a-f0-9]){4} *= *\['log', *'bar'];/;
+            const stringArrayCallsWrapperRegExp: RegExp = /eval *\('console\[_0x([a-f0-9]){4,6}\(\\'0x0\\'\)]\(_0x([a-f0-9]){4,6}\(\\'0x1\\'\)\);'\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/string-array-calls-wrapper-call.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayIndexesType: [
+                            StringArrayIndexesType.HexadecimalNumericString
+                        ],
+                        stringArrayThreshold: 1
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('match #1: should add strings from eval expression to the string array', () => {
+                assert.match(obfuscatedCode, stringArrayRegExp);
+            });
+
+            it('match #1: should replace string with call to the string array calls wrapper', () => {
+                assert.match(obfuscatedCode, stringArrayCallsWrapperRegExp);
+            });
         });
     });
 

+ 1 - 1
test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/black-list-obfuscating-guard/BlackListObfuscatingGuard.spec.ts

@@ -11,7 +11,7 @@ describe('BlackListObfuscatingGuard', () => {
         describe('`\'use strict\';` operator', () => {
             const useStrictOperatorRegExp: RegExp = /'use *strict';/;
             const stringArrayLatinRegExp: RegExp = /var _0x(\w){4} *= *\['abc'\];/;
-            const stringArrayCallRegExp: RegExp = /var test *= *_0x(\w){4}\('0x0'\);$/;
+            const stringArrayCallRegExp: RegExp = /var test *= *_0x(\w){4}\(0x0\);$/;
 
             let obfuscatedCode: string;
 

+ 1 - 1
test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/force-transform-string-obfuscating-guard/ForceTransformStringObfuscatingGuard.spec.ts

@@ -13,7 +13,7 @@ describe('ForceTransformStringObfuscatingGuard', () => {
         describe('`forceTransformStrings` option is enabled', () => {
             const obfuscatingGuardRegExp: RegExp = new RegExp(
                 'var foo *= *\'foo\';' +
-                'var bar *= *b\\(\'0x0\'\\);'
+                'var bar *= *b\\(0x0\\);'
             );
 
             let obfuscatedCode: string;

+ 3 - 3
test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/ignored-require-import-obfuscating-guard/IgnoredRequireImportObfuscatingGuard.spec.ts

@@ -12,7 +12,7 @@ describe('IgnoredRequireImportObfuscatingGuard', () => {
             const obfuscatingGuardRegExp: RegExp = new RegExp(
                 'const foo *= *require\\(\'\\./foo\'\\); *' +
                 'import _0x(?:[a-f0-9]){4,6} from *\'\\./bar\'; *' +
-                'const baz *= *_0x(?:[a-f0-9]){4,6}\\(\'0x0\'\\);'
+                'const baz *= *_0x(?:[a-f0-9]){4,6}\\(0x0\\);'
             );
 
             let obfuscatedCode: string;
@@ -38,9 +38,9 @@ describe('IgnoredRequireImportObfuscatingGuard', () => {
 
         describe('`ignoreRequireImports` option is disabled', () => {
             const obfuscatingGuardRegExp: RegExp = new RegExp(
-                'const foo *= *require\\(_0x(?:[a-f0-9]){4,6}\\(\'0x0\'\\)\\); *' +
+                'const foo *= *require\\(_0x(?:[a-f0-9]){4,6}\\(0x0\\)\\); *' +
                 'import _0x(?:[a-f0-9]){4,6} from *\'\\./bar\'; *' +
-                'const baz *= *_0x(?:[a-f0-9]){4,6}\\(\'0x1\'\\);'
+                'const baz *= *_0x(?:[a-f0-9]){4,6}\\(0x1\\);'
             );
 
             let obfuscatedCode: string;

+ 2 - 2
test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/VariablePreserveTransformer.spec.ts

@@ -12,7 +12,7 @@ describe('VariablePreserveTransformer', () => {
     describe('Variant #1: string array storage name conflicts with identifier name', () => {
         describe('Variant #1: `renameGlobals` option is disabled', () => {
             const stringArrayStorageNameRegExp: RegExp = /const b *= *\['abc'];/;
-            const identifierNameRegExp: RegExp = /const a *= *c\('0x0'\);/;
+            const identifierNameRegExp: RegExp = /const a *= *c\(0x0\);/;
 
             let obfuscatedCode: string;
 
@@ -41,7 +41,7 @@ describe('VariablePreserveTransformer', () => {
 
         describe('Variant #2: `renameGlobals` option is enabled', () => {
             const stringArrayStorageNameRegExp: RegExp = /const b *= *\['abc'];/;
-            const identifierNameRegExp: RegExp = /const d *= *c\('0x0'\);/;
+            const identifierNameRegExp: RegExp = /const d *= *c\(0x0\);/;
 
             let obfuscatedCode: string;
 

+ 108 - 57
test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/StringArrayScopeCallsWrapperTransformer.spec.ts

@@ -9,6 +9,7 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/N
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
+import { StringArrayIndexesType } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 
 describe('StringArrayScopeCallsWrapperTransformer', function () {
     this.timeout(120000);
@@ -21,9 +22,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     '};' +
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
-                    'const foo *= *_0x([a-f0-9]){4,6}\\(\'0x0\'\\);' +
-                    'const bar *= *_0x([a-f0-9]){4,6}\\(\'0x1\'\\);' +
-                    'const baz *= *_0x([a-f0-9]){4,6}\\(\'0x2\'\\);'
+                    'const foo *= *_0x([a-f0-9]){4,6}\\(0x0\\);' +
+                    'const bar *= *_0x([a-f0-9]){4,6}\\(0x1\\);' +
+                    'const baz *= *_0x([a-f0-9]){4,6}\\(0x2\\);'
                 );
 
                 let obfuscatedCode: string;
@@ -54,9 +55,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                     'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
-                    'const foo *= *_0x([a-f0-9]){4,6}\\(\'0x0\'\\);' +
-                    'const bar *= *_0x([a-f0-9]){4,6}\\(\'0x1\'\\);' +
-                    'const baz *= *_0x([a-f0-9]){4,6}\\(\'0x2\'\\);'
+                    'const foo *= *_0x([a-f0-9]){4,6}\\(0x0\\);' +
+                    'const bar *= *_0x([a-f0-9]){4,6}\\(0x1\\);' +
+                    'const baz *= *_0x([a-f0-9]){4,6}\\(0x2\\);'
                 );
 
                 let obfuscatedCode: string;
@@ -84,9 +85,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                 const stringArrayCallRegExp: RegExp = new RegExp(
                     'const f *= *b;' +
                     'const g *= *b;' +
-                    'const foo *= *[f|g]\\(\'0x0\'\\);' +
-                    'const bar *= *[f|g]\\(\'0x1\'\\);' +
-                    'const baz *= *[f|g]\\(\'0x2\'\\);'
+                    'const foo *= *[f|g]\\(0x0\\);' +
+                    'const bar *= *[f|g]\\(0x1\\);' +
+                    'const baz *= *[f|g]\\(0x2\\);'
                 );
 
                 let obfuscatedCode: string;
@@ -118,9 +119,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     'function test *\\( *\\) *{' +
                         'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                         'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x3\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x4\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x5\\);' +
                     '}'
                 );
 
@@ -151,9 +152,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                         'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                         'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x3\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x4\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x5\\);' +
                     '}'
                 );
 
@@ -183,9 +184,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     'function test *\\( *\\) *{' +
                         'const h *= *b;' +
                         'const i *= *b;' +
-                        'const c *= *[h|i]\\(\'0x3\'\\);' +
-                        'const d *= *[h|i]\\(\'0x4\'\\);' +
-                        'const e *= *[h|i]\\(\'0x5\'\\);' +
+                        'const c *= *[h|i]\\(0x3\\);' +
+                        'const d *= *[h|i]\\(0x4\\);' +
+                        'const e *= *[h|i]\\(0x5\\);' +
                     '}'
                 );
 
@@ -214,10 +215,10 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
             describe('Variant #4: correct wrapper for the function default parameter', () => {
                 const stringArrayCallRegExp: RegExp = new RegExp(
                     'const e *= *b;' +
-                    'const foo *= *e\\(\'0x0\'\\);' +
-                    'function test *\\(c *= *e\\(\'0x1\'\\)\\) *{' +
+                    'const foo *= *e\\(0x0\\);' +
+                    'function test *\\(c *= *e\\(0x1\\)\\) *{' +
                         'const f *= *b;' +
-                        'const d *= *f\\(\'0x2\'\\);' +
+                        'const d *= *f\\(0x2\\);' +
                     '}'
                 );
 
@@ -249,7 +250,7 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                 const stringArrayCallRegExp: RegExp = new RegExp(
                     'var c *= *b;' +
                     'if *\\(!!\\[]\\) *{' +
-                        'var foo *= *c\\(\'0x0\'\\);' +
+                        'var foo *= *c\\(0x0\\);' +
                     '}'
                 );
 
@@ -278,7 +279,7 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
             describe('Variant #2: arrow function scope without statements', () => {
                 const stringArrayCallRegExp: RegExp = new RegExp(
                     'var c *= *b;' +
-                    '\\[]\\[c\\(\'0x0\'\\)]\\(\\(\\) *=> *c\\(\'0x1\'\\)\\);'
+                    '\\[]\\[c\\(0x0\\)]\\(\\(\\) *=> *c\\(0x1\\)\\);'
                 );
 
                 let obfuscatedCode: string;
@@ -310,9 +311,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                 '};' +
                 'var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
                 'var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
-                'var foo *= *_0x([a-f0-9]){4,6}\\(\'0x0\'\\);' +
-                'var bar *= *_0x([a-f0-9]){4,6}\\(\'0x1\'\\);' +
-                'var baz *= *_0x([a-f0-9]){4,6}\\(\'0x2\'\\);'
+                'var foo *= *_0x([a-f0-9]){4,6}\\(0x0\\);' +
+                'var bar *= *_0x([a-f0-9]){4,6}\\(0x1\\);' +
+                'var baz *= *_0x([a-f0-9]){4,6}\\(0x2\\);'
             );
 
             let obfuscatedCode: string;
@@ -366,19 +367,19 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                 describe('Variant #1: `Mangled` identifier names generator', () => {
                     const stringArrayCallRegExp: RegExp = new RegExp(
                         'const q *= *b;' +
-                        'const foo *= *q\\(\'0x0\'\\);' +
+                        'const foo *= *q\\(0x0\\);' +
                         'function test\\(c, *d\\) *{' +
                             'const r *= *q;' +
-                            'const e *= *r\\(\'0x1\'\\);' +
-                            'const f *= *r\\(\'0x2\'\\);' +
+                            'const e *= *r\\(0x1\\);' +
+                            'const f *= *r\\(0x2\\);' +
                             'function g\\(h, *i\\) *{' +
                                 'const s *= *r;' +
-                                'const j *= *s\\(\'0x3\'\\);' +
-                                'const k *= *s\\(\'0x4\'\\);' +
+                                'const j *= *s\\(0x3\\);' +
+                                'const k *= *s\\(0x4\\);' +
                                 'function l\\(m, *n *\\) *{' +
                                     'const t *= *s;' +
-                                    'const o *= *t\\(\'0x3\'\\);' +
-                                    'const p *= *t\\(\'0x4\'\\);' +
+                                    'const o *= *t\\(0x3\\);' +
+                                    'const p *= *t\\(0x4\\);' +
                                     'return o *\\+ *p;' +
                                 '}' +
                                 'return j *\\+ *k;' +
@@ -578,6 +579,52 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
             const hexadecimalIndexMatch: string = '0x[a-z0-9]{1,3}';
 
             describe('Variant #1: base', () => {
+                describe('Variant #1: `hexadecimal-number` indexes type', () => {
+                    const stringArrayCallRegExp: RegExp = new RegExp(
+                        'const f *= *function *\\(c, *d\\) *{' +
+                            `return b\\(c *-(?: -)?${hexadecimalIndexMatch}, *d\\);` +
+                        '};' +
+                        `const foo *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
+                        `const bar *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
+                        `const baz *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
+                        'function test *\\( *\\) *{' +
+                            'const g *= *function *\\(c, *d\\) *{' +
+                                `return b\\(c *-(?: -)?${hexadecimalIndexMatch}, *d\\);` +
+                            '};' +
+                            `const c *= *g\\(-? *${hexadecimalIndexMatch}\\);` +
+                            `const d *= *g\\(-? *${hexadecimalIndexMatch}\\);` +
+                            `const e *= *g\\(-? *${hexadecimalIndexMatch}\\);` +
+                        '}'
+                    );
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/wrappers-count-const.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                stringArray: true,
+                                stringArrayIndexesType: [
+                                    StringArrayIndexesType.HexadecimalNumber
+                                ],
+                                stringArrayThreshold: 1,
+                                stringArrayWrappersChainedCalls: false,
+                                stringArrayWrappersCount: 1,
+                                stringArrayWrappersType: StringArrayWrappersType.Function
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('should add correct scope calls wrappers', () => {
+                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    });
+                });
+
+                describe('Variant #2: `hexadecimal-numeric-string` indexes type', () => {
                     const stringArrayCallRegExp: RegExp = new RegExp(
                         'const f *= *function *\\(c, *d\\) *{' +
                             `return b\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
@@ -606,6 +653,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                                 ...NO_ADDITIONAL_NODES_PRESET,
                                 identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
                                 stringArray: true,
+                                stringArrayIndexesType: [
+                                    StringArrayIndexesType.HexadecimalNumericString
+                                ],
                                 stringArrayThreshold: 1,
                                 stringArrayWrappersChainedCalls: false,
                                 stringArrayWrappersCount: 1,
@@ -618,22 +668,23 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         assert.match(obfuscatedCode, stringArrayCallRegExp);
                     });
                 });
+            });
 
             describe('Variant #2: correct chained calls', () => {
                     const stringArrayCallRegExp: RegExp = new RegExp(
                         'const f *= *function *\\(c, *d\\) *{' +
-                            `return b\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
+                            `return b\\(c *-(?: -)?${hexadecimalIndexMatch}, *d\\);` +
                         '};' +
-                        `const foo *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
-                        `const bar *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
-                        `const baz *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                        `const foo *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
+                        `const bar *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
+                        `const baz *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
                         'function test *\\( *\\) *{' +
                             'const g *= *function *\\(c, *d\\) *{' +
-                                `return f\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
+                                `return f\\(c *-(?: -)?${hexadecimalIndexMatch}, *d\\);` +
                             '};' +
-                            `const c *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
-                            `const d *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
-                            `const e *= *g\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                            `const c *= *g\\(-? *${hexadecimalIndexMatch}\\);` +
+                            `const d *= *g\\(-? *${hexadecimalIndexMatch}\\);` +
+                            `const e *= *g\\(-? *${hexadecimalIndexMatch}\\);` +
                         '}'
                     );
 
@@ -667,11 +718,11 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         '};' +
                         'function test *\\( *\\) *{' +
                             'const f *= *function *\\(c, *d\\) *{' +
-                                `return b\\(c *-(?: -)?'${hexadecimalIndexMatch}', *d\\);` +
+                                `return b\\(c *-(?: -)?${hexadecimalIndexMatch}, *d\\);` +
                             '};' +
-                            `const c *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
-                            `const d *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
-                            `const e *= *f\\(-? *'${hexadecimalIndexMatch}'\\);` +
+                            `const c *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
+                            `const d *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
+                            `const e *= *f\\(-? *${hexadecimalIndexMatch}\\);` +
                         '}'
                     );
 
@@ -875,9 +926,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     // this one may be added or not depends on:
                     // if all literal values encoded with a single encoding or not
                     '(?:const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};)?' +
-                    'const foo *= *_0x([a-f0-9]){4,6}\\(\'0x0\'\\);' +
-                    'const bar *= *_0x([a-f0-9]){4,6}\\(\'0x1\'\\);' +
-                    'const baz *= *_0x([a-f0-9]){4,6}\\(\'0x2\'\\);'
+                    'const foo *= *_0x([a-f0-9]){4,6}\\(0x0\\);' +
+                    'const bar *= *_0x([a-f0-9]){4,6}\\(0x1\\);' +
+                    'const baz *= *_0x([a-f0-9]){4,6}\\(0x2\\);'
                 );
 
                 let obfuscatedCode: string;
@@ -914,9 +965,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     // this one may be added or not depends on:
                     // if all literal values encoded with a single encoding or not
                     '(?:const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};)?' +
-                    'const foo *= *_0x([a-f0-9]){4,6}\\(\'0x0\'\\);' +
-                    'const bar *= *_0x([a-f0-9]){4,6}\\(\'0x1\'\\);' +
-                    'const baz *= *_0x([a-f0-9]){4,6}\\(\'0x2\'\\);'
+                    'const foo *= *_0x([a-f0-9]){4,6}\\(0x0\\);' +
+                    'const bar *= *_0x([a-f0-9]){4,6}\\(0x1\\);' +
+                    'const baz *= *_0x([a-f0-9]){4,6}\\(0x2\\);'
                 );
 
                 let obfuscatedCode: string;
@@ -953,9 +1004,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         // this one may be added or not depends on:
                         // if all literal values encoded with a single encoding or not
                         '(?:const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};)?' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x3\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x4\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x5\\);' +
                     '}'
                 );
 
@@ -992,9 +1043,9 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         // this one may be added or not depends on:
                         // if all literal values encoded with a single encoding or not
                         '(?:const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};)?' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
-                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x3\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x4\\);' +
+                        'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(0x5\\);' +
                     '}'
                 );
 

+ 151 - 43
test/functional-tests/node-transformers/string-array-transformers/string-array-transformer/StringArrayTransformer.spec.ts

@@ -2,6 +2,7 @@ import { assert } from 'chai';
 
 import { IdentifierNamesGenerator } from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { StringArrayEncoding } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
+import { StringArrayIndexesType } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayWrappersType } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
@@ -17,7 +18,7 @@ describe('StringArrayTransformer', function () {
 
     describe('Variant #1: default behaviour', () => {
         const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['test'\];/;
-        const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
+        const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/;
 
         let obfuscatedCode: string;
 
@@ -64,11 +65,118 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #3: `stringArrayIndexShift` option is enabled', () => {
+    describe('Variant #3: `stringArrayIndexesType` option', () => {
+        describe('Variant #1: `hexadecimal-number` type', () => {
+            const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1,
+                        stringArrayIndexesType: [
+                            StringArrayIndexesType.HexadecimalNumber
+                        ]
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('match #1: should transform string array index with the passed index type', () => {
+                assert.match(obfuscatedCode, stringArrayCallRegExp);
+            });
+        });
+
+        describe('Variant #2: `hexadecimal-numeric-string` type', () => {
+            const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1,
+                        stringArrayIndexesType: [
+                            StringArrayIndexesType.HexadecimalNumericString
+                        ]
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('match #1: should transform string array index with the passed index type', () => {
+                assert.match(obfuscatedCode, stringArrayCallRegExp);
+            });
+        });
+
+        describe('Variant #3: multiple types', () => {
+            const samplesCount: number = 500;
+            const expectedMatchesChance: number = 0.5;
+            const expectedMatchesDelta: number = 0.15;
+
+            const hexadecimalNumberIndexTypeRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/;
+            const hexadecimalNumericStringIndexTypeRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
+
+            let hexadecimalNumberIndexTypeMatchesCount: number = 0;
+            let hexadecimalNumericStringIndexTypeMatchesCount: number = 0;
+
+            let hexadecimalNumberIndexTypeMatchesChance: number = 0;
+            let hexadecimalNumericStringIndexTypeMatchesChance: number = 0;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
+
+                for (let i = 0; i < samplesCount; i++) {
+                    const obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            stringArray: true,
+                            stringArrayThreshold: 1,
+                            stringArrayIndexesType: [
+                                StringArrayIndexesType.HexadecimalNumber,
+                                StringArrayIndexesType.HexadecimalNumericString
+                            ]
+                        }
+                    ).getObfuscatedCode();
+
+                    if (obfuscatedCode.match(hexadecimalNumberIndexTypeRegExp)) {
+                        hexadecimalNumberIndexTypeMatchesCount += 1;
+                    }
+
+                    if (obfuscatedCode.match(hexadecimalNumericStringIndexTypeRegExp)) {
+                        hexadecimalNumericStringIndexTypeMatchesCount += 1;
+                    }
+
+                    hexadecimalNumberIndexTypeMatchesChance = hexadecimalNumberIndexTypeMatchesCount / samplesCount;
+                    hexadecimalNumericStringIndexTypeMatchesChance = hexadecimalNumericStringIndexTypeMatchesCount / samplesCount;
+                }
+            });
+
+            it('should transform string array indexes with a `hexadecimal-number` type', () => {
+                assert.closeTo(hexadecimalNumberIndexTypeMatchesChance, expectedMatchesChance, expectedMatchesDelta);
+            });
+
+            it('should transform string array indexes with a `hexadecimal-numeric-string` type', () => {
+                assert.closeTo(hexadecimalNumericStringIndexTypeMatchesChance, expectedMatchesChance, expectedMatchesDelta);
+            });
+        });
+    });
+
+    describe('Variant #4: `stringArrayIndexShift` option is enabled', () => {
         const stringArrayIndexShiftRegExp: RegExp = /_0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4,6} *- *(0x[a-z0-9]{1,3});/;
-        const stringArrayCallRegExp1: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4}\('(0x[a-z0-9]{1,3})'\) *\+ *0x1;/;
-        const stringArrayCallRegExp2: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4}\('(0x[a-z0-9]{1,3})'\) *\+ *0x2;/;
-        const stringArrayCallRegExp3: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4}\('(0x[a-z0-9]{1,3})'\) *\+ *0x3;/;
+        const stringArrayCallRegExp1: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4}\((0x[a-z0-9]{1,3})\) *\+ *0x1;/;
+        const stringArrayCallRegExp2: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4}\((0x[a-z0-9]{1,3})\) *\+ *0x2;/;
+        const stringArrayCallRegExp3: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *_0x(?:[a-f0-9]){4}\((0x[a-z0-9]{1,3})\) *\+ *0x3;/;
 
         const expectedEvaluationResult: string = 'foo1bar2baz3';
 
@@ -258,9 +366,9 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #4: same literal node values', () => {
+    describe('Variant #5: same literal node values', () => {
         const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['test'\];/;
-        const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
+        const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/;
 
         let obfuscatedCode: string;
 
@@ -286,7 +394,7 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #5: short literal node value', () => {
+    describe('Variant #6: short literal node value', () => {
         const regExp: RegExp = /var test *= *'te';/;
 
         let obfuscatedCode: string;
@@ -309,9 +417,9 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #6: base64 encoding', () => {
+    describe('Variant #7: base64 encoding', () => {
         const stringArrayRegExp: RegExp = new RegExp(`^var _0x([a-f0-9]){4} *= *\\['${swapLettersCase('dGVzdA==')}'];`);
-        const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
+        const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/;
 
         let obfuscatedCode: string;
 
@@ -338,9 +446,9 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #7: rc4 encoding', () => {
+    describe('Variant #8: rc4 encoding', () => {
         describe('Variant #1: single string literal', () => {
-            const regExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0', *'.{4}'\);/;
+            const regExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0, *'.{4}'\);/;
 
             let obfuscatedCode: string;
 
@@ -364,8 +472,8 @@ describe('StringArrayTransformer', function () {
         });
 
         describe('Variant #2: multiple string literals', () => {
-            const variableRegExp1: RegExp = /var test *= *_0x(?:[a-f0-9]){4}\('0x0', *'(.{4})'\);/;
-            const variableRegExp2: RegExp = /var test *= *_0x(?:[a-f0-9]){4}\('0x1', *'(.{4})'\);/;
+            const variableRegExp1: RegExp = /var test *= *_0x(?:[a-f0-9]){4}\(0x0, *'(.{4})'\);/;
+            const variableRegExp2: RegExp = /var test *= *_0x(?:[a-f0-9]){4}\(0x1, *'(.{4})'\);/;
 
             let encodedLiteralValue1: string;
             let encodedLiteralValue2: string;
@@ -404,7 +512,7 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #8: none and base64 encoding', () => {
+    describe('Variant #9: none and base64 encoding', () => {
         describe('Variant #1: string array values', () => {
             const samplesCount: number = 300;
             const expectedMatchesChance: number = 0.5;
@@ -460,14 +568,14 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #9: none and rc4 encoding', () => {
+    describe('Variant #10: none and rc4 encoding', () => {
         describe('Variant #1: string array calls wrapper call', () => {
             const samplesCount: number = 300;
             const expectedMatchesChance: number = 0.5;
             const expectedMatchesDelta: number = 0.15;
 
-            const noneEncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
-            const rc4EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0', *'.{4}'\);/;
+            const noneEncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/;
+            const rc4EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0, *'.{4}'\);/;
 
             let noneEncodingMatchesCount: number = 0;
             let rc4EncodingMatchesCount: number = 0;
@@ -516,14 +624,14 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #10: base64 and rc4 encoding', () => {
+    describe('Variant #11: base64 and rc4 encoding', () => {
         describe('Variant #1: single string literal', () => {
             const samplesCount: number = 300;
             const expectedMatchesChance: number = 0.5;
             const expectedMatchesDelta: number = 0.15;
 
-            const base64EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
-            const rc4EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0', *'.{4}'\);/;
+            const base64EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/;
+            const rc4EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0, *'.{4}'\);/;
 
             let base64EncodingMatchesCount: number = 0;
             let rc4EncodingMatchesCount: number = 0;
@@ -572,12 +680,12 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #11: `stringArrayThreshold` option value', () => {
+    describe('Variant #12: `stringArrayThreshold` option value', () => {
         const samples: number = 1000;
         const stringArrayThreshold: number = 0.5;
         const delta: number = 0.1;
 
-        const regExp1: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/g;
+        const regExp1: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/g;
         const regExp2: RegExp = /var test *= *'test';/g;
 
         let stringArrayProbability: number,
@@ -615,8 +723,8 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #12: string array calls wrapper name', () => {
-        const regExp: RegExp = /console\[b\('0x0'\)]\('a'\);/;
+    describe('Variant #13: string array calls wrapper name', () => {
+        const regExp: RegExp = /console\[b\(0x0\)]\('a'\);/;
 
         let obfuscatedCode: string;
 
@@ -639,11 +747,11 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #13: `reservedStrings` option is enabled', () => {
+    describe('Variant #14: `reservedStrings` option is enabled', () => {
         describe('Variant #1: base `reservedStrings` values', () => {
             describe('Variant #1: single reserved string value', () => {
                 const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
-                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x0'\);/;
+                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -702,7 +810,7 @@ describe('StringArrayTransformer', function () {
 
         describe('Variant #2: RegExp `reservedStrings` values', () => {
             describe('Variant #1: single reserved string value', () => {
-                const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\('0x0'\);/;
+                const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\(0x0\);/;
                 const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
 
                 let obfuscatedCode: string;
@@ -761,11 +869,11 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #14: `forceTransformStrings` option is enabled', () => {
+    describe('Variant #15: `forceTransformStrings` option is enabled', () => {
         describe('Variant #1: base `forceTransformStrings` values', () => {
             describe('Variant #1: single force transform string value', () => {
                 const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
-                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x0'\);/;
+                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -793,8 +901,8 @@ describe('StringArrayTransformer', function () {
             });
 
             describe('Variant #2: two force transform string values', () => {
-                const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\('0x0'\);/;
-                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x1'\);/;
+                const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\(0x0\);/;
+                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\(0x1\);/;
 
                 let obfuscatedCode: string;
 
@@ -825,7 +933,7 @@ describe('StringArrayTransformer', function () {
         describe('Variant #2: RegExp `forceTransformStrings` values', () => {
             describe('Variant #1: single force transform string value', () => {
                 const stringLiteralRegExp1: RegExp = /const foo *= *'foo'/;
-                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x0'\);/;
+                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -853,8 +961,8 @@ describe('StringArrayTransformer', function () {
             });
 
             describe('Variant #2: two force transform string values', () => {
-                const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\('0x0'\);/;
-                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x1'\);/;
+                const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\(0x0\);/;
+                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\(0x1\);/;
 
                 let obfuscatedCode: string;
 
@@ -916,7 +1024,7 @@ describe('StringArrayTransformer', function () {
         describe('Variant #4: Priority over `reservedStrings` option', () => {
             describe('Variant #1: base case', () => {
                 const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
-                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x0'\);/;
+                const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -949,7 +1057,7 @@ describe('StringArrayTransformer', function () {
             describe('Variant #1: base case', () => {
                 const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
                 const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
-                const stringLiteralRegExp3: RegExp = /const baz *= *_0x([a-f0-9]){4}\('0x0'\);/;
+                const stringLiteralRegExp3: RegExp = /const baz *= *_0x([a-f0-9]){4}\(0x0\);/;
 
                 let obfuscatedCode: string;
 
@@ -982,10 +1090,10 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #15: object expression key literal', () => {
+    describe('Variant #16: object expression key literal', () => {
         describe('Variant #1: base key literal', () => {
             const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['bar'];/;
-            const objectExpressionRegExp: RegExp = /var test *= *{'foo' *: *_0x([a-f0-9]){4}\('0x0'\)};/;
+            const objectExpressionRegExp: RegExp = /var test *= *{'foo' *: *_0x([a-f0-9]){4}\(0x0\)};/;
 
             let obfuscatedCode: string;
 
@@ -1013,7 +1121,7 @@ describe('StringArrayTransformer', function () {
 
         describe('Variant #2: computed key literal', () => {
             const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['foo', *'bar'];/;
-            const objectExpressionRegExp: RegExp = /var test *= *{\[_0x([a-f0-9]){4}\('0x0'\)] *: *_0x([a-f0-9]){4}\('0x1'\)};/;
+            const objectExpressionRegExp: RegExp = /var test *= *{\[_0x([a-f0-9]){4}\(0x0\)] *: *_0x([a-f0-9]){4}\(0x1\)};/;
 
             let obfuscatedCode: string;
 
@@ -1040,7 +1148,7 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #16: import declaration source literal', () => {
+    describe('Variant #17: import declaration source literal', () => {
         const importDeclarationRegExp: RegExp = /import *{ *bar *} *from *'foo';/;
 
         let obfuscatedCode: string;
@@ -1063,7 +1171,7 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #17: export all declaration source literal', () => {
+    describe('Variant #18: export all declaration source literal', () => {
         const exportAllDeclarationRegExp: RegExp = /export *\* *from *'foo';/;
 
         let obfuscatedCode: string;
@@ -1086,7 +1194,7 @@ describe('StringArrayTransformer', function () {
         });
     });
 
-    describe('Variant #18: export named declaration source literal', () => {
+    describe('Variant #19: export named declaration source literal', () => {
         const exportNamedDeclarationRegExp: RegExp = /export *{ *bar *} *from *'foo';/;
 
         let obfuscatedCode: string;

+ 23 - 23
test/functional-tests/storages/string-array-transformers/string-array-storage/StringArrayStorage.spec.ts

@@ -16,7 +16,7 @@ describe('StringArrayStorage', () => {
             const expectedVariantProbability: number = 1;
 
             const stringArrayVariant1RegExp1: RegExp = /var _0x([a-f0-9]){4} *= *\['test'];/g;
-            const literalNodeVariant1RegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/g;
+            const literalNodeVariant1RegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/g;
 
             let stringArrayVariant1Probability: number,
                 literalNodeVariant1Probability: number;
@@ -80,9 +80,9 @@ describe('StringArrayStorage', () => {
             ];
             const literalNodeVariantRegExps: RegExp[] = [
                 new RegExp(
-                    `var foo *= *_0x([a-f0-9]){4}\\('0x0'\\); *` +
-                    `var bar *= *_0x([a-f0-9]){4}\\('0x1'\\); *` +
-                    `var baz *= *_0x([a-f0-9]){4}\\('0x2'\\);`
+                    `var foo *= *_0x([a-f0-9]){4}\\(0x0\\); *` +
+                    `var bar *= *_0x([a-f0-9]){4}\\(0x1\\); *` +
+                    `var baz *= *_0x([a-f0-9]){4}\\(0x2\\);`
                 )
             ];
 
@@ -157,7 +157,7 @@ describe('StringArrayStorage', () => {
             const expectedVariantProbability: number = 1;
 
             const stringArrayVariantRegExp1: RegExp = /var _0x([a-f0-9]){4} *= *\['test'];/g;
-            const literalNodeVariant1RegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/g;
+            const literalNodeVariant1RegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/g;
 
             let stringArrayVariant1Probability: number,
                 literalNodeVariant1Probability: number;
@@ -223,34 +223,34 @@ describe('StringArrayStorage', () => {
 
             const literalNodeVariantRegExps: RegExp[] = [
                 new RegExp(
-                    `var foo *= *_0x([a-f0-9]){4}\\('0x0'\\); *` +
-                    `var bar *= *_0x([a-f0-9]){4}\\('0x1'\\); *` +
-                    `var baz *= *_0x([a-f0-9]){4}\\('0x2'\\);`
+                    `var foo *= *_0x([a-f0-9]){4}\\(0x0\\); *` +
+                    `var bar *= *_0x([a-f0-9]){4}\\(0x1\\); *` +
+                    `var baz *= *_0x([a-f0-9]){4}\\(0x2\\);`
                 ),
                 new RegExp(
-                    `var foo *= *_0x([a-f0-9]){4}\\('0x0'\\); *` +
-                    `var bar *= *_0x([a-f0-9]){4}\\('0x2'\\); *` +
-                    `var baz *= *_0x([a-f0-9]){4}\\('0x1'\\);`
+                    `var foo *= *_0x([a-f0-9]){4}\\(0x0\\); *` +
+                    `var bar *= *_0x([a-f0-9]){4}\\(0x2\\); *` +
+                    `var baz *= *_0x([a-f0-9]){4}\\(0x1\\);`
                 ),
                 new RegExp(
-                    `var foo *= *_0x([a-f0-9]){4}\\('0x1'\\); *` +
-                    `var bar *= *_0x([a-f0-9]){4}\\('0x0'\\); *` +
-                    `var baz *= *_0x([a-f0-9]){4}\\('0x2'\\);`
+                    `var foo *= *_0x([a-f0-9]){4}\\(0x1\\); *` +
+                    `var bar *= *_0x([a-f0-9]){4}\\(0x0\\); *` +
+                    `var baz *= *_0x([a-f0-9]){4}\\(0x2\\);`
                 ),
                 new RegExp(
-                    `var foo *= *_0x([a-f0-9]){4}\\('0x1'\\); *` +
-                    `var bar *= *_0x([a-f0-9]){4}\\('0x2'\\); *` +
-                    `var baz *= *_0x([a-f0-9]){4}\\('0x0'\\);`
+                    `var foo *= *_0x([a-f0-9]){4}\\(0x1\\); *` +
+                    `var bar *= *_0x([a-f0-9]){4}\\(0x2\\); *` +
+                    `var baz *= *_0x([a-f0-9]){4}\\(0x0\\);`
                 ),
                 new RegExp(
-                    `var foo *= *_0x([a-f0-9]){4}\\('0x2'\\); *` +
-                    `var bar *= *_0x([a-f0-9]){4}\\('0x0'\\); *` +
-                    `var baz *= *_0x([a-f0-9]){4}\\('0x1'\\);`
+                    `var foo *= *_0x([a-f0-9]){4}\\(0x2\\); *` +
+                    `var bar *= *_0x([a-f0-9]){4}\\(0x0\\); *` +
+                    `var baz *= *_0x([a-f0-9]){4}\\(0x1\\);`
                 ),
                 new RegExp(
-                    `var foo *= *_0x([a-f0-9]){4}\\('0x2'\\); *` +
-                    `var bar *= *_0x([a-f0-9]){4}\\('0x1'\\); *` +
-                    `var baz *= *_0x([a-f0-9]){4}\\('0x0'\\);`
+                    `var foo *= *_0x([a-f0-9]){4}\\(0x2\\); *` +
+                    `var bar *= *_0x([a-f0-9]){4}\\(0x1\\); *` +
+                    `var baz *= *_0x([a-f0-9]){4}\\(0x0\\);`
                 )
             ];
 

+ 5 - 0
test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts

@@ -4,6 +4,7 @@ import { TInputOptions } from '../../src/types/options/TInputOptions';
 
 import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { StringArrayEncoding } from '../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
+import { StringArrayIndexesType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayWrappersType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
 import { evaluateInWorker } from '../helpers/evaluateInWorker';
@@ -40,6 +41,10 @@ describe('JavaScriptObfuscator runtime eval', function () {
             StringArrayEncoding.Base64,
             StringArrayEncoding.Rc4
         ],
+        stringArrayIndexesType: [
+            StringArrayIndexesType.HexadecimalNumber,
+            StringArrayIndexesType.HexadecimalNumericString
+        ],
         stringArrayIndexShift: true,
         stringArrayWrappersChainedCalls: true,
         stringArrayWrappersCount: 5,

Некоторые файлы не были показаны из-за большого количества измененных файлов