/*
* FindBugs - Find Bugs in Java programs
* Copyright (C) 2003-2007 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.classfile.engine.bcel;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.IFNE;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MethodGen;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.AnalysisFeatures;
import edu.umd.cs.findbugs.ba.BasicBlock;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.CFGBuilder;
import edu.umd.cs.findbugs.ba.CFGBuilderFactory;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.DepthFirstSearch;
import edu.umd.cs.findbugs.ba.Edge;
import edu.umd.cs.findbugs.ba.EdgeTypes;
import edu.umd.cs.findbugs.ba.JavaClassAndMethod;
import edu.umd.cs.findbugs.ba.MethodUnprofitableException;
import edu.umd.cs.findbugs.ba.MissingClassException;
import edu.umd.cs.findbugs.ba.PruneInfeasibleExceptionEdges;
import edu.umd.cs.findbugs.ba.PruneUnconditionalExceptionThrowerEdges;
import edu.umd.cs.findbugs.ba.SignatureConverter;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.classfile.IAnalysisCache;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
/**
* Analysis engine to produce CFG (control flow graph) objects for an analyzed
* method.
*
* @author David Hovemeyer
*/
public class CFGFactory extends AnalysisFactory<CFG> {
private static final boolean DEBUG_CFG = SystemProperties.getBoolean("classContext.debugCFG");
/**
* Constructor.
*/
public CFGFactory() {
super("control flow graph factory", CFG.class);
}
/*
* (non-Javadoc)
*
* @see
* edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
* .classfile.IAnalysisCache, java.lang.Object)
*/
public CFG analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
// Construct the CFG in its raw form
MethodGen methodGen = analysisCache.getMethodAnalysis(MethodGen.class, descriptor);
if (methodGen == null) {
JavaClass jclass = analysisCache.getClassAnalysis(JavaClass.class, descriptor.getClassDescriptor());
Method method = analysisCache.getMethodAnalysis(Method.class, descriptor);
JavaClassAndMethod javaClassAndMethod = new JavaClassAndMethod(jclass, method);
AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportSkippedAnalysis(descriptor);
throw new MethodUnprofitableException(javaClassAndMethod);
}
CFGBuilder cfgBuilder = CFGBuilderFactory.create(descriptor, methodGen);
cfgBuilder.build();
CFG cfg = cfgBuilder.getCFG();
// Mark as busy while we're pruning the CFG.
cfg.setFlag(CFG.BUSY);
// Important: eagerly put the CFG in the analysis cache.
// Recursively performed analyses required to prune the CFG,
// such as TypeAnalysis, will operate on the raw CFG.
analysisCache.eagerlyPutMethodAnalysis(CFG.class, descriptor, cfg);
// Record method name and signature for informational purposes
cfg.setMethodName(SignatureConverter.convertMethodSignature(methodGen));
cfg.setMethodGen(methodGen);
// System.out.println("CC: getting refined CFG for " + methodId);
if (CFGFactory.DEBUG_CFG) {
String methodId = methodGen.getClassName() + "." + methodGen.getName() + ":" + methodGen.getSignature();
System.out.println("CC: getting refined CFG for " + methodId);
}
if (ClassContext.DEBUG) {
String methodId = methodGen.getClassName() + "." + methodGen.getName() + ":" + methodGen.getSignature();
System.out.println("ClassContext: request to prune " + methodId);
}
// Remove CFG edges corresponding to failed assertions.
boolean changed = false;
boolean ASSUME_ASSERTIONS_ENABLED = true;
if (ASSUME_ASSERTIONS_ENABLED) {
LinkedList<Edge> edgesToRemove = new LinkedList<Edge>();
for (Iterator<Edge> i = cfg.edgeIterator(); i.hasNext();) {
Edge e = i.next();
if (e.getType() == EdgeTypes.IFCMP_EDGE) {
try {
BasicBlock source = e.getSource();
InstructionHandle last = source.getLastInstruction();
Instruction lastInstruction = last.getInstruction();
InstructionHandle prev = last.getPrev();
Instruction prevInstruction = prev.getInstruction();
if (prevInstruction instanceof GETSTATIC && lastInstruction instanceof IFNE) {
GETSTATIC getStatic = (GETSTATIC) prevInstruction;
if (getStatic.getFieldName(methodGen.getConstantPool()).equals("$assertionsDisabled")
&& getStatic.getSignature(methodGen.getConstantPool()).equals("Z"))
edgesToRemove.add(e);
}
} catch (RuntimeException exception) {
assert true; // ignore it
}
}
}
if (edgesToRemove.size() > 0) {
changed = true;
for (Edge e : edgesToRemove) {
cfg.removeEdge(e);
}
}
}
cfg.setFlag(CFG.PRUNED_FAILED_ASSERTION_EDGES);
final boolean PRUNE_INFEASIBLE_EXCEPTION_EDGES = AnalysisContext.currentAnalysisContext().getBoolProperty(
AnalysisFeatures.ACCURATE_EXCEPTIONS);
if (PRUNE_INFEASIBLE_EXCEPTION_EDGES && !cfg.isFlagSet(CFG.PRUNED_INFEASIBLE_EXCEPTIONS)) {
try {
TypeDataflow typeDataflow = analysisCache.getMethodAnalysis(TypeDataflow.class, descriptor);
// Exception edge pruning based on ExceptionSets.
// Note: this is quite slow.
PruneInfeasibleExceptionEdges pruner = new PruneInfeasibleExceptionEdges(cfg, methodGen, typeDataflow);
pruner.execute();
changed = changed || pruner.wasCFGModified();
} catch (MissingClassException e) {
AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
.reportMissingClass(e.getClassNotFoundException());
} catch (DataflowAnalysisException e) {
AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
.logError("unable to extract type analysis", e);
} catch (ClassNotFoundException e) {
AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e);
}
}
cfg.setFlag(CFG.PRUNED_INFEASIBLE_EXCEPTIONS);
final boolean PRUNE_UNCONDITIONAL_EXCEPTION_THROWER_EDGES = !AnalysisContext.currentAnalysisContext().getBoolProperty(
AnalysisFeatures.CONSERVE_SPACE);
if (PRUNE_UNCONDITIONAL_EXCEPTION_THROWER_EDGES && !cfg.isFlagSet(CFG.PRUNED_UNCONDITIONAL_THROWERS)) {
try {
JavaClass jclass = analysisCache.getClassAnalysis(JavaClass.class, descriptor.getClassDescriptor());
Method method = analysisCache.getMethodAnalysis(Method.class, descriptor);
ConstantPoolGen cpg = analysisCache.getClassAnalysis(ConstantPoolGen.class, descriptor.getClassDescriptor());
TypeDataflow typeDataflow = analysisCache.getMethodAnalysis(TypeDataflow.class, descriptor);
PruneUnconditionalExceptionThrowerEdges pruner = new PruneUnconditionalExceptionThrowerEdges(jclass, method,
methodGen, cfg, cpg, typeDataflow, AnalysisContext.currentAnalysisContext());
pruner.execute();
if (pruner.wasCFGModified()) {
changed = true;
}
} catch (DataflowAnalysisException e) {
AnalysisContext.logError("Error pruning normal return edges for unconditionally throwing methods for "
+ descriptor, e);
}
}
cfg.setFlag(CFG.PRUNED_UNCONDITIONAL_THROWERS);
// Now we are done with the CFG refining process
cfg.setFlag(CFG.REFINED);
cfg.clearFlag(CFG.BUSY);
// If the CFG changed as a result of pruning, purge all analysis results
// for the method.
if (changed) {
DepthFirstSearch dfs = new DepthFirstSearch(cfg);
dfs.search();
Collection<BasicBlock> unreachable = dfs.unvisitedVertices();
if (!unreachable.isEmpty()) {
if (DEBUG_CFG)
System.out.println("Unreachable blocks");
for (BasicBlock b : unreachable) {
if (DEBUG_CFG)
System.out.println(" removing " + b);
cfg.removeVertex(b);
}
}
Global.getAnalysisCache().purgeMethodAnalyses(descriptor);
}
return cfg;
}
/*
* (non-Javadoc)
*
* @see
* edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
* .findbugs.classfile.IAnalysisCache)
*/
@Override
public void registerWith(IAnalysisCache analysisCache) {
analysisCache.registerMethodAnalysisEngine(CFG.class, this);
}
}