/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.visit; import java.util.*; import polyglot.ast.Node; import polyglot.ast.Term; import polyglot.types.Type; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; import polyglot.util.IdentityKey; public class FlowGraph { /** * Maps from AST nodes to path maps and hence to <code>Peer</code>s that * represent occurrences of the AST node in the flow graph. In particular, * <code>peerMap</code> maps <code>IdentityKey(Node)</code>s to path * maps. A path map is a map from paths (<code>ListKey(List of Terms)</code>) * to <code>Peer</code>s. In particular, if <code>n</code> is an AST * node in a finally block, then there will be a <code>Peer</code> of * <code>n</code> for each possible path to the finally block, and the * path map records which <code>Peer</code> corresponds to which path. If * <code>n</code> does not occur in a finally block, then the path map * should have only a single entry, from an empty list to the unique * <code>Peer</code> for <code>n</code>. * * <p> * <b>WARNING</b>: the AST must be a tree, not a DAG. Otherwise the same * peer may be used for a node that appears at multiple points in the AST. * These points may have different data flows. * </p> */ protected Map<IdentityKey,Map<PeerKey, Peer>> peerMap; /** * The root of the AST that this is a flow graph for. */ protected Term root; /** * Is the flow in this flow graph forward or backward? */ protected boolean forward; public FlowGraph(Term root, boolean forward) { this.root = root; this.forward = forward; this.peerMap = CollectionFactory.newHashMap(); } public Term root() { return root; } public boolean forward() { return forward; } public Collection<Peer> entryPeers() { return peers(root, Term.ENTRY); } public Collection<Peer> exitPeers() { return peers(root, Term.EXIT); } public Collection<Peer> startPeers() { return forward ? entryPeers() : exitPeers(); } public Collection<Peer> finishPeers() { return forward ? exitPeers() : entryPeers(); } public Collection<Map<PeerKey, Peer>> pathMaps() { return peerMap.values(); } public Map<PeerKey, Peer> pathMap(Node n) { return peerMap.get(new IdentityKey(n)); } /** * Return a collection of all <code>Peer</code>s in this flow graph. */ public Collection<Peer> peers() { Collection<Peer> c = new ArrayList<Peer>(); for (Map<PeerKey, Peer> m : peerMap.values()) { for (Peer peer : m.values()) { c.add(peer); } } return c; } /** * Retrieve the entry or exit <code>Peer</code> for the * <code>Term n</code>, where <code>n</code> does not appear in a * finally block. If no such Peer exists, then one will be created. * * <code>entry</code> can be Term.ENTRY or Term.EXIT. */ public Peer peer(Term n, int entry) { return peer(n, Collections.<Term>emptyList(), entry); } /** * Return a collection of all of the entry or exit <code>Peer</code>s for * the given <code>Term n</code>. * * <code>entry</code> can be Term.ENTRY or Term.EXIT. */ public Collection<Peer> peers(Term n, int entry) { IdentityKey k = new IdentityKey(n); Map<PeerKey, Peer> pathMap = peerMap.get(k); if (pathMap == null) { return Collections.<Peer>emptyList(); } Collection<Peer> peers = pathMap.values(); List<Peer> l = new ArrayList<Peer>(peers.size()); for (Peer p : peers) { if (p.entry == entry) { l.add(p); } } return l; } /** * Retrieve the <code>Peer</code> for the <code>Term n</code> that is * associated with the given path to the finally block. (A term that occurs * in a finally block has one Peer for each possible path to that finally * block.) If no such Peer exists, then one will be created. * * <code>entry</code> can be Term.ENTRY or Term.EXIT. */ public Peer peer(Term n, List<Term> path_to_finally, int entry) { IdentityKey k = new IdentityKey(n); Map<PeerKey, Peer> pathMap = peerMap.get(k); if (pathMap == null) { pathMap = CollectionFactory.newHashMap(); peerMap.put(k, pathMap); } PeerKey lk = new PeerKey(path_to_finally, entry); Peer p = (Peer) pathMap.get(lk); if (p == null) { p = new Peer(n, path_to_finally, entry); pathMap.put(lk, p); } return p; } /** * This class provides an identifying label for edges in the flow graph. * Thus, the condition of an if statement will have at least two edges * leaving it (in a forward flow graph): one will have the EdgeKey * FlowGraph.EDGE_KEY_TRUE, and is the flow that is taken when the condition * evaluates to true, and one will have the EdgeKey FlowGraph.EDGE_KEY_FALSE, * and is the flow that is taken when the condition evaluates to false. * * The differentiation of the flow graph edges allows for a finer grain * data flow analysis, as the dataflow equations can incorporate the * knowledge that a condition is true or false on certain flow paths. */ public static class EdgeKey { protected Object o; protected EdgeKey(Object o) { this.o = o; } public int hashCode() { return o.hashCode(); } public boolean equals(Object other) { return (other instanceof EdgeKey) && (((EdgeKey)other).o.equals(this.o)); } public String toString() { return o.toString(); } } /** * This class extends EdgeKey and is the key for edges that are * taken when an exception of type t is thrown. Thus, the flow from * line 2 in the example below to the catch block (line 4) would have an * ExceptionEdgeKey constructed with the Type representing * NullPointerExceptions. * * <pre> * ... * try { // line 1 * o.foo(); // line 2 * } // line 3 * catch (NullPointerException e) { // line 4 * ... * } * ... * </pre> */ public static class ExceptionEdgeKey extends EdgeKey { public ExceptionEdgeKey(Type t) { super(t); } public Type type() { return (Type) o; } public String toString() { return (type().isClass() ? type().toClass().name().toString() : type().toString() ); } } /** * This EdgeKey is the EdgeKey for edges where the expression evaluates * to true. */ public static final EdgeKey EDGE_KEY_TRUE = new EdgeKey("true"); /** * This EdgeKey is the EdgeKey for edges where the expression evaluates * to false. */ public static final EdgeKey EDGE_KEY_FALSE = new EdgeKey("false"); /** * This EdgeKey is the EdgeKey for edges where the flow is not suitable * for EDGE_KEY_TRUE, EDGE_KEY_FALSE or an * ExceptionEdgeKey, such as the edges from a switch * statement to its cases and * the flow from a sink node in the control flow graph. */ public static final EdgeKey EDGE_KEY_OTHER = new EdgeKey(""); /** * This class represents an edge in the flow graph. The target of the edge * is either the head or the tail of the edge, depending on how the Edge is * used. Thus, the target field in Edges in the collection Peer.preds is the * source Peer, while the target field in Edges in the collection Peer.succs * is the destination Peer of edges. * * Each Edge has an EdgeKey, which identifies when flow uses that edge in * the flow graph. See EdgeKey for more information. */ public static class Edge { protected Edge(EdgeKey key, Peer target) { this.key = key; this.target = target; } public EdgeKey getKey() { return key; } public Peer getTarget() { return target; } protected EdgeKey key; protected Peer target; public String toString() { return "(" + key + ")" + target; } } /** * A <code>Peer</code> is an occurance of an AST node in a flow graph. * For most AST nodes, there will be only one Peer for each AST node. * However, if the AST node occurs in a finally block, then there will be * multiple <code>Peer</code>s for that AST node, one for each possible * path to the finally block. This is becuase flow graphs for finally blocks * are copied, one copy for each possible path to the finally block. */ public static class Peer { protected DataFlow.Item inItem; // Input Item for dataflow analysis protected Map<EdgeKey, DataFlow.Item> outItems; // Output Items for dataflow analysis, // a map from EdgeKeys to DataFlowlItems protected Term node; // The AST node that this peer is an occurrence of. protected List<Edge> succs; // List of successor Edges protected List<Edge> preds; // List of predecessor Edges protected List<Term> path_to_finally; // the path to the finally block that // uniquely distinguishes this Peer // from the other Peers for the AST node. protected int entry; // Term.ENTRY or Term.EXIT /** * Set of all the different EdgeKeys that occur in the Edges in the * succs. This Set is lazily constructed, as needed, by the * method succEdgeKeys() */ private Set<EdgeKey> succEdgeKeys; public Peer(Term node, List<Term> path_to_finally, int entry) { this.node = node; this.path_to_finally = path_to_finally; this.inItem = null; this.outItems = null; this.succs = new ArrayList<Edge>(); this.preds = new ArrayList<Edge>(); this.entry = entry; this.succEdgeKeys = null; } /** The successor Edges. */ public List<Edge> succs() { return succs; } /** The predecessor Edges. */ public List<Edge> preds() { return preds; } /** The node for which this is a peer. */ public Term node() { return node; } /** * The input data flow item. Should only be called * after data flow analysis is performed. */ public DataFlow.Item inItem() { return inItem; } /** * The output item for a particular EdgeKey. Should only be called * after data flow analysis is performed. */ public DataFlow.Item outItem(EdgeKey key) { return outItems.get(key); } public String toString() { return (entry == Term.ENTRY ? "entry: " : "") + node + path_to_finally; } public Set<EdgeKey> succEdgeKeys() { if (this.succEdgeKeys == null) { // the successor edge keys have not yet been calculated. do it // now. this.succEdgeKeys = CollectionFactory.newHashSet(); for (Edge e : this.succs) { this.succEdgeKeys.add(e.getKey()); } if (this.succEdgeKeys.isEmpty()) { // There are no successors for this node. Add in the OTHER // edge key, so that there is something to map the output // item from... this.succEdgeKeys.add(FlowGraph.EDGE_KEY_OTHER); } } return this.succEdgeKeys; } } /** * Class to be used for inserting Lists in hashtables using collection * equality (as defined in * {@link x10.util.CollectionUtil CollectionUtil}). */ protected static class PeerKey { protected List<Term> list; protected int entry; public PeerKey(List<Term> list, int entry) { this.list = list; this.entry = entry; } public int hashCode() { return list.hashCode() ^ entry; } public boolean equals(Object other) { if (other instanceof PeerKey) { PeerKey k = (PeerKey) other; return CollectionUtil.allEqual(list, k.list) && entry == k.entry; } else { return false; } } } public String toString() { StringBuffer sb = new StringBuffer(); Set<Peer> todo = CollectionFactory.newHashSet(this.peers()); LinkedList<Peer> queue = new LinkedList<Peer>(startPeers()); while (!queue.isEmpty()) { Peer p = (Peer)queue.removeFirst(); todo.remove(p); //sb.append(StringUtil.getShortNameComponent(p.node.getClass().getName()) + " ["+p.node+"]" + "\n"); sb.append(p.node+" (" + p.node.position()+ ")\n"); for (Edge e : p.succs) { Peer q = e.getTarget(); sb.append(" -> " + q.node+" (" + q.node.position()+ ")\n"); //sb.append(" " + StringUtil.getShortNameComponent(q.node.getClass().getName()) + " ["+q.node+"]" + "\n"); if (todo.contains(q) && !queue.contains(q)) { queue.addLast(q); } } if (queue.isEmpty() && !todo.isEmpty()) { sb.append("\n\n***UNREACHABLE***\n"); queue.addAll(todo); todo = Collections.<Peer>emptySet(); } } return sb.toString(); } }