diff --git a/src/main/java/soot/jimple/toolkits/scalar/DeadAssignmentEliminator.java b/src/main/java/soot/jimple/toolkits/scalar/DeadAssignmentEliminator.java index ac35885ab7b..7560748bec3 100644 --- a/src/main/java/soot/jimple/toolkits/scalar/DeadAssignmentEliminator.java +++ b/src/main/java/soot/jimple/toolkits/scalar/DeadAssignmentEliminator.java @@ -72,6 +72,9 @@ import soot.jimple.RemExpr; import soot.jimple.Stmt; import soot.options.Options; +import soot.shimple.ShimpleBody; +import soot.toolkits.graph.ExceptionalUnitGraph; +import soot.toolkits.graph.FullExceptionalUnitGraph; import soot.toolkits.scalar.LocalDefs; import soot.toolkits.scalar.LocalUses; import soot.toolkits.scalar.UnitValueBoxPair; @@ -237,7 +240,8 @@ protected void internalTransform(Body b, String phaseName, Map o // Add all the statements which are used to compute values // for the essential statements, recursively - final LocalDefs localDefs = G.v().soot_toolkits_scalar_LocalDefsFactory().newLocalDefs(b); + final LocalDefs localDefs = G.v().soot_toolkits_scalar_LocalDefsFactory() + .newLocalDefs(b instanceof ShimpleBody ? new FullExceptionalUnitGraph(b) : new ExceptionalUnitGraph(b)); if (!allEssential) { Set essential = new HashSet(units.size()); diff --git a/src/main/java/soot/shimple/DefaultShimpleFactory.java b/src/main/java/soot/shimple/DefaultShimpleFactory.java index 347bf029dbb..d4251434bb9 100644 --- a/src/main/java/soot/shimple/DefaultShimpleFactory.java +++ b/src/main/java/soot/shimple/DefaultShimpleFactory.java @@ -35,6 +35,7 @@ import soot.toolkits.graph.DominatorsFinder; import soot.toolkits.graph.ExceptionalBlockGraph; import soot.toolkits.graph.ExceptionalUnitGraph; +import soot.toolkits.graph.FullExceptionalUnitGraph; import soot.toolkits.graph.HashReversibleGraph; import soot.toolkits.graph.ReversibleGraph; import soot.toolkits.graph.SimpleDominatorsFinder; @@ -144,7 +145,7 @@ public UnitGraph getUnitGraph() { if (ug == null) { Body body = getBody(); UnreachableCodeEliminator.v().transform(body); - ug = new ExceptionalUnitGraph(body); + ug = new FullExceptionalUnitGraph(body); this.ug = ug; } return ug; diff --git a/src/main/java/soot/toolkits/graph/ExceptionalUnitGraph.java b/src/main/java/soot/toolkits/graph/ExceptionalUnitGraph.java index 32ebac07cf4..581a1f7a4b3 100644 --- a/src/main/java/soot/toolkits/graph/ExceptionalUnitGraph.java +++ b/src/main/java/soot/toolkits/graph/ExceptionalUnitGraph.java @@ -322,7 +322,7 @@ protected Map> buildExceptionDests(ThrowAnalysis * @return a Map which whose contents are equivalent to the input map, plus the information that * u throws caught to t. */ - private Map> addDestToMap(Map> map, Unit u, Trap t, + protected Map> addDestToMap(Map> map, Unit u, Trap t, ThrowableSet caught) { Collection dests = (map == null ? null : map.get(u)); if (dests == null) { diff --git a/src/main/java/soot/toolkits/graph/FullExceptionalUnitGraph.java b/src/main/java/soot/toolkits/graph/FullExceptionalUnitGraph.java new file mode 100644 index 00000000000..28a3dc00720 --- /dev/null +++ b/src/main/java/soot/toolkits/graph/FullExceptionalUnitGraph.java @@ -0,0 +1,96 @@ +package soot.toolkits.graph; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2021 Timothy Hoffman + * %% + * This program 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 program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +import soot.Body; +import soot.RefType; +import soot.Trap; +import soot.Unit; +import soot.toolkits.exceptions.PedanticThrowAnalysis; +import soot.toolkits.exceptions.ThrowAnalysis; +import soot.toolkits.exceptions.ThrowableSet; +import soot.util.Chain; + +/** + * Extension of {@link ExceptionalUnitGraph} that aligns more closely with the representation used by the JVM bytecode + * verifier. The standard {@link ExceptionalUnitGraph} will not add an exception edge for an exception table entry if an + * earlier entry already caught a broader exception type (i.e. the edge for the later entry will never actually execute + * during runtime). However, the JVM bytecode verifier considers all exceptional edges verbatim from the exception table and + * thus, may consider more possible paths in the CFG. Furthermore, this graph uses a {@link PedanticThrowAnalysis} to ensure + * that all Units covered by an exception table entry will have an edge to the exception handler which forces phi-node + * removal to back-propagate assignments all the way back to their original location to avoid "uninitialized register" errors + * from the JVM bytecode verifier. + * + * @author Timothy Hoffman + */ +public class FullExceptionalUnitGraph extends ExceptionalUnitGraph { + + public FullExceptionalUnitGraph(Body body) { + super(body); + // Set the 'omitExceptingUnitEdges' parameter to false and use a ThrowAnalysis + // based on the PedanticThrowAnalysis so that all units (except for AssignStmt) + // will have an edge into exception handler blocks. + initialize(PedanticThrowAnalysis.v(), false); + } + + @Override + protected Map> buildExceptionDests(ThrowAnalysis throwAnalysis) { + // Identical to the original except it doesn't track the uncaught + // throwables when multiple Traps cover the same Unit. That way, the full + // effect of all traps is reflected in the graph, even if some edges + // will never be used because an earlier trap subsumes a later one. + // + Map> result = null; + + final Chain traps = body.getTraps(); + if (!traps.isEmpty()) { + final ThrowableSet EMPTY = ThrowableSet.Manager.v().EMPTY; + final Chain units = body.getUnits(); + + // Record the caught exceptions. + for (Trap trap : traps) { + RefType catcher = trap.getException().getType(); + for (Iterator it = units.iterator(trap.getBeginUnit(), units.getPredOf(trap.getEndUnit())); it.hasNext();) { + Unit unit = it.next(); + ThrowableSet thrownSet = throwAnalysis.mightThrow(unit); + ThrowableSet.Pair catchableAs = thrownSet.whichCatchableAs(catcher); + if (!EMPTY.equals(catchableAs.getCaught())) { + result = addDestToMap(result, unit, trap, catchableAs.getCaught()); + } else { + assert (thrownSet.equals(catchableAs.getUncaught())) : + "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(); + } + } + } + } + return result == null ? Collections.emptyMap() : result; + } +}