/** * Verify that some non-looping paths from {@code a} to {@code b} pass through at least one node * where {@code nodePredicate} is true. */ private boolean checkSomePathsWithoutBackEdges(DiGraphNode<N, E> a, DiGraphNode<N, E> b) { if (nodePredicate.apply(a.getValue()) && (inclusive || (a != start && a != end))) { return true; } if (a == b) { return false; } for (DiGraphEdge<N, E> e : a.getOutEdges()) { // Once we visited that edge once, we no longer need to // re-visit it again. if (e.getAnnotation() == VISITED_EDGE) { continue; } e.setAnnotation(VISITED_EDGE); if (ignoreEdge(e)) { continue; } if (e.getAnnotation() == BACK_EDGE) { continue; } DiGraphNode<N, E> next = e.getDestination(); if (checkSomePathsWithoutBackEdges(next, b)) { return true; } } return false; }
/** * Gets all the edges of the graph. */ private static List<DiGraphEdge<Node, Branch>> getAllEdges( ControlFlowGraph<Node> cfg) { List<DiGraphEdge<Node, Branch>> edges = new ArrayList<>(); for (DiGraphNode<Node, Branch> n : cfg.getDirectedGraphNodes()) { edges.addAll(cfg.getOutEdges(n.getValue())); } return edges; }
/** @return true if all paths from block must exit with an explicit return. */ private boolean allPathsReturn(Node function) { // Computes the control flow graph. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); cfa.process(null, function); ControlFlowGraph<Node> cfg = cfa.getCfg(); Node returnPathsParent = cfg.getImplicitReturn().getValue(); for (DiGraphNode<Node, Branch> pred : cfg.getDirectedPredNodes(returnPathsParent)) { Node n = pred.getValue(); if (!n.isReturn()) { return false; } } return true; }
@Override public void process(Node externs, Node root) { this.root = root; astPositionCounter = 0; astPosition = Maps.newHashMap(); nodePriorities = Maps.newHashMap(); cfg = new AstControlFlowGraph(computeFallThrough(root), nodePriorities); NodeTraversal.traverse(compiler, root, this); astPosition.put(null, ++astPositionCounter); // the implicit return is last. // Now, generate the priority of nodes by doing a depth-first // search on the CFG. priorityCounter = 0; DiGraphNode<Node, Branch> entry = cfg.getEntry(); prioritizeFromEntryNode(entry); if (shouldTraverseFunctions) { // If we're traversing inner functions, we need to rank the // priority of them too. for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) { Node value = candidate.getValue(); if (value != null && value.getType() == Token.FUNCTION) { Preconditions.checkState(!nodePriorities.containsKey(candidate) || candidate == entry); prioritizeFromEntryNode(candidate); } } } // At this point, all reachable nodes have been given a priority, but // unreachable nodes have not been given a priority. Put them last. // Presumably, it doesn't really matter what priority they get, since // this shouldn't happen in real code. for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) { if (!nodePriorities.containsKey(candidate)) { nodePriorities.put(candidate, ++priorityCounter); } } // Again, the implicit return node is always last. nodePriorities.put(cfg.getImplicitReturn(), ++priorityCounter); }
private void discoverBackEdges(DiGraphNode<N, E> u) { u.setAnnotation(GRAY); for (DiGraphEdge<N, E> e : u.getOutEdges()) { if (ignoreEdge(e)) { continue; } DiGraphNode<N, E> v = e.getDestination(); if (v.getAnnotation() == WHITE) { discoverBackEdges(v); } else if (v.getAnnotation() == GRAY) { e.setAnnotation(BACK_EDGE); } } u.setAnnotation(BLACK); }
@Override public int compare(DiGraphNode<Node, Branch> a, DiGraphNode<Node, Branch> b) { return astPosition.get(a.getValue()) - astPosition.get(b.getValue()); }