Code coverage report for src/node-transformers/obfuscation-transformers/FunctionDeclarationTransformer.ts

# All files / src/node-transformers/obfuscation-transformers FunctionDeclarationTransformer.ts

100% Statements 40/40
100% Branches 8/8
100% Functions 2/2
100% Lines 35/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
    1211x
    1x

    1x

    1x
    1x

    1x
    1x
    1x

    1x

    4751x

    4751x

    4751x

    12641x
    12641x

    12641x
    11x

    12630x
    12630x

    12630x

    12630x
    12630x

    12630x

    12630x
    5105x

    113174x
    113174x

    5105x

    7525x

    7525x

    546341x
    167701x

    167701x
    10426x

    157275x

    7525x

     import { injectable, inject } from 'inversify';
    import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';

    import * as estraverse from 'estraverse';
    import * as ESTree from 'estree';

    import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';

    import { IOptions } from '../../interfaces/options/IOptions';
    import { IObfuscationReplacer } from '../../interfaces/node-transformers/IObfuscationReplacer';
    import { IObfuscationReplacerWithStorage } from '../../interfaces/node-transformers/IObfuscationReplacerWithStorage';

    import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscationReplacers';
    import { NodeType } from '../../enums/NodeType';

    import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
    import { Node } from '../../node/Node';
    import { NodeUtils } from '../../node/NodeUtils';

    /**
     * replaces:
     *     function foo () { //... };
     *     foo();
     *
     * on:
     *     function _0x12d45f () { //... };
     *     _0x12d45f();
     */
    @injectable()
    export class FunctionDeclarationTransformer extends AbstractNodeTransformer {
        /**
         * @type {IObfuscationReplacerWithStorage}
         */
        private readonly identifierReplacer: IObfuscationReplacerWithStorage;

        /**
         * @type {Map<ESTree.Node, ESTree.Identifier[]>}
         */
        private readonly replaceableIdentifiers: Map <ESTree.Node, ESTree.Identifier[]> = new Map();

        /**
         * @param nodeObfuscatorsReplacersFactory
         * @param options
         */
        constructor(
            @inject(ServiceIdentifiers.Factory__IObfuscatorReplacer) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscationReplacer,
            @inject(ServiceIdentifiers.IOptions) options: IOptions
        ) {
            super(options);

            this.identifierReplacer = <IObfuscationReplacerWithStorage>nodeObfuscatorsReplacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
        }

        /**
         * @param functionDeclarationNode
         * @param parentNode
         * @returns {ESTree.Node}
         */
        public transformNode (functionDeclarationNode: ESTree.FunctionDeclaration, parentNode: ESTree.Node): ESTree.Node {
            const nodeIdentifier: number = this.nodeIdentifier++;
            const blockScopeOfFunctionDeclarationNode: TNodeWithBlockStatement = NodeUtils
                .getBlockScopesOfNode(functionDeclarationNode)[0];

            if (blockScopeOfFunctionDeclarationNode.type === NodeType.Program) {
                return functionDeclarationNode;
            }

            this.storeFunctionName(functionDeclarationNode, nodeIdentifier);
            this.replaceFunctionName(blockScopeOfFunctionDeclarationNode, nodeIdentifier);

            return functionDeclarationNode;
        }

        /**
         * @param functionDeclarationNode
         * @param nodeIdentifier
         */
        private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration, nodeIdentifier: number): void {
            NodeUtils.typedTraverse(functionDeclarationNode.id, NodeType.Identifier, {
                enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
            });
        }

        /**
         * @param scopeNode
         * @param nodeIdentifier
         */
        private replaceFunctionName (scopeNode: ESTree.Node, nodeIdentifier: number): void {
            let replaceableIdentifiersForCurrentScope: ESTree.Identifier[];

            // check for cached identifiers for current scope node. If exist - loop through them.
            if (this.replaceableIdentifiers.has(scopeNode)) {
                replaceableIdentifiersForCurrentScope = <ESTree.Identifier[]>this.replaceableIdentifiers.get(scopeNode);

                for (const replaceableIdentifier of replaceableIdentifiersForCurrentScope) {
                    replaceableIdentifier.name = this.identifierReplacer.replace(replaceableIdentifier.name, nodeIdentifier);
                }

                return;
            }

            replaceableIdentifiersForCurrentScope = [];

            estraverse.replace(scopeNode, {
                enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                    if (Node.isReplaceableIdentifierNode(node, parentNode)) {
                        const newNodeName: string = this.identifierReplacer.replace(node.name, nodeIdentifier);

                        if (node.name !== newNodeName) {
                            node.name = newNodeName;
                        } else {
                            replaceableIdentifiersForCurrentScope.push(node);
                        }
                    }
                }
            });

            this.replaceableIdentifiers.set(scopeNode, replaceableIdentifiersForCurrentScope);
        }
    }