Browse Source

WIP: shifted indexes of string array wrappers #1

sanex 4 năm trước cách đây
mục cha
commit
559e3a440a
19 tập tin đã thay đổi với 398 bổ sung76 xóa
  1. 0 0
      dist/index.cli.js
  2. 0 0
      dist/index.js
  3. 4 4
      src/custom-nodes/string-array-nodes/StringArrayCallNode.ts
  4. 16 2
      src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode.ts
  5. 16 5
      src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData.ts
  6. 13 0
      src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperNamesData.ts
  7. 6 0
      src/interfaces/storages/IArrayStorage.ts
  8. 3 2
      src/interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperDataStorage.ts
  9. 5 0
      src/interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage.ts
  10. 25 15
      src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts
  11. 62 9
      src/node-transformers/string-array-transformers/StringArrayTransformer.ts
  12. 14 0
      src/storages/ArrayStorage.ts
  13. 2 2
      src/storages/string-array-transformers/StringArrayScopeCallsWrapperDataStorage.ts
  14. 15 2
      src/storages/string-array-transformers/VisitedLexicalScopeNodesStackStorage.ts
  15. 0 7
      src/types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperDataByEncoding.ts
  16. 7 0
      src/types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding.ts
  17. 2 0
      test/dev/dev.ts
  18. 75 0
      test/unit-tests/storages/ArrayStorage.spec.ts
  19. 133 28
      test/unit-tests/storages/string-array-transformers/visited-lexical-scope-nodes-stack/VisitedLexicalScopeNodesStackStorage.spec.ts

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/index.cli.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/index.js


+ 4 - 4
src/custom-nodes/string-array-nodes/StringArrayCallNode.ts

@@ -61,10 +61,11 @@ export class StringArrayCallNode extends AbstractCustomNode {
     }
 
     /**
-     * @param {string} hexadecimalIndex
+     * @param {string} index
      * @returns {Literal}
      */
-    private static getHexadecimalLiteralNode (hexadecimalIndex: string): ESTree.Literal {
+    public static getHexadecimalLiteralNode (index: number): ESTree.Literal {
+        const hexadecimalIndex: string = NumberUtils.toHex(index);
         const hexadecimalLiteralNode: ESTree.Literal = NodeFactory.literalNode(hexadecimalIndex);
 
         NodeMetadata.set(hexadecimalLiteralNode, { replacedLiteral: true });
@@ -103,9 +104,8 @@ export class StringArrayCallNode extends AbstractCustomNode {
      * @returns {TStatement[]}
      */
     protected getNodeStructure (): TStatement[] {
-        const hexadecimalIndex: string = NumberUtils.toHex(this.index);
         const callExpressionArgs: ESTree.Literal[] = [
-            StringArrayCallNode.getHexadecimalLiteralNode(hexadecimalIndex)
+            StringArrayCallNode.getHexadecimalLiteralNode(this.index)
         ];
 
         if (this.decodeKey) {

+ 16 - 2
src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode.ts

@@ -15,9 +15,16 @@ import { initializable } from '../../decorators/Initializable';
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeUtils } from '../../node/NodeUtils';
+import { StringArrayCallNode } from './StringArrayCallNode';
 
 @injectable()
 export class StringArrayScopeCallsWrapperFunctionNode extends AbstractCustomNode {
+    /**
+     * @type {number}
+     */
+    @initializable()
+    private shiftedIndex!: number;
+
     /**
      * @type {string}
      */
@@ -55,13 +62,16 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractCustomNode
     /**
      * @param {string} stringArrayScopeCallsWrapperName
      * @param {string} stringArrayCallsWrapperName
+     * @param {number} shiftedIndex
      */
     public initialize (
         stringArrayScopeCallsWrapperName: string,
-        stringArrayCallsWrapperName: string
+        stringArrayCallsWrapperName: string,
+        shiftedIndex: number
     ): void {
         this.stringArrayScopeCallsWrapperName = stringArrayScopeCallsWrapperName;
         this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
+        this.shiftedIndex = shiftedIndex;
     }
 
     /**
@@ -89,7 +99,11 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractCustomNode
                     NodeFactory.callExpressionNode(
                         NodeFactory.identifierNode(this.stringArrayCallsWrapperName),
                         [
-                            firstCallArgumentIdentifierNode,
+                            NodeFactory.binaryExpressionNode(
+                                '-',
+                                firstCallArgumentIdentifierNode,
+                                StringArrayCallNode.getHexadecimalLiteralNode(this.shiftedIndex)
+                            ),
                             secondCallArgumentIdentifierNode
                         ]
                     )

+ 16 - 5
src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData.ts

@@ -1,13 +1,24 @@
-import { TStringArrayEncoding } from '../../../types/options/TStringArrayEncoding';
+import { TNodeWithLexicalScopeStatements } from '../../../types/node/TNodeWithLexicalScopeStatements';
+import { TStringArrayScopeCallsWrapperNamesDataByEncoding } from '../../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding';
 
 export interface IStringArrayScopeCallsWrapperData {
     /**
-     * @type {TStringArrayEncoding}
+     * @type {number}
      */
-    encoding: TStringArrayEncoding;
+    globalIndexShift: number;
 
     /**
-     * @type {string[]}
+     * @type {TStringArrayScopeCallsWrapperNamesDataByEncoding}
      */
-    names: string[];
+    names: TStringArrayScopeCallsWrapperNamesDataByEncoding;
+
+    /**
+     * @type {TNodeWithLexicalScopeStatements | null}
+     */
+    parentLexicalScopeNode: TNodeWithLexicalScopeStatements | null;
+
+    /**
+     * @type {number}
+     */
+    scopeIndexShift: number;
 }

+ 13 - 0
src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperNamesData.ts

@@ -0,0 +1,13 @@
+import { TStringArrayEncoding } from '../../../types/options/TStringArrayEncoding';
+
+export interface IStringArrayScopeCallsWrapperNamesData {
+    /**
+     * @type {TStringArrayEncoding}
+     */
+    encoding: TStringArrayEncoding;
+
+    /**
+     * @type {string[]}
+     */
+    names: string[];
+}

+ 6 - 0
src/interfaces/storages/IArrayStorage.ts

@@ -1,6 +1,12 @@
 import { IInitializable } from '../IInitializable';
 
 export interface IArrayStorage <V> extends IInitializable {
+    /**
+     * @param {number} key
+     * @returns {V | undefined}
+     */
+    delete (key: number): V | undefined;
+
     /**
      * @param {number} key
      * @returns {V | undefined}

+ 3 - 2
src/interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperDataStorage.ts

@@ -1,10 +1,11 @@
 import { TNodeWithLexicalScopeStatements } from '../../../types/node/TNodeWithLexicalScopeStatements';
-import { TStringArrayScopeCallsWrapperDataByEncoding } from '../../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperDataByEncoding';
+
+import { IStringArrayScopeCallsWrapperData } from '../../node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 
 import { IMapStorage } from '../IMapStorage';
 
 // eslint-disable-next-line @typescript-eslint/no-empty-interface
 export interface IStringArrayScopeCallsWrapperDataStorage extends IMapStorage<
     TNodeWithLexicalScopeStatements,
-    TStringArrayScopeCallsWrapperDataByEncoding
+    IStringArrayScopeCallsWrapperData
 > {}

+ 5 - 0
src/interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage.ts

@@ -8,6 +8,11 @@ export interface IVisitedLexicalScopeNodesStackStorage extends IArrayStorage<TNo
      */
     getLastElement (): TNodeWithLexicalScopeStatements | undefined;
 
+    /**
+     * @returns {TNodeWithLexicalScopeStatements | undefined}
+     */
+    getPenultimateElement (): TNodeWithLexicalScopeStatements | undefined;
+
     /**
      * @returns {TNodeWithLexicalScopeStatements | undefined}
      */

+ 25 - 15
src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts

@@ -6,13 +6,13 @@ import * as ESTree from 'estree';
 import { TInitialData } from '../../types/TInitialData';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
-import { TStringArrayScopeCallsWrapperDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperDataByEncoding';
 import { TStringArrayTransformerCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayTransformerCustomNodeFactory';
 
 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 { IStringArrayScopeCallsWrapperNamesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperNamesData';
 import { IStringArrayScopeCallsWrapperDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperDataStorage';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IVisitedLexicalScopeNodesStackStorage } from '../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage';
@@ -114,23 +114,26 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     public transformNode (
         lexicalScopeBodyNode: TNodeWithLexicalScopeStatements
     ): TNodeWithLexicalScopeStatements {
-        const stringArrayScopeCallsWrapperDataByEncoding: TStringArrayScopeCallsWrapperDataByEncoding | null =
+        const stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData | null =
             this.stringArrayScopeCallsWrapperDataStorage.get(lexicalScopeBodyNode) ?? null;
 
-        if (!stringArrayScopeCallsWrapperDataByEncoding) {
+        if (!stringArrayScopeCallsWrapperData) {
             return lexicalScopeBodyNode;
         }
 
-        const stringArrayScopeCallsWrapperDataList: (IStringArrayScopeCallsWrapperData | undefined)[] =
-            Object.values(stringArrayScopeCallsWrapperDataByEncoding);
+        const indexShift: number = this.options.stringArrayWrappersChainedCalls
+            ? stringArrayScopeCallsWrapperData.scopeIndexShift
+            : stringArrayScopeCallsWrapperData.globalIndexShift;
+        const stringArrayScopeCallsWrapperNamesDataList: (IStringArrayScopeCallsWrapperNamesData | undefined)[] =
+            Object.values(stringArrayScopeCallsWrapperData.names);
 
         // iterates over data for each encoding type
-        for (const stringArrayScopeCallsWrapperData of stringArrayScopeCallsWrapperDataList) {
-            if (!stringArrayScopeCallsWrapperData) {
+        for (const stringArrayScopeCallsWrapperNamesData of stringArrayScopeCallsWrapperNamesDataList) {
+            if (!stringArrayScopeCallsWrapperNamesData) {
                 continue;
             }
 
-            const {encoding, names} = stringArrayScopeCallsWrapperData;
+            const {encoding, names} = stringArrayScopeCallsWrapperNamesData;
             const namesLength: number = names.length;
 
             /**
@@ -143,7 +146,8 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
                 const stringArrayScopeCallsWrapperNode: TStatement[] = this.getStringArrayScopeCallsWrapperNode(
                     stringArrayScopeCallsWrapperName,
-                    upperStringArrayCallsWrapperName
+                    upperStringArrayCallsWrapperName,
+                    indexShift
                 );
 
                 NodeAppender.prepend(
@@ -182,9 +186,9 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
             return rootStringArrayCallsWrapperName;
         }
 
-        const parentLexicalScopeDataByEncoding = this.stringArrayScopeCallsWrapperDataStorage
+        const parentLexicalScopeData: IStringArrayScopeCallsWrapperData | null = this.stringArrayScopeCallsWrapperDataStorage
             .get(parentLexicalScopeBodyNode) ?? null;
-        const parentLexicalScopeNames: string[] | null = parentLexicalScopeDataByEncoding?.[encoding]?.names ?? null;
+        const parentLexicalScopeNames: string[] | null = parentLexicalScopeData?.names[encoding]?.names ?? null;
 
         return parentLexicalScopeNames?.length
             ? this.randomGenerator
@@ -196,17 +200,20 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     /**
      * @param {string} stringArrayScopeCallsWrapperName
      * @param {string} upperStringArrayCallsWrapperName
+     * @param {number} indexShift
      * @returns {TStatement[]}
      */
     private getStringArrayScopeCallsWrapperNode (
         stringArrayScopeCallsWrapperName: string,
-        upperStringArrayCallsWrapperName: string
+        upperStringArrayCallsWrapperName: string,
+        indexShift: number
     ): TStatement[] {
         switch (this.options.stringArrayWrappersType) {
             case StringArrayWrappersType.Function:
                 return this.getStringArrayScopeCallsWrapperFunctionNode(
                     stringArrayScopeCallsWrapperName,
-                    upperStringArrayCallsWrapperName
+                    upperStringArrayCallsWrapperName,
+                    indexShift
                 );
 
             case StringArrayWrappersType.Variable:
@@ -243,11 +250,13 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     /**
      * @param {string} stringArrayScopeCallsWrapperName
      * @param {string} upperStringArrayCallsWrapperName
+     * @param {number} indexShift
      * @returns {TStatement[]}
      */
     private getStringArrayScopeCallsWrapperFunctionNode (
         stringArrayScopeCallsWrapperName: string,
-        upperStringArrayCallsWrapperName: string
+        upperStringArrayCallsWrapperName: string,
+        indexShift: number
     ): TStatement[] {
         const stringArrayScopeCallsWrapperFunctionNode: ICustomNode<TInitialData<StringArrayScopeCallsWrapperFunctionNode>> =
             this.stringArrayTransformerCustomNodeFactory(
@@ -256,7 +265,8 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
         stringArrayScopeCallsWrapperFunctionNode.initialize(
             stringArrayScopeCallsWrapperName,
-            upperStringArrayCallsWrapperName
+            upperStringArrayCallsWrapperName,
+            indexShift
         );
 
         return stringArrayScopeCallsWrapperFunctionNode.getNode();

+ 62 - 9
src/node-transformers/string-array-transformers/StringArrayTransformer.ts

@@ -7,7 +7,6 @@ import { TInitialData } from '../../types/TInitialData';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TStatement } from '../../types/node/TStatement';
 import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
-import { TStringArrayScopeCallsWrapperDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperDataByEncoding';
 import { TStringArrayTransformerCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayTransformerCustomNodeFactory';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
@@ -17,6 +16,7 @@ import { TIdentifierNamesGeneratorFactory } from '../../types/container/generato
 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 { IStringArrayScopeCallsWrapperDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperDataStorage';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
@@ -26,6 +26,7 @@ import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
 import { StringArrayTransformerCustomNode } from '../../enums/custom-nodes/StringArrayTransformerCustomNode';
+import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeFactory } from '../../node/NodeFactory';
@@ -37,6 +38,16 @@ import { StringArrayCallNode } from '../../custom-nodes/string-array-nodes/Strin
 
 @injectable()
 export class StringArrayTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {number}
+     */
+    private static readonly minShiftedIndexValue: number = 10;
+
+    /**
+     * @type {number}
+     */
+    private static readonly maxShiftedIndexValue: number = 100;
+
     /**
      * @type {IEscapeSequenceEncoder}
      */
@@ -211,10 +222,20 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
         const stringArrayCallsWrapperName: string = this.getStringArrayCallsWrapperName(stringArrayStorageItemData);
         const { index, decodeKey } = stringArrayStorageItemData;
 
+        // todo: refactor this
+        const lastElement: TNodeWithLexicalScopeStatements | null =
+            this.visitedLexicalScopeNodesStackStorage.getLastElement() ?? null;
+        const stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData | null = lastElement
+            ? this.stringArrayScopeCallsWrapperDataStorage.get(lastElement) ?? null
+            : null;
+
+        const resultIndex: number = stringArrayScopeCallsWrapperData
+            ? stringArrayScopeCallsWrapperData.globalIndexShift + index
+            : index;
         const stringArrayCallCustomNode: ICustomNode<TInitialData<StringArrayCallNode>> =
             this.stringArrayTransformerCustomNodeFactory(StringArrayTransformerCustomNode.StringArrayCallNode);
 
-        stringArrayCallCustomNode.initialize(stringArrayCallsWrapperName, index, decodeKey);
+        stringArrayCallCustomNode.initialize(stringArrayCallsWrapperName, resultIndex, decodeKey);
 
         const statementNode: TStatement = stringArrayCallCustomNode.getNode()[0];
 
@@ -251,36 +272,68 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @returns {string}
      */
     private getUpperStringArrayCallsWrapperName (encoding: TStringArrayEncoding): string {
-        const currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | undefined =
-            this.visitedLexicalScopeNodesStackStorage.getLastElement();
+        const currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
+            this.visitedLexicalScopeNodesStackStorage.getLastElement() ?? null;
+        const parentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
+            this.visitedLexicalScopeNodesStackStorage.getPenultimateElement() ?? null;
 
         if (!currentLexicalScopeBodyNode) {
             throw new Error('Cannot find current lexical scope body node');
         }
 
-        const stringArrayScopeCallsWrapperDataByEncoding: TStringArrayScopeCallsWrapperDataByEncoding =
-            this.stringArrayScopeCallsWrapperDataStorage.get(currentLexicalScopeBodyNode) ?? {};
-        const stringArrayScopeCallsWrapperNames: string[] = stringArrayScopeCallsWrapperDataByEncoding[encoding]?.names ?? [];
+        const stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData =
+            this.stringArrayScopeCallsWrapperDataStorage.get(currentLexicalScopeBodyNode)
+                ?? this.initializeStringArrayScopeCallsWrapperData(parentLexicalScopeBodyNode);
+        const stringArrayScopeCallsWrapperNames: string[] = stringArrayScopeCallsWrapperData.names[encoding]?.names ?? [];
         const isFilledScopeCallsWrapperNamesList: boolean = stringArrayScopeCallsWrapperNames.length === this.options.stringArrayWrappersCount;
 
         if (!isFilledScopeCallsWrapperNamesList) {
             const nextScopeCallsWrapperName: string = this.identifierNamesGenerator.generateNext();
 
             stringArrayScopeCallsWrapperNames.push(nextScopeCallsWrapperName);
-            stringArrayScopeCallsWrapperDataByEncoding[encoding] = {
+            stringArrayScopeCallsWrapperData.names[encoding] = {
                 encoding,
                 names: stringArrayScopeCallsWrapperNames
             };
 
             this.stringArrayScopeCallsWrapperDataStorage.set(
                 currentLexicalScopeBodyNode,
-                stringArrayScopeCallsWrapperDataByEncoding
+                stringArrayScopeCallsWrapperData
             );
         }
 
         return this.randomGenerator.getRandomGenerator().pickone(stringArrayScopeCallsWrapperNames);
     }
 
+    /**
+     * @param {TNodeWithLexicalScopeStatements | null} parentLexicalScopeBodyNode
+     * @returns {IStringArrayScopeCallsWrapperData}
+     */
+    private initializeStringArrayScopeCallsWrapperData (
+        parentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null
+    ): IStringArrayScopeCallsWrapperData {
+        const parentStringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData | null = parentLexicalScopeBodyNode
+            ? this.stringArrayScopeCallsWrapperDataStorage.get(parentLexicalScopeBodyNode) ?? null
+            : null;
+
+        const scopeIndexShift: number = this.options.stringArrayWrappersType === StringArrayWrappersType.Function
+            ? this.randomGenerator.getRandomInteger(
+                StringArrayTransformer.minShiftedIndexValue,
+                StringArrayTransformer.maxShiftedIndexValue
+            )
+            : 0;
+        const globalIndexShift: number = parentStringArrayScopeCallsWrapperData
+            ? parentStringArrayScopeCallsWrapperData.globalIndexShift + scopeIndexShift
+            : scopeIndexShift;
+
+        return {
+            globalIndexShift,
+            scopeIndexShift,
+            names: {},
+            parentLexicalScopeNode: parentLexicalScopeBodyNode ?? null
+        };
+    }
+
     /**
      * @param {Literal} literalNode
      * @param {Node} parentNode

+ 14 - 0
src/storages/ArrayStorage.ts

@@ -54,6 +54,20 @@ export abstract class ArrayStorage <V> implements IArrayStorage <V> {
         this.storageId = this.randomGenerator.getRandomString(6);
     }
 
+    /**
+     * @param {number} key
+     * @returns {V | undefined}
+     */
+    public delete (key: number): V | undefined {
+        const deletedElement: V | undefined = this.storage.splice(key, 1)[0] ?? undefined;
+
+        if (deletedElement) {
+            this.storageLength--;
+        }
+
+        return deletedElement;
+    }
+
     /**
      * @param {number} key
      * @returns {V | undefined}

+ 2 - 2
src/storages/string-array-transformers/StringArrayScopeCallsWrapperDataStorage.ts

@@ -2,10 +2,10 @@ import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
-import { TStringArrayScopeCallsWrapperDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperDataByEncoding';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 import { IStringArrayScopeCallsWrapperDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperDataStorage';
 
 import { MapStorage } from '../MapStorage';
@@ -13,7 +13,7 @@ import { MapStorage } from '../MapStorage';
 @injectable()
 export class StringArrayScopeCallsWrapperDataStorage extends MapStorage <
     TNodeWithLexicalScopeStatements,
-    TStringArrayScopeCallsWrapperDataByEncoding
+    IStringArrayScopeCallsWrapperData
 > implements IStringArrayScopeCallsWrapperDataStorage {
     /**
      * @param {IRandomGenerator} randomGenerator

+ 15 - 2
src/storages/string-array-transformers/VisitedLexicalScopeNodesStackStorage.ts

@@ -39,17 +39,30 @@ export class VisitedLexicalScopeNodesStackStorage extends ArrayStorage <TNodeWit
         return this.arrayUtils.getLastElement(this.getStorage());
     }
 
+    /**
+     * @returns {TNodeWithLexicalScopeStatements | undefined}
+     */
+    public getPenultimateElement (): TNodeWithLexicalScopeStatements | undefined {
+        const storageLength: number = this.getLength();
+
+        return this.get(storageLength - 2) ?? undefined;
+    }
+
     /**
      * @param {TNodeWithLexicalScopeStatements} nodeWithLexicalScopeStatements
      */
     public push (nodeWithLexicalScopeStatements: TNodeWithLexicalScopeStatements): void {
-        this.storage.push(nodeWithLexicalScopeStatements);
+        const storageLength: number = this.getLength();
+
+        this.set(storageLength, nodeWithLexicalScopeStatements);
     }
 
     /**
      * @returns {TNodeWithLexicalScopeStatements| undefined}
      */
     public pop (): TNodeWithLexicalScopeStatements | undefined {
-        return this.storage.pop();
+        const storageLength: number = this.getLength();
+
+        return this.delete(storageLength - 1);
     }
 }

+ 0 - 7
src/types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperDataByEncoding.ts

@@ -1,7 +0,0 @@
-import { TStringArrayEncoding } from '../../options/TStringArrayEncoding';
-
-import { IStringArrayScopeCallsWrapperData } from '../../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
-
-export type TStringArrayScopeCallsWrapperDataByEncoding = Partial<{
-    [key in TStringArrayEncoding]: IStringArrayScopeCallsWrapperData;
-}>;

+ 7 - 0
src/types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding.ts

@@ -0,0 +1,7 @@
+import { TStringArrayEncoding } from '../../options/TStringArrayEncoding';
+
+import { IStringArrayScopeCallsWrapperNamesData } from '../../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperNamesData';
+
+export type TStringArrayScopeCallsWrapperNamesDataByEncoding = Partial<{
+    [key in TStringArrayEncoding]: IStringArrayScopeCallsWrapperNamesData;
+}>;

+ 2 - 0
test/dev/dev.ts

@@ -20,6 +20,8 @@ import { StringArrayWrappersType } from '../../src/enums/node-transformers/strin
                 console.log(arg, hawk, eagle);
             }
             
+            console.log(foo, bar, baz);
+            
             test();
         `,
         {

+ 75 - 0
test/unit-tests/storages/ArrayStorage.spec.ts

@@ -245,6 +245,81 @@ describe('ArrayStorage', () => {
         });
     });
 
+    describe('delete', () => {
+        describe('Variant #1: value exist', () => {
+            const expectedUpdatedStorage: string[] = [
+                'foo',
+                'baz'
+            ];
+            const expectedUpdatedStorageLength: number = 2;
+            const expectedDeletedValue: string = 'bar';
+
+            let updatedStorage: string[];
+            let updatedStorageLength: number;
+            let deletedValue: string;
+
+            before(() => {
+                storage = getStorageInstance<string>();
+                storage.set(0, 'foo');
+                storage.set(1, 'bar');
+                storage.set(2, 'baz');
+
+                deletedValue = storage.delete(1);
+                updatedStorage = storage.getStorage();
+                updatedStorageLength = storage.getLength();
+            });
+
+            it('should delete value from the storage by index', () => {
+                assert.deepEqual(updatedStorage, expectedUpdatedStorage);
+            });
+
+            it('should update storage length', () => {
+                assert.deepEqual(updatedStorageLength, expectedUpdatedStorageLength);
+            });
+
+            it('should return deleted value', () => {
+                assert.equal(deletedValue, expectedDeletedValue);
+            });
+        });
+
+        describe('Variant #2: value isn\'t exist', () => {
+            const expectedUpdatedStorage: string[] = [
+                'foo',
+                'bar',
+                'baz'
+            ];
+            const expectedUpdatedStorageLength: number = 3;
+            const expectedDeletedValue: undefined = undefined;
+
+            let updatedStorage: string[];
+            let updatedStorageLength: number;
+            let deletedValue: string;
+
+            before(() => {
+                storage = getStorageInstance<string>();
+                storage.set(0, 'foo');
+                storage.set(1, 'bar');
+                storage.set(2, 'baz');
+
+                deletedValue = storage.delete(3);
+                updatedStorage = storage.getStorage();
+                updatedStorageLength = storage.getLength();
+            });
+
+            it('should keep storage the same', () => {
+                assert.deepEqual(updatedStorage, expectedUpdatedStorage);
+            });
+
+            it('should keep storage length', () => {
+                assert.deepEqual(updatedStorageLength, expectedUpdatedStorageLength);
+            });
+
+            it('should return undefined', () => {
+                assert.equal(deletedValue, expectedDeletedValue);
+            });
+        });
+    });
+
     describe('mergeWith', () => {
         describe('Base merge', () => {
             const secondStorageKey: number = 1;

+ 133 - 28
test/unit-tests/storages/string-array-transformers/visited-lexical-scope-nodes-stack/VisitedLexicalScopeNodesStackStorage.spec.ts

@@ -67,6 +67,79 @@ describe('VisitedLexicalScopeNodesStackStorage', () => {
         });
     });
 
+    describe('getPenultimateElement', () => {
+        describe('Variant #1: three array elements', () => {
+            const firstElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
+                NodeFactory.expressionStatementNode(
+                    NodeFactory.literalNode('first')
+                )
+            ]);
+            const expectedSecondElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
+                NodeFactory.expressionStatementNode(
+                    NodeFactory.literalNode('second')
+                )
+            ]);
+            const lastElement: TNodeWithLexicalScopeStatements =  NodeFactory.blockStatementNode([
+                NodeFactory.expressionStatementNode(
+                    NodeFactory.literalNode('last')
+                )
+            ]);
+
+            let penultimateElement: TNodeWithLexicalScopeStatements | undefined;
+
+            before(() => {
+                const visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage = getStorageInstance();
+
+                visitedLexicalScopeNodesStackStorage.push(firstElement);
+                visitedLexicalScopeNodesStackStorage.push(expectedSecondElement);
+                visitedLexicalScopeNodesStackStorage.push(lastElement);
+                penultimateElement = visitedLexicalScopeNodesStackStorage.getPenultimateElement();
+            });
+
+            it('should return a penultimate element from the stack', () => {
+                assert.equal(penultimateElement, expectedSecondElement);
+            });
+        });
+
+        describe('Variant #2: one array element', () => {
+            const expectedPenultimateElement: undefined = undefined;
+            const firstElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
+                NodeFactory.expressionStatementNode(
+                    NodeFactory.literalNode('first')
+                )
+            ]);
+
+            let penultimateElement: TNodeWithLexicalScopeStatements | undefined;
+
+            before(() => {
+                const visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage = getStorageInstance();
+
+                visitedLexicalScopeNodesStackStorage.push(firstElement);
+                penultimateElement = visitedLexicalScopeNodesStackStorage.getPenultimateElement();
+            });
+
+            it('should return a penultimate element from the stack', () => {
+                assert.equal(penultimateElement, expectedPenultimateElement);
+            });
+        });
+
+        describe('Variant #3: empty array', () => {
+            const expectedPenultimateElement: undefined = undefined;
+
+            let penultimateElement: TNodeWithLexicalScopeStatements | undefined;
+
+            before(() => {
+                const visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage = getStorageInstance();
+
+                penultimateElement = visitedLexicalScopeNodesStackStorage.getPenultimateElement();
+            });
+
+            it('should return a penultimate element from the stack', () => {
+                assert.equal(penultimateElement, expectedPenultimateElement);
+            });
+        });
+    });
+
     describe('push', () => {
         const firstElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
             NodeFactory.expressionStatementNode(
@@ -99,40 +172,72 @@ describe('VisitedLexicalScopeNodesStackStorage', () => {
     });
 
     describe('pop', () => {
-        const firstElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
-            NodeFactory.expressionStatementNode(
-                NodeFactory.literalNode('first')
-            )
-        ]);
-        const secondElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
-            NodeFactory.expressionStatementNode(
-                NodeFactory.literalNode('second')
-            )
-        ]);
-        const expectedStorage: TNodeWithLexicalScopeStatements[] = [
-            firstElement
-        ];
-        const expectedPoppedElement: TNodeWithLexicalScopeStatements = secondElement;
+        describe('Variant #1: few elements', () => {
+            const firstElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
+                NodeFactory.expressionStatementNode(
+                    NodeFactory.literalNode('first')
+                )
+            ]);
+            const secondElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
+                NodeFactory.expressionStatementNode(
+                    NodeFactory.literalNode('second')
+                )
+            ]);
+            const expectedStorage: TNodeWithLexicalScopeStatements[] = [
+                firstElement
+            ];
+            const expectedPoppedElement: TNodeWithLexicalScopeStatements = secondElement;
+
+            let storage: TNodeWithLexicalScopeStatements[];
+            let poppedElement: TNodeWithLexicalScopeStatements | undefined;
+
+            before(() => {
+                const visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage = getStorageInstance();
+
+                visitedLexicalScopeNodesStackStorage.push(firstElement);
+                visitedLexicalScopeNodesStackStorage.push(secondElement);
+
+                poppedElement = visitedLexicalScopeNodesStackStorage.pop();
+                storage = visitedLexicalScopeNodesStackStorage.getStorage();
+            });
+
+            it('should pop a last element from the storage', () => {
+                assert.deepEqual(storage, expectedStorage);
+            });
+
+            it('should return a popped element from the storage', () => {
+                assert.equal(poppedElement, expectedPoppedElement);
+            });
+        });
 
-        let storage: TNodeWithLexicalScopeStatements[];
-        let poppedElement: TNodeWithLexicalScopeStatements | undefined;
+        describe('Variant #2: single element', () => {
+            const firstElement: TNodeWithLexicalScopeStatements = NodeFactory.blockStatementNode([
+                NodeFactory.expressionStatementNode(
+                    NodeFactory.literalNode('first')
+                )
+            ]);
+            const expectedStorage: TNodeWithLexicalScopeStatements[] = [];
+            const expectedPoppedElement: TNodeWithLexicalScopeStatements = firstElement;
 
-        before(() => {
-            const visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage = getStorageInstance();
+            let storage: TNodeWithLexicalScopeStatements[];
+            let poppedElement: TNodeWithLexicalScopeStatements | undefined;
 
-            visitedLexicalScopeNodesStackStorage.push(firstElement);
-            visitedLexicalScopeNodesStackStorage.push(secondElement);
+            before(() => {
+                const visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage = getStorageInstance();
 
-            poppedElement = visitedLexicalScopeNodesStackStorage.pop();
-            storage = visitedLexicalScopeNodesStackStorage.getStorage();
-        });
+                visitedLexicalScopeNodesStackStorage.push(firstElement);
 
-        it('should pop a last element from the storage', () => {
-            assert.deepEqual(storage, expectedStorage);
-        });
+                poppedElement = visitedLexicalScopeNodesStackStorage.pop();
+                storage = visitedLexicalScopeNodesStackStorage.getStorage();
+            });
+
+            it('should pop a last element from the storage', () => {
+                assert.deepEqual(storage, expectedStorage);
+            });
 
-        it('should return a popped element from the storage', () => {
-            assert.equal(poppedElement, expectedPoppedElement);
+            it('should return a popped element from the storage', () => {
+                assert.equal(poppedElement, expectedPoppedElement);
+            });
         });
     });
 });

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác