/* Soot - a J*va Optimization Framework * Copyright (C) 1999 Patrice Pominville, Raja Vallee-Rai * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the Sable Research Group and others 1997-2003. * See the 'credits' file distributed with Soot for the complete list of * contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot) */ package soot.toolkits.graph; import soot.*; import soot.util.*; import java.util.*; import java.util.Map.Entry; import soot.options.Options; /** * <p> * Represents a CFG where the nodes are {@link Unit} instances and * edges represent unexceptional and (possibly) exceptional control * flow between <tt>Unit</tt>s.</p> * * <p> * This is an abstract class, providing the facilities used to build * CFGs for specific purposes.</p> */ public abstract class UnitGraph implements DirectedGraph<Unit> { protected List<Unit> heads; protected List<Unit> tails; protected Map<Unit,List<Unit>> unitToSuccs; protected Map<Unit,List<Unit>> unitToPreds; protected SootMethod method; protected Body body; protected Chain<Unit> unitChain; /** * Performs the work that is required to construct any sort of * <tt>UnitGraph</tt>. * * @param body The body of the method for which to construct a * control flow graph. */ protected UnitGraph( Body body) { this.body = body; unitChain = body.getUnits(); method = body.getMethod(); if(Options.v().verbose()) G.v().out.println("[" + method.getName() + "] Constructing " + this.getClass().getName() + "..."); } /** * Utility method for <tt>UnitGraph</tt> constructors. It computes * the edges corresponding to unexceptional control flow. * * @param unitToSuccs A {@link Map} from {@link Unit}s to * {@link List}s of {@link Unit}s. This is * an ``out parameter''; callers must pass an empty * {@link Map}. <tt>buildUnexceptionalEdges</tt> will * add a mapping for every <tt>Unit</tt> in the * body to a list of its unexceptional successors. * * @param unitToPreds A {@link Map} from {@link Unit}s to * {@link List}s of {@link Unit}s. This is an * ``out parameter''; callers must pass an empty * {@link Map}. <tt>buildUnexceptionalEdges</tt> will * add a mapping for every <tt>Unit</tt> in the body * to a list of its unexceptional predecessors. */ protected void buildUnexceptionalEdges(Map<Unit,List<Unit>> unitToSuccs, Map<Unit,List<Unit>> unitToPreds) { // Initialize the predecessor sets to empty for (Iterator<Unit> unitIt = unitChain.iterator(); unitIt.hasNext(); ) { unitToPreds.put(unitIt.next(), new ArrayList<Unit>()); } Iterator<Unit> unitIt = unitChain.iterator(); Unit currentUnit, nextUnit; nextUnit = unitIt.hasNext() ? (Unit) unitIt.next(): null; while(nextUnit != null) { currentUnit = nextUnit; nextUnit = unitIt.hasNext() ? (Unit) unitIt.next(): null; List<Unit> successors = new ArrayList<Unit>(); if( currentUnit.fallsThrough() ) { // Add the next unit as the successor if(nextUnit != null) { successors.add(nextUnit); unitToPreds.get(nextUnit).add(currentUnit); } } if( currentUnit.branches() ) { for (Iterator<UnitBox> targetIt = currentUnit.getUnitBoxes().iterator(); targetIt.hasNext(); ) { Unit target = targetIt.next().getUnit(); // Arbitrary bytecode can branch to the same // target it falls through to, so we screen for duplicates: if (! successors.contains(target)) { successors.add(target); unitToPreds.get(target).add(currentUnit); } } } // Store away successors unitToSuccs.put(currentUnit, successors); } } /** * <p>Utility method used in the construction of {@link UnitGraph}s, to be * called only after the unitToPreds and unitToSuccs maps have * been built.</p> * * <p><code>UnitGraph</code> provides an implementation of * <code>buildHeadsAndTails()</code> which defines the graph's set * of heads to include the first {@link Unit} in the graph's body, * together with any other <tt>Unit</tt> which has no predecessors. * It defines the graph's set of tails to include all * <tt>Unit</tt>s with no successors. Subclasses of * <code>UnitGraph</code> may override this method to change the * criteria for classifying a node as a head or tail.</p> */ protected void buildHeadsAndTails() { List<Unit> tailList = new ArrayList<Unit>(); List<Unit> headList = new ArrayList<Unit>(); for (Iterator<Unit> unitIt = unitChain.iterator(); unitIt.hasNext(); ) { Unit s = (Unit) unitIt.next(); List<Unit> succs = unitToSuccs.get(s); if(succs.size() == 0) { tailList.add(s); } List<Unit> preds = unitToPreds.get(s); if(preds.size() == 0) { headList.add(s); } } // Add the first Unit, even if it is the target of // a branch. Unit entryPoint = (Unit) unitChain.getFirst(); if (! headList.contains(entryPoint)) { headList.add(entryPoint); } tails = Collections.unmodifiableList(tailList); heads = Collections.unmodifiableList(headList); } /** * Utility method that replaces the values of a {@link Map}, * which must be instances of {@link List}, with unmodifiable * equivalents. * * @param map The map whose values are to be made unmodifiable. */ protected static void makeMappedListsUnmodifiable(Map<?,List<Unit>> map) { for (Entry<?, List<Unit>> entry : map.entrySet()) { List<Unit> value = entry.getValue(); if (value.size() == 0) { entry.setValue(Collections.<Unit>emptyList()); } else { entry.setValue(Collections.unmodifiableList(value)); } } } /** * Utility method that produces a new map from the {@link Unit}s * of this graph's body to the union of the values stored in the * two argument {@link Map}s, used to combine the maps of * exceptional and unexceptional predecessors and successors into * maps of all predecessors and successors. The values stored in * both argument maps must be {@link List}s of {@link Unit}s, * which are assumed not to contain any duplicate <tt>Unit</tt>s. * * @param mapA The first map to be combined. * * @param mapB The second map to be combined. */ protected Map<Unit,List<Unit>> combineMapValues (Map<Unit,List<Unit>> mapA, Map<Unit,List<Unit>> mapB) { // The duplicate screen Map<Unit,List<Unit>> result = new HashMap<Unit,List<Unit>>(mapA.size() * 2 + 1, 0.7f); for (Iterator<Unit> chainIt = unitChain.iterator(); chainIt.hasNext(); ) { Unit unit = (Unit) chainIt.next(); List<Unit> listA = mapA.get(unit); if (listA == null) { listA = Collections.emptyList(); } List<Unit> listB = mapB.get(unit); if (listB == null) { listB = Collections.emptyList(); } int resultSize = listA.size() + listB.size(); if (resultSize == 0) { result.put(unit, Collections.<Unit>emptyList()); } else { List<Unit> resultList = new ArrayList<Unit>(resultSize); Iterator<Unit> listIt = null; // As a minor optimization of the duplicate screening, // copy the longer list first. if (listA.size() >= listB.size()) { resultList.addAll(listA); listIt = listB.iterator(); } else { resultList.addAll(listB); listIt = listA.iterator(); } while (listIt.hasNext()) { Unit element = listIt.next(); // It is possible for there to be both an exceptional // and an unexceptional edge connecting two Units // (though probably not in a class generated by // javac), so we need to screen for duplicates. On the // other hand, we expect most of these lists to have // only one or two elements, so it doesn't seem worth // the cost to build a Set to do the screening. if (! resultList.contains(element)) { resultList.add(element); } } result.put(unit, Collections.unmodifiableList(resultList)); } } return result; } /** * Utility method for adding an edge to maps representing the CFG. * * @param unitToSuccs The {@link Map} from {@link Unit}s to {@link List}s * of their successors. * * @param unitToPreds The {@link Map} from {@link Unit}s to {@link List}s * of their successors. * * @param head The {@link Unit} from which the edge starts. * * @param tail The {@link Unit} to which the edge flows. */ protected void addEdge(Map<Unit,List<Unit>> unitToSuccs, Map<Unit,List<Unit>> unitToPreds, Unit head, Unit tail) { List<Unit> headsSuccs = unitToSuccs.get(head); if (headsSuccs == null) { headsSuccs = new ArrayList<Unit>(3); // We expect this list to // remain short. unitToSuccs.put(head, headsSuccs); } if (! headsSuccs.contains(tail)) { headsSuccs.add(tail); List<Unit> tailsPreds = unitToPreds.get(tail); if (tailsPreds == null) { tailsPreds = new ArrayList<Unit>(); unitToPreds.put(tail, tailsPreds); } tailsPreds.add(head); } } /** * @return The body from which this UnitGraph was built. * * @see Body */ public Body getBody() { return body; } /** * Look for a path in graph, from def to use. * This path has to lie inside an extended basic block * (and this property implies uniqueness.). The path returned * includes from and to. * * @param from start point for the path. * @param to end point for the path. * @return null if there is no such path. */ public List<Unit> getExtendedBasicBlockPathBetween(Unit from, Unit to) { UnitGraph g = this; // if this holds, we're doomed to failure!!! if (g.getPredsOf(to).size() > 1) return null; // pathStack := list of succs lists // pathStackIndex := last visited index in pathStack LinkedList<Unit> pathStack = new LinkedList<Unit>(); LinkedList<Integer> pathStackIndex = new LinkedList<Integer>(); pathStack.add(from); pathStackIndex.add(new Integer(0)); int psiMax = (g.getSuccsOf(pathStack.get(0))).size(); int level = 0; while (pathStackIndex.get(0).intValue() != psiMax) { int p = (pathStackIndex.get(level)).intValue(); List<Unit> succs = g.getSuccsOf((pathStack.get(level))); if (p >= succs.size()) { // no more succs - backtrack to previous level. pathStack.remove(level); pathStackIndex.remove(level); level--; int q = pathStackIndex.get(level).intValue(); pathStackIndex.set(level, new Integer(q+1)); continue; } Unit betweenUnit = (Unit)(succs.get(p)); // we win! if (betweenUnit == to) { pathStack.add(to); return pathStack; } // check preds of betweenUnit to see if we should visit its kids. if (g.getPredsOf(betweenUnit).size() > 1) { pathStackIndex.set(level, new Integer(p+1)); continue; } // visit kids of betweenUnit. level++; pathStackIndex.add(new Integer(0)); pathStack.add(betweenUnit); } return null; } /* DirectedGraph implementation */ public List<Unit> getHeads() { return heads; } public List<Unit> getTails() { return tails; } public List<Unit> getPredsOf(Unit u) { if(!unitToPreds.containsKey(u)) throw new NoSuchElementException("Invalid unit " + u); return unitToPreds.get(u); } public List<Unit> getSuccsOf(Unit u) { List<Unit> l = unitToSuccs.get(u); if (l == null) throw new RuntimeException("Invalid unit " + u); return l; } public int size() { return unitChain.size(); } public Iterator<Unit> iterator() { return unitChain.iterator(); } public String toString() { Iterator<Unit> it = unitChain.iterator(); StringBuffer buf = new StringBuffer(); while(it.hasNext()) { Unit u = (Unit) it.next(); buf.append("// preds: "+getPredsOf(u)+"\n"); buf.append(u.toString() + '\n'); buf.append("// succs "+getSuccsOf(u)+"\n"); } return buf.toString(); } }