NodeAppender.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import * as ESTree from 'estree';
  2. import { TNodeWithStatements } from '../types/node/TNodeWithStatements';
  3. import { TStatement } from '../types/node/TStatement';
  4. import { ICallsGraphData } from '../interfaces/analyzers/calls-graph-analyzer/ICallsGraphData';
  5. import { NodeGuards } from './NodeGuards';
  6. export class NodeAppender {
  7. /**
  8. * @param {TNodeWithStatements} nodeWithStatements
  9. * @param {TStatement[]} statements
  10. */
  11. public static append (nodeWithStatements: TNodeWithStatements, statements: TStatement[]): void {
  12. statements = NodeAppender.parentizeScopeStatementsBeforeAppend(nodeWithStatements, statements);
  13. NodeAppender.setScopeStatements(nodeWithStatements, [
  14. ...NodeAppender.getScopeStatements(nodeWithStatements),
  15. ...statements
  16. ]);
  17. }
  18. /**
  19. * Appends node into a first deepest BlockStatement in order of function calls
  20. *
  21. * For example:
  22. *
  23. * function Foo () {
  24. * var baz = function () {
  25. *
  26. * }
  27. *
  28. * baz();
  29. * }
  30. *
  31. * foo();
  32. *
  33. * Appends node into block statement of `baz` function expression
  34. *
  35. * @param {ICallsGraphData[]} callsGraphData
  36. * @param {TNodeWithStatements} nodeWithStatements
  37. * @param {TStatement[]} bodyStatements
  38. * @param {number} index
  39. */
  40. public static appendToOptimalBlockScope (
  41. callsGraphData: ICallsGraphData[],
  42. nodeWithStatements: TNodeWithStatements,
  43. bodyStatements: TStatement[],
  44. index: number = 0
  45. ): void {
  46. const targetBlockScope: TNodeWithStatements = callsGraphData.length
  47. ? NodeAppender.getOptimalBlockScope(callsGraphData, index)
  48. : nodeWithStatements;
  49. NodeAppender.prepend(targetBlockScope, bodyStatements);
  50. }
  51. /**
  52. * Returns deepest block scope node at given deep.
  53. *
  54. * @param {ICallsGraphData[]} callsGraphData
  55. * @param {number} index
  56. * @param {number} deep
  57. * @returns {BlockStatement}
  58. */
  59. public static getOptimalBlockScope (
  60. callsGraphData: ICallsGraphData[],
  61. index: number,
  62. deep: number = Infinity
  63. ): ESTree.BlockStatement {
  64. const firstCall: ICallsGraphData = callsGraphData[index];
  65. if (deep <= 0) {
  66. throw new Error('Invalid `deep` argument value. Value should be bigger then 0.');
  67. }
  68. if (deep > 1 && firstCall.callsGraph.length) {
  69. return NodeAppender.getOptimalBlockScope(firstCall.callsGraph, 0, --deep);
  70. } else {
  71. return firstCall.callee;
  72. }
  73. }
  74. /**
  75. * @param {TNodeWithStatements} nodeWithStatements
  76. * @returns {TStatement[]}
  77. */
  78. public static getScopeStatements (nodeWithStatements: TNodeWithStatements): TStatement[] {
  79. if (NodeGuards.isSwitchCaseNode(nodeWithStatements)) {
  80. return nodeWithStatements.consequent;
  81. }
  82. return nodeWithStatements.body;
  83. }
  84. /**
  85. * @param {TNodeWithStatements} nodeWithStatements
  86. * @param {TStatement[]} statements
  87. * @param {Node} target
  88. */
  89. public static insertBefore (
  90. nodeWithStatements: TNodeWithStatements,
  91. statements: TStatement[],
  92. target: ESTree.Statement
  93. ): void {
  94. const indexInScopeStatement: number = NodeAppender
  95. .getScopeStatements(nodeWithStatements)
  96. .indexOf(target);
  97. NodeAppender.insertAtIndex(nodeWithStatements, statements, indexInScopeStatement);
  98. }
  99. /**
  100. * @param {TNodeWithStatements} nodeWithStatements
  101. * @param {TStatement[]} statements
  102. * @param {Node} target
  103. */
  104. public static insertAfter (
  105. nodeWithStatements: TNodeWithStatements,
  106. statements: TStatement[],
  107. target: ESTree.Statement
  108. ): void {
  109. const indexInScopeStatement: number = NodeAppender
  110. .getScopeStatements(nodeWithStatements)
  111. .indexOf(target);
  112. NodeAppender.insertAtIndex(nodeWithStatements, statements, indexInScopeStatement + 1);
  113. }
  114. /**
  115. * @param {TNodeWithStatements} nodeWithStatements
  116. * @param {TStatement[]} statements
  117. * @param {number} index
  118. */
  119. public static insertAtIndex (
  120. nodeWithStatements: TNodeWithStatements,
  121. statements: TStatement[],
  122. index: number
  123. ): void {
  124. statements = NodeAppender.parentizeScopeStatementsBeforeAppend(nodeWithStatements, statements);
  125. NodeAppender.setScopeStatements(nodeWithStatements, [
  126. ...NodeAppender.getScopeStatements(nodeWithStatements).slice(0, index),
  127. ...statements,
  128. ...NodeAppender.getScopeStatements(nodeWithStatements).slice(index)
  129. ]);
  130. }
  131. /**
  132. * @param {TNodeWithStatements} nodeWithStatements
  133. * @param {TStatement[]} statements
  134. */
  135. public static prepend (nodeWithStatements: TNodeWithStatements, statements: TStatement[]): void {
  136. statements = NodeAppender.parentizeScopeStatementsBeforeAppend(nodeWithStatements, statements);
  137. NodeAppender.setScopeStatements(nodeWithStatements, [
  138. ...statements,
  139. ...NodeAppender.getScopeStatements(nodeWithStatements),
  140. ]);
  141. }
  142. /**
  143. * @param {TNodeWithStatements} nodeWithStatements
  144. * @param {Statement} statement
  145. */
  146. public static remove (nodeWithStatements: TNodeWithStatements, statement: ESTree.Statement): void {
  147. const scopeStatements: TStatement[] = NodeAppender.getScopeStatements(nodeWithStatements);
  148. const indexInScopeStatement: number = scopeStatements.indexOf(statement);
  149. if (indexInScopeStatement === -1) {
  150. return;
  151. }
  152. const updatedStatements: TStatement[] = [...scopeStatements];
  153. updatedStatements.splice(indexInScopeStatement, 1);
  154. NodeAppender.setScopeStatements(nodeWithStatements, updatedStatements);
  155. }
  156. /**
  157. * @param {TNodeWithStatements} nodeWithStatements
  158. * @param {TStatement[]} statements
  159. * @returns {TStatement[]}
  160. */
  161. private static parentizeScopeStatementsBeforeAppend (
  162. nodeWithStatements: TNodeWithStatements,
  163. statements: TStatement[]
  164. ): TStatement[] {
  165. statements.forEach((statement: TStatement) => {
  166. statement.parentNode = nodeWithStatements;
  167. });
  168. return statements;
  169. }
  170. /**
  171. * @param {TNodeWithStatements} nodeWithStatements
  172. * @param {TStatement[]} statements
  173. */
  174. private static setScopeStatements (nodeWithStatements: TNodeWithStatements, statements: TStatement[]): void {
  175. if (NodeGuards.isSwitchCaseNode(nodeWithStatements)) {
  176. nodeWithStatements.consequent = <ESTree.Statement[]>statements;
  177. return;
  178. }
  179. nodeWithStatements.body = statements;
  180. }
  181. }