NodeAppender.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import * as ESTree from 'estree';
  2. import { TNodeWithBlockStatement } from '../types/node/TNodeWithBlockStatement';
  3. import { TStatement } from '../types/node/TStatement';
  4. import { IStackTraceData } from '../interfaces/stack-trace-analyzer/IStackTraceData';
  5. import { RandomGeneratorUtils } from '../utils/RandomGeneratorUtils';
  6. /**
  7. * This class appends node into a first deepest BlockStatement in order of function calls
  8. *
  9. * For example:
  10. *
  11. * function Foo () {
  12. * var baz = function () {
  13. *
  14. * }
  15. *
  16. * baz();
  17. * }
  18. *
  19. * foo();
  20. *
  21. * Appends node into block statement of `baz` function expression.
  22. */
  23. export class NodeAppender {
  24. /**
  25. * @param blockScopeNode
  26. * @param nodeBodyStatements
  27. */
  28. public static appendNode (
  29. blockScopeNode: TNodeWithBlockStatement,
  30. nodeBodyStatements: TStatement[]
  31. ): void {
  32. if (!NodeAppender.validateBodyStatements(nodeBodyStatements)) {
  33. nodeBodyStatements = [];
  34. }
  35. nodeBodyStatements = NodeAppender.parentizeBodyStatementsBeforeAppend(blockScopeNode, nodeBodyStatements);
  36. blockScopeNode.body = [
  37. ...blockScopeNode.body,
  38. ...nodeBodyStatements
  39. ];
  40. }
  41. /**
  42. * @param blockScopeStackTraceData
  43. * @param blockScopeNode
  44. * @param nodeBodyStatements
  45. * @param index
  46. */
  47. public static appendNodeToOptimalBlockScope (
  48. blockScopeStackTraceData: IStackTraceData[],
  49. blockScopeNode: TNodeWithBlockStatement,
  50. nodeBodyStatements: TStatement[],
  51. index: number = 0
  52. ): void {
  53. let targetBlockScope: TNodeWithBlockStatement;
  54. if (!blockScopeStackTraceData.length) {
  55. targetBlockScope = blockScopeNode;
  56. } else {
  57. targetBlockScope = NodeAppender.getOptimalBlockScope(
  58. blockScopeStackTraceData,
  59. index
  60. );
  61. }
  62. NodeAppender.prependNode(targetBlockScope, nodeBodyStatements);
  63. }
  64. /**
  65. * Returns deepest block scope node at given deep.
  66. *
  67. * @param blockScopeTraceData
  68. * @param index
  69. * @param deep
  70. * @returns {ESTree.BlockStatement}
  71. */
  72. public static getOptimalBlockScope (
  73. blockScopeTraceData: IStackTraceData[],
  74. index: number,
  75. deep: number = Infinity
  76. ): ESTree.BlockStatement {
  77. const firstCall: IStackTraceData = blockScopeTraceData[index];
  78. if (deep <= 0) {
  79. throw new Error(`Invalid \`deep\` argument value. Value should be bigger then 0.`);
  80. }
  81. if (deep > 1 && firstCall.stackTrace.length) {
  82. return NodeAppender.getOptimalBlockScope(firstCall.stackTrace, 0, --deep);
  83. } else {
  84. return firstCall.callee;
  85. }
  86. }
  87. /**
  88. * @param stackTraceRootLength
  89. */
  90. public static getRandomStackTraceIndex (stackTraceRootLength: number): number {
  91. return RandomGeneratorUtils.getRandomInteger(0, Math.max(0, Math.round(stackTraceRootLength - 1)));
  92. }
  93. /**
  94. * @param blockScopeNode
  95. * @param nodeBodyStatements
  96. * @param index
  97. */
  98. public static insertNodeAtIndex (
  99. blockScopeNode: TNodeWithBlockStatement,
  100. nodeBodyStatements: TStatement[],
  101. index: number
  102. ): void {
  103. if (!NodeAppender.validateBodyStatements(nodeBodyStatements)) {
  104. nodeBodyStatements = [];
  105. }
  106. nodeBodyStatements = NodeAppender.parentizeBodyStatementsBeforeAppend(blockScopeNode, nodeBodyStatements);
  107. blockScopeNode.body = [
  108. ...blockScopeNode.body.slice(0, index),
  109. ...nodeBodyStatements,
  110. ...blockScopeNode.body.slice(index)
  111. ];
  112. }
  113. /**
  114. * @param blockScopeNode
  115. * @param nodeBodyStatements
  116. */
  117. public static prependNode (
  118. blockScopeNode: TNodeWithBlockStatement,
  119. nodeBodyStatements: TStatement[]
  120. ): void {
  121. if (!NodeAppender.validateBodyStatements(nodeBodyStatements)) {
  122. nodeBodyStatements = [];
  123. }
  124. nodeBodyStatements = NodeAppender.parentizeBodyStatementsBeforeAppend(blockScopeNode, nodeBodyStatements);
  125. blockScopeNode.body = [
  126. ...nodeBodyStatements,
  127. ...blockScopeNode.body,
  128. ];
  129. }
  130. /**
  131. * @param blockScopeNode
  132. * @param nodeBodyStatements
  133. */
  134. private static parentizeBodyStatementsBeforeAppend (
  135. blockScopeNode: TNodeWithBlockStatement,
  136. nodeBodyStatements: TStatement[]
  137. ): TStatement[] {
  138. nodeBodyStatements.forEach((statement: TStatement) => {
  139. statement.parentNode = blockScopeNode;
  140. });
  141. return nodeBodyStatements;
  142. }
  143. /**
  144. * @param nodeBodyStatements
  145. * @returns {boolean}
  146. */
  147. private static validateBodyStatements (nodeBodyStatements: TStatement[]): boolean {
  148. return nodeBodyStatements.every((statementNode: TStatement) => {
  149. return !!statementNode && statementNode.hasOwnProperty('type');
  150. });
  151. }
  152. }