edu.umd.cs.findbugs.ba.PruneInfeasibleExceptionEdges Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
The newest version!
/*
* Bytecode Analysis Framework
* Copyright (C) 2003,2004 University of Maryland
*
* 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
*/
package edu.umd.cs.findbugs.ba;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.bcel.generic.MethodGen;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.ba.type.ExceptionSet;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
/**
* Prune a CFG to remove infeasible exception edges. In order to determine what
* kinds of exceptions can be thrown by explicit ATHROW instructions, type
* analysis must first be performed on the unpruned CFG.
*
* @author David Hovemeyer
* @see CFG
* @see edu.umd.cs.findbugs.ba.type.TypeAnalysis
*/
public class PruneInfeasibleExceptionEdges implements EdgeTypes {
private static final boolean DEBUG = SystemProperties.getBoolean("cfg.prune.debug");
private static final boolean STATS = SystemProperties.getBoolean("cfg.prune.stats");
private static int numEdgesPruned = 0;
static {
if (STATS) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.err.println("Exception edges pruned: " + numEdgesPruned);
}
});
}
}
/**
* A memento to remind us of how we classified a particular exception edge.
* If pruning and classifying succeeds, then these momentos can be applied
* to actually change the state of the edges. The issue is that the entire
* pruning/classifying operation must either fail or succeed as a whole.
* Thus, we don't commit any CFG changes until we know everything was
* successful.
*/
private static class MarkedEdge {
private final Edge edge;
private final int flag;
public MarkedEdge(Edge edge, int flag) {
this.edge = edge;
this.flag = flag;
}
public void apply() {
int flags = edge.getFlags();
flags |= this.flag;
edge.setFlags(flags);
}
}
private final CFG cfg;
private final TypeDataflow typeDataflow;
private boolean cfgModified;
/**
* Constructor.
*
* @param cfg
* the CFG to prune
* @param methodGen
* the method
* @param typeDataflow
* initialized TypeDataflow object for the CFG, indicating the
* types of all stack locations
*/
public PruneInfeasibleExceptionEdges(CFG cfg, MethodGen methodGen, TypeDataflow typeDataflow) {
this.cfg = cfg;
this.typeDataflow = typeDataflow;
}
/**
* Prune infeasible exception edges from the CFG. If the method returns
* normally, then the operation was successful, and the CFG should no longer
* contain infeasible exception edges. If ClassNotFoundException or
* DataflowAnalysisException are thrown, then the operation was
* unsuccessful,
* but the CFG is still valid because it was not modified. If a
* runtime exception is thrown, then the CFG may be partially modified and
* should be considered invalid.
*/
@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
public void execute() throws ClassNotFoundException {
Set deletedEdgeSet = new HashSet<>();
List markedEdgeList = new LinkedList<>();
// Mark edges to delete,
// mark edges to set properties of
for (Iterator i = cfg.edgeIterator(); i.hasNext();) {
Edge edge = i.next();
if (!edge.isExceptionEdge()) {
continue;
}
ExceptionSet exceptionSet = typeDataflow.getEdgeExceptionSet(edge);
if (exceptionSet.isEmpty()) {
// No exceptions are actually thrown on this edge,
// so we can delete the edge.
deletedEdgeSet.add(edge);
} else {
if (exceptionSet.isSingleton("java.lang.CloneNotSupportedException") && cfg.getMethodName().endsWith(".clone()")) {
String className = cfg.getMethodGen().getClassName();
if (Subtypes2.instanceOf(className, "java.lang.Cloneable")) {
deletedEdgeSet.add(edge);
continue;
}
}
// Some exceptions appear to be thrown on the edge.
// Mark to indicate if any of the exceptions are checked,
// and if any are explicit (checked or explicitly declared
// or thrown unchecked).
boolean someChecked = exceptionSet.containsCheckedExceptions();
boolean someExplicit = exceptionSet.containsExplicitExceptions();
int flags = 0;
if (someChecked) {
flags |= CHECKED_EXCEPTIONS_FLAG;
}
if (someExplicit) {
flags |= EXPLICIT_EXCEPTIONS_FLAG;
}
markedEdgeList.add(new MarkedEdge(edge, flags));
}
}
// Remove deleted edges
for (Edge edge : deletedEdgeSet) {
cfg.removeEdge(edge);
if (STATS) {
++numEdgesPruned;
}
cfgModified = true;
}
// Mark edges
for (MarkedEdge markedEdge : markedEdgeList) {
markedEdge.apply();
}
}
/**
* @return true if modified
*/
public boolean wasCFGModified() {
return cfgModified;
}
}