|
16 | 16 |
|
17 | 17 | package com.google.javascript.jscomp;
|
18 | 18 |
|
| 19 | +import static com.google.common.base.Preconditions.checkNotNull; |
19 | 20 | import static com.google.common.base.Preconditions.checkState;
|
20 | 21 |
|
21 | 22 | import com.google.common.base.Preconditions;
|
@@ -71,7 +72,9 @@ public final class ControlFlowAnalysis implements NodeTraversal.Callback {
|
71 | 72 | // CFG nodes that come first lexically should be visited first, because
|
72 | 73 | // they will often be executed first in the source program.
|
73 | 74 | private final Comparator<DiGraphNode<Node, Branch>> priorityComparator =
|
74 |
| - Comparator.comparingInt(digraphNode -> astPosition.get(digraphNode.getValue())); |
| 75 | + Comparator.comparingInt( |
| 76 | + digraphNode -> |
| 77 | + checkNotNull(astPosition.get(digraphNode.getValue()), digraphNode.getValue())); |
75 | 78 |
|
76 | 79 | private int astPositionCounter;
|
77 | 80 | private int priorityCounter;
|
@@ -257,7 +260,22 @@ private void prioritizeFromEntryNode(DiGraphNode<Node, Branch> entry) {
|
257 | 260 |
|
258 | 261 | @Override
|
259 | 262 | public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) {
|
260 |
| - astPosition.put(n, astPositionCounter++); |
| 263 | + if (shouldTraverseIntoChildren(n, parent)) { |
| 264 | + // Any AST node that will later have a corresponding CFG node must be in astPosition. |
| 265 | + // To avoid having astPosition grow too large, we exclude AST nodes that are not traversed |
| 266 | + // further, as they usually are not put in the CFG. |
| 267 | + astPosition.put(n, astPositionCounter++); |
| 268 | + return true; |
| 269 | + } |
| 270 | + return false; |
| 271 | + } |
| 272 | + |
| 273 | + /** |
| 274 | + * Returns whether the children of this node should be traversed as part of this control-flow |
| 275 | + * analysis, i.e. whether the control-flow graph being built may require any edges into children |
| 276 | + * of this node. |
| 277 | + */ |
| 278 | + private boolean shouldTraverseIntoChildren(Node n, Node parent) { |
261 | 279 | switch (n.getToken()) {
|
262 | 280 | case FUNCTION:
|
263 | 281 | if (shouldTraverseFunctions || n == cfg.getEntry().getValue()) {
|
@@ -295,7 +313,13 @@ public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent)
|
295 | 313 | case FOR_OF:
|
296 | 314 | case FOR_AWAIT_OF:
|
297 | 315 | // Only traverse the body of the for loop.
|
298 |
| - return n == parent.getLastChild(); |
| 316 | + boolean shouldTraverseForChild = n == parent.getLastChild(); |
| 317 | + if (!shouldTraverseForChild) { |
| 318 | + // The control-flow graph contains edges from a FOR node to all its children, even |
| 319 | + // though only the body is actually traversed. So put the other children in astPosition. |
| 320 | + astPosition.put(n, astPositionCounter++); |
| 321 | + } |
| 322 | + return shouldTraverseForChild; |
299 | 323 |
|
300 | 324 | case DO:
|
301 | 325 | // Only traverse the body of the do-while.
|
|
0 commit comments