| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 | import 'reflect-metadata';import { ServiceIdentifiers } from '../../../../src/container/ServiceIdentifiers';import * as estraverse from 'estraverse';import * as ESTree from 'estree';import { assert } from 'chai';import { TNodeWithStatements } from '../../../../src/types/node/TNodeWithStatements';import { IInversifyContainerFacade } from '../../../../src/interfaces/container/IInversifyContainerFacade';import { ICallsGraphAnalyzer } from '../../../../src/interfaces/analyzers/calls-graph-analyzer/ICallsGraphAnalyzer';import { ICallsGraphData } from '../../../../src/interfaces/analyzers/calls-graph-analyzer/ICallsGraphData';import { readFileAsString } from '../../../helpers/readFileAsString';import { InversifyContainerFacade } from '../../../../src/container/InversifyContainerFacade';import { NodeFactory } from '../../../../src/node/NodeFactory';import { NodeGuards } from '../../../../src/node/NodeGuards';import { NodeUtils } from '../../../../src/node/NodeUtils';/** * @param astTree * @param name * @returns {ESTree.FunctionDeclaration|null} */function getFunctionDeclarationByName (astTree: ESTree.Node, name: string): ESTree.FunctionDeclaration|null {    let functionDeclarationNode: ESTree.FunctionDeclaration|null = null;    estraverse.traverse(astTree, {        enter: (node: ESTree.Node): any => {            if (                NodeGuards.isFunctionDeclarationNode(node) &&                NodeGuards.isIdentifierNode(node.id) &&                node.id.name === name            ) {                functionDeclarationNode = node;                return estraverse.VisitorOption.Break;            }        }    });    return functionDeclarationNode;}/** * @param astTree * @param name * @returns {ESTree.FunctionExpression|null} */function getFunctionExpressionByName (astTree: ESTree.Node, name: string): ESTree.FunctionExpression|null {    let functionExpressionNode: ESTree.FunctionExpression|null = null;    estraverse.traverse(astTree, {        enter: (node: ESTree.Node): any => {            if (                NodeGuards.isVariableDeclaratorNode(node) &&                node.init &&                NodeGuards.isFunctionExpressionNode(node.init) &&                NodeGuards.isIdentifierNode(node.id) &&                node.id.name === name            ) {                functionExpressionNode = node.init;                return estraverse.VisitorOption.Break;            }        }    });    return functionExpressionNode;}/** * @param astTree * @param id * @returns {ESTree.FunctionExpression|null} */function getFunctionExpressionById (astTree: ESTree.Node, id: string): ESTree.FunctionExpression|null {    let functionExpressionNode: ESTree.FunctionExpression|null = null;    estraverse.traverse(astTree, {        enter: (node: ESTree.Node): any => {            if (                NodeGuards.isFunctionExpressionNode(node) &&                node.id &&                NodeGuards.isIdentifierNode(node.id) &&                node.id.name === id            ) {                functionExpressionNode = node;                return estraverse.VisitorOption.Break;            }        }    });    return functionExpressionNode;}/** * @param astTree * @param objectName * @param name * @returns {ESTree.FunctionExpression|null} */function getObjectFunctionExpressionByName (astTree: ESTree.Node, objectName: string, name: string|number): ESTree.FunctionExpression|null {    let functionExpressionNode: ESTree.FunctionExpression|null = null,        targetObjectExpressionNode: ESTree.ObjectExpression|null = null;    estraverse.traverse(astTree, {        enter: (node: ESTree.Node): any => {            if (                NodeGuards.isVariableDeclaratorNode(node) &&                NodeGuards.isIdentifierNode(node.id) &&                node.init &&                NodeGuards.isObjectExpressionNode(node.init) &&                node.id.name === objectName            ) {                targetObjectExpressionNode = node.init;                return estraverse.VisitorOption.Break;            }        }    });    if (!targetObjectExpressionNode) {        return null;    }    estraverse.traverse(targetObjectExpressionNode, {        enter: (node: ESTree.Node): any => {            if (                NodeGuards.isPropertyNode(node) &&                NodeGuards.isFunctionExpressionNode(node.value) &&                (                    (NodeGuards.isIdentifierNode(node.key) && node.key.name === name) ||                    (NodeGuards.isLiteralNode(node.key) && node.key.value === name)                )            ) {                functionExpressionNode = node.value;                return estraverse.VisitorOption.Break;            }        }    });    return functionExpressionNode;}describe('CallsGraphAnalyzer', () => {    describe('extract', () => {        let callsGraphAnalyzer: ICallsGraphAnalyzer,            expectedCallsGraphData: ICallsGraphData[],            callsGraphData: ICallsGraphData[];        before(() => {            const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();            inversifyContainerFacade.load('', '', {});            callsGraphAnalyzer = inversifyContainerFacade                .get<ICallsGraphAnalyzer>(ServiceIdentifiers.ICallsGraphAnalyzer);        });        describe('Variant #1: basic-1', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/basic-1.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [                    {                        name: 'baz',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'baz')).body,                        callsGraph: []                    },                    {                        name: 'foo',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'foo')).body,                        callsGraph: []                    },                    {                        name: 'bar',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'bar')).body,                        callsGraph: [                            {                                name: 'inner2',                                callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'inner2')).body,                                callsGraph: [                                    {                                        name: 'inner3',                                        callee: (<ESTree.FunctionExpression>getFunctionExpressionByName(astTree, 'inner3')).body,                                        callsGraph: []                                    },                                ]                            },                            {                                name: 'inner1',                                callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'inner1')).body,                                callsGraph: []                            },                        ]                    }                ];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #2: basic-2', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/basic-2.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [                    {                        name: 'bar',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'bar')).body,                        callsGraph: []                    },                    {                        name: 'baz',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'baz')).body,                        callsGraph: [                            {                                name: 'inner1',                                callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'inner1')).body,                                callsGraph: []                            },                        ]                    },                    {                        name: 'foo',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'foo')).body,                        callsGraph: []                    }                ];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #3: deep conditions nesting', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/deep-conditions-nesting.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [                    {                        name: 'bar',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'bar')).body,                        callsGraph: []                    },                    {                        name: 'baz',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'baz')).body,                        callsGraph: [                            {                                name: 'inner1',                                callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'inner1')).body,                                callsGraph: []                            },                        ]                    },                    {                        name: 'foo',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'foo')).body,                        callsGraph: []                    }                ];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #4: call before declaration', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/call-before-declaration.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [                    {                        name: 'bar',                        callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'bar')).body,                        callsGraph: []                    }                ];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #5: call expression of object member #1', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/call-expression-of-object-member-1.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [                    {                        name: 'baz',                        callee: (<ESTree.FunctionExpression>getObjectFunctionExpressionByName(astTree, 'object1', 'baz')).body,                        callsGraph: []                    },                    {                        name: 'baz',                        callee: (<ESTree.FunctionExpression>getObjectFunctionExpressionByName(astTree, 'object1', 'baz')).body,                        callsGraph: []                    },                    {                        name: 'func',                        callee: (<ESTree.FunctionExpression>getObjectFunctionExpressionByName(astTree, 'object1', 'func')).body,                        callsGraph: []                    },                    {                        name: 'bar',                        callee: (<ESTree.FunctionExpression>getObjectFunctionExpressionByName(astTree, 'object1', 'bar')).body,                        callsGraph: [                            {                                name: 'inner1',                                callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'inner1')).body,                                callsGraph: [                                ]                            },                        ]                    },                    {                        name: 'bar',                        callee: (<ESTree.FunctionExpression>getObjectFunctionExpressionByName(astTree, 'object', 'bar')).body,                        callsGraph: [                            {                                name: 'inner',                                callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'inner')).body,                                callsGraph: [                                ]                            },                        ]                    }                ];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #5: call expression of object member #2', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/call-expression-of-object-member-2.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [                    {                        name: 'baz',                        callee: (<ESTree.FunctionExpression>getObjectFunctionExpressionByName(astTree, 'object', 'baz')).body,                        callsGraph: []                    },                    {                        name: 1,                        callee: (<ESTree.FunctionExpression>getObjectFunctionExpressionByName(astTree, 'object1', 1)).body,                        callsGraph: []                    },                ];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #6: no call expressions', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/no-call-expressions.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #7: only call expression', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/only-call-expression.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #8: self-invoking functions', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/self-invoking-functions.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [                    {                        name: null,                        callee: (<ESTree.FunctionExpression>getFunctionExpressionById(astTree, 'foo')).body,                        callsGraph: [{                            name: null,                            callee: (<ESTree.FunctionExpression>getFunctionExpressionById(astTree, 'bar')).body,                            callsGraph: [{                                name: null,                                callee: (<ESTree.FunctionExpression>getFunctionExpressionById(astTree, 'baz')).body,                                callsGraph: [{                                    name: 'inner',                                    callee: (<ESTree.FunctionDeclaration>getFunctionDeclarationByName(astTree, 'inner')).body,                                    callsGraph: []                                }]                            }]                        }]                    }                ];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });        describe('Variant #9: no recursion', () => {            before(() => {                const code: string = readFileAsString(__dirname + '/fixtures/no-recursion.js');                const astTree: TNodeWithStatements = NodeFactory.programNode(                    NodeUtils.convertCodeToStructure(code)                );                expectedCallsGraphData = [                    {                        name: 'bar',                        callee: (<ESTree.FunctionExpression>getFunctionExpressionByName(astTree, 'bar')).body,                        callsGraph: []                    }                ];                callsGraphData = callsGraphAnalyzer.analyze(astTree);            });            it('should return correct calls graph data', () => {                assert.deepEqual(callsGraphData, expectedCallsGraphData);            });        });    });});
 |