soot.toolkits.graph.UnitGraph Maven / Gradle / Ivy
/* 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;
/**
*
* Represents a CFG where the nodes are {@link Unit} instances and
* edges represent unexceptional and (possibly) exceptional control
* flow between Units.
*
*
* This is an abstract class, providing the facilities used to build
* CFGs for specific purposes.
*/
public abstract class UnitGraph implements DirectedGraph
{
protected List heads;
protected List tails;
protected Map> unitToSuccs;
protected Map> unitToPreds;
protected SootMethod method;
protected Body body;
protected Chain unitChain;
/**
* Performs the work that is required to construct any sort of
* UnitGraph.
*
* @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 UnitGraph 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}. buildUnexceptionalEdges will
* add a mapping for every Unit 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}. buildUnexceptionalEdges will
* add a mapping for every Unit in the body
* to a list of its unexceptional predecessors.
*/
protected void buildUnexceptionalEdges(Map> unitToSuccs, Map> unitToPreds) {
// Initialize the predecessor sets to empty
for (Iterator unitIt = unitChain.iterator(); unitIt.hasNext(); ) {
unitToPreds.put(unitIt.next(), new ArrayList());
}
Iterator 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 successors = new ArrayList();
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 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);
}
}
/**
* Utility method used in the construction of {@link UnitGraph}s, to be
* called only after the unitToPreds and unitToSuccs maps have
* been built.
*
* UnitGraph
provides an implementation of
* buildHeadsAndTails()
which defines the graph's set
* of heads to include the first {@link Unit} in the graph's body,
* together with any other Unit which has no predecessors.
* It defines the graph's set of tails to include all
* Units with no successors. Subclasses of
* UnitGraph
may override this method to change the
* criteria for classifying a node as a head or tail.
*/
protected void buildHeadsAndTails() {
List tailList = new ArrayList();
List headList = new ArrayList();
for (Iterator unitIt = unitChain.iterator(); unitIt.hasNext(); ) {
Unit s = (Unit) unitIt.next();
List succs = unitToSuccs.get(s);
if(succs.size() == 0) {
tailList.add(s);
}
List 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> map) {
for (Entry, List> entry : map.entrySet()) {
List value = entry.getValue();
if (value.size() == 0) {
entry.setValue(Collections.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 Units.
*
* @param mapA The first map to be combined.
*
* @param mapB The second map to be combined.
*/
protected Map> combineMapValues
(Map> mapA, Map> mapB) {
// The duplicate screen
Map> result = new HashMap>(mapA.size() * 2 + 1, 0.7f);
for (Iterator chainIt = unitChain.iterator(); chainIt.hasNext(); ) {
Unit unit = (Unit) chainIt.next();
List listA = mapA.get(unit);
if (listA == null) {
listA = Collections.emptyList();
}
List listB = mapB.get(unit);
if (listB == null) {
listB = Collections.emptyList();
}
int resultSize = listA.size() + listB.size();
if (resultSize == 0) {
result.put(unit, Collections.emptyList());
} else {
List resultList = new ArrayList(resultSize);
Iterator 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> unitToSuccs, Map> unitToPreds,
Unit head, Unit tail) {
List headsSuccs = unitToSuccs.get(head);
if (headsSuccs == null) {
headsSuccs = new ArrayList(3); // We expect this list to
// remain short.
unitToSuccs.put(head, headsSuccs);
}
if (! headsSuccs.contains(tail)) {
headsSuccs.add(tail);
List tailsPreds = unitToPreds.get(tail);
if (tailsPreds == null) {
tailsPreds = new ArrayList();
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 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 pathStack = new LinkedList();
LinkedList pathStackIndex = new LinkedList();
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 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 getHeads()
{
return heads;
}
public List getTails()
{
return tails;
}
public List getPredsOf(Unit u)
{
if(!unitToPreds.containsKey(u))
throw new NoSuchElementException("Invalid unit " + u);
return unitToPreds.get(u);
}
public List getSuccsOf(Unit u)
{
List l = unitToSuccs.get(u);
if (l == null) throw new RuntimeException("Invalid unit " + u);
return l;
}
public int size()
{
return unitChain.size();
}
public Iterator iterator()
{
return unitChain.iterator();
}
public String toString()
{
Iterator 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();
}
}