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

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

100% Statements 39/39
100% Branches 10/10
100% Functions 4/4
100% Lines 36/36

    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
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    1381x
    1x

    1x

    1x
    1x

    1x
    1x
    1x

    1x

    6262x

    6262x

    6262x

    6262x

    665151x
    12622x

    12622x
    12622x

    12622x
    9x

    12613x

    12613x
    5086x

    7527x

    12613x

    12613x

    5086x

    5086x
    116858x

    7527x

    7527x

    609266x
    186040x

    186040x
    9950x

    176090x

    7527x

     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 { TObfuscationReplacerFactory } from '../../types/container/TObfuscationReplacerFactory';

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

    import { ObfuscationReplacers } from '../../enums/container/ObfuscationReplacers';
    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 obfuscationReplacerFactory
         * @param options
         */
        constructor (
            @inject(ServiceIdentifiers.Factory__IObfuscationReplacer) obfuscationReplacerFactory: TObfuscationReplacerFactory,
            @inject(ServiceIdentifiers.IOptions) options: IOptions
        ) {
            super(options);

            this.identifierReplacer = <IObfuscationReplacerWithStorage>obfuscationReplacerFactory(ObfuscationReplacers.IdentifierReplacer);
        }

        /**
         * @return {IVisitor}
         */
        public getVisitor (): IVisitor {
            return {
                enter: (node: ESTree.Node, parentNode: ESTree.Node) => {
                    if (Node.isFunctionDeclarationNode(node)) {
                        return this.transformNode(node, parentNode);
                    }
                }
            };
        }

        /**
         * @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);

            // check for cached identifiers for current scope node. If exist - loop through them.
            if (this.replaceableIdentifiers.has(blockScopeOfFunctionDeclarationNode)) {
                this.replaceScopeCachedIdentifiers(blockScopeOfFunctionDeclarationNode, nodeIdentifier);
            } else {
                this.replaceScopeIdentifiers(blockScopeOfFunctionDeclarationNode, nodeIdentifier);
            }

            return functionDeclarationNode;
        }

        /**
         * @param functionDeclarationNode
         * @param nodeIdentifier
         */
        private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration, nodeIdentifier: number): void {
            this.identifierReplacer.storeNames(functionDeclarationNode.id.name, nodeIdentifier);
        }

        /**
         * @param scopeNode
         * @param nodeIdentifier
         */
        private replaceScopeCachedIdentifiers (scopeNode: ESTree.Node, nodeIdentifier: number): void {
            const cachedReplaceableIdentifiers: ESTree.Identifier[] = <ESTree.Identifier[]>this.replaceableIdentifiers.get(scopeNode);

            cachedReplaceableIdentifiers.forEach((replaceableIdentifier: ESTree.Identifier) => {
                replaceableIdentifier.name = this.identifierReplacer.replace(replaceableIdentifier.name, nodeIdentifier);
            });
        }

        /**
         * @param scopeNode
         * @param nodeIdentifier
         */
        private replaceScopeIdentifiers (scopeNode: ESTree.Node, nodeIdentifier: number): void {
            const storedReplaceableIdentifiers: ESTree.Identifier[] = [];

            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 {
                            storedReplaceableIdentifiers.push(node);
                        }
                    }
                }
            });

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