BugAccumulator accumulator = new BugAccumulator(bugReporter);
CFG cfg = classContext.getCFG(method);
TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
ValueNumberDataflow vnDataflow = classContext.getValueNumberDataflow(method);
ConstantPoolGen cpg = classContext.getConstantPoolGen();
MethodGen methodGen = classContext.getMethodGen(method);
if (methodGen == null) {
return;
}
String fullMethodName = methodGen.getClassName() + "." + methodGen.getName();
String sourceFile = classContext.getJavaClass().getSourceFileName();
if (DEBUG) {
System.out.println("\n" + fullMethodName);
}
// Process each instruction
for (Iterator<Location> iter = cfg.locationIterator(); iter.hasNext();) {
Location location = iter.next();
InstructionHandle handle = location.getHandle();
Instruction ins = handle.getInstruction();
// Only consider invoke instructions
if (!(ins instanceof InvokeInstruction)) {
continue;
}
InvokeInstruction inv = (InvokeInstruction) ins;
XMethod invokedMethod = XFactory.createXMethod(inv, cpg);
String invokedMethodName = invokedMethod.getName();
String argSignature = invokedMethod.getSignature();
argSignature = argSignature.substring(0, argSignature.indexOf(')') + 1);
String call = invokedMethodName+argSignature;
SignatureParser sigParser = new SignatureParser(inv.getSignature(cpg));
Collection<Info> collection = callMap.get(call);
if (!callMap.containsKey(call)) {
continue;
}
for(Info info : collection) {
Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
if (DEBUG) {
System.out.println("at " + handle.getPosition() + " Checking call to " + info.interfaceForCall + " : " + invokedMethod);
}
try {
if (!subtypes2.isSubtype(invokedMethod.getClassDescriptor(), info.interfaceForCall)) {
continue;
}
} catch (ClassNotFoundException e) {
if (info.interfaceForCall.getClassName().equals("java/util/Collection")
&& invokedMethod.getClassName().equals("com.google.common.collect.Multiset")) {
assert true;
// we know this is OK without needing to find definition of Multiset
} else {
AnalysisContext.reportMissingClass(e);
continue;
}
}
boolean allMethod;
int typeArgument;
if (info.typeIndex >= 0) {
allMethod = false;
typeArgument = info.typeIndex;
} else {
allMethod = true;
typeArgument = -(1+info.typeIndex);
}
int pos = info.argumentIndex;
int lhsPos;
if (inv instanceof INVOKESTATIC) {
lhsPos = sigParser.getSlotsFromTopOfStackForParameter(0);
} else {
lhsPos = sigParser.getTotalArgumentSize();
}
int stackPos = sigParser.getSlotsFromTopOfStackForParameter(pos);
TypeFrame frame = typeDataflow.getFactAtLocation(location);
if (!frame.isValid()) {
// This basic block is probably dead
continue;
}
Type operandType = frame.getStackValue(stackPos);
if (operandType.equals(TopType.instance())) {
// unreachable
continue;
}
if (operandType.equals(NullType.instance())) {
// ignore
continue;
}
ValueNumberFrame vnFrame = vnDataflow.getFactAtLocation(location);
if (!vnFrame.isValid()) {
AnalysisContext.logError("Invalid value number frame in " + xmethod);
continue;
}
ValueNumber objectVN = vnFrame.getStackValue(lhsPos);
ValueNumber argVN = vnFrame.getStackValue(stackPos);
if (objectVN.equals(argVN)) {
String bugPattern = "DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES";
int priority = HIGH_PRIORITY;
if (invokedMethodName.equals("removeAll")) {
bugPattern = "DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION";
priority = NORMAL_PRIORITY;
} else if (invokedMethodName.endsWith("All")) {
bugPattern = "DMI_VACUOUS_SELF_COLLECTION_CALL";
priority = NORMAL_PRIORITY;
}
if (invokedMethodName.startsWith("contains")) {
InstructionHandle next = handle.getNext();
if (next != null) {
Instruction nextIns = next.getInstruction();
if (nextIns instanceof InvokeInstruction) {
XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
if (nextMethod.getName().equals("assertFalse")) {
continue;
}
}
}
}
accumulator.accumulateBug(
new BugInstance(this, bugPattern, priority)
.addClassAndMethod(methodGen, sourceFile)
.addCalledMethod(methodGen, (InvokeInstruction) ins)
.addOptionalAnnotation(
ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, objectVN,
vnFrame, "INVOKED_ON")), SourceLineAnnotation.fromVisitedInstruction(
classContext, methodGen, sourceFile, handle));
}
// Only consider generic...
Type objectType = frame.getStackValue(lhsPos);
if (!(objectType instanceof GenericObjectType)) {
continue;
}
GenericObjectType operand = (GenericObjectType) objectType;
int expectedTypeParameters = 1;
String simpleName = info.interfaceForCall.getSimpleName();
if ( simpleName.toLowerCase().endsWith("map") || simpleName.equals("Hashtable")) {
expectedTypeParameters = 2;
} else if (simpleName.equals("Table")) {
expectedTypeParameters = 3;
}
// ... containers
if (!operand.hasParameters()) {
continue;
}
if (operand.getNumParameters() != expectedTypeParameters) {
continue;
}
ClassDescriptor operandClass = DescriptorFactory.getClassDescriptor(operand);
if (!isGenericCollection(operandClass)) {
continue;
}
if (expectedTypeParameters == 2 &&
Subtypes2.instanceOf(operandClass, Map.class)
&& !TypeFrameModelingVisitor.isStraightGenericMap(operandClass)) {
continue;
}
Type expectedType;
if (allMethod) {
expectedType = operand;
} else {
expectedType = operand.getParameterAt(typeArgument);
}
Type actualType = frame.getStackValue(stackPos);
Type equalsType = actualType;
if (allMethod) {
if (!(actualType instanceof GenericObjectType)) {
continue;
}
equalsType = ((GenericObjectType)actualType).getParameterAt(typeArgument);
}
IncompatibleTypes matchResult = compareTypes(expectedType, actualType, allMethod);
boolean parmIsObject = expectedType.getSignature().equals("Ljava/lang/Object;");
boolean selfOperation = !allMethod && operand.equals(actualType) && !parmIsObject;
if (!allMethod && !parmIsObject && actualType instanceof GenericObjectType) {
GenericObjectType p2 = (GenericObjectType) actualType;
List<? extends ReferenceType> parameters = p2.getParameters();
if (parameters != null && parameters.equals(operand.getParameters())) {
selfOperation = true;
}
}
if (!selfOperation && ( matchResult == IncompatibleTypes.SEEMS_OK || matchResult.getPriority() == Priorities.IGNORE_PRIORITY)) {
continue;
}
if (invokedMethodName.startsWith("contains") || invokedMethodName.equals("remove")) {
InstructionHandle next = handle.getNext();
if (next != null) {
Instruction nextIns = next.getInstruction();
if (nextIns instanceof InvokeInstruction) {
XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
if (nextMethod.getName().equals("assertFalse")) {
continue;
}
}
}
} else if (invokedMethodName.equals("get") || invokedMethodName.equals("remove")) {
InstructionHandle next = handle.getNext();
if (next != null) {
Instruction nextIns = next.getInstruction();
if (nextIns instanceof InvokeInstruction) {
XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
if (nextMethod.getName().equals("assertNull")) {
continue;
}
}
}
}
boolean noisy = false;
if (invokedMethodName.equals("get")) {
UnconditionalValueDerefDataflow unconditionalValueDerefDataflow = classContext
.getUnconditionalValueDerefDataflow(method);
UnconditionalValueDerefSet unconditionalDeref = unconditionalValueDerefDataflow.getFactAtLocation(location);
ValueNumberFrame vnAfter = vnDataflow.getFactAfterLocation(location);
ValueNumber top = vnAfter.getTopValue();
noisy = unconditionalDeref.getValueNumbersThatAreUnconditionallyDereferenced().contains(top);
}
// Prepare bug report
SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,