StackTraceAnalyzer.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import * as estraverse from 'estraverse';
  2. import * as ESTree from 'estree';
  3. import { TCalleeDataExtractor } from '../types/TCalleeDataExtractor';
  4. import { ICalleeData } from '../interfaces/stack-trace-analyzer/ICalleeData';
  5. import { IStackTraceAnalyzer } from '../interfaces/stack-trace-analyzer/IStackTraceAnalyzer';
  6. import { IStackTraceData } from '../interfaces/stack-trace-analyzer/IStackTraceData';
  7. import { NodeType } from '../enums/NodeType';
  8. import { FunctionDeclarationCalleeDataExtractor } from './callee-data-extractors/FunctionDeclarationCalleeDataExtractor';
  9. import { FunctionExpressionCalleeDataExtractor } from './callee-data-extractors/FunctionExpressionCalleeDataExtractor';
  10. import { ObjectExpressionCalleeDataExtractor } from './callee-data-extractors/ObjectExpressionCalleeDataExtractor';
  11. import { Nodes } from '../Nodes';
  12. import { NodeUtils } from '../NodeUtils';
  13. /**
  14. * This class generates a data with code stack trace functions calls
  15. *
  16. * For example:
  17. *
  18. * function Foo () {
  19. * var baz = function () {
  20. *
  21. * }
  22. *
  23. * baz();
  24. * }
  25. *
  26. * foo();
  27. *
  28. * Will generate a structure like:
  29. *
  30. * [
  31. * {
  32. * callee: FOO_FUNCTION_NODE
  33. * name: 'Foo',
  34. * trace: [
  35. * {
  36. * callee: BAZ_FUNCTION_NODE,
  37. * name: 'baz,
  38. * trace: []
  39. * }
  40. * ]
  41. * }
  42. * ]
  43. */
  44. export class StackTraceAnalyzer implements IStackTraceAnalyzer {
  45. /**
  46. * @type {ESTree.Node[]}
  47. */
  48. private blockScopeBody: ESTree.Node[];
  49. /**
  50. * @type {Map<string, TCalleeDataExtractor>}
  51. */
  52. private calleeDataExtractors: Map <string, TCalleeDataExtractor> = new Map <string, TCalleeDataExtractor> ([
  53. [NodeType.FunctionDeclaration, FunctionDeclarationCalleeDataExtractor],
  54. [NodeType.FunctionExpression, FunctionExpressionCalleeDataExtractor],
  55. [NodeType.ObjectExpression, ObjectExpressionCalleeDataExtractor]
  56. ]);
  57. /**
  58. * @param blockScopeBody
  59. */
  60. constructor (blockScopeBody: ESTree.Node[]) {
  61. this.blockScopeBody = blockScopeBody;
  62. }
  63. /**
  64. * @returns {IStackTraceData[]}
  65. */
  66. public analyze (): IStackTraceData[] {
  67. return this.analyzeRecursive(this.blockScopeBody);
  68. }
  69. /**
  70. * @param blockScopeBody
  71. * @returns {IStackTraceData[]}
  72. */
  73. private analyzeRecursive (blockScopeBody: ESTree.Node[]): IStackTraceData[] {
  74. const stackTraceData: IStackTraceData[] = [];
  75. for (let rootNode of blockScopeBody) {
  76. estraverse.traverse(rootNode, {
  77. enter: (node: ESTree.Node): any => {
  78. if (!Nodes.isCallExpressionNode(node) || rootNode.parentNode !== NodeUtils.getBlockScopeOfNode(node)) {
  79. return;
  80. }
  81. this.calleeDataExtractors.forEach((calleeDataExtractor: TCalleeDataExtractor) => {
  82. const calleeData: ICalleeData|null = new calleeDataExtractor(
  83. blockScopeBody,
  84. node.callee
  85. ).extract();
  86. if (!calleeData) {
  87. return;
  88. }
  89. stackTraceData.push(Object.assign({}, calleeData, {
  90. stackTrace: this.analyzeRecursive(calleeData.callee.body)
  91. }));
  92. });
  93. }
  94. });
  95. }
  96. return stackTraceData;
  97. }
  98. }