/* 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-2004.
* 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;
import soot.toolkits.exceptions.ThrowAnalysis;
import soot.toolkits.exceptions.ThrowableSet;
import soot.jimple.Stmt;
import soot.jimple.ThrowStmt;
import soot.jimple.StaticFieldRef;
import soot.jimple.InvokeExpr;
import soot.jimple.NewExpr;
/**
* <p>Represents a control flow graph for a {@link Body} instance
* where the nodes are {@link Unit} instances, and where control flow
* associated with exceptions is taken into account.</p>
*
* <p>To describe precisely the circumstances under which exceptional
* edges are added to the graph, we need to distinguish the
* exceptions thrown explicitly by a <code>throw</code> instruction
* from the exceptions which are thrown implicitly by the VM to
* signal an error it encounters in the course of executing
* an instruction, which need not be a <code>throw</code>.</p>
*
* <p>For every {@link ThrowInst} or {@link ThrowStmt}
* <code>Unit</code> which may explicitly throw an exception that
* would be caught by a {@link Trap} in the <code>Body</code>, there
* will be an edge from the <code>throw</code> <code>Unit</code> to
* the <code>Trap</code> handler's first <code>Unit</code>.</p>
*
* <p>For every <code>Unit</code> which may implicitly throw an
* exception that could be caught by a <code>Trap</code> in the
* <code>Body</code>, there will be an edge from each of the
* excepting <code>Unit</code>'s predecessors to the
* <code>Trap</code> handler's first <code>Unit</code> (since any of
* those predecessors may have been the last <code>Unit</code> to
* complete execution before the handler starts execution). If the
* excepting <code>Unit</code> might have the side effect of changing
* some field, then there will definitely be an edge from the excepting
* <code>Unit</code> itself to its handlers, since the side effect
* might occur before the exception is raised. If the excepting
* <code>Unit</code> has no side effects, then parameters passed to
* the <code>ExceptionalUnitGraph</code> constructor determine
* whether or not there is an edge from the excepting
* <code>Unit</code> itself to the handler <code>Unit</code>.</p>
*/
public class ExceptionalUnitGraph extends UnitGraph implements ExceptionalGraph<Unit>
{
protected Map<Unit,List<Unit>> unitToUnexceptionalSuccs; // If there are no Traps within
protected Map<Unit,List<Unit>> unitToUnexceptionalPreds; // the method, these will be the
// same maps as unitToSuccs and
// unitToPreds.
protected Map<Unit,List<Unit>> unitToExceptionalSuccs;
protected Map<Unit,List<Unit>> unitToExceptionalPreds;
protected Map<Unit, Collection<ExceptionDest>> unitToExceptionDests;
protected ThrowAnalysis throwAnalysis; // Cached reference to the
// analysis used to generate this
// graph, for generating responses
// to getExceptionDests() on the
// fly for nodes from which all
// exceptions escape the method.
/**
* Constructs the graph for a given Body instance, using the
* <code>ThrowAnalysis</code> and <code>omitExceptingUnitEdges</code>
* value that are passed as parameters.
*
* @param body the <code>Body</code> from which to build a graph.
*
* @param throwAnalysis the source of information about the exceptions
* which each {@link Unit} may throw.
*
* @param omitExceptingUnitEdges indicates whether the CFG should
* omit edges to a handler from trapped
* <code>Unit</code>s which may implicitly throw an
* exception which the handler catches but
* which have no potential side effects.
* The CFG will contain edges to the handler
* from all predecessors of
* <code>Unit</code>s which may implicitly throw
* a caught exception regardless of the setting for
* this parameter. If this parameter is
* <code>false</code>, there will also be
* edges to the handler from all the
* potentially excepting <code>Unit</code>s
* themselves. If this parameter is
* <code>true</code>, there will be edges to
* the handler from the excepting
* <code>Unit</code>s themselves only if they
* have potential side effects (or if they
* are themselves the predecessors of other
* potentially excepting <code>Unit</code>s).
* A setting of <code>true</code> produces
* CFGs which allow for more precise
* analyses, since a <code>Unit</code> without
* side effects has no effect on the
* computational state when it throws an
* exception. Use settings of
* <code>false</code> for compatibility with
* more conservative analyses, or to cater
* to conservative bytecode verifiers.
*/
public ExceptionalUnitGraph(Body body, ThrowAnalysis throwAnalysis,
boolean omitExceptingUnitEdges) {
super(body);
initialize(throwAnalysis, omitExceptingUnitEdges);
}
/**
* Constructs the graph from a given Body instance using the
* passed {@link ThrowAnalysis} and a default value, provided by
* the {@link Options} class, for the
* <code>omitExceptingUnitEdges</code> parameter.
*
* @param body the {@link Body} from which to build a graph.
*
* @param throwAnalysis the source of information about the exceptions
* which each {@link Unit} may throw.
*
*/
public ExceptionalUnitGraph(Body body, ThrowAnalysis throwAnalysis) {
this(body, throwAnalysis, Options.v().omit_excepting_unit_edges());
}
/**
* Constructs the graph from a given Body instance, using the
* {@link Scene}'s default {@link ThrowAnalysis} to estimate the
* set of exceptions that each {@link Unit} might throw and a
* default value, provided by the {@link Options} class, for the
* <code>omitExceptingUnitEdges</code> parameter.
*
* @param body the <code>Body</code> from which to build a graph.
*
*/
public ExceptionalUnitGraph(Body body) {
this(body, Scene.v().getDefaultThrowAnalysis(),
Options.v().omit_excepting_unit_edges());
}
/**
* <p>Allocates an <code>ExceptionalUnitGraph</code> object
* without initializing it. This “partial
* constructor” is provided for the benefit of subclasses
* whose constructors need to perform some subclass-specific
* processing before actually creating the graph edges (because,
* for example, the subclass overrides a utility method like
* {@link #buildExceptionDests(ThrowAnalysis)} or {@link
* #buildExceptionalEdges(ThrowAnalysis, Map, Map, Map, boolean)}
* with a replacement method that depends on additional
* parameters passed to the subclass's constructor). The
* subclass constructor is responsible for calling {@link
* #initialize(ThrowAnalysis, boolean)}, or otherwise performing
* the initialization required to implement
* <code>ExceptionalUnitGraph</code>'s interface.</p>
*
* <p>Clients who opt to extend <code>ExceptionalUnitGraph</code>
* should be warned that the class has not been carefully
* designed for inheritance; code that uses the
* <code>protected</code> members of this class may need to be
* rewritten for each new Soot release.</p>
*
* @param body the <code>Body</code> from which to build a graph.
*
* @param ignoredBogusParameter a meaningless placeholder, which exists
* solely to distinguish this
* constructor from the public
* {@link #ExceptionalUnitGraph(Body)}
* constructor.
*/
protected ExceptionalUnitGraph(Body body, boolean ignoredBogusParameter) {
super(body);
}
/**
* Performs the real work of constructing an
* <code>ExceptionalUnitGraph</code>, factored out of the
* constructors so that subclasses have the option to delay
* creating the graph's edges until after they have performed
* some subclass-specific initialization.
*
* @param throwAnalysis the source of information about the exceptions
* which each {@link Unit} may throw.
*
* @param omitExceptingUnitEdges indicates whether the CFG should
* omit edges to a handler from trapped
* <code>Unit</code>s which may throw an
* exception which the handler catches but
* which have no potential side effects.
*/
protected void initialize(ThrowAnalysis throwAnalysis,
boolean omitExceptingUnitEdges) {
int size = unitChain.size();
Set<Unit> trapUnitsThatAreHeads = Collections.emptySet();
if(Options.v().time())
Timers.v().graphTimer.start();
unitToUnexceptionalSuccs = new HashMap<Unit,List<Unit>>(size * 2 + 1, 0.7f);
unitToUnexceptionalPreds = new HashMap<Unit,List<Unit>>(size * 2 + 1, 0.7f);
buildUnexceptionalEdges(unitToUnexceptionalSuccs,
unitToUnexceptionalPreds);
makeMappedListsUnmodifiable(unitToUnexceptionalSuccs);
makeMappedListsUnmodifiable(unitToUnexceptionalPreds);
this.throwAnalysis = throwAnalysis;
if (body.getTraps().size() == 0) {
// No handlers, so all exceptional control flow exits the
// method.
unitToExceptionDests = Collections.emptyMap();
unitToExceptionalSuccs = Collections.emptyMap();
unitToExceptionalPreds = Collections.emptyMap();
unitToSuccs = unitToUnexceptionalSuccs;
unitToPreds = unitToUnexceptionalPreds;
} else {
unitToExceptionDests = buildExceptionDests(throwAnalysis);
unitToExceptionalSuccs =
new HashMap<Unit,List<Unit>>(unitToExceptionDests.size() * 2 + 1, 0.7f);
unitToExceptionalPreds =
new HashMap<Unit,List<Unit>>(body.getTraps().size() * 2 + 1, 0.7f);
trapUnitsThatAreHeads = buildExceptionalEdges(throwAnalysis,
unitToExceptionDests,
unitToExceptionalSuccs,
unitToExceptionalPreds,
omitExceptingUnitEdges);
makeMappedListsUnmodifiable(unitToExceptionalSuccs);
makeMappedListsUnmodifiable(unitToExceptionalPreds);
// We'll need separate maps for the combined
// exceptional and unexceptional edges:
unitToSuccs = combineMapValues(unitToUnexceptionalSuccs,
unitToExceptionalSuccs);
unitToPreds = combineMapValues(unitToUnexceptionalPreds,
unitToExceptionalPreds);
}
buildHeadsAndTails(trapUnitsThatAreHeads);
if(Options.v().time())
Timers.v().graphTimer.end();
}
/**
* <p>Utility method used in the construction of
* {@link soot.toolkits.graph.UnitGraph UnitGraph}
* variants which include exceptional control flow. It determines
* which {@link Unit}s may throw exceptions that would be caught
* by {@link Trap}s within the method.</p>
*
* @param throwAnalysis The source of information about which
* exceptions each <code>Unit</code> may throw.
*
* @return <code>null</code> if no <code>Unit</code>s in the method throw
* any exceptions caught within the method. Otherwise, a
* {@link Map} from <code>Unit</code>s to {@link
* Collection}s of {@link ExceptionDest}s.
*
* <p>The returned map has an idiosyncracy which is hidden
* from most client code, but which is exposed to
* subclasses extending <code>ExceptionalUnitGraph</code>.
* If a <code>Unit</code> throws one or more exceptions
* which are caught within the method, it will be mapped
* to a <code>Collection</code> of
* <code>ExceptionDest</code>s describing the sets of
* exceptions that the <code>Unit</code> might throw to
* each {@link Trap}. But if all of a <code>Unit</code>'s
* exceptions escape the method, it will be mapped to
* <code>null</code, rather than to a
* <code>Collection</code> containing a single
* <code>ExceptionDest</code> with a <code>null</code>
* trap. (The special case for <code>Unit</code>s with
* no caught exceptions allows
* <code>buildExceptionDests()</code> to ignore completely
* <code>Unit</code>s which are outside the scope of all
* <code>Trap</code>s.)</p>
*/
protected Map<Unit,Collection<ExceptionDest>> buildExceptionDests(ThrowAnalysis throwAnalysis) {
Chain<Unit> units = body.getUnits();
Map<Unit, ThrowableSet> unitToUncaughtThrowables = new HashMap<Unit, ThrowableSet>(units.size());
Map<Unit,Collection<ExceptionDest>> result = null;
// Record the caught exceptions.
for (Iterator<Trap> trapIt = body.getTraps().iterator(); trapIt.hasNext(); ) {
Trap trap = trapIt.next();
RefType catcher = trap.getException().getType();
for (Iterator<Unit> unitIt = units.iterator(trap.getBeginUnit(),
units.getPredOf(trap.getEndUnit()));
unitIt.hasNext(); ) {
Unit unit = unitIt.next();
ThrowableSet thrownSet = unitToUncaughtThrowables.get(unit);
if (thrownSet == null) {
thrownSet = throwAnalysis.mightThrow(unit);
}
ThrowableSet.Pair catchableAs = thrownSet.whichCatchableAs(catcher);
if (catchableAs.getCaught() != ThrowableSet.Manager.v().EMPTY) {
result = addDestToMap(result, unit, trap, catchableAs.getCaught());
unitToUncaughtThrowables.put(unit, catchableAs.getUncaught());
} else {
// An assertion check:
if (thrownSet != catchableAs.getUncaught()) {
throw new IllegalStateException("ExceptionalUnitGraph.buildExceptionDests(): catchableAs.caught == EMPTY, but catchableAs.uncaught != thrownSet"
+ System.getProperty("line.separator") + body.getMethod().getSubSignature() + " Unit: " + unit.toString()
+ System.getProperty("line.separator") + " catchableAs.getUncaught() == " + catchableAs.getUncaught().toString()
+ System.getProperty("line.separator") + " thrownSet == " + thrownSet.toString());
}
}
}
}
for (Map.Entry<Unit,ThrowableSet> entry : unitToUncaughtThrowables.entrySet()) {
Unit unit = (Unit) entry.getKey();
ThrowableSet escaping = (ThrowableSet) entry.getValue();
if (escaping != ThrowableSet.Manager.v().EMPTY) {
result = addDestToMap(result, unit, null, escaping);
}
}
if (result == null) {
result = Collections.emptyMap();
}
return result;
}
/**
* A utility method for recording the exceptions that a
* <code>Unit</code> throws to a particular <code>Trap</code>.
* Note that this method relies on the fact that the call to add
* escaping exceptions for a <code>Unit</code> will always follow
* all calls for its caught exceptions.
*
* @param map A <code>Map</code> from <code>Unit</code>s to
* <code>Collection</code>s of <code>ExceptionDest</code>s.
* <code>null</code> if no exceptions have been recorded yet.
*
* @param u The <code>Unit</code> throwing the exceptions.
*
* @param t The <code>Trap</code> which catches the exceptions, or
* <code>null</code> if the exceptions escape the method.
*
* @param caught The set of exception types thrown by <code>u</code> which
* are caught by <code>t</code>.
*
* @return a <code>Map</code> which whose contents are equivalent to the
* input <code>map</code>, plus the information that <code>u</code>
* throws <code>caught</code> to <code>t</code>.
*/
private Map<Unit,Collection<ExceptionDest>> addDestToMap
(Map<Unit,Collection<ExceptionDest>> map, Unit u, Trap t, ThrowableSet caught) {
Collection<ExceptionDest> dests = (map == null ? null : map.get(u));
if (dests == null) {
if (t == null) {
// All exceptions from u escape, so don't record any.
return map;
} else {
if (map == null) {
map = new HashMap<Unit,Collection<ExceptionDest>>(unitChain.size() * 2 + 1);
}
dests = new ArrayList<ExceptionDest>(3);
map.put(u, dests);
}
}
dests.add(new ExceptionDest(t, caught));
return map;
}
/**
* Method to compute the edges corresponding to exceptional
* control flow.
*
* @param throwAnalysis the source of information about the exceptions
* which each {@link Unit} may throw.
*
* @param unitToExceptionDests2 A <code>Map</code> from {@link Unit}s to
* {@link Collection}s of {@link
* ExceptionalUnitGraph.ExceptionDest
* ExceptionDest}s which represent the handlers
* that might catch exceptions thrown by the
* <code>Unit</code>. This is an ``in
* parameter''.
*
* @param unitToSuccs A <code>Map</code> from <code>Unit</code>s to
* {@link List}s of <code>Unit</code>s. This is
* an ``out parameter'';
* <code>buildExceptionalEdges</code> will add
* a mapping from every <code>Unit</code> in
* the body that may throw an exception that
* could be caught by a {@link Trap} in the
* body to a list of its exceptional
* successors.
*
* @param unitToPreds A <code>Map</code> from <code>Unit</code>s to
* <code>List</code>s of
* <code>Unit</code>s. This is an ``out
* parameter'';
* <code>buildExceptionalEdges</code> will add
* a mapping from each handler unit that may
* catch an exception to the list of
* <code>Unit</code>s whose exceptions it may
* catch.
* @param omitExceptingUnitEdges Indicates whether to omit
* exceptional edges from excepting units which
* lack side effects
*
* @return a {@link Set} of trap <code>Unit</code>s that might catch
* exceptions thrown by the first <code>Unit</code> in the
* {@link Body} associated with the graph being
* constructed. Such trap <code>Unit</code>s may need to
* be added to the list of heads (depending on your
* definition of heads), since they can be the first
* <code>Unit</code> in the <code>Body</code> which
* actually completes execution.
*/
protected Set<Unit> buildExceptionalEdges(ThrowAnalysis throwAnalysis,
Map<Unit, Collection<ExceptionDest>> unitToExceptionDests,
Map<Unit,List<Unit>> unitToSuccs, Map<Unit,List<Unit>> unitToPreds,
boolean omitExceptingUnitEdges) {
Set<Unit> trapsThatAreHeads = new ArraySet<Unit>();
Unit entryPoint = (Unit) unitChain.getFirst();
for (Iterator<Entry<Unit, Collection<ExceptionDest>>> it = unitToExceptionDests.entrySet().iterator();
it.hasNext(); ) {
Entry<Unit, Collection<ExceptionDest>> entry = it.next();
Unit thrower = (Unit) entry.getKey();
List<Unit> throwersPreds = getUnexceptionalPredsOf(thrower);
Collection<ExceptionDest> dests = entry.getValue();
// We need to recognize:
// - caught exceptions for which we must add edges from the
// thrower's predecessors to the catcher:
// - all exceptions of non-throw instructions;
// - implicit exceptions of throw instructions.
//
// - caught exceptions where we must add edges from the
// thrower itself to the catcher:
// - any exception of non-throw instructions if
// omitExceptingUnitEdges is not set.
// - any exception of non-throw instructions with side effects.
// - explicit exceptions of throw instructions
// - implicit exceptions of throw instructions if
// omitExceptingUnitEdges is not set.
// - implicit exceptions of throw instructions with possible
// side effects (this is only possible for the grimp
// IR, where the throw's argument may be an
// expression---probably a NewInvokeExpr---which
// might have executed partially before the
// exception arose).
//
// Note that a throw instruction may be capable of throwing a given
// Throwable type both implicitly and explicitly.
//
// We track these situations using predThrowables and
// selfThrowables. Essentially predThrowables is the set
// of Throwable types to whose catchers there should be
// edges from predecessors of the thrower, while
// selfThrowables is the set of Throwable types to whose
// catchers there should be edges from the thrower itself,
// but we we take some short cuts to avoid calling
// ThrowableSet.catchableAs() when we can avoid it.
boolean alwaysAddSelfEdges = ((! omitExceptingUnitEdges) ||
mightHaveSideEffects(thrower));
ThrowableSet predThrowables = null;
ThrowableSet selfThrowables = null;
if (thrower instanceof ThrowStmt) {
ThrowStmt throwStmt = (ThrowStmt) thrower;
predThrowables = throwAnalysis.mightThrowImplicitly(throwStmt);
selfThrowables = throwAnalysis.mightThrowExplicitly(throwStmt);
}
for (Iterator<ExceptionDest> destIt = dests.iterator(); destIt.hasNext(); ) {
ExceptionDest dest = destIt.next();
if (dest.getTrap() != null) {
Unit catcher = dest.getTrap().getHandlerUnit();
RefType trapsType = dest.getTrap().getException().getType();
if (predThrowables == null ||
predThrowables.catchableAs(trapsType)) {
// Add edges from the thrower's predecessors to the catcher.
if (thrower == entryPoint) {
trapsThatAreHeads.add(catcher);
}
for (Iterator<Unit> p = throwersPreds.iterator(); p.hasNext(); ) {
Unit pred = p.next();
addEdge(unitToSuccs, unitToPreds, pred, catcher);
}
}
if (alwaysAddSelfEdges ||
(selfThrowables != null &&
selfThrowables.catchableAs(trapsType))) {
addEdge(unitToSuccs, unitToPreds, thrower, catcher);
}
}
}
}
// Now we have to worry about transitive exceptional
// edges, when a handler might itself throw an exception
// that is caught within the method. For that we need a
// worklist containing CFG edges that lead to such a handler.
class CFGEdge {
Unit head; // If null, represents an edge to the handler
// from the fictitious "predecessor" of the
// very first unit in the chain. I.e., tail
// is a handler which might be reached as a
// result of an exception thrown by the
// first Unit in the Body.
Unit tail;
CFGEdge(Unit head, Unit tail) {
if (tail == null)
throw new RuntimeException("invalid CFGEdge("
+ head.toString() + ','
+ "null" + ')');
this.head = head;
this.tail = tail;
}
public boolean equals(Object rhs) {
if (rhs == this) {
return true;
}
if (! (rhs instanceof CFGEdge)) {
return false;
}
CFGEdge rhsEdge = (CFGEdge) rhs;
return ((this.head == rhsEdge.head) &&
(this.tail == rhsEdge.tail));
}
public int hashCode() {
// Following Joshua Bloch's recipe in "Effective Java", Item 8:
int result = 17;
result = 37 * result + this.head.hashCode();
result = 37 * result + this.tail.hashCode();
return result;
}
}
LinkedList<CFGEdge> workList = new LinkedList<CFGEdge>();
for (Iterator<Trap> trapIt = body.getTraps().iterator(); trapIt.hasNext(); ) {
Trap trap = trapIt.next();
Unit handlerStart = trap.getHandlerUnit();
if (mightThrowToIntraproceduralCatcher(handlerStart)) {
List<Unit> handlerPreds = getUnexceptionalPredsOf(handlerStart);
for (Iterator<Unit> it = handlerPreds.iterator(); it.hasNext(); ) {
Unit pred = it.next();
workList.addLast(new CFGEdge(pred, handlerStart));
}
handlerPreds = getExceptionalPredsOf(handlerStart);
for (Iterator<Unit> it = handlerPreds.iterator(); it.hasNext(); ) {
Unit pred = it.next();
workList.addLast(new CFGEdge(pred, handlerStart));
}
if (trapsThatAreHeads.contains(handlerStart)) {
workList.addLast(new CFGEdge(null, handlerStart));
}
}
}
// Now for every CFG edge that leads to a handler that may
// itself throw an exception catchable within the method, add
// edges from the head of that edge to the unit that catches
// the handler's exception.
while (workList.size() > 0) {
CFGEdge edgeToThrower = workList.removeFirst();
Unit pred = edgeToThrower.head;
Unit thrower = edgeToThrower.tail;
Collection<ExceptionDest> throwerDests = getExceptionDests(thrower);
for (Iterator<ExceptionDest> i = throwerDests.iterator(); i.hasNext(); ) {
ExceptionDest dest = i.next();
if (dest.getTrap() != null) {
Unit handlerStart = dest.getTrap().getHandlerUnit();
boolean edgeAdded = false;
if (pred == null) {
if (! trapsThatAreHeads.contains(handlerStart)) {
trapsThatAreHeads.add(handlerStart);
edgeAdded = true;
}
} else {
if (! getExceptionalSuccsOf(pred).contains(handlerStart)) {
addEdge(unitToSuccs, unitToPreds, pred, handlerStart);
edgeAdded = true;
}
}
if (edgeAdded &&
mightThrowToIntraproceduralCatcher(handlerStart)) {
workList.addLast(new CFGEdge(pred, handlerStart));
}
}
}
}
return trapsThatAreHeads;
}
/**
* <p>Utility method for checking if a {@link Unit} might have side
* effects. It simply returns true for any unit which invokes a
* method directly or which might invoke static initializers
* indirectly (by creating a new object or by refering to a static
* field; see sections 2.17.4, 2.17.5, and 5.5 of the Java Virtual
* Machine Specification).</p>
*
* <code>mightHaveSideEffects()</code> is declared package-private
* so that it is available to unit tests that are part of this
* package.
*
* @param u The unit whose potential for side effects is to be checked.
*
* @return whether or not <code>u</code> has the potential for side effects.
*/
static boolean mightHaveSideEffects(Unit u) {
for (Iterator<ValueBox> it = u.getUseBoxes().iterator(); it.hasNext(); ) {
Value v = it.next().getValue();
if ((v instanceof StaticFieldRef) ||
(v instanceof InvokeExpr) ||
(v instanceof NewExpr)) {
return true;
}
}
return false;
}
/**
* Utility method for checking if a Unit might throw an exception which
* may be caught by a {@link Trap} within this method.
*
* @param u The unit for whose exceptions are to be checked
*
* @return whether or not <code>u</code> may throw an exception which may be
* caught by a <code>Trap</code> in this method,
*/
private boolean mightThrowToIntraproceduralCatcher(Unit u) {
Collection<ExceptionDest> dests = getExceptionDests(u);
for (Iterator<ExceptionDest> i = dests.iterator(); i.hasNext(); ) {
ExceptionDest dest = i.next();
if (dest.getTrap() != null) {
return true;
}
}
return false;
}
/**
* <p>A placeholder that overrides {@link UnitGraph#buildHeadsAndTails()}
* with a method which always throws an exception. The placeholder serves
* to indicate that <code>ExceptionalUnitGraph</code> does not use
* <code>buildHeadsAndTails()</code>, and to document the conditions under
* which <code>ExceptionalUnitGraph considers a node to be a head or
* tail.</p>
*
* <p><code>ExceptionalUnitGraph</code> defines the graph's set of
* heads to include the first {@link Unit} in the graph's body,
* together with the first <code>Unit</code> in any exception
* handler which might catch an exception thrown by the first
* <code>Unit</code> in the body (because any of those
* <code>Unit</code>s might be the first to successfully complete
* execution). <code>ExceptionalUnitGraph</code> defines the
* graph's set of tails to include all <code>Unit</code>s which
* represent some variety of return bytecode or an
* <code>athrow</code> bytecode whose argument might escape the
* method.</p>
*/
protected void buildHeadsAndTails() throws IllegalStateException {
throw new IllegalStateException("ExceptionalUnitGraph uses buildHeadsAndTails(List) instead of buildHeadsAndTails()");
}
/**
* Utility method, to be called only after the unitToPreds and
* unitToSuccs maps have been built. It defines the graph's set of
* heads to include the first {@link Unit} in the graph's body,
* together with all the <code>Unit</code>s in
* <code>additionalHeads</code>. It defines the graph's set of
* tails to include all <code>Unit</code>s which represent some
* sort of return bytecode or an <code>athrow</code> bytecode
* which may escape the method.
*/
private void buildHeadsAndTails(Set<Unit> additionalHeads) {
List<Unit> headList = new ArrayList<Unit>(additionalHeads.size() + 1);
headList.addAll(additionalHeads);
Unit entryPoint = (Unit) unitChain.getFirst();
if (! headList.contains(entryPoint)) {
headList.add(entryPoint);
}
List<Unit> tailList = new ArrayList<Unit>();
for (Iterator<Unit> it = unitChain.iterator(); it.hasNext(); ) {
Unit u = (Unit) it.next();
if (u instanceof soot.jimple.ReturnStmt ||
u instanceof soot.jimple.ReturnVoidStmt) {
tailList.add(u);
} else if (u instanceof soot.jimple.ThrowStmt) {
Collection<ExceptionDest> dests = getExceptionDests(u);
int escapeMethodCount = 0;
for (Iterator<ExceptionDest> destIt = dests.iterator(); destIt.hasNext(); ) {
ExceptionDest dest = destIt.next();
if (dest.getTrap() == null) {
escapeMethodCount++;
}
}
if (escapeMethodCount > 0) {
tailList.add(u);
}
}
}
tails = Collections.unmodifiableList(tailList);
heads = Collections.unmodifiableList(headList);
}
/**
* Returns a collection of
* {@link ExceptionalUnitGraph.ExceptionDest ExceptionDest}
* objects which represent how exceptions thrown by a specified
* unit will be handled.
*
* @param u The unit for which to provide exception information.
* (<code>u</code> must be a <code>Unit</code>, though the parameter is
* declared as an <code>Object</code> to satisfy the interface of
* {@link soot.toolkits.graph.ExceptionalGraph ExceptionalGraph}.
*
* @return a collection of <code>ExceptionDest</code> objects describing
* the traps, if any, which catch the exceptions
* which may be thrown by <code>u</code>.
*/
public Collection<ExceptionDest> getExceptionDests(Unit u) {
Collection<ExceptionDest> result = (Collection<ExceptionDest>) unitToExceptionDests.get(u);
if (result == null) {
result = new LinkedList<ExceptionDest>();
result.add(new ExceptionDest(null, throwAnalysis.mightThrow((Unit) u)));
}
return result;
}
public static class ExceptionDest implements ExceptionalGraph.ExceptionDest<Unit> {
private Trap trap;
private ThrowableSet throwables;
protected ExceptionDest(Trap trap, ThrowableSet throwables) {
this.trap = trap;
this.throwables = throwables;
}
public Trap getTrap() {
return trap;
}
public ThrowableSet getThrowables() {
return throwables;
}
public Unit getHandlerNode() {
if (trap == null) {
return null;
} else {
return trap.getHandlerUnit();
}
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append(throwables.toString());
buf.append(" -> ");
if (trap == null) {
buf.append("(escapes)");
} else {
buf.append(trap.toString());
}
return buf.toString();
}
}
public List<Unit> getUnexceptionalPredsOf(Unit u) {
if (!unitToUnexceptionalPreds.containsKey(u))
throw new RuntimeException("Invalid unit " + u);
return (List<Unit>) unitToUnexceptionalPreds.get(u);
}
public List<Unit> getUnexceptionalSuccsOf(Unit u) {
if (!unitToUnexceptionalSuccs.containsKey(u))
throw new RuntimeException("Invalid unit " + u);
return (List<Unit>) unitToUnexceptionalSuccs.get(u);
}
public List<Unit> getExceptionalPredsOf(Unit u) {
if (!unitToExceptionalPreds.containsKey(u)) {
return Collections.emptyList();
} else {
return (List<Unit>) unitToExceptionalPreds.get(u);
}
}
public List<Unit> getExceptionalSuccsOf(Unit u) {
if (!unitToExceptionalSuccs.containsKey(u)) {
return Collections.emptyList();
} else {
return (List<Unit>) unitToExceptionalSuccs.get(u);
}
}
/**
* <p>Return the {@link ThrowAnalysis} used to construct this
* graph, if the graph contains no {@link Trap}s, or
* <code>null</code> if the graph does contain
* <code>Trap</code>s. A reference to the
* <code>ThrowAnalysis</code> is kept when there are no
* <code>Trap</code>s so that the graph can generate responses to
* {@link #getExceptionDests(Object)} on the fly, rather than precomputing
* information that may never be needed.</p>
*
* <p>This method is package-private because it exposes a detail
* of the implementation of <code>ExceptionalUnitGraph</code> so
* that the
* {@link soot.toolkits.graph.ExceptionalBlockGraph ExceptionalBlockGraph}
* constructor can cache the same <code>ThrowAnalysis</code> for
* the same purpose.
*
* @return the {@link ThrowAnalysis} used to generate this graph if the
* graph contains no {@link Trap}s, or <code>null</code> if the graph
* contains one or more {@link Trap}s.
*/
ThrowAnalysis getThrowAnalysis() {
return throwAnalysis;
}
public String toString() {
Iterator<Unit> it = unitChain.iterator();
StringBuffer buf = new StringBuffer();
while(it.hasNext()) {
Unit u = it.next();
buf.append(" preds: "+getPredsOf(u)+"\n");
buf.append(" unexceptional preds: "+getUnexceptionalPredsOf(u)+"\n");
buf.append(" exceptional preds: "+getExceptionalPredsOf(u)+"\n");
buf.append(u.toString() + '\n');
buf.append(" exception destinations: "+getExceptionDests(u)+"\n");
buf.append(" unexceptional succs: "+getUnexceptionalSuccsOf(u)+"\n");
buf.append(" exceptional succs: "+getExceptionalSuccsOf(u)+"\n");
buf.append(" succs "+getSuccsOf(u)+"\n\n");
}
return buf.toString();
}
}