瀏覽代碼

WIP: added random string array scope calls wrappers parameters

sanex 4 年之前
父節點
當前提交
4079b42fbd
共有 22 個文件被更改,包括 552 次插入76 次删除
  1. 0 1
      dist/index.cli.js
  2. 0 1
      dist/index.js
  3. 6 0
      src/constants/node-transformers/string-array-transformers/StringArrayRootCallsWrapperParametersDefaultCount.ts
  4. 6 0
      src/constants/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperParametersMaxCount.ts
  5. 2 0
      src/container/modules/custom-nodes/CustomNodesModule.ts
  6. 18 0
      src/custom-nodes/string-array-nodes/AbstractStringArrayCallNode.ts
  7. 75 10
      src/custom-nodes/string-array-nodes/StringArrayCallNode.ts
  8. 108 32
      src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode.ts
  9. 8 0
      src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode.ts
  10. 18 0
      src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData.ts
  11. 7 0
      src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData.ts
  12. 11 0
      src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData.ts
  13. 7 0
      src/interfaces/utils/IArrayUtils.ts
  14. 8 0
      src/interfaces/utils/IRandomGenerator.ts
  15. 45 18
      src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts
  16. 46 14
      src/node-transformers/string-array-transformers/StringArrayTransformer.ts
  17. 15 0
      src/utils/ArrayUtils.ts
  18. 20 0
      src/utils/RandomGenerator.ts
  19. 4 0
      test/dev/dev.ts
  20. 1 0
      test/index.spec.ts
  21. 55 0
      test/unit-tests/utils/ArrayUtils.spec.ts
  22. 92 0
      test/unit-tests/utils/RandomGenerator.spec.ts

File diff suppressed because it is too large
+ 0 - 1
dist/index.cli.js


File diff suppressed because it is too large
+ 0 - 1
dist/index.js


+ 6 - 0
src/constants/node-transformers/string-array-transformers/StringArrayRootCallsWrapperParametersDefaultCount.ts

@@ -0,0 +1,6 @@
+/**
+ * Max count of root string array calls wrapper parameters
+ *
+ * @type {number}
+ */
+export const stringArrayRootCallsWrapperParametersDefaultCount: number = 2;

+ 6 - 0
src/constants/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperParametersMaxCount.ts

@@ -0,0 +1,6 @@
+/**
+ * Max count of scope string array calls wrapper parameters
+ *
+ * @type {number}
+ */
+export const stringArrayScopeCallsWrapperParametersMaxCount: number = 5;

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

@@ -141,6 +141,8 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
                 ServiceIdentifiers.Factory__IIdentifierNamesGenerator,
                 ServiceIdentifiers.Factory__IStringArrayIndexNode,
                 ServiceIdentifiers.ICustomCodeHelperFormatter,
+                ServiceIdentifiers.IStringArrayStorage,
+                ServiceIdentifiers.IArrayUtils,
                 ServiceIdentifiers.IRandomGenerator,
                 ServiceIdentifiers.IOptions
             ));

+ 18 - 0
src/custom-nodes/string-array-nodes/AbstractStringArrayCallNode.ts

@@ -10,6 +10,7 @@ import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 
 import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayIndexNode } from '../../enums/custom-nodes/string-array-index-nodes/StringArrayIndexNode';
@@ -18,6 +19,7 @@ import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeMetadata } from '../../node/NodeMetadata';
 import { NodeUtils } from '../../node/NodeUtils';
+import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
 
 @injectable()
 export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
@@ -29,6 +31,16 @@ export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
         [StringArrayIndexesType.HexadecimalNumericString, StringArrayIndexNode.StringArrayHexadecimalNumericStringIndexNode]
     ]);
 
+    /**
+     * @type {IArrayUtils}
+     */
+    protected readonly arrayUtils: IArrayUtils;
+
+    /**
+     * @type {IStringArrayStorage}
+     */
+    protected readonly stringArrayStorage: IStringArrayStorage;
+
     /**
      * @type {TStringArrayIndexNodeFactory}
      */
@@ -38,6 +50,8 @@ export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
+     * @param {IStringArrayStorage} stringArrayStorage
+     * @param {IArrayUtils} arrayUtils
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
@@ -47,6 +61,8 @@ export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
         @inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
             stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
+        @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
+        @inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
@@ -58,6 +74,8 @@ export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
         );
 
         this.stringArrayIndexNodeFactory = stringArrayIndexNodeFactory;
+        this.stringArrayStorage = stringArrayStorage;
+        this.arrayUtils = arrayUtils;
     }
 
     /**

+ 75 - 10
src/custom-nodes/string-array-nodes/StringArrayCallNode.ts

@@ -7,12 +7,18 @@ import { TIdentifierNamesGeneratorFactory } from '../../types/container/generato
 import { TStatement } from '../../types/node/TStatement';
 import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory';
 
+import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
+import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 
 import { initializable } from '../../decorators/Initializable';
 
+import { stringArrayRootCallsWrapperParametersDefaultCount } from '../../constants/node-transformers/string-array-transformers/StringArrayRootCallsWrapperParametersDefaultCount';
+import { stringArrayScopeCallsWrapperParametersMaxCount } from '../../constants/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperParametersMaxCount';
+
 import { AbstractStringArrayCallNode } from './AbstractStringArrayCallNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeUtils } from '../../node/NodeUtils';
@@ -43,10 +49,18 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
     @initializable()
     private stringArrayCallsWrapperName!: string;
 
+    /**
+     * @type {IStringArrayScopeCallsWrapperParameterIndexesData | null}
+     */
+    @initializable()
+    private stringArrayCallsWrapperParameterIndexesData!: IStringArrayScopeCallsWrapperParameterIndexesData | null;
+
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
+     * @param {IStringArrayStorage} stringArrayStorage
+     * @param {IArrayUtils} arrayUtils
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
@@ -56,6 +70,8 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
         @inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
             stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
+        @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
+        @inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
@@ -63,6 +79,8 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
             identifierNamesGeneratorFactory,
             stringArrayIndexNodeFactory,
             customCodeHelperFormatter,
+            stringArrayStorage,
+            arrayUtils,
             randomGenerator,
             options
         );
@@ -70,17 +88,20 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
 
     /**
      * @param {string} stringArrayCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayCallsWrapperParameterIndexesData
      * @param {number} index
      * @param {number} indexShiftAmount
      * @param {string | null} decodeKey
      */
     public initialize (
         stringArrayCallsWrapperName: string,
+        stringArrayCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
         index: number,
         indexShiftAmount: number,
         decodeKey: string | null
     ): void {
         this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
+        this.stringArrayCallsWrapperParameterIndexesData = stringArrayCallsWrapperParameterIndexesData;
         this.index = index;
         this.indexShiftAmount = indexShiftAmount;
         this.decodeKey = decodeKey;
@@ -90,21 +111,42 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
      * @returns {TStatement[]}
      */
     protected getNodeStructure (): TStatement[] {
-        const callExpressionArgs: ESTree.Expression[] = [
-            this.getStringArrayIndexNode(this.indexShiftAmount + this.index)
-        ];
+        const resultIndex: number = this.indexShiftAmount + this.index;
+
+        const indexNode: ESTree.Expression = this.getStringArrayIndexNode(resultIndex);
+        const rc4KeyLiteralNode: ESTree.Literal | null = this.decodeKey
+            ? this.getRc4KeyLiteralNode(this.decodeKey)
+            : null;
+
+        // filling call expression arguments with a fake arguments first
+        const callExpressionArgs: ESTree.Expression[] = this.arrayUtils.fillWithRange(
+            !this.stringArrayCallsWrapperParameterIndexesData
+                // root string array calls wrapper
+                ? stringArrayRootCallsWrapperParametersDefaultCount
+                // scope string array calls wrapper
+                : stringArrayScopeCallsWrapperParametersMaxCount,
+            () => this.getFakeStringArrayIndexNode(resultIndex)
+        );
 
-        if (this.decodeKey) {
-            callExpressionArgs.push(this.getRc4KeyLiteralNode(this.decodeKey));
+        callExpressionArgs.splice(this.stringArrayCallsWrapperParameterIndexesData?.index ?? 0, 1, indexNode);
+
+        if (this.stringArrayCallsWrapperParameterIndexesData) {
+            callExpressionArgs.splice(
+                this.stringArrayCallsWrapperParameterIndexesData.decodeKey,
+                1,
+                // use rc4 key literal node if exists or a node with fake string array wrapper index
+                rc4KeyLiteralNode ?? this.getFakeStringArrayIndexNode(resultIndex)
+            );
+        } else if (rc4KeyLiteralNode) {
+            callExpressionArgs.splice(1, 1, rc4KeyLiteralNode);
+        } else {
+            // have to delete element
+            callExpressionArgs.splice(1, 1);
         }
 
-        const stringArrayIdentifierNode: ESTree.Identifier =
-            NodeFactory.identifierNode(this.stringArrayCallsWrapperName);
-
-
         const structure: TStatement = NodeFactory.expressionStatementNode(
             NodeFactory.callExpressionNode(
-                stringArrayIdentifierNode,
+                NodeFactory.identifierNode(this.stringArrayCallsWrapperName),
                 callExpressionArgs
             )
         );
@@ -113,4 +155,27 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
 
         return [structure];
     }
+
+    /**
+     * @param {number} actualIndex
+     * @returns {Expression}
+     */
+    private getFakeStringArrayIndexNode (actualIndex: number): ESTree.Expression {
+        return this.getStringArrayIndexNode(this.getFakeStringArrayIndex(actualIndex));
+    }
+
+    /**
+     * @param {number} actualIndex
+     * @returns {number}
+     */
+    private getFakeStringArrayIndex (actualIndex: number): number {
+        const stringArrayStorageLength: number = this.stringArrayStorage.getLength();
+
+        const fakeIndexOffset: number = stringArrayStorageLength / 2;
+
+        const minimumIndex: number = actualIndex - fakeIndexOffset;
+        const maximumIndex: number = actualIndex + fakeIndexOffset;
+
+        return this.randomGenerator.getRandomInteger(minimumIndex, maximumIndex);
+    }
 }

+ 108 - 32
src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode.ts

@@ -7,15 +7,21 @@ import { TIdentifierNamesGeneratorFactory } from '../../types/container/generato
 import { TStatement } from '../../types/node/TStatement';
 import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory';
 
+import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
+import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 
 import { initializable } from '../../decorators/Initializable';
 
+import { stringArrayScopeCallsWrapperParametersMaxCount } from '../../constants/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperParametersMaxCount';
+
 import { AbstractStringArrayCallNode } from './AbstractStringArrayCallNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeUtils } from '../../node/NodeUtils';
+import { stringArrayRootCallsWrapperParametersDefaultCount } from '../../constants/node-transformers/string-array-transformers/StringArrayRootCallsWrapperParametersDefaultCount';
 
 @injectable()
 export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArrayCallNode {
@@ -29,7 +35,13 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
      * @type {string}
      */
     @initializable()
-    private stringArrayCallsWrapperName!: string;
+    private upperStringArrayCallsWrapperName!: string;
+
+    /**
+     * @type {IStringArrayScopeCallsWrapperParameterIndexesData}
+     */
+    @initializable()
+    private upperStringArrayCallsWrapperParameterIndexesData!: IStringArrayScopeCallsWrapperParameterIndexesData | null;
 
     /**
      * @type {string}
@@ -37,11 +49,19 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
     @initializable()
     private stringArrayScopeCallsWrapperName!: string;
 
+    /**
+     * @type {IStringArrayScopeCallsWrapperParameterIndexesData}
+     */
+    @initializable()
+    private stringArrayScopeCallsWrapperParameterIndexesData!: IStringArrayScopeCallsWrapperParameterIndexesData | null;
+
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
+     * @param {IStringArrayStorage} stringArrayStorage
+     * @param {IArrayUtils} arrayUtils
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
@@ -51,6 +71,8 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
         @inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
             stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
+        @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
+        @inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
@@ -58,6 +80,8 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
             identifierNamesGeneratorFactory,
             stringArrayIndexNodeFactory,
             customCodeHelperFormatter,
+            stringArrayStorage,
+            arrayUtils,
             randomGenerator,
             options
         );
@@ -65,16 +89,22 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
 
     /**
      * @param {string} stringArrayScopeCallsWrapperName
-     * @param {string} stringArrayCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayScopeCallsWrapperParameterIndexesData
+     * @param {string} upperStringArrayCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} upperStringArrayCallsWrapperParameterIndexesData
      * @param {number} shiftedIndex
      */
     public initialize (
         stringArrayScopeCallsWrapperName: string,
-        stringArrayCallsWrapperName: string,
-        shiftedIndex: number
+        stringArrayScopeCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
+        upperStringArrayCallsWrapperName: string,
+        upperStringArrayCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
+        shiftedIndex: number,
     ): void {
         this.stringArrayScopeCallsWrapperName = stringArrayScopeCallsWrapperName;
-        this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
+        this.stringArrayScopeCallsWrapperParameterIndexesData = stringArrayScopeCallsWrapperParameterIndexesData;
+        this.upperStringArrayCallsWrapperName = upperStringArrayCallsWrapperName;
+        this.upperStringArrayCallsWrapperParameterIndexesData = upperStringArrayCallsWrapperParameterIndexesData;
         this.shiftedIndex = shiftedIndex;
     }
 
@@ -84,32 +114,52 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
     protected getNodeStructure (): TStatement[] {
         // identifiers of function expression parameters
         // as a temporary names use random strings
-        const firstParameterIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.randomGenerator.getRandomString(6));
-        const secondParameterIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.randomGenerator.getRandomString(6));
+        const stringArrayCallIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.randomGenerator.getRandomString(6));
+        const decodeKeyIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.randomGenerator.getRandomString(6));
 
-        // identifiers of call to the parent string array scope wrapper
-        // as a temporary names use random strings
-        const firstCallArgumentIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.randomGenerator.getRandomString(6));
-        const secondCallArgumentIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.randomGenerator.getRandomString(6));
+        const stringArrayCallNode: ESTree.Expression = this.getUpperStringArrayCallNode(
+            stringArrayCallIdentifierNode,
+            this.getStringArrayIndexNode(this.shiftedIndex)
+        );
+
+        // stage 1: function expression node parameters
+        // filling all parameters with a fake parameters first
+        const parameters: ESTree.Identifier[] = this.arrayUtils.fillWithRange(
+            !this.stringArrayScopeCallsWrapperParameterIndexesData
+                // root string array calls wrapper
+                ? stringArrayRootCallsWrapperParametersDefaultCount
+                // scope string array calls wrapper
+                : stringArrayScopeCallsWrapperParametersMaxCount,
+            () => this.getFakeParameterNode()
+        );
+        parameters.splice(this.stringArrayScopeCallsWrapperParameterIndexesData?.index ?? 0, 1, stringArrayCallIdentifierNode);
+        parameters.splice(this.stringArrayScopeCallsWrapperParameterIndexesData?.decodeKey ?? 1, 1, decodeKeyIdentifierNode);
+
+        // stage 2: upper string array call expression arguments
+        // filling all call expression arguments with a fake string array calls
+        const callExpressionArgs: ESTree.Expression[] = this.arrayUtils.fillWithRange(
+            !this.upperStringArrayCallsWrapperParameterIndexesData
+                // root string array calls wrapper
+                ? stringArrayRootCallsWrapperParametersDefaultCount
+                // scope string array calls wrapper
+                : stringArrayScopeCallsWrapperParametersMaxCount,
+            (index: number) => this.getUpperStringArrayCallNode(
+                parameters[index],
+                this.getFakeUpperStringArrayIndexNode()
+            )
+        );
+
+        callExpressionArgs.splice(this.upperStringArrayCallsWrapperParameterIndexesData?.index ?? 0, 1, stringArrayCallNode);
+        callExpressionArgs.splice(this.upperStringArrayCallsWrapperParameterIndexesData?.decodeKey ?? 1, 1, decodeKeyIdentifierNode);
 
-        // function expression node
+        // stage 3: function expression node
         const functionExpressionNode: ESTree.FunctionExpression =  NodeFactory.functionExpressionNode(
-            [
-                firstParameterIdentifierNode,
-                secondParameterIdentifierNode,
-            ],
+            parameters,
             NodeFactory.blockStatementNode([
                 NodeFactory.returnStatementNode(
                     NodeFactory.callExpressionNode(
-                        NodeFactory.identifierNode(this.stringArrayCallsWrapperName),
-                        [
-                            NodeFactory.binaryExpressionNode(
-                                '-',
-                                firstCallArgumentIdentifierNode,
-                                this.getStringArrayIndexNode(this.shiftedIndex)
-                            ),
-                            secondCallArgumentIdentifierNode
-                        ]
+                        NodeFactory.identifierNode(this.upperStringArrayCallsWrapperName),
+                        callExpressionArgs
                     )
                 )
             ])
@@ -127,16 +177,42 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
 
         NodeUtils.parentizeAst(structure);
 
+        // stage 4: rename
         // have to generate names for both parameter and call identifiers
-        const firstParameterName: string = this.identifierNamesGenerator.generateForLexicalScope(functionExpressionNode);
-        const secondParameterName: string = this.identifierNamesGenerator.generateForLexicalScope(functionExpressionNode);
+        for (const parameter of parameters) {
+            parameter.name = this.identifierNamesGenerator.generateForLexicalScope(functionExpressionNode);
+        }
 
-        firstParameterIdentifierNode.name = firstParameterName;
-        secondParameterIdentifierNode.name = secondParameterName;
+        return [structure];
+    }
 
-        firstCallArgumentIdentifierNode.name = firstParameterName;
-        secondCallArgumentIdentifierNode.name = secondParameterName;
+    /**
+     * @param {Identifier} indexParameterIdentifierNode
+     * @param {Expression} indexShiftNode
+     * @returns {Expression}
+     */
+    private getUpperStringArrayCallNode (
+        indexParameterIdentifierNode: ESTree.Identifier,
+        indexShiftNode: ESTree.Expression
+    ): ESTree.Expression {
+        return NodeFactory.binaryExpressionNode(
+            '-',
+            indexParameterIdentifierNode,
+            indexShiftNode
+        );
+    }
 
-        return [structure];
+    /**
+     * @returns {Identifier}
+     */
+    private getFakeParameterNode (): ESTree.Identifier {
+        return NodeFactory.identifierNode(this.randomGenerator.getRandomString(6));
+    }
+
+    /**
+     * @returns {Expression}
+     */
+    private getFakeUpperStringArrayIndexNode (): ESTree.Expression {
+        return this.getStringArrayIndexNode(this.randomGenerator.getRandomInteger(0, 500));
     }
 }

+ 8 - 0
src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode.ts

@@ -5,9 +5,11 @@ import { TIdentifierNamesGeneratorFactory } from '../../types/container/generato
 import { TStatement } from '../../types/node/TStatement';
 import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory';
 
+import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -34,6 +36,8 @@ export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArra
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
+     * @param {IStringArrayStorage} stringArrayStorage
+     * @param {IArrayUtils} arrayUtils
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
@@ -43,6 +47,8 @@ export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArra
         @inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
             stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
+        @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
+        @inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions,
     ) {
@@ -50,6 +56,8 @@ export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArra
             identifierNamesGeneratorFactory,
             stringArrayIndexNodeFactory,
             customCodeHelperFormatter,
+            stringArrayStorage,
+            arrayUtils,
             randomGenerator,
             options
         );

+ 18 - 0
src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData.ts

@@ -0,0 +1,18 @@
+import { IStringArrayScopeCallsWrapperParameterIndexesData } from './IStringArrayScopeCallsWrapperParameterIndexesData';
+
+export interface IStringArrayScopeCallsWrapperData {
+    /**
+     * @type {number}
+     */
+    index: number;
+
+    /**
+     * @type {string}
+     */
+    name: string;
+
+    /**
+     * @type {IStringArrayScopeCallsWrapperParameterIndexesData | null}
+     */
+    parameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null;
+}

+ 7 - 0
src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData.ts

@@ -1,5 +1,7 @@
 import { TNodeWithLexicalScopeStatements } from '../../../types/node/TNodeWithLexicalScopeStatements';
 
+import { IStringArrayScopeCallsWrapperParameterIndexesData } from './IStringArrayScopeCallsWrapperParameterIndexesData';
+
 export interface IStringArrayScopeCallsWrapperLexicalScopeData {
     /**
      * @type {TNodeWithLexicalScopeStatements | null}
@@ -15,4 +17,9 @@ export interface IStringArrayScopeCallsWrapperLexicalScopeData {
      * @type {number}
      */
     scopeShiftedIndex: number;
+
+    /**
+     * @type {IStringArrayScopeCallsWrapperParameterIndexesData | null}
+     */
+    callsWrappersParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null;
 }

+ 11 - 0
src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData.ts

@@ -0,0 +1,11 @@
+export interface IStringArrayScopeCallsWrapperParameterIndexesData {
+    /**
+     * @type {number}
+     */
+    index: number;
+
+    /**
+     * @type {number}
+     */
+    decodeKey: number;
+}

+ 7 - 0
src/interfaces/utils/IArrayUtils.ts

@@ -5,6 +5,13 @@ export interface IArrayUtils {
      */
     createWithRange (length: number): number[];
 
+    /**
+     * @param {number} length
+     * @param {(index: number) => TValue} valueFunction
+     * @returns {TValue[]}
+     */
+    fillWithRange <TValue> (length: number, valueFunction: (index: number) => TValue): TValue[];
+
     /**
      * @param {T[]} array
      * @returns {T | null}

+ 8 - 0
src/interfaces/utils/IRandomGenerator.ts

@@ -20,6 +20,14 @@ export interface IRandomGenerator {
      */
     getRandomInteger (min: number, max: number): number;
 
+    /**
+     * @param {number} min
+     * @param {number} max
+     * @param {number[]} valuesToExclude
+     * @returns {number}
+     */
+    getRandomIntegerExcluding (min: number, max: number, valuesToExclude: number[]): number;
+
     /**
      * @param length
      * @param pool

+ 45 - 18
src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts

@@ -12,10 +12,12 @@ import { TStringArrayCustomNodeFactory } from '../../types/container/custom-node
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 import { IStringArrayScopeCallsWrapperLexicalScopeData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData';
 import { IStringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage';
 import { IStringArrayScopeCallsWrapperNamesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperNamesData';
 import { IStringArrayScopeCallsWrapperNamesDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage';
+import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IVisitedLexicalScopeNodesStackStorage } from '../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
@@ -132,6 +134,9 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
             return lexicalScopeBodyNode;
         }
 
+        const {
+            callsWrappersParameterIndexesData: stringArrayScopeCallsWrapperParameterIndexes
+        } = stringArrayScopeCallsWrapperLexicalScopeData;
         const stringArrayScopeCallsWrapperNamesDataList: (IStringArrayScopeCallsWrapperNamesData | undefined)[] =
             Object.values(stringArrayScopeCallsWrapperNamesDataByEncoding);
 
@@ -150,17 +155,20 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
              */
             for (let i = namesLength - 1; i >= 0; i--) {
                 const stringArrayScopeCallsWrapperName: string = names[i];
-                const [
-                    upperStringArrayCallsWrapperName,
-                    upperStringArrayCallsWrapperShiftedIndex,
-                ] = this.getUpperStringArrayCallsWrapperData(
+                const {
+                    name: upperStringArrayCallsWrapperName,
+                    index: upperStringArrayCallsWrapperShiftedIndex,
+                    parameterIndexesData: upperStringArrayCallsWrapperParameterIndexes
+                } = this.getUpperStringArrayCallsWrapperData(
                     stringArrayScopeCallsWrapperNamesData,
                     stringArrayScopeCallsWrapperLexicalScopeData,
                 );
 
                 const stringArrayScopeCallsWrapperNode: TStatement[] = this.getStringArrayScopeCallsWrapperNode(
                     stringArrayScopeCallsWrapperName,
+                    stringArrayScopeCallsWrapperParameterIndexes,
                     upperStringArrayCallsWrapperName,
+                    upperStringArrayCallsWrapperParameterIndexes,
                     upperStringArrayCallsWrapperShiftedIndex
                 );
 
@@ -177,30 +185,31 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     /**
      * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
      * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperLexicalScopeData
-     * @returns {[name: string, index: number]}
+     * @returns {IStringArrayScopeCallsWrapperData}
      */
     private getRootStringArrayCallsWrapperData (
         stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
         stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
-    ): [name: string, index: number] {
+    ): IStringArrayScopeCallsWrapperData {
         const {encoding} = stringArrayScopeCallsWrapperNamesData;
         const {resultShiftedIndex} = stringArrayScopeCallsWrapperLexicalScopeData;
 
-        return [
-            this.stringArrayStorage.getStorageCallsWrapperName(encoding),
-            resultShiftedIndex
-        ];
+        return {
+            name: this.stringArrayStorage.getStorageCallsWrapperName(encoding),
+            index: resultShiftedIndex,
+            parameterIndexesData: null
+        };
     }
 
     /**
      * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
      * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperLexicalScopeData
-     * @returns {[name: string, index: number]}
+     * @returns {IStringArrayScopeCallsWrapperData}
      */
     private getUpperStringArrayCallsWrapperData (
         stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
         stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
-    ): [name: string, index: number] {
+    ): IStringArrayScopeCallsWrapperData {
         const {encoding} = stringArrayScopeCallsWrapperNamesData;
         const {scopeShiftedIndex} = stringArrayScopeCallsWrapperLexicalScopeData;
 
@@ -223,6 +232,9 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
         const parentLexicalScopeNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding | null = this.stringArrayScopeCallsWrapperNamesDataStorage
             .get(parentLexicalScopeBodyNode) ?? null;
+        const parentScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData | null = this.stringArrayScopeCallsWrapperLexicalScopeDataStorage
+            .get(parentLexicalScopeBodyNode) ?? null;
+
         const parentLexicalScopeNames: string[] | null = parentLexicalScopeNamesDataByEncoding?.[encoding]?.names ?? null;
 
         if (!parentLexicalScopeNames?.length) {
@@ -232,29 +244,38 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
         const upperStringArrayCallsWrapperName: string = this.randomGenerator
             .getRandomGenerator()
             .pickone(parentLexicalScopeNames);
-
-        return [
-            upperStringArrayCallsWrapperName,
-            scopeShiftedIndex
-        ];
+        const parameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null =
+            parentScopeCallsWrapperLexicalScopeData?.callsWrappersParameterIndexesData ?? null;
+
+        return {
+            name: upperStringArrayCallsWrapperName,
+            index: scopeShiftedIndex,
+            parameterIndexesData
+        };
     }
 
     /**
      * @param {string} stringArrayScopeCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayScopeCallsWrapperParameterIndexes
      * @param {string} upperStringArrayCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} upperStringArrayCallsWrapperParameterIndexes
      * @param {number} stringArrayScopeCallsWrapperShiftedIndex
      * @returns {TStatement[]}
      */
     private getStringArrayScopeCallsWrapperNode (
         stringArrayScopeCallsWrapperName: string,
+        stringArrayScopeCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
         upperStringArrayCallsWrapperName: string,
+        upperStringArrayCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
         stringArrayScopeCallsWrapperShiftedIndex: number
     ): TStatement[] {
         switch (this.options.stringArrayWrappersType) {
             case StringArrayWrappersType.Function:
                 return this.getStringArrayScopeCallsWrapperFunctionNode(
                     stringArrayScopeCallsWrapperName,
+                    stringArrayScopeCallsWrapperParameterIndexes,
                     upperStringArrayCallsWrapperName,
+                    upperStringArrayCallsWrapperParameterIndexes,
                     stringArrayScopeCallsWrapperShiftedIndex
                 );
 
@@ -291,13 +312,17 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
     /**
      * @param {string} stringArrayScopeCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayScopeCallsWrapperParameterIndexes
      * @param {string} upperStringArrayCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} upperStringArrayCallsWrapperParameterIndexes
      * @param {number} stringArrayScopeCallsWrapperShiftedIndex
      * @returns {TStatement[]}
      */
     private getStringArrayScopeCallsWrapperFunctionNode (
         stringArrayScopeCallsWrapperName: string,
+        stringArrayScopeCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
         upperStringArrayCallsWrapperName: string,
+        upperStringArrayCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
         stringArrayScopeCallsWrapperShiftedIndex: number
     ): TStatement[] {
         const stringArrayScopeCallsWrapperFunctionNode: ICustomNode<TInitialData<StringArrayScopeCallsWrapperFunctionNode>> =
@@ -307,8 +332,10 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
         stringArrayScopeCallsWrapperFunctionNode.initialize(
             stringArrayScopeCallsWrapperName,
+            stringArrayScopeCallsWrapperParameterIndexes,
             upperStringArrayCallsWrapperName,
-            stringArrayScopeCallsWrapperShiftedIndex
+            upperStringArrayCallsWrapperParameterIndexes,
+            stringArrayScopeCallsWrapperShiftedIndex,
         );
 
         return stringArrayScopeCallsWrapperFunctionNode.getNode();

+ 46 - 14
src/node-transformers/string-array-transformers/StringArrayTransformer.ts

@@ -15,9 +15,11 @@ import { IIdentifierNamesGenerator } from '../../interfaces/generators/identifie
 import { ILiteralNodesCacheStorage } from '../../interfaces/storages/string-array-transformers/ILiteralNodesCacheStorage';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 import { IStringArrayScopeCallsWrapperLexicalScopeData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData';
 import { IStringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage';
 import { IStringArrayScopeCallsWrapperNamesDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage';
+import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
 import { IStringArrayStorageItemData } from '../../interfaces/storages/string-array-transformers/IStringArrayStorageItem';
@@ -28,6 +30,8 @@ import { NodeTransformationStage } from '../../enums/node-transformers/NodeTrans
 import { StringArrayCustomNode } from '../../enums/custom-nodes/StringArrayCustomNode';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
+import { stringArrayScopeCallsWrapperParametersMaxCount } from '../../constants/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperParametersMaxCount';
+
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
@@ -207,7 +211,11 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @returns {Expression}
      */
     private getStringArrayCallNode (stringArrayStorageItemData: IStringArrayStorageItemData): ESTree.Expression {
-        const [stringArrayCallsWrapperName, index] = this.getStringArrayCallsWrapperData(stringArrayStorageItemData);
+        const {
+            name: stringArrayCallsWrapperName,
+            index,
+            parameterIndexesData
+        } = this.getStringArrayCallsWrapperData(stringArrayStorageItemData);
         const {decodeKey } = stringArrayStorageItemData;
 
         const stringArrayCallCustomNode: ICustomNode<TInitialData<StringArrayCallNode>> =
@@ -215,6 +223,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
 
         stringArrayCallCustomNode.initialize(
             stringArrayCallsWrapperName,
+            parameterIndexesData,
             index,
             this.stringArrayStorage.getIndexShiftAmount(),
             decodeKey
@@ -231,11 +240,11 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
 
     /**
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
-     * @returns {[name: string, index: number]}
+     * @returns {IStringArrayScopeCallsWrapperData}
      */
     private getStringArrayCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
-    ): [name: string, index: number] {
+    ): IStringArrayScopeCallsWrapperData {
         return !this.options.stringArrayWrappersCount
             ? this.getRootStringArrayCallsWrapperData(stringArrayStorageItemData)
             : this.getUpperStringArrayCallsWrapperData(stringArrayStorageItemData);
@@ -243,28 +252,29 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
 
     /**
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
-     * @returns {[name: string, index: number]}
+     * @returns {IStringArrayScopeCallsWrapperData}
      */
     private getRootStringArrayCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
-    ): [name: string, index: number] {
+    ): IStringArrayScopeCallsWrapperData {
         const {encoding, index} = stringArrayStorageItemData;
 
         const rootStringArrayCallsWrapperName: string = this.stringArrayStorage.getStorageCallsWrapperName(encoding);
 
-        return [
-            rootStringArrayCallsWrapperName,
+        return {
+            name: rootStringArrayCallsWrapperName,
+            parameterIndexesData: null,
             index
-        ];
+        };
     }
 
     /**
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
-     * @returns {[name: string, index: number]}
+     * @returns {IStringArrayScopeCallsWrapperData}
      */
     private getUpperStringArrayCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
-    ): [name: string, index: number] {
+    ): IStringArrayScopeCallsWrapperData {
         const {encoding, index} = stringArrayStorageItemData;
         const currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
             this.visitedLexicalScopeNodesStackStorage.getLastElement() ?? null;
@@ -295,10 +305,11 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
             ? stringArrayScopeCallsWrapperLexicalScopeData.resultShiftedIndex + index
             : index;
 
-        return [
-            randomUpperStringArrayCallsWrapperName,
-            resultIndex
-        ];
+        return {
+            name: randomUpperStringArrayCallsWrapperName,
+            index: resultIndex,
+            parameterIndexesData: stringArrayScopeCallsWrapperLexicalScopeData.callsWrappersParameterIndexesData
+        };
     }
 
     /**
@@ -362,6 +373,10 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
             ? this.stringArrayScopeCallsWrapperLexicalScopeDataStorage.get(parentLexicalScopeBodyNode) ?? null
             : null;
 
+        const callsWrappersParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null =
+            this.options.stringArrayWrappersType === StringArrayWrappersType.Function
+            ? this.getStringArrayCallsWrapperParameterIndexesData()
+            : null;
         const scopeShiftedIndex: number = this.options.stringArrayWrappersType === StringArrayWrappersType.Function
             ? this.randomGenerator.getRandomInteger(
                 StringArrayTransformer.minShiftedIndexValue,
@@ -373,6 +388,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
             : scopeShiftedIndex;
 
         const lexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData = {
+            callsWrappersParameterIndexesData,
             parentLexicalScopeBodyNode,
             resultShiftedIndex,
             scopeShiftedIndex
@@ -385,4 +401,20 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
 
         return lexicalScopeData;
     }
+
+    /**
+     * @returns {IStringArrayScopeCallsWrapperParameterIndexesData}
+     */
+    private getStringArrayCallsWrapperParameterIndexesData (): IStringArrayScopeCallsWrapperParameterIndexesData {
+        const minIndexValue: number = 0;
+        const maxIndexValue: number = stringArrayScopeCallsWrapperParametersMaxCount - 1;
+
+        const index: number = this.randomGenerator.getRandomInteger(minIndexValue, maxIndexValue);
+        const decodeKey: number = this.randomGenerator.getRandomIntegerExcluding(minIndexValue, maxIndexValue, [index]);
+
+        return {
+            index,
+            decodeKey
+        };
+    }
 }

+ 15 - 0
src/utils/ArrayUtils.ts

@@ -34,6 +34,21 @@ export class ArrayUtils implements IArrayUtils {
         return range;
     }
 
+    /**
+     * @param {number} length
+     * @param {(index: number) => TValue} valueFunction
+     * @returns {TValue[]}
+     */
+    public fillWithRange <TValue> (length: number, valueFunction: (index: number) => TValue): TValue[] {
+        const range: TValue[] = [];
+
+        for (let i: number = 0; i < length; i++) {
+            range.push(valueFunction(i));
+        }
+
+        return range;
+    }
+
     /**
      * @param {T[]} array
      * @returns {T | null}

+ 20 - 0
src/utils/RandomGenerator.ts

@@ -80,6 +80,26 @@ export class RandomGenerator implements IRandomGenerator, IInitializable {
         });
     }
 
+    /**
+     * @param {number} min
+     * @param {number} max
+     * @param {number[]} valuesToExclude
+     * @returns {number}
+     */
+    public getRandomIntegerExcluding (min: number, max: number, valuesToExclude: number[]): number {
+        const valuesToPickArray: number[] = [];
+
+        for (let value: number = min; value <= max; value++) {
+            if (valuesToExclude.includes(value)) {
+                continue;
+            }
+
+            valuesToPickArray.push(value);
+        }
+
+        return this.randomGenerator.pickone(valuesToPickArray);
+    }
+
     /**
      * @param {number} length
      * @param {string} pool

+ 4 - 0
test/dev/dev.ts

@@ -2,6 +2,7 @@
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
 import { StringArrayIndexesType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
+import { StringArrayEncoding } from '../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
 
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
@@ -35,6 +36,9 @@ import { StringArrayIndexesType } from '../../src/enums/node-transformers/string
             rotateStringArray: true,
             shuffleStringArray: true,
             stringArray: true,
+            stringArrayEncoding: [
+                StringArrayEncoding.Rc4
+            ],
             stringArrayIndexesType: [
                 StringArrayIndexesType.HexadecimalNumericString,
                 StringArrayIndexesType.HexadecimalNumber

+ 1 - 0
test/index.spec.ts

@@ -44,6 +44,7 @@ import './unit-tests/utils/CryptUtilsSwappedAlphabet.spec';
 import './unit-tests/utils/EscapeSequenceEncoder.spec';
 import './unit-tests/utils/LevelledTopologicalSorter.spec';
 import './unit-tests/utils/NumberUtils.spec';
+import './unit-tests/utils/RandomGenerator.spec';
 import './unit-tests/utils/StringUtils.spec';
 import './unit-tests/utils/Utils.spec';
 

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

@@ -66,6 +66,61 @@ describe('ArrayUtils', () => {
         });
     });
 
+    describe('fillWithRange', () => {
+        const valueFunction: (index: number) => string = (index: number) => `foo${index}`;
+
+        describe('range length more than 0', () => {
+            const rangeLength: number = 5;
+            const expectedArray: string[] = [
+                'foo0',
+                'foo1',
+                'foo2',
+                'foo3',
+                'foo4',
+            ];
+
+            let array: string[];
+
+            before(() => {
+                array = arrayUtils.fillWithRange(rangeLength, valueFunction);
+            });
+
+            it('should return array with range of strings', () => {
+                assert.deepEqual(array, expectedArray);
+            });
+        });
+
+        describe('range length is 0', () => {
+            const rangeLength: number = 0;
+            const expectedArray: string[] = [];
+
+            let array: string[];
+
+            before(() => {
+                array = arrayUtils.fillWithRange(rangeLength, valueFunction);
+            });
+
+            it('should return empty array', () => {
+                assert.deepEqual(array, expectedArray);
+            });
+        });
+
+        describe('range length less than 0', () => {
+            const rangeLength: number = -5;
+            const expectedArray: string[] = [];
+
+            let array: string[];
+
+            before(() => {
+                array = arrayUtils.fillWithRange(rangeLength, valueFunction);
+            });
+
+            it('should return empty array', () => {
+                assert.deepEqual(array, expectedArray);
+            });
+        });
+    });
+
     describe('findMostOccurringElement', () => {
         describe('empty array', () => {
             const array: string[] = [];

+ 92 - 0
test/unit-tests/utils/RandomGenerator.spec.ts

@@ -0,0 +1,92 @@
+import 'reflect-metadata';
+
+import { assert } from 'chai';
+
+import { ServiceIdentifiers } from '../../../src/container/ServiceIdentifiers';
+
+import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade';
+import { IRandomGenerator } from '../../../src/interfaces/utils/IRandomGenerator';
+
+import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
+
+describe('RandomGenerator', () => {
+    let randomGenerator: IRandomGenerator;
+
+    before(() => {
+        const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
+
+        inversifyContainerFacade.load('', '', {});
+        randomGenerator = inversifyContainerFacade.get<IRandomGenerator>(ServiceIdentifiers.IRandomGenerator);
+    });
+
+    describe('getRandomIntegerExcluding', () => {
+        describe('Variant #1: avoid excluded values', () => {
+            const samplesCount: number = 500;
+
+            const minValue: number = 5;
+            const maxValue: number = 10;
+            const valuesToExclude: number[] = [6, 9];
+
+            const expectedRandomIntegerValues: number[] = [5, 7, 8, 10];
+
+            let isRandomIntegerInAllowedValuesRange: boolean = true;
+
+            before(() => {
+                for (let i = 0; i < samplesCount; i++) {
+                    const randomInteger = randomGenerator.getRandomIntegerExcluding(minValue, maxValue, valuesToExclude);
+
+                    if (!expectedRandomIntegerValues.includes(randomInteger)) {
+                        isRandomIntegerInAllowedValuesRange = false;
+                    }
+                }
+            });
+
+            it('should return a random integer in allowed values range', () => {
+                assert.isTrue(isRandomIntegerInAllowedValuesRange);
+            });
+        });
+
+        describe('Variant #2: values boundaries', () => {
+            const samplesCount: number = 500;
+
+            const minValue: number = 5;
+            const maxValue: number = 10;
+            const valuesToExclude: number[] = [6, 9];
+
+            const delta: number = 0.15;
+
+            const expectedValueChance: number = 0.2
+
+            let minValuesCount: number = 0;
+            let maxValuesCount: number = 0;
+
+            let minValueChance: number;
+            let maxValueChance: number;
+
+            before(() => {
+                for (let i = 0; i < samplesCount; i++) {
+                    const randomInteger: number = randomGenerator.getRandomIntegerExcluding(minValue, maxValue, valuesToExclude);
+
+                    if (randomInteger === minValue) {
+                        minValuesCount += 1;
+                    }
+
+                    if (randomInteger === maxValue) {
+                        maxValuesCount += 1;
+                    }
+
+                    minValueChance = minValuesCount / samplesCount;
+                    maxValueChance = maxValuesCount / samplesCount;
+                }
+            });
+
+            it('should generate min values', () => {
+                assert.closeTo(minValueChance, expectedValueChance, delta);
+            });
+
+            it('should generate max values', () => {
+                assert.closeTo(maxValueChance, expectedValueChance, delta);
+            });
+        });
+    });
+});

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