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