code-path-analyzer.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. /**
  2. * @fileoverview A class of the code path analyzer.
  3. * @author Toru Nagashima
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const assert = require("assert"),
  10. { breakableTypePattern } = require("../../shared/ast-utils"),
  11. CodePath = require("./code-path"),
  12. CodePathSegment = require("./code-path-segment"),
  13. IdGenerator = require("./id-generator"),
  14. debug = require("./debug-helpers");
  15. //------------------------------------------------------------------------------
  16. // Helpers
  17. //------------------------------------------------------------------------------
  18. /**
  19. * Checks whether or not a given node is a `case` node (not `default` node).
  20. * @param {ASTNode} node A `SwitchCase` node to check.
  21. * @returns {boolean} `true` if the node is a `case` node (not `default` node).
  22. */
  23. function isCaseNode(node) {
  24. return Boolean(node.test);
  25. }
  26. /**
  27. * Checks if a given node appears as the value of a PropertyDefinition node.
  28. * @param {ASTNode} node THe node to check.
  29. * @returns {boolean} `true` if the node is a PropertyDefinition value,
  30. * false if not.
  31. */
  32. function isPropertyDefinitionValue(node) {
  33. const parent = node.parent;
  34. return parent && parent.type === "PropertyDefinition" && parent.value === node;
  35. }
  36. /**
  37. * Checks whether the given logical operator is taken into account for the code
  38. * path analysis.
  39. * @param {string} operator The operator found in the LogicalExpression node
  40. * @returns {boolean} `true` if the operator is "&&" or "||" or "??"
  41. */
  42. function isHandledLogicalOperator(operator) {
  43. return operator === "&&" || operator === "||" || operator === "??";
  44. }
  45. /**
  46. * Checks whether the given assignment operator is a logical assignment operator.
  47. * Logical assignments are taken into account for the code path analysis
  48. * because of their short-circuiting semantics.
  49. * @param {string} operator The operator found in the AssignmentExpression node
  50. * @returns {boolean} `true` if the operator is "&&=" or "||=" or "??="
  51. */
  52. function isLogicalAssignmentOperator(operator) {
  53. return operator === "&&=" || operator === "||=" || operator === "??=";
  54. }
  55. /**
  56. * Gets the label if the parent node of a given node is a LabeledStatement.
  57. * @param {ASTNode} node A node to get.
  58. * @returns {string|null} The label or `null`.
  59. */
  60. function getLabel(node) {
  61. if (node.parent.type === "LabeledStatement") {
  62. return node.parent.label.name;
  63. }
  64. return null;
  65. }
  66. /**
  67. * Checks whether or not a given logical expression node goes different path
  68. * between the `true` case and the `false` case.
  69. * @param {ASTNode} node A node to check.
  70. * @returns {boolean} `true` if the node is a test of a choice statement.
  71. */
  72. function isForkingByTrueOrFalse(node) {
  73. const parent = node.parent;
  74. switch (parent.type) {
  75. case "ConditionalExpression":
  76. case "IfStatement":
  77. case "WhileStatement":
  78. case "DoWhileStatement":
  79. case "ForStatement":
  80. return parent.test === node;
  81. case "LogicalExpression":
  82. return isHandledLogicalOperator(parent.operator);
  83. case "AssignmentExpression":
  84. return isLogicalAssignmentOperator(parent.operator);
  85. default:
  86. return false;
  87. }
  88. }
  89. /**
  90. * Gets the boolean value of a given literal node.
  91. *
  92. * This is used to detect infinity loops (e.g. `while (true) {}`).
  93. * Statements preceded by an infinity loop are unreachable if the loop didn't
  94. * have any `break` statement.
  95. * @param {ASTNode} node A node to get.
  96. * @returns {boolean|undefined} a boolean value if the node is a Literal node,
  97. * otherwise `undefined`.
  98. */
  99. function getBooleanValueIfSimpleConstant(node) {
  100. if (node.type === "Literal") {
  101. return Boolean(node.value);
  102. }
  103. return void 0;
  104. }
  105. /**
  106. * Checks that a given identifier node is a reference or not.
  107. *
  108. * This is used to detect the first throwable node in a `try` block.
  109. * @param {ASTNode} node An Identifier node to check.
  110. * @returns {boolean} `true` if the node is a reference.
  111. */
  112. function isIdentifierReference(node) {
  113. const parent = node.parent;
  114. switch (parent.type) {
  115. case "LabeledStatement":
  116. case "BreakStatement":
  117. case "ContinueStatement":
  118. case "ArrayPattern":
  119. case "RestElement":
  120. case "ImportSpecifier":
  121. case "ImportDefaultSpecifier":
  122. case "ImportNamespaceSpecifier":
  123. case "CatchClause":
  124. return false;
  125. case "FunctionDeclaration":
  126. case "FunctionExpression":
  127. case "ArrowFunctionExpression":
  128. case "ClassDeclaration":
  129. case "ClassExpression":
  130. case "VariableDeclarator":
  131. return parent.id !== node;
  132. case "Property":
  133. case "PropertyDefinition":
  134. case "MethodDefinition":
  135. return (
  136. parent.key !== node ||
  137. parent.computed ||
  138. parent.shorthand
  139. );
  140. case "AssignmentPattern":
  141. return parent.key !== node;
  142. default:
  143. return true;
  144. }
  145. }
  146. /**
  147. * Updates the current segment with the head segment.
  148. * This is similar to local branches and tracking branches of git.
  149. *
  150. * To separate the current and the head is in order to not make useless segments.
  151. *
  152. * In this process, both "onCodePathSegmentStart" and "onCodePathSegmentEnd"
  153. * events are fired.
  154. * @param {CodePathAnalyzer} analyzer The instance.
  155. * @param {ASTNode} node The current AST node.
  156. * @returns {void}
  157. */
  158. function forwardCurrentToHead(analyzer, node) {
  159. const codePath = analyzer.codePath;
  160. const state = CodePath.getState(codePath);
  161. const currentSegments = state.currentSegments;
  162. const headSegments = state.headSegments;
  163. const end = Math.max(currentSegments.length, headSegments.length);
  164. let i, currentSegment, headSegment;
  165. // Fires leaving events.
  166. for (i = 0; i < end; ++i) {
  167. currentSegment = currentSegments[i];
  168. headSegment = headSegments[i];
  169. if (currentSegment !== headSegment && currentSegment) {
  170. debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
  171. if (currentSegment.reachable) {
  172. analyzer.emitter.emit(
  173. "onCodePathSegmentEnd",
  174. currentSegment,
  175. node
  176. );
  177. }
  178. }
  179. }
  180. // Update state.
  181. state.currentSegments = headSegments;
  182. // Fires entering events.
  183. for (i = 0; i < end; ++i) {
  184. currentSegment = currentSegments[i];
  185. headSegment = headSegments[i];
  186. if (currentSegment !== headSegment && headSegment) {
  187. debug.dump(`onCodePathSegmentStart ${headSegment.id}`);
  188. CodePathSegment.markUsed(headSegment);
  189. if (headSegment.reachable) {
  190. analyzer.emitter.emit(
  191. "onCodePathSegmentStart",
  192. headSegment,
  193. node
  194. );
  195. }
  196. }
  197. }
  198. }
  199. /**
  200. * Updates the current segment with empty.
  201. * This is called at the last of functions or the program.
  202. * @param {CodePathAnalyzer} analyzer The instance.
  203. * @param {ASTNode} node The current AST node.
  204. * @returns {void}
  205. */
  206. function leaveFromCurrentSegment(analyzer, node) {
  207. const state = CodePath.getState(analyzer.codePath);
  208. const currentSegments = state.currentSegments;
  209. for (let i = 0; i < currentSegments.length; ++i) {
  210. const currentSegment = currentSegments[i];
  211. debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
  212. if (currentSegment.reachable) {
  213. analyzer.emitter.emit(
  214. "onCodePathSegmentEnd",
  215. currentSegment,
  216. node
  217. );
  218. }
  219. }
  220. state.currentSegments = [];
  221. }
  222. /**
  223. * Updates the code path due to the position of a given node in the parent node
  224. * thereof.
  225. *
  226. * For example, if the node is `parent.consequent`, this creates a fork from the
  227. * current path.
  228. * @param {CodePathAnalyzer} analyzer The instance.
  229. * @param {ASTNode} node The current AST node.
  230. * @returns {void}
  231. */
  232. function preprocess(analyzer, node) {
  233. const codePath = analyzer.codePath;
  234. const state = CodePath.getState(codePath);
  235. const parent = node.parent;
  236. switch (parent.type) {
  237. // The `arguments.length == 0` case is in `postprocess` function.
  238. case "CallExpression":
  239. if (parent.optional === true && parent.arguments.length >= 1 && parent.arguments[0] === node) {
  240. state.makeOptionalRight();
  241. }
  242. break;
  243. case "MemberExpression":
  244. if (parent.optional === true && parent.property === node) {
  245. state.makeOptionalRight();
  246. }
  247. break;
  248. case "LogicalExpression":
  249. if (
  250. parent.right === node &&
  251. isHandledLogicalOperator(parent.operator)
  252. ) {
  253. state.makeLogicalRight();
  254. }
  255. break;
  256. case "AssignmentExpression":
  257. if (
  258. parent.right === node &&
  259. isLogicalAssignmentOperator(parent.operator)
  260. ) {
  261. state.makeLogicalRight();
  262. }
  263. break;
  264. case "ConditionalExpression":
  265. case "IfStatement":
  266. /*
  267. * Fork if this node is at `consequent`/`alternate`.
  268. * `popForkContext()` exists at `IfStatement:exit` and
  269. * `ConditionalExpression:exit`.
  270. */
  271. if (parent.consequent === node) {
  272. state.makeIfConsequent();
  273. } else if (parent.alternate === node) {
  274. state.makeIfAlternate();
  275. }
  276. break;
  277. case "SwitchCase":
  278. if (parent.consequent[0] === node) {
  279. state.makeSwitchCaseBody(false, !parent.test);
  280. }
  281. break;
  282. case "TryStatement":
  283. if (parent.handler === node) {
  284. state.makeCatchBlock();
  285. } else if (parent.finalizer === node) {
  286. state.makeFinallyBlock();
  287. }
  288. break;
  289. case "WhileStatement":
  290. if (parent.test === node) {
  291. state.makeWhileTest(getBooleanValueIfSimpleConstant(node));
  292. } else {
  293. assert(parent.body === node);
  294. state.makeWhileBody();
  295. }
  296. break;
  297. case "DoWhileStatement":
  298. if (parent.body === node) {
  299. state.makeDoWhileBody();
  300. } else {
  301. assert(parent.test === node);
  302. state.makeDoWhileTest(getBooleanValueIfSimpleConstant(node));
  303. }
  304. break;
  305. case "ForStatement":
  306. if (parent.test === node) {
  307. state.makeForTest(getBooleanValueIfSimpleConstant(node));
  308. } else if (parent.update === node) {
  309. state.makeForUpdate();
  310. } else if (parent.body === node) {
  311. state.makeForBody();
  312. }
  313. break;
  314. case "ForInStatement":
  315. case "ForOfStatement":
  316. if (parent.left === node) {
  317. state.makeForInOfLeft();
  318. } else if (parent.right === node) {
  319. state.makeForInOfRight();
  320. } else {
  321. assert(parent.body === node);
  322. state.makeForInOfBody();
  323. }
  324. break;
  325. case "AssignmentPattern":
  326. /*
  327. * Fork if this node is at `right`.
  328. * `left` is executed always, so it uses the current path.
  329. * `popForkContext()` exists at `AssignmentPattern:exit`.
  330. */
  331. if (parent.right === node) {
  332. state.pushForkContext();
  333. state.forkBypassPath();
  334. state.forkPath();
  335. }
  336. break;
  337. default:
  338. break;
  339. }
  340. }
  341. /**
  342. * Updates the code path due to the type of a given node in entering.
  343. * @param {CodePathAnalyzer} analyzer The instance.
  344. * @param {ASTNode} node The current AST node.
  345. * @returns {void}
  346. */
  347. function processCodePathToEnter(analyzer, node) {
  348. let codePath = analyzer.codePath;
  349. let state = codePath && CodePath.getState(codePath);
  350. const parent = node.parent;
  351. /**
  352. * Creates a new code path and trigger the onCodePathStart event
  353. * based on the currently selected node.
  354. * @param {string} origin The reason the code path was started.
  355. * @returns {void}
  356. */
  357. function startCodePath(origin) {
  358. if (codePath) {
  359. // Emits onCodePathSegmentStart events if updated.
  360. forwardCurrentToHead(analyzer, node);
  361. debug.dumpState(node, state, false);
  362. }
  363. // Create the code path of this scope.
  364. codePath = analyzer.codePath = new CodePath({
  365. id: analyzer.idGenerator.next(),
  366. origin,
  367. upper: codePath,
  368. onLooped: analyzer.onLooped
  369. });
  370. state = CodePath.getState(codePath);
  371. // Emits onCodePathStart events.
  372. debug.dump(`onCodePathStart ${codePath.id}`);
  373. analyzer.emitter.emit("onCodePathStart", codePath, node);
  374. }
  375. /*
  376. * Special case: The right side of class field initializer is considered
  377. * to be its own function, so we need to start a new code path in this
  378. * case.
  379. */
  380. if (isPropertyDefinitionValue(node)) {
  381. startCodePath("class-field-initializer");
  382. /*
  383. * Intentional fall through because `node` needs to also be
  384. * processed by the code below. For example, if we have:
  385. *
  386. * class Foo {
  387. * a = () => {}
  388. * }
  389. *
  390. * In this case, we also need start a second code path.
  391. */
  392. }
  393. switch (node.type) {
  394. case "Program":
  395. startCodePath("program");
  396. break;
  397. case "FunctionDeclaration":
  398. case "FunctionExpression":
  399. case "ArrowFunctionExpression":
  400. startCodePath("function");
  401. break;
  402. case "StaticBlock":
  403. startCodePath("class-static-block");
  404. break;
  405. case "ChainExpression":
  406. state.pushChainContext();
  407. break;
  408. case "CallExpression":
  409. if (node.optional === true) {
  410. state.makeOptionalNode();
  411. }
  412. break;
  413. case "MemberExpression":
  414. if (node.optional === true) {
  415. state.makeOptionalNode();
  416. }
  417. break;
  418. case "LogicalExpression":
  419. if (isHandledLogicalOperator(node.operator)) {
  420. state.pushChoiceContext(
  421. node.operator,
  422. isForkingByTrueOrFalse(node)
  423. );
  424. }
  425. break;
  426. case "AssignmentExpression":
  427. if (isLogicalAssignmentOperator(node.operator)) {
  428. state.pushChoiceContext(
  429. node.operator.slice(0, -1), // removes `=` from the end
  430. isForkingByTrueOrFalse(node)
  431. );
  432. }
  433. break;
  434. case "ConditionalExpression":
  435. case "IfStatement":
  436. state.pushChoiceContext("test", false);
  437. break;
  438. case "SwitchStatement":
  439. state.pushSwitchContext(
  440. node.cases.some(isCaseNode),
  441. getLabel(node)
  442. );
  443. break;
  444. case "TryStatement":
  445. state.pushTryContext(Boolean(node.finalizer));
  446. break;
  447. case "SwitchCase":
  448. /*
  449. * Fork if this node is after the 2st node in `cases`.
  450. * It's similar to `else` blocks.
  451. * The next `test` node is processed in this path.
  452. */
  453. if (parent.discriminant !== node && parent.cases[0] !== node) {
  454. state.forkPath();
  455. }
  456. break;
  457. case "WhileStatement":
  458. case "DoWhileStatement":
  459. case "ForStatement":
  460. case "ForInStatement":
  461. case "ForOfStatement":
  462. state.pushLoopContext(node.type, getLabel(node));
  463. break;
  464. case "LabeledStatement":
  465. if (!breakableTypePattern.test(node.body.type)) {
  466. state.pushBreakContext(false, node.label.name);
  467. }
  468. break;
  469. default:
  470. break;
  471. }
  472. // Emits onCodePathSegmentStart events if updated.
  473. forwardCurrentToHead(analyzer, node);
  474. debug.dumpState(node, state, false);
  475. }
  476. /**
  477. * Updates the code path due to the type of a given node in leaving.
  478. * @param {CodePathAnalyzer} analyzer The instance.
  479. * @param {ASTNode} node The current AST node.
  480. * @returns {void}
  481. */
  482. function processCodePathToExit(analyzer, node) {
  483. const codePath = analyzer.codePath;
  484. const state = CodePath.getState(codePath);
  485. let dontForward = false;
  486. switch (node.type) {
  487. case "ChainExpression":
  488. state.popChainContext();
  489. break;
  490. case "IfStatement":
  491. case "ConditionalExpression":
  492. state.popChoiceContext();
  493. break;
  494. case "LogicalExpression":
  495. if (isHandledLogicalOperator(node.operator)) {
  496. state.popChoiceContext();
  497. }
  498. break;
  499. case "AssignmentExpression":
  500. if (isLogicalAssignmentOperator(node.operator)) {
  501. state.popChoiceContext();
  502. }
  503. break;
  504. case "SwitchStatement":
  505. state.popSwitchContext();
  506. break;
  507. case "SwitchCase":
  508. /*
  509. * This is the same as the process at the 1st `consequent` node in
  510. * `preprocess` function.
  511. * Must do if this `consequent` is empty.
  512. */
  513. if (node.consequent.length === 0) {
  514. state.makeSwitchCaseBody(true, !node.test);
  515. }
  516. if (state.forkContext.reachable) {
  517. dontForward = true;
  518. }
  519. break;
  520. case "TryStatement":
  521. state.popTryContext();
  522. break;
  523. case "BreakStatement":
  524. forwardCurrentToHead(analyzer, node);
  525. state.makeBreak(node.label && node.label.name);
  526. dontForward = true;
  527. break;
  528. case "ContinueStatement":
  529. forwardCurrentToHead(analyzer, node);
  530. state.makeContinue(node.label && node.label.name);
  531. dontForward = true;
  532. break;
  533. case "ReturnStatement":
  534. forwardCurrentToHead(analyzer, node);
  535. state.makeReturn();
  536. dontForward = true;
  537. break;
  538. case "ThrowStatement":
  539. forwardCurrentToHead(analyzer, node);
  540. state.makeThrow();
  541. dontForward = true;
  542. break;
  543. case "Identifier":
  544. if (isIdentifierReference(node)) {
  545. state.makeFirstThrowablePathInTryBlock();
  546. dontForward = true;
  547. }
  548. break;
  549. case "CallExpression":
  550. case "ImportExpression":
  551. case "MemberExpression":
  552. case "NewExpression":
  553. case "YieldExpression":
  554. state.makeFirstThrowablePathInTryBlock();
  555. break;
  556. case "WhileStatement":
  557. case "DoWhileStatement":
  558. case "ForStatement":
  559. case "ForInStatement":
  560. case "ForOfStatement":
  561. state.popLoopContext();
  562. break;
  563. case "AssignmentPattern":
  564. state.popForkContext();
  565. break;
  566. case "LabeledStatement":
  567. if (!breakableTypePattern.test(node.body.type)) {
  568. state.popBreakContext();
  569. }
  570. break;
  571. default:
  572. break;
  573. }
  574. // Emits onCodePathSegmentStart events if updated.
  575. if (!dontForward) {
  576. forwardCurrentToHead(analyzer, node);
  577. }
  578. debug.dumpState(node, state, true);
  579. }
  580. /**
  581. * Updates the code path to finalize the current code path.
  582. * @param {CodePathAnalyzer} analyzer The instance.
  583. * @param {ASTNode} node The current AST node.
  584. * @returns {void}
  585. */
  586. function postprocess(analyzer, node) {
  587. /**
  588. * Ends the code path for the current node.
  589. * @returns {void}
  590. */
  591. function endCodePath() {
  592. let codePath = analyzer.codePath;
  593. // Mark the current path as the final node.
  594. CodePath.getState(codePath).makeFinal();
  595. // Emits onCodePathSegmentEnd event of the current segments.
  596. leaveFromCurrentSegment(analyzer, node);
  597. // Emits onCodePathEnd event of this code path.
  598. debug.dump(`onCodePathEnd ${codePath.id}`);
  599. analyzer.emitter.emit("onCodePathEnd", codePath, node);
  600. debug.dumpDot(codePath);
  601. codePath = analyzer.codePath = analyzer.codePath.upper;
  602. if (codePath) {
  603. debug.dumpState(node, CodePath.getState(codePath), true);
  604. }
  605. }
  606. switch (node.type) {
  607. case "Program":
  608. case "FunctionDeclaration":
  609. case "FunctionExpression":
  610. case "ArrowFunctionExpression":
  611. case "StaticBlock": {
  612. endCodePath();
  613. break;
  614. }
  615. // The `arguments.length >= 1` case is in `preprocess` function.
  616. case "CallExpression":
  617. if (node.optional === true && node.arguments.length === 0) {
  618. CodePath.getState(analyzer.codePath).makeOptionalRight();
  619. }
  620. break;
  621. default:
  622. break;
  623. }
  624. /*
  625. * Special case: The right side of class field initializer is considered
  626. * to be its own function, so we need to end a code path in this
  627. * case.
  628. *
  629. * We need to check after the other checks in order to close the
  630. * code paths in the correct order for code like this:
  631. *
  632. *
  633. * class Foo {
  634. * a = () => {}
  635. * }
  636. *
  637. * In this case, The ArrowFunctionExpression code path is closed first
  638. * and then we need to close the code path for the PropertyDefinition
  639. * value.
  640. */
  641. if (isPropertyDefinitionValue(node)) {
  642. endCodePath();
  643. }
  644. }
  645. //------------------------------------------------------------------------------
  646. // Public Interface
  647. //------------------------------------------------------------------------------
  648. /**
  649. * The class to analyze code paths.
  650. * This class implements the EventGenerator interface.
  651. */
  652. class CodePathAnalyzer {
  653. /**
  654. * @param {EventGenerator} eventGenerator An event generator to wrap.
  655. */
  656. constructor(eventGenerator) {
  657. this.original = eventGenerator;
  658. this.emitter = eventGenerator.emitter;
  659. this.codePath = null;
  660. this.idGenerator = new IdGenerator("s");
  661. this.currentNode = null;
  662. this.onLooped = this.onLooped.bind(this);
  663. }
  664. /**
  665. * Does the process to enter a given AST node.
  666. * This updates state of analysis and calls `enterNode` of the wrapped.
  667. * @param {ASTNode} node A node which is entering.
  668. * @returns {void}
  669. */
  670. enterNode(node) {
  671. this.currentNode = node;
  672. // Updates the code path due to node's position in its parent node.
  673. if (node.parent) {
  674. preprocess(this, node);
  675. }
  676. /*
  677. * Updates the code path.
  678. * And emits onCodePathStart/onCodePathSegmentStart events.
  679. */
  680. processCodePathToEnter(this, node);
  681. // Emits node events.
  682. this.original.enterNode(node);
  683. this.currentNode = null;
  684. }
  685. /**
  686. * Does the process to leave a given AST node.
  687. * This updates state of analysis and calls `leaveNode` of the wrapped.
  688. * @param {ASTNode} node A node which is leaving.
  689. * @returns {void}
  690. */
  691. leaveNode(node) {
  692. this.currentNode = node;
  693. /*
  694. * Updates the code path.
  695. * And emits onCodePathStart/onCodePathSegmentStart events.
  696. */
  697. processCodePathToExit(this, node);
  698. // Emits node events.
  699. this.original.leaveNode(node);
  700. // Emits the last onCodePathStart/onCodePathSegmentStart events.
  701. postprocess(this, node);
  702. this.currentNode = null;
  703. }
  704. /**
  705. * This is called on a code path looped.
  706. * Then this raises a looped event.
  707. * @param {CodePathSegment} fromSegment A segment of prev.
  708. * @param {CodePathSegment} toSegment A segment of next.
  709. * @returns {void}
  710. */
  711. onLooped(fromSegment, toSegment) {
  712. if (fromSegment.reachable && toSegment.reachable) {
  713. debug.dump(`onCodePathSegmentLoop ${fromSegment.id} -> ${toSegment.id}`);
  714. this.emitter.emit(
  715. "onCodePathSegmentLoop",
  716. fromSegment,
  717. toSegment,
  718. this.currentNode
  719. );
  720. }
  721. }
  722. }
  723. module.exports = CodePathAnalyzer;