package edu.umd.cs.findbugs.detect;
import java.util.BitSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ARETURN;
import org.apache.bcel.generic.BranchInstruction;
import org.apache.bcel.generic.GOTO;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MethodGen;
import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.CFGBuilderException;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.MethodUnprofitableException;
import edu.umd.cs.findbugs.ba.npe.IsNullValue;
import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow;
import edu.umd.cs.findbugs.ba.npe.IsNullValueFrame;
public class LoadOfKnownNullValue implements Detector {
private BugReporter bugReporter;
private BugAccumulator bugAccumulator;
public LoadOfKnownNullValue(BugReporter bugReporter) {
this.bugReporter = bugReporter;
this.bugAccumulator = new BugAccumulator(bugReporter);
}
public void visitClassContext(ClassContext classContext) {
Method[] methodList = classContext.getJavaClass().getMethods();
for (Method method : methodList) {
if (method.getCode() == null)
continue;
try {
analyzeMethod(classContext, method);
} catch (MethodUnprofitableException mue) {
if (SystemProperties.getBoolean("unprofitable.debug")) // otherwise
// don't
// report
bugReporter.logError("skipping unprofitable method in " + getClass().getName());
} catch (CFGBuilderException e) {
bugReporter.logError("Detector " + this.getClass().getName() + " caught exception", e);
} catch (DataflowAnalysisException e) {
bugReporter.logError("Detector " + this.getClass().getName() + " caught exception", e);
}
bugAccumulator.reportAccumulatedBugs();
}
}
private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
BitSet lineMentionedMultipleTimes = classContext.linesMentionedMultipleTimes(method);
BitSet linesWithLoadsOfNotDefinitelyNullValues = null;
CFG cfg = classContext.getCFG(method);
IsNullValueDataflow nullValueDataflow = classContext.getIsNullValueDataflow(method);
MethodGen methodGen = classContext.getMethodGen(method);
String sourceFile = classContext.getJavaClass().getSourceFileName();
if (lineMentionedMultipleTimes.cardinality() > 0) {
linesWithLoadsOfNotDefinitelyNullValues = new BitSet();
LineNumberTable lineNumbers = method.getLineNumberTable();
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
Location location = i.next();
InstructionHandle handle = location.getHandle();
Instruction ins = handle.getInstruction();
if (!(ins instanceof ALOAD))
continue;
IsNullValueFrame frame = nullValueDataflow.getFactAtLocation(location);
if (!frame.isValid()) {
// This basic block is probably dead
continue;
}
// System.out.println(handle.getPosition() + "\t" +
// ins.getName() + "\t" + frame);
ALOAD load = (ALOAD) ins;
int index = load.getIndex();
IsNullValue v = frame.getValue(index);
if (!v.isDefinitelyNull()) {
int sourceLine = lineNumbers.getSourceLine(handle.getPosition());
if (sourceLine > 0)
linesWithLoadsOfNotDefinitelyNullValues.set(sourceLine);
}
}
}
IdentityHashMap<InstructionHandle, Object> sometimesGood = new IdentityHashMap<InstructionHandle, Object>();
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
Location location = i.next();
InstructionHandle handle = location.getHandle();
Instruction ins = handle.getInstruction();
if (!(ins instanceof ALOAD))
continue;
IsNullValueFrame frame = nullValueDataflow.getFactAtLocation(location);
if (!frame.isValid()) {
// This basic block is probably dead
continue;
}
// System.out.println(handle.getPosition() + "\t" + ins.getName() +
// "\t" + frame);
ALOAD load = (ALOAD) ins;
int index = load.getIndex();
IsNullValue v = frame.getValue(index);
if (!v.isDefinitelyNull())
sometimesGood.put(handle, null);
}
// System.out.println(nullValueDataflow);
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
Location location = i.next();
InstructionHandle handle = location.getHandle();
Instruction ins = handle.getInstruction();
if (!(ins instanceof ALOAD))
continue;
if (sometimesGood.containsKey(handle))
continue;
IsNullValueFrame frame = nullValueDataflow.getFactAtLocation(location);
if (!frame.isValid()) {
// This basic block is probably dead
continue;
}
// System.out.println(handle.getPosition() + "\t" + ins.getName() +
// "\t" + frame);
ALOAD load = (ALOAD) ins;
int index = load.getIndex();
IsNullValue v = frame.getValue(index);
if (v.isDefinitelyNull()) {
Instruction next = handle.getNext().getInstruction();
InstructionHandle prevHandle = handle.getPrev();
SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
sourceFile, handle);
SourceLineAnnotation prevSourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext,
methodGen, sourceFile, prevHandle);
if (next instanceof ARETURN) {
// probably stored for duration of finally block
continue;
}
if (next instanceof GOTO) {
InstructionHandle targ = ((BranchInstruction) next).getTarget();
if (targ.getInstruction() instanceof ARETURN) {
// Skip for the same reason we would skip if
// (next instanceof ARETURN) were true. This
// is necessary because the bytecode compiler
// compiles the ternary ? operator with a GOTO
// to an ARETURN instead of just an ARETURN.
continue;
}
}
int startLine = sourceLineAnnotation.getStartLine();
if (startLine > 0 && lineMentionedMultipleTimes.get(startLine)
&& linesWithLoadsOfNotDefinitelyNullValues.get(startLine))
continue;
int previousLine = prevSourceLineAnnotation.getEndLine();
if (startLine < previousLine) {
// probably stored for duration of finally block
// System.out.println("Inverted line");
continue;
}
int priority = NORMAL_PRIORITY;
if (!v.isChecked())
priority++;
// System.out.println("lineMentionedMultipleTimes: " +
// lineMentionedMultipleTimes);
// System.out.println("linesWithLoadsOfNonNullValues: " +
// linesWithLoadsOfNotDefinitelyNullValues);
bugAccumulator.accumulateBug(
new BugInstance(this, "NP_LOAD_OF_KNOWN_NULL_VALUE", priority).addClassAndMethod(methodGen, sourceFile),
sourceLineAnnotation);
}
}
}
public void report() {
}
}