All files / src/node-transformers/node-control-flow-transformers/control-flow-replacers BinaryExpressionControlFlowReplacer.ts

97.37% Statements 37/38
90.91% Branches 10/11
100% Functions 1/1
97.14% Lines 34/35
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 1261x 1x                     1x   1x 1x 1x 1x     1x       1x         202x                             202x   202x                       481x   481x 269x     212x     481x                           481x 481x 481x           481x 481x   481x         125x   356x   356x   356x 356x 356x 356x     481x             481x   481x       481x      
import { injectable, inject } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
import * as ESTree from 'estree';
 
import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
import { TStatement } from '../../../types/node/TStatement';
 
import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IStorage } from '../../../interfaces/storages/IStorage';
 
import { CustomNodes } from '../../../enums/container/CustomNodes';
 
import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
import { Node } from '../../../node/Node';
import { NodeUtils } from '../../../node/NodeUtils';
import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
 
@injectable()
export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowReplacer {
    /**
     * @type {number}
     */
    private static readonly useExistingOperatorKeyThreshold: number = 0.5;
 
    /**
     * @type {Map<string, Map<ESTree.BinaryOperator, string[]>>}
     */
    private readonly binaryOperatorsDataByControlFlowStorageId: Map <string, Map<ESTree.BinaryOperator, string[]>> = new Map();
 
    /**
     * @type {TCustomNodeFactory}
     */
    private readonly customNodeFactory: TCustomNodeFactory;
 
    /**
     * @param customNodeFactory
     * @param options
     */
    constructor (
        @inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
        @inject(ServiceIdentifiers.IOptions) options: IOptions
    ) {
        super(options);
 
        this.customNodeFactory = customNodeFactory;
    }
 
    /**
     * @param binaryOperatorsDataByControlFlowStorageId
     * @param controlFlowStorageId
     * @returns {Map<ESTree.BinaryOperator, string[]>}
     */
    private static getStorageKeysByBinaryOperatorForCurrentStorage (
        binaryOperatorsDataByControlFlowStorageId: Map<string, Map<ESTree.BinaryOperator, string[]>>,
        controlFlowStorageId: string
    ): Map<ESTree.BinaryOperator, string[]> {
        let storageKeysByBinaryOperator: Map<ESTree.BinaryOperator, string[]>;
 
        if (binaryOperatorsDataByControlFlowStorageId.has(controlFlowStorageId)) {
            storageKeysByBinaryOperator = <Map<ESTree.BinaryOperator, string[]>>binaryOperatorsDataByControlFlowStorageId
                .get(controlFlowStorageId);
        } else {
            storageKeysByBinaryOperator = new Map <ESTree.BinaryOperator, string[]> ();
        }
 
        return storageKeysByBinaryOperator;
    }
 
    /**
     * @param binaryExpressionNode
     * @param parentNode
     * @param controlFlowStorage
     * @returns {ESTree.Node}
     */
    public replace (
        binaryExpressionNode: ESTree.BinaryExpression,
        parentNode: ESTree.Node,
        controlFlowStorage: IStorage <ICustomNode>
    ): ESTree.Node {
        const controlFlowStorageId: string = controlFlowStorage.getStorageId();
        const controlFlowStorageCallCustomNode: ICustomNode = this.customNodeFactory(CustomNodes.ControlFlowStorageCallNode);
        const storageKeysByBinaryOperator: Map<ESTree.BinaryOperator, string[]> = BinaryExpressionControlFlowReplacer
            .getStorageKeysByBinaryOperatorForCurrentStorage(
                this.binaryOperatorsDataByControlFlowStorageId,
                controlFlowStorageId
            );
 
        let storageKeysForCurrentOperator: string[] | undefined = storageKeysByBinaryOperator.get(binaryExpressionNode.operator);
        let storageKey: string;
 
        if (
            RandomGeneratorUtils.getRandomFloat(0, 1) > BinaryExpressionControlFlowReplacer.useExistingOperatorKeyThreshold &&
            storageKeysForCurrentOperator &&
            storageKeysForCurrentOperator.length
        ) {
            storageKey = RandomGeneratorUtils.getRandomGenerator().pickone(storageKeysForCurrentOperator);
        } else {
            const binaryExpressionFunctionCustomNode: ICustomNode = this.customNodeFactory(CustomNodes.BinaryExpressionFunctionNode);
 
            binaryExpressionFunctionCustomNode.initialize(binaryExpressionNode.operator);
 
            storageKey = RandomGeneratorUtils.getRandomString(3);
            storageKeysByBinaryOperator.set(binaryExpressionNode.operator, [storageKey]);
            this.binaryOperatorsDataByControlFlowStorageId.set(controlFlowStorageId, storageKeysByBinaryOperator);
            controlFlowStorage.set(storageKey, binaryExpressionFunctionCustomNode);
        }
 
        controlFlowStorageCallCustomNode.initialize(
            controlFlowStorageId,
            storageKey,
            NodeUtils.convertStructureToCode([binaryExpressionNode.left]),
            NodeUtils.convertStructureToCode([binaryExpressionNode.right])
        );
 
        const statementNode: TStatement = controlFlowStorageCallCustomNode.getNode()[0];
 
        Iif (!statementNode || !Node.isExpressionStatementNode(statementNode)) {
            throw new Error(`\`controlFlowStorageCallNode.getNode()[0]\` should returns array with \`ExpressionStatement\` node`);
        }
 
        return statementNode.expression;
    }
}