soot.jimple.toolkits.scalar.UnreachableCodeEliminator Maven / Gradle / Ivy
package soot.jimple.toolkits.scalar;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 1999 Phong Co
* %%
* 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.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.Body;
import soot.BodyTransformer;
import soot.G;
import soot.PhaseOptions;
import soot.Scene;
import soot.Singletons;
import soot.Trap;
import soot.Unit;
import soot.options.Options;
import soot.toolkits.exceptions.PedanticThrowAnalysis;
import soot.toolkits.exceptions.ThrowAnalysis;
import soot.toolkits.graph.DirectedGraph;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.util.Chain;
public class UnreachableCodeEliminator extends BodyTransformer {
private static final Logger logger = LoggerFactory.getLogger(UnreachableCodeEliminator.class);
protected ThrowAnalysis throwAnalysis = null;
public UnreachableCodeEliminator(Singletons.Global g) {
}
public static UnreachableCodeEliminator v() {
return G.v().soot_jimple_toolkits_scalar_UnreachableCodeEliminator();
}
public UnreachableCodeEliminator(ThrowAnalysis ta) {
this.throwAnalysis = ta;
}
protected void internalTransform(Body body, String phaseName, Map options) {
if (Options.v().verbose()) {
logger.debug("[" + body.getMethod().getName() + "] Eliminating unreachable code...");
}
// Force a conservative ExceptionalUnitGraph() which
// necessarily includes an edge from every trapped Unit to
// its handler, so that we retain Traps in the case where
// trapped units remain, but the default ThrowAnalysis
// says that none of them can throw the caught exception.
if (this.throwAnalysis == null) {
this.throwAnalysis
= PhaseOptions.getBoolean(options, "remove-unreachable-traps", true) ? Scene.v().getDefaultThrowAnalysis()
: PedanticThrowAnalysis.v();
}
ExceptionalUnitGraph graph = new ExceptionalUnitGraph(body, throwAnalysis, false);
Chain units = body.getUnits();
int numPruned = units.size();
Set reachable = units.isEmpty() ? Collections.emptySet() : reachable(units.getFirst(), graph);
// Now eliminate empty traps. (and unreachable handlers)
//
// For the most part, this is an atavism, an an artifact of
// pre-ExceptionalUnitGraph code, when the only way for a trap to
// become unreachable was if all its trapped units were removed, and
// the stmtIt loop did not remove Traps as it removed handler units.
// We've left this separate test for empty traps here, even though
// most such traps would already have been eliminated by the preceding
// loop, because in arbitrary bytecode you could have
// handler unit that was still reachable by normal control flow, even
// though it no longer trapped any units (though such code is unlikely
// to occur in practice, and certainly no in code generated from Java
// source.
for (Iterator it = body.getTraps().iterator(); it.hasNext();) {
Trap trap = it.next();
if ((trap.getBeginUnit() == trap.getEndUnit()) || !reachable.contains(trap.getHandlerUnit())) {
it.remove();
}
}
// We must make sure that the end units of all traps which are still
// alive are kept in the code
for (Trap t : body.getTraps()) {
if (t.getEndUnit() == body.getUnits().getLast()) {
reachable.add(t.getEndUnit());
}
}
Set notReachable = new HashSet();
if (Options.v().verbose()) {
for (Unit u : units) {
if (!reachable.contains(u)) {
notReachable.add(u);
}
}
}
units.retainAll(reachable);
numPruned -= units.size();
if (Options.v().verbose()) {
logger.debug("[" + body.getMethod().getName() + "] Removed " + numPruned + " statements: ");
for (Unit u : notReachable) {
logger.debug("[" + body.getMethod().getName() + "] " + u);
}
}
}
// Used to be: "mark first statement and all its successors, recursively"
// Bad idea! Some methods are extremely long. It broke because the recursion reached the
// 3799th level.
private Set reachable(T first, DirectedGraph g) {
if (first == null || g == null) {
return Collections.emptySet();
}
Set visited = new HashSet(g.size());
Deque q = new ArrayDeque();
q.addFirst(first);
do {
T t = q.removeFirst();
if (visited.add(t)) {
q.addAll(g.getSuccsOf(t));
}
} while (!q.isEmpty());
return visited;
}
}