/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.Node; import java.util.Comparator; /** * Control flow graph. * * * @param <N> The instruction type of the control flow graph. */ public class ControlFlowGraph<N> extends LinkedDirectedGraph<N, ControlFlowGraph.Branch> { /** * A special node marked by the node value key null to a singleton * "return" when control is transferred outside of the current control flow * graph. */ private final DiGraphNode<N, ControlFlowGraph.Branch> implicitReturn; private final DiGraphNode<N, ControlFlowGraph.Branch> entry; /** * Constructor. */ ControlFlowGraph( N entry, boolean nodeAnnotations, boolean edgeAnnotations) { super(nodeAnnotations, edgeAnnotations); implicitReturn = createDirectedGraphNode(null); this.entry = createDirectedGraphNode(entry); } /** * Gets the implicit return node. * * @return Return node. */ public DiGraphNode<N, ControlFlowGraph.Branch> getImplicitReturn() { return implicitReturn; } /** * Gets the entry point of the control flow graph. In general, this should be * the beginning of the global script or beginning of a function. * * @return The entry point. */ public DiGraphNode<N, ControlFlowGraph.Branch> getEntry() { return entry; } /** * Checks whether node is the implicit return. * * @param node Node. * @return True if the node is the implicit return. */ public boolean isImplicitReturn( DiGraphNode<N, ControlFlowGraph.Branch> node) { return node == implicitReturn; } /** * Gets a comparator for the nodes. The default implementation returns * {@code null}. See {@link ControlFlowGraph#getOptionalNodeComparator}. * @param isForward Whether the comparator sorts the nodes in the direction of * the flow. * @return a comparator or null (in particular, if not overridden) */ public Comparator<DiGraphNode<N, Branch>> getOptionalNodeComparator( boolean isForward) { return null; } /** * The edge object for the control flow graph. */ public static enum Branch { /** Edge is taken if the condition is true. */ ON_TRUE, /** Edge is taken if the condition is false. */ ON_FALSE, /** Unconditional branch. */ UNCOND, /** * Exception-handling code paths. * Conflates two kind of control flow passing: * - An exception is thrown, and falls into a catch or finally block * - During exception handling, a finally block finishes and control * passes to the next finally block. * In theory, we need 2 different edge types. In practice, we * can just treat them as "the edges we can't really optimize". */ ON_EX, /** Possible folded-away template */ SYN_BLOCK; public boolean isConditional() { return this == ON_TRUE || this == ON_FALSE; } } /** * Abstract callback to visit a control flow graph node without going into * subtrees of the node that are also represented by other * control flow graph nodes. * * <p>For example, traversing an IF node as root will visit the two subtrees * pointed by the {@link ControlFlowGraph.Branch#ON_TRUE} and * {@link ControlFlowGraph.Branch#ON_FALSE} edges. */ public abstract static class AbstractCfgNodeTraversalCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { if (parent == null) { return true; } return !isEnteringNewCfgNode(n); } } /** * @return True if n should be represented by a new CFG node in the control * flow graph. */ public static boolean isEnteringNewCfgNode(Node n) { Node parent = n.getParent(); switch (parent.getToken()) { case BLOCK: case ROOT: case SCRIPT: case TRY: return true; case FUNCTION: // A function node represents the start of a function where the name // bleeds into the local scope and parameters are assigned // to the formal argument names. The node includes the name of the // function and the PARAM_LIST since we assume the whole set up process // is atomic without change in control flow. The next change of // control is going into the function's body, represented by the second // child. return n != parent.getSecondChild(); case WHILE: case DO: case IF: // These control structures are represented by a node that holds the // condition. Each of them is a branch node based on its condition. return NodeUtil.getConditionExpression(parent) != n; case FOR: // The FOR(;;) node differs from other control structures in that // it has an initialization and an increment statement. Those // two statements have corresponding CFG nodes to represent them. // The FOR node only represents the condition check for each iteration. // That way the following: // for(var x = 0; x < 10; x++) { } has a graph that is isomorphic to // var x = 0; while(x<10) { x++; } return NodeUtil.getConditionExpression(parent) != n; case FOR_IN: // TODO(user): Investigate how we should handle the case where // we have a very complex expression inside the FOR-IN header. return n != parent.getFirstChild(); case SWITCH: case CASE: case CATCH: case WITH: return n != parent.getFirstChild(); default: return false; } } @Override public String toString() { String s = "CFG:\n"; for (GraphvizEdge e : getGraphvizEdges()) { s += e.toString() + '\n'; } return s; } }