System.out.println(" Analyzing method " + classContext.getJavaClass().getClassName() + "." + method.getName());
}
JavaClass javaClass = classContext.getJavaClass();
BitSet linesMentionedMultipleTimes = classContext.linesMentionedMultipleTimes(method);
BugAccumulator accumulator = new BugAccumulator(bugReporter);
Dataflow<BitSet, LiveLocalStoreAnalysis> llsaDataflow = classContext.getLiveLocalStoreDataflow(method);
int numLocals = method.getCode().getMaxLocals();
int[] localStoreCount = new int[numLocals];
int[] localLoadCount = new int[numLocals];
int[] localIncrementCount = new int[numLocals];
MethodGen methodGen = classContext.getMethodGen(method);
CFG cfg = classContext.getCFG(method);
if (cfg.isFlagSet(CFG.FOUND_INEXACT_UNCONDITIONAL_THROWERS)) {
return;
}
BitSet liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry());
BitSet complainedAbout = new BitSet();
TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
// Get number of locals that are parameters.
int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
if (!method.isStatic()) {
localsThatAreParameters++;
}
// Scan method to determine number of loads, stores, and increments
// of local variables.
countLocalStoresLoadsAndIncrements(localStoreCount, localLoadCount, localIncrementCount, cfg);
for (int i = 0; i < localsThatAreParameters; i++) {
localStoreCount[i]++;
}
// For each source line, keep track of # times
// the line was a live store. This can eliminate false positives
// due to inlining of finally blocks.
BitSet liveStoreSourceLineSet = new BitSet();
// Scan method for
// - dead stores
// - stores to parameters that are dead upon entry to the method
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
Location location = i.next();
BugInstance pendingBugReportAboutOverwrittenParameter = null;
try {
WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
// Skip any instruction which is not a store
if (!isStore(location)) {
continue;
}
// Heuristic: exception handler blocks often contain
// dead stores generated by the compiler.
if (location.getBasicBlock().isExceptionHandler()) {
propertySet.addProperty(DeadLocalStoreProperty.EXCEPTION_HANDLER);
}
InstructionHandle handle = location.getHandle();
int pc = handle.getPosition();
IndexedInstruction ins = (IndexedInstruction) location.getHandle().getInstruction();
int local = ins.getIndex();
// Get live stores at this instruction.
// Note that the analysis also computes which stores were
// killed by a subsequent unconditional store.
BitSet liveStoreSet = llsaDataflow.getAnalysis().getFactAtLocation(location);
// Is store alive?
boolean storeLive = llsaDataflow.getAnalysis().isStoreAlive(liveStoreSet, local);
LocalVariableAnnotation lvAnnotation = LocalVariableAnnotation.getLocalVariableAnnotation(method, location, ins);
String sourceFileName = javaClass.getSourceFileName();
if (lvAnnotation.getName().equals("?")) {
if (sourceFileName.endsWith(".groovy")) {
continue;
}
if (method.getCode().getLocalVariableTable() != null) {
continue;
}
}
SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
sourceFileName, location.getHandle());
if (DEBUG) {
System.out.println(" Store at " + sourceLineAnnotation.getStartLine() + "@"
+ location.getHandle().getPosition() + " is " + (storeLive ? "live" : "dead"));
System.out.println("Previous is: " + location.getHandle().getPrev());
}
// Note source lines of live stores.
if (storeLive && sourceLineAnnotation.getStartLine() > 0) {
liveStoreSourceLineSet.set(sourceLineAnnotation.getStartLine());
}
String lvName = lvAnnotation.getName();
if (lvName.charAt(0) == '$' || lvName.charAt(0) == '_') {
propertySet.addProperty(DeadLocalStoreProperty.SYNTHETIC_NAME);
}
if (EXCLUDED_LOCALS.contains(lvName)) {
continue;
}
propertySet.setProperty(DeadLocalStoreProperty.LOCAL_NAME, lvName);
boolean isParameter = local < localsThatAreParameters;
if (isParameter) {
propertySet.addProperty(DeadLocalStoreProperty.IS_PARAMETER);
}
Field shadowedField = null;
for (Field f : javaClass.getFields()) {
if (f.getName().equals(lvName) && f.isStatic() == method.isStatic()) {
shadowedField = f;
propertySet.addProperty(DeadLocalStoreProperty.SHADOWS_FIELD);
break;
}
}
// Is this a store to a parameter which was dead on entry to the
// method?
boolean parameterThatIsDeadAtEntry = isParameter
&& !llsaDataflow.getAnalysis().isStoreAlive(liveStoreSetAtEntry, local);
if (parameterThatIsDeadAtEntry && !complainedAbout.get(local)) {
int priority = storeLive ? LOW_PRIORITY : NORMAL_PRIORITY;
if (shadowedField != null) {
priority--;
}
pendingBugReportAboutOverwrittenParameter = new BugInstance(this, "IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN",
priority).addClassAndMethod(methodGen, sourceFileName).add(lvAnnotation);
if (shadowedField != null) {
pendingBugReportAboutOverwrittenParameter.addField(
FieldAnnotation.fromBCELField(classContext.getJavaClass(), shadowedField)).describe(
FieldAnnotation.DID_YOU_MEAN_ROLE);
}
pendingBugReportAboutOverwrittenParameter.addSourceLine(classContext, methodGen, sourceFileName,
location.getHandle());
complainedAbout.set(local);
}
if (storeLive) {
continue;
}
TypeFrame typeFrame = typeDataflow.getAnalysis().getFactAtLocation(location);
Type typeOfValue = null;
if (typeFrame.isValid() && typeFrame.getStackDepth() > 0) {
typeOfValue = typeFrame.getTopValue();
}
boolean storeOfNull = false;
InstructionHandle prevInsHandle = location.getHandle().getPrev();
if (prevInsHandle != null) {
Instruction prevIns = prevInsHandle.getInstruction();
boolean foundDeadClassInitialization = false;
String initializationOf = null;
if (prevIns instanceof ConstantPushInstruction) {
continue; // not an interesting dead store
} else if (prevIns instanceof GETSTATIC) {
GETSTATIC getStatic = (GETSTATIC) prevIns;
ConstantPoolGen cpg = methodGen.getConstantPool();
foundDeadClassInitialization = getStatic.getFieldName(cpg).startsWith("class$")
&& getStatic.getSignature(cpg).equals("Ljava/lang/Class;");
for (Iterator<Location> j = cfg.locationIterator(); j.hasNext();) {
Location location2 = j.next();
if (location2.getHandle().getPosition() + 15 == location.getHandle().getPosition()) {
Instruction instruction2 = location2.getHandle().getInstruction();
if (instruction2 instanceof LDC) {
Object value = ((LDC) instruction2).getValue(methodGen.getConstantPool());
if (value instanceof String) {
String n = (String) value;
if (n.length() > 0) {
initializationOf = ClassName.toSignature(n);
}
}
}
}
}
} else if (prevIns instanceof LDC) {
LDC ldc = (LDC) prevIns;
Type t = ldc.getType(methodGen.getConstantPool());
if (t.getSignature().equals("Ljava/lang/Class;")) {
Object value = ldc.getValue(methodGen.getConstantPool());
if (value instanceof ConstantClass) {
ConstantClass v = (ConstantClass) value;
initializationOf = ClassName.toSignature(v.getBytes(javaClass.getConstantPool()));
foundDeadClassInitialization = true;
} else if (value instanceof ObjectType) {
ObjectType v = (ObjectType) value;
initializationOf = ClassName.toSignature(v.getClassName());
foundDeadClassInitialization = true;
} else {
AnalysisContext.logError("LDC loaded " + value + "at " + location.getHandle().getPosition() + " in " + classContext.getFullyQualifiedMethodName(method));
}
}
else {
continue; // not an interesting DLS
}
} else if (prevIns instanceof DUP2) {
// Check for the case where, due to the bytecode
// compiler, a long is needlessly stored just
// after we've DUP2'ed the stack and just
// before we return
Instruction cur = location.getHandle().getInstruction();
Instruction nxt = location.getHandle().getNext().getInstruction();
if (cur instanceof LSTORE && nxt instanceof LRETURN) {
continue; // not an interesting DLS
}
}
if (foundDeadClassInitialization) {
if (classContext.getJavaClass().getSuperclassName().equals("org.apache.axis.client.Stub")) {
continue;
}
BugInstance bugInstance = new BugInstance(this, "DLS_DEAD_STORE_OF_CLASS_LITERAL",
Priorities.NORMAL_PRIORITY).addClassAndMethod(methodGen, sourceFileName).add(lvAnnotation)
.addType(initializationOf);
accumulator.accumulateBug(bugInstance, sourceLineAnnotation);
continue;
}
if (prevIns instanceof LDC || prevIns instanceof ConstantPushInstruction) {
propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_CONSTANT);
} else if (prevIns instanceof ACONST_NULL) {
storeOfNull = true;
propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_NULL);
}
}
if (typeOfValue instanceof BasicType || Type.STRING.equals(typeOfValue)) {
propertySet.addProperty(DeadLocalStoreProperty.BASE_VALUE);
}
// Ignore assignments that were killed by a subsequent
// assignment.
boolean killedBySubsequentStore = llsaDataflow.getAnalysis().killedByStore(liveStoreSet, local);
if (killedBySubsequentStore) {
if (propertySet.containsProperty(DeadLocalStoreProperty.STORE_OF_NULL)
|| propertySet.containsProperty(DeadLocalStoreProperty.STORE_OF_CONSTANT)) {
continue;
}
propertySet.addProperty(DeadLocalStoreProperty.KILLED_BY_SUBSEQUENT_STORE);
}
// Ignore dead assignments of null and 0.
// These often indicate defensive programming.
InstructionHandle prev = location.getBasicBlock().getPredecessorOf(location.getHandle());
// int prevOpCode = -1;
if (prev != null) {
if (defensiveConstantValueOpcodes.get(prev.getInstruction().getOpcode())) {
propertySet.addProperty(DeadLocalStoreProperty.DEFENSIVE_CONSTANT_OPCODE);
// prevOpCode = prev.getInstruction().getOpcode();
}
if (prev.getInstruction() instanceof GETFIELD) {
InstructionHandle prev2 = prev.getPrev();
if (prev2 != null && prev2.getInstruction() instanceof ALOAD) {
propertySet.addProperty(DeadLocalStoreProperty.CACHING_VALUE);
}
}
if (prev.getInstruction() instanceof LoadInstruction) {
propertySet.addProperty(DeadLocalStoreProperty.COPY_VALUE);
}
if (prev.getInstruction() instanceof InvokeInstruction) {
propertySet.addProperty(DeadLocalStoreProperty.METHOD_RESULT);
}
}
boolean deadObjectStore = false;
if (ins instanceof IINC) {
// special handling of IINC
if (method.getName().equals("main") && method.isStatic()
&& method.getSignature().equals("([Ljava/lang/String;)V")) {
propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT_IN_MAIN);
}
InstructionHandle next = location.getHandle().getNext();
if (next != null && next.getInstruction() instanceof IRETURN) {
propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT_IN_RETURN);
} else {
propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT);
}
if (localIncrementCount[local] == 1) {
propertySet.addProperty(DeadLocalStoreProperty.SINGLE_DEAD_INCREMENT);
} else {
propertySet.removeProperty(DeadLocalStoreProperty.IS_PARAMETER);
}
} else if (ins instanceof ASTORE && prev != null) {
// Look for objects created but never used
Instruction prevIns = prev.getInstruction();
if ((prevIns instanceof INVOKESPECIAL && ((INVOKESPECIAL) prevIns).getMethodName(methodGen.getConstantPool())
.equals("<init>"))
|| prevIns instanceof ANEWARRAY
|| prevIns instanceof NEWARRAY
|| prevIns instanceof MULTIANEWARRAY) {
deadObjectStore = true;
} else if (prevIns instanceof DUP) {
propertySet.addProperty(DeadLocalStoreProperty.DUP_THEN_STORE);
}
}
if (deadObjectStore) {
propertySet.addProperty(DeadLocalStoreProperty.DEAD_OBJECT_STORE);
} else if (!killedBySubsequentStore && localStoreCount[local] == 2 && localLoadCount[local] > 0) {
// TODO: why is this significant?
propertySet.addProperty(DeadLocalStoreProperty.TWO_STORES_MULTIPLE_LOADS);
} else if (!parameterThatIsDeadAtEntry && localStoreCount[local] == 1 && localLoadCount[local] == 0
&& propertySet.containsProperty(DeadLocalStoreProperty.DEFENSIVE_CONSTANT_OPCODE)) {
// might be final local constant
propertySet.addProperty(DeadLocalStoreProperty.SINGLE_STORE);
} else if (!parameterThatIsDeadAtEntry && !propertySet.containsProperty(DeadLocalStoreProperty.SHADOWS_FIELD)
&& localLoadCount[local] == 0) {
// TODO: why is this significant?
propertySet.addProperty(DeadLocalStoreProperty.NO_LOADS);
}
if (!storeOfNull && typeOfValue != null
&& !propertySet.containsProperty(DeadLocalStoreProperty.EXCEPTION_HANDLER)) {
String signatureOfValue = typeOfValue.getSignature();
if ((signatureOfValue.startsWith("Ljava/sql/") || signatureOfValue.startsWith("Ljavax/sql/"))
&& !signatureOfValue.endsWith("Exception")) {
propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_DATABASE_VALUE);
}
}
if (parameterThatIsDeadAtEntry) {
propertySet.addProperty(DeadLocalStoreProperty.PARAM_DEAD_ON_ENTRY);
if (pendingBugReportAboutOverwrittenParameter != null) {
pendingBugReportAboutOverwrittenParameter.setPriority(Priorities.HIGH_PRIORITY);
}
}
if (localStoreCount[local] > 3) {
propertySet.addProperty(DeadLocalStoreProperty.MANY_STORES);
}
int occurrences = cfg.getLocationsContainingInstructionWithOffset(pc).size();
if (occurrences > 2 || sourceLineAnnotation.getStartLine() > 0
&& linesMentionedMultipleTimes.get(sourceLineAnnotation.getStartLine())) {
propertySet.addProperty(DeadLocalStoreProperty.CLONED_STORE);
}
String sourceFile = javaClass.getSourceFileName();
if (Subtypes2.isJSP(javaClass)) {
propertySet.addProperty(DeadLocalStoreProperty.IN_JSP_PAGE);
} else if (BCELUtil.isSynthetic(javaClass) || sourceFile != null && !sourceFile.endsWith(".java")) {
if (sourceFile != null && sourceFile.endsWith(".gxp") && (lvName.startsWith("gxp$") || lvName.startsWith("gxp_"))) {
continue;
}
propertySet.addProperty(DeadLocalStoreProperty.NOT_JAVA);
}
// Report the warning
String bugPattern;
if (storeOfNull) {
bugPattern = "DLS_DEAD_LOCAL_STORE_OF_NULL";
} else if (shadowedField != null) {
bugPattern = "DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD";
} else if (propertySet.containsProperty(DeadLocalStoreProperty.DEAD_INCREMENT_IN_RETURN)) {
bugPattern = "DLS_DEAD_LOCAL_INCREMENT_IN_RETURN";
} else {
bugPattern = "DLS_DEAD_LOCAL_STORE";
}
BugInstance bugInstance = new BugInstance(this, bugPattern, NORMAL_PRIORITY).addClassAndMethod(methodGen,
sourceFileName).add(lvAnnotation);
if (shadowedField != null) {
bugInstance.addField(FieldAnnotation.fromBCELField(classContext.getJavaClass(), shadowedField)).describe(
FieldAnnotation.DID_YOU_MEAN_ROLE);
}
// If in relaxed reporting mode, encode heuristic
// information.
if (FindBugsAnalysisFeatures.isRelaxedMode()) {
// Add general-purpose warning properties
WarningPropertyUtil.addPropertiesForDataMining(propertySet, classContext, method, location);
}
// Turn all warning properties into BugProperties
propertySet.decorateBugInstance(bugInstance);
if (DEBUG) {
System.out.println(sourceFileName + " : " + methodGen.getName());
System.out.println("priority: " + bugInstance.getPriority());
System.out.println("Reporting " + bugInstance);
System.out.println(propertySet);
}
accumulator.accumulateBug(bugInstance, sourceLineAnnotation);
} finally {
if (pendingBugReportAboutOverwrittenParameter != null) {
bugReporter.reportBug(pendingBugReportAboutOverwrittenParameter);
}
}
}
suppressWarningsIfOneLiveStoreOnLine(accumulator, liveStoreSourceLineSet);
accumulator.reportAccumulatedBugs();
}