soot.toolkits.graph.ExceptionalUnitGraph 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-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; /** *
containing a single *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.
* *To describe precisely the circumstances under which exceptional * edges are added to the graph, we need to distinguish the * exceptions thrown explicitly by a
* *throw
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 athrow
.For every {@link ThrowInst} or {@link ThrowStmt} *
* *Unit
which may explicitly throw an exception that * would be caught by a {@link Trap} in theBody
, there * will be an edge from thethrow
Unit
to * theTrap
handler's firstUnit
.For every
*/ public class ExceptionalUnitGraph extends UnitGraph implements ExceptionalGraphUnit
which may implicitly throw an * exception that could be caught by aTrap
in the *Body
, there will be an edge from each of the * exceptingUnit
's predecessors to the *Trap
handler's firstUnit
(since any of * those predecessors may have been the lastUnit
to * complete execution before the handler starts execution). If the * exceptingUnit
might have the side effect of changing * some field, then there will definitely be an edge from the excepting *Unit
itself to its handlers, since the side effect * might occur before the exception is raised. If the excepting *Unit
has no side effects, then parameters passed to * theExceptionalUnitGraph
constructor determine * whether or not there is an edge from the excepting *Unit
itself to the handlerUnit
.{ protected Map > unitToUnexceptionalSuccs; // If there are no Traps within protected Map > unitToUnexceptionalPreds; // the method, these will be the // same maps as unitToSuccs and // unitToPreds. protected Map > unitToExceptionalSuccs; protected Map > unitToExceptionalPreds; protected Map > 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 * ThrowAnalysis
andomitExceptingUnitEdges
* value that are passed as parameters. * * @param body theBody
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 *Unit
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 *Unit
s which may implicitly throw * a caught exception regardless of the setting for * this parameter. If this parameter is *false
, there will also be * edges to the handler from all the * potentially exceptingUnit
s * themselves. If this parameter is *true
, there will be edges to * the handler from the excepting *Unit
s themselves only if they * have potential side effects (or if they * are themselves the predecessors of other * potentially exceptingUnit
s). * A setting oftrue
produces * CFGs which allow for more precise * analyses, since aUnit
without * side effects has no effect on the * computational state when it throws an * exception. Use settings of *false
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 *omitExceptingUnitEdges
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 *omitExceptingUnitEdges
parameter. * * @param body theBody
from which to build a graph. * */ public ExceptionalUnitGraph(Body body) { this(body, Scene.v().getDefaultThrowAnalysis(), Options.v().omit_excepting_unit_edges()); } /** *Allocates an
* *ExceptionalUnitGraph
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 *ExceptionalUnitGraph
's interface.Clients who opt to extend
* * @param body theExceptionalUnitGraph
* should be warned that the class has not been carefully * designed for inheritance; code that uses the *protected
members of this class may need to be * rewritten for each new Soot release.Body
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 *ExceptionalUnitGraph
, 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 *Unit
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(); SettrapUnitsThatAreHeads = Collections.emptySet(); if(Options.v().time()) Timers.v().graphTimer.start(); unitToUnexceptionalSuccs = new HashMap >(size * 2 + 1, 0.7f); unitToUnexceptionalPreds = new HashMap >(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 >(unitToExceptionDests.size() * 2 + 1, 0.7f); unitToExceptionalPreds = new HashMap >(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(); } /** * 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.
* * @param throwAnalysis The source of information about which * exceptions eachUnit
may throw. * * @returnnull
if noUnit
s in the method throw * any exceptions caught within the method. Otherwise, a * {@link Map} fromUnit
s to {@link * Collection}s of {@link ExceptionDest}s. * *The returned map has an idiosyncracy which is hidden * from most client code, but which is exposed to * subclasses extending
ExceptionalUnitGraph
. * If aUnit
throws one or more exceptions * which are caught within the method, it will be mapped * to aCollection
of *ExceptionDest
s describing the sets of * exceptions that theUnit
might throw to * each {@link Trap}. But if all of aUnit
's * exceptions escape the method, it will be mapped to *null
CollectionExceptionDest
with anull
* trap. (The special case forUnit
s with * no caught exceptions allows *buildExceptionDests()
to ignore completely *Unit
s which are outside the scope of all *Trap
s.) */ protected Map> buildExceptionDests(ThrowAnalysis throwAnalysis) { Chain units = body.getUnits(); Map unitToUncaughtThrowables = new HashMap (units.size()); Map > result = null; // Record the caught exceptions. for (Iterator trapIt = body.getTraps().iterator(); trapIt.hasNext(); ) { Trap trap = trapIt.next(); RefType catcher = trap.getException().getType(); for (Iterator 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 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 * Unit
throws to a particularTrap
. * Note that this method relies on the fact that the call to add * escaping exceptions for aUnit
will always follow * all calls for its caught exceptions. * * @param map AMap
fromUnit
s to *Collection
s ofExceptionDest
s. *null
if no exceptions have been recorded yet. * * @param u TheUnit
throwing the exceptions. * * @param t TheTrap
which catches the exceptions, or *null
if the exceptions escape the method. * * @param caught The set of exception types thrown byu
which * are caught byt
. * * @return aMap
which whose contents are equivalent to the * inputmap
, plus the information thatu
* throwscaught
tot
. */ private Map> addDestToMap (Map > map, Unit u, Trap t, ThrowableSet caught) { Collection 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 >(unitChain.size() * 2 + 1); } dests = new ArrayList (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 Map
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 *Unit
. This is an ``in * parameter''. * * @param unitToSuccs AMap
fromUnit
s to * {@link List}s ofUnit
s. This is * an ``out parameter''; *buildExceptionalEdges
will add * a mapping from everyUnit
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 AMap
fromUnit
s to *List
s of *Unit
s. This is an ``out * parameter''; *buildExceptionalEdges
will add * a mapping from each handler unit that may * catch an exception to the list of *Unit
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 trapUnit
s that might catch * exceptions thrown by the firstUnit
in the * {@link Body} associated with the graph being * constructed. Such trapUnit
s may need to * be added to the list of heads (depending on your * definition of heads), since they can be the first *Unit
in theBody
which * actually completes execution. */ protected SetbuildExceptionalEdges(ThrowAnalysis throwAnalysis, Map > unitToExceptionDests, Map > unitToSuccs, Map > unitToPreds, boolean omitExceptingUnitEdges) { Set trapsThatAreHeads = new ArraySet (); Unit entryPoint = (Unit) unitChain.getFirst(); for (Iterator >> it = unitToExceptionDests.entrySet().iterator(); it.hasNext(); ) { Entry > entry = it.next(); Unit thrower = (Unit) entry.getKey(); List throwersPreds = getUnexceptionalPredsOf(thrower); Collection 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 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 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 workList = new LinkedList (); for (Iterator trapIt = body.getTraps().iterator(); trapIt.hasNext(); ) { Trap trap = trapIt.next(); Unit handlerStart = trap.getHandlerUnit(); if (mightThrowToIntraproceduralCatcher(handlerStart)) { List handlerPreds = getUnexceptionalPredsOf(handlerStart); for (Iterator it = handlerPreds.iterator(); it.hasNext(); ) { Unit pred = it.next(); workList.addLast(new CFGEdge(pred, handlerStart)); } handlerPreds = getExceptionalPredsOf(handlerStart); for (Iterator 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 throwerDests = getExceptionDests(thrower); for (Iterator 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; } /** * 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).
* *mightHaveSideEffects()
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 notu
has the potential for side effects. */ static boolean mightHaveSideEffects(Unit u) { for (Iteratorit = 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 u
may throw an exception which may be * caught by aTrap
in this method, */ private boolean mightThrowToIntraproceduralCatcher(Unit u) { Collectiondests = getExceptionDests(u); for (Iterator i = dests.iterator(); i.hasNext(); ) { ExceptionDest dest = i.next(); if (dest.getTrap() != null) { return true; } } return false; } /** * A placeholder that overrides {@link UnitGraph#buildHeadsAndTails()} * with a method which always throws an exception. The placeholder serves * to indicate that
* *ExceptionalUnitGraph
does not use *buildHeadsAndTails()
, and to document the conditions under * whichExceptionalUnitGraph considers a node to be a head or * tail.
*/ 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
ExceptionalUnitGraph
defines the graph's set of * heads to include the first {@link Unit} in the graph's body, * together with the firstUnit
in any exception * handler which might catch an exception thrown by the first *Unit
in the body (because any of those *Unit
s might be the first to successfully complete * execution).ExceptionalUnitGraph
defines the * graph's set of tails to include allUnit
s which * represent some variety of return bytecode or an *athrow
bytecode whose argument might escape the * method.Unit
s in *additionalHeads
. It defines the graph's set of * tails to include allUnit
s which represent some * sort of return bytecode or anathrow
bytecode * which may escape the method. */ private void buildHeadsAndTails(SetadditionalHeads) { List headList = new ArrayList (additionalHeads.size() + 1); headList.addAll(additionalHeads); Unit entryPoint = (Unit) unitChain.getFirst(); if (! headList.contains(entryPoint)) { headList.add(entryPoint); } List tailList = new ArrayList (); for (Iterator 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 dests = getExceptionDests(u); int escapeMethodCount = 0; for (Iterator 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. * ( u
must be aUnit
, though the parameter is * declared as anObject
to satisfy the interface of * {@link soot.toolkits.graph.ExceptionalGraph ExceptionalGraph}. * * @return a collection ofExceptionDest
objects describing * the traps, if any, which catch the exceptions * which may be thrown byu
. */ public CollectiongetExceptionDests(Unit u) { Collection result = (Collection ) unitToExceptionDests.get(u); if (result == null) { result = new LinkedList (); result.add(new ExceptionDest(null, throwAnalysis.mightThrow((Unit) u))); } return result; } public static class ExceptionDest implements ExceptionalGraph.ExceptionDest { 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 getUnexceptionalPredsOf(Unit u) { if (!unitToUnexceptionalPreds.containsKey(u)) throw new RuntimeException("Invalid unit " + u); return (List ) unitToUnexceptionalPreds.get(u); } public List getUnexceptionalSuccsOf(Unit u) { if (!unitToUnexceptionalSuccs.containsKey(u)) throw new RuntimeException("Invalid unit " + u); return (List ) unitToUnexceptionalSuccs.get(u); } public List getExceptionalPredsOf(Unit u) { if (!unitToExceptionalPreds.containsKey(u)) { return Collections.emptyList(); } else { return (List ) unitToExceptionalPreds.get(u); } } public List getExceptionalSuccsOf(Unit u) { if (!unitToExceptionalSuccs.containsKey(u)) { return Collections.emptyList(); } else { return (List ) unitToExceptionalSuccs.get(u); } } /** * Return the {@link ThrowAnalysis} used to construct this * graph, if the graph contains no {@link Trap}s, or *
* *null
if the graph does contain *Trap
s. A reference to the *ThrowAnalysis
is kept when there are no *Trap
s so that the graph can generate responses to * {@link #getExceptionDests(Object)} on the fly, rather than precomputing * information that may never be needed.This method is package-private because it exposes a detail * of the implementation of
ExceptionalUnitGraph
so * that the * {@link soot.toolkits.graph.ExceptionalBlockGraph ExceptionalBlockGraph} * constructor can cache the sameThrowAnalysis
for * the same purpose. * * @return the {@link ThrowAnalysis} used to generate this graph if the * graph contains no {@link Trap}s, ornull
if the graph * contains one or more {@link Trap}s. */ ThrowAnalysis getThrowAnalysis() { return throwAnalysis; } public String toString() { Iteratorit = 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(); } }