123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- import { injectable } from 'inversify';
- import * as estraverse from 'estraverse';
- import * as ESTree from 'estree';
- import { TObjectMembersCallsChain } from '../../../types/analyzers/stack-trace-analyzer/TObjectMembersCallsChain';
- import { ICalleeData } from '../../../interfaces/analyzers/stack-trace-analyzer/ICalleeData';
- import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
- import { NodeGuards } from '../../../node/NodeGuards';
- import { NodeUtils } from '../../../node/NodeUtils';
- @injectable()
- export class ObjectExpressionCalleeDataExtractor extends AbstractCalleeDataExtractor {
- /**
- * @param {Property} propertyNode
- * @param {string | number} nextItemInCallsChain
- * @returns {boolean}
- */
- private static isValidTargetPropertyNode (propertyNode: ESTree.Property, nextItemInCallsChain: string | number): boolean {
- if (!propertyNode.key) {
- return false;
- }
- const isTargetPropertyNodeWithIdentifierKey: boolean =
- NodeGuards.isIdentifierNode(propertyNode.key) && propertyNode.key.name === nextItemInCallsChain;
- const isTargetPropertyNodeWithLiteralKey: boolean =
- NodeGuards.isLiteralNode(propertyNode.key) &&
- Boolean(propertyNode.key.value) &&
- propertyNode.key.value === nextItemInCallsChain;
- return isTargetPropertyNodeWithIdentifierKey || isTargetPropertyNodeWithLiteralKey;
- }
- /**
- * @param {NodeGuards[]} blockScopeBody
- * @param {MemberExpression} callee
- * @returns {ICalleeData}
- */
- public extract (blockScopeBody: ESTree.Node[], callee: ESTree.MemberExpression): ICalleeData | null {
- if (!NodeGuards.isMemberExpressionNode(callee)) {
- return null;
- }
- const objectMembersCallsChain: TObjectMembersCallsChain = this.createObjectMembersCallsChain([], callee);
- if (!objectMembersCallsChain.length) {
- return null;
- }
- const functionExpressionName: string | number | null = objectMembersCallsChain[objectMembersCallsChain.length - 1];
- const calleeBlockStatement: ESTree.BlockStatement | null = this.getCalleeBlockStatement(
- NodeUtils.getBlockScopeOfNode(blockScopeBody[0]),
- objectMembersCallsChain
- );
- if (!calleeBlockStatement) {
- return null;
- }
- return {
- callee: calleeBlockStatement,
- name: functionExpressionName
- };
- }
- /**
- * Creates array with MemberExpression calls chain.
- *
- * Example: object.foo.bar(); // ['object', 'foo', 'bar']
- *
- * @param {TObjectMembersCallsChain} currentChain
- * @param {MemberExpression} memberExpression
- * @returns {TObjectMembersCallsChain}
- */
- private createObjectMembersCallsChain (
- currentChain: TObjectMembersCallsChain,
- memberExpression: ESTree.MemberExpression
- ): TObjectMembersCallsChain {
- // first step: processing memberExpression `property` property
- if (NodeGuards.isIdentifierNode(memberExpression.property) && memberExpression.computed === false) {
- currentChain.unshift(memberExpression.property.name);
- } else if (
- NodeGuards.isLiteralNode(memberExpression.property) &&
- (
- typeof memberExpression.property.value === 'string' ||
- typeof memberExpression.property.value === 'number'
- )
- ) {
- currentChain.unshift(memberExpression.property.value);
- } else {
- return currentChain;
- }
- // second step: processing memberExpression `object` property
- if (NodeGuards.isMemberExpressionNode(memberExpression.object)) {
- return this.createObjectMembersCallsChain(currentChain, memberExpression.object);
- } else if (NodeGuards.isIdentifierNode(memberExpression.object)) {
- currentChain.unshift(memberExpression.object.name);
- }
- return currentChain;
- }
- /**
- * @param {NodeGuards} targetNode
- * @param {TObjectMembersCallsChain} objectMembersCallsChain
- * @returns {BlockStatement}
- */
- private getCalleeBlockStatement (
- targetNode: ESTree.Node,
- objectMembersCallsChain: TObjectMembersCallsChain
- ): ESTree.BlockStatement | null {
- const objectName: string | number | undefined = objectMembersCallsChain.shift();
- if (!objectName) {
- return null;
- }
- let calleeBlockStatement: ESTree.BlockStatement | null = null;
- estraverse.traverse(targetNode, {
- enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
- if (
- NodeGuards.isVariableDeclaratorNode(node) &&
- NodeGuards.isIdentifierNode(node.id) &&
- node.init &&
- NodeGuards.isObjectExpressionNode(node.init) &&
- node.id.name === objectName
- ) {
- calleeBlockStatement = this.findCalleeBlockStatement(node.init.properties, objectMembersCallsChain);
- return estraverse.VisitorOption.Break;
- }
- }
- });
- return calleeBlockStatement;
- }
- /**
- * @param {Property[]} objectExpressionProperties
- * @param {TObjectMembersCallsChain} objectMembersCallsChain
- * @returns {BlockStatement}
- */
- private findCalleeBlockStatement (
- objectExpressionProperties: ESTree.Property[],
- objectMembersCallsChain: TObjectMembersCallsChain
- ): ESTree.BlockStatement | null {
- const nextItemInCallsChain: string | number | undefined = objectMembersCallsChain.shift();
- if (!nextItemInCallsChain) {
- return null;
- }
- for (const propertyNode of objectExpressionProperties) {
- if (!ObjectExpressionCalleeDataExtractor.isValidTargetPropertyNode(propertyNode, nextItemInCallsChain)) {
- continue;
- }
- if (NodeGuards.isObjectExpressionNode(propertyNode.value)) {
- return this.findCalleeBlockStatement(propertyNode.value.properties, objectMembersCallsChain);
- }
- if (NodeGuards.isFunctionExpressionNode(propertyNode.value)) {
- return propertyNode.value.body;
- }
- }
- return null;
- }
- }
|