}
ValueNumberFrame vFrame = vnaDataflow.getFactAtLocation(location);
if (paramValueNumberSet == null) {
paramValueNumberSet = getParameterValueNumbers(classContext, method, cfg);
}
ValueNumber valueNumber = vFrame.getTopValue();
BugAnnotation valueSource = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, valueNumber, vFrame,
"VALUE_OF");
// XXX call below causes 86% of all OpcodeStackDetector.EarlyExitException (getPC() == targetPC) thrown (13000 on java* JDK7 classes)
BugAnnotation source = BugInstance.getSourceForTopStackValue(classContext, method, location);
boolean isParameter = paramValueNumberSet.contains(valueNumber) && source instanceof LocalVariableAnnotation;
try {
JavaClass castJavaClass = Repository.lookupClass(castName);
JavaClass refJavaClass = Repository.lookupClass(refName);
boolean upcast = Repository.instanceOf(refJavaClass, castJavaClass);
if (upcast || typesAreEqual) {
if (!isCast) {
accumulator.accumulateBug(new BugInstance(this, "BC_VACUOUS_INSTANCEOF", NORMAL_PRIORITY)
.addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(refType, castType),
sourceLineAnnotation);
}
} else {
boolean castMayThrow = !Repository.instanceOf(refJavaClass, castJavaClass);
boolean downCast = Repository.instanceOf(castJavaClass, refJavaClass);
if (!operandTypeIsExact && refName.equals("java.lang.Object")) {
continue;
}
double rank = 0.0;
boolean castToConcreteCollection = concreteCollectionClasses.contains(castName)
&& abstractCollectionClasses.contains(refName);
boolean castToAbstractCollection = abstractCollectionClasses.contains(castName)
&& veryAbstractCollectionClasses.contains(refName);
int position = location.getHandle().getPosition();
int catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
"java/lang/ClassCastException", position);
if (!operandTypeIsExact) {
rank = Analyze.deepInstanceOf(refJavaClass, castJavaClass);
if (castToConcreteCollection && rank > 0.6) {
rank = (rank + 0.6) / 2;
} else if (castToAbstractCollection && rank > 0.3) {
rank = (rank + 0.3) / 2;
}
}
/*
if (false) {
System.out.println("Rank:\t" + rank + "\t" + refName + "\t" + castName);
}
*/
boolean completeInformation = (!castJavaClass.isInterface() && !refJavaClass.isInterface())
|| refJavaClass.isFinal() || castJavaClass.isFinal();
if (DEBUG) {
System.out.println(" In " + classContext.getFullyQualifiedMethodName(method));
System.out.println("At pc: " + handle.getPosition());
System.out.println("cast from " + refName + " to " + castName);
System.out.println(" cast may throw: " + castMayThrow);
System.out.println(" is downcast: " + downCast);
System.out.println(" operand type is exact: " + operandTypeIsExact);
System.out.println(" complete information: " + completeInformation);
System.out.println(" isParameter: " + valueNumber);
System.out.println(" score: " + rank);
System.out.println(" source is: " + valueSource);
if (catchSize < Integer.MAX_VALUE) {
System.out.println(" catch block size is: " + catchSize);
}
if (constantClass != null) {
System.out.println(" constant class " + constantClass + " at " + pcForConstantClass);
}
if (handle.getPrev() == null) {
System.out.println(" prev is null");
} else {
System.out.println(" prev is " + handle.getPrev());
}
}
if (!isCast && castMayThrow && valueSource != null) {
String oldCheck = instanceOfChecks.get(valueSource);
if (oldCheck == null) {
instanceOfChecks.put(valueSource, castName);
} else if (!oldCheck.equals(castName)) {
instanceOfChecks.put(valueSource, "");
}
}
if (!downCast && completeInformation || operandTypeIsExact) {
String bugPattern;
if (isCast) {
if (downCast && operandTypeIsExact) {
if (refSig.equals("[Ljava/lang/Object;") && source instanceof MethodAnnotation
&& ((MethodAnnotation) source).getMethodName().equals("toArray")
&& ((MethodAnnotation) source).getMethodSignature().equals("()[Ljava/lang/Object;")) {
bugPattern = "BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY";
} else {
bugPattern = "BC_IMPOSSIBLE_DOWNCAST";
}
} else {
bugPattern = "BC_IMPOSSIBLE_CAST";
}
} else {
bugPattern = "BC_IMPOSSIBLE_INSTANCEOF";
}
bugReporter.reportBug(new BugInstance(this, bugPattern, isCast ? HIGH_PRIORITY : NORMAL_PRIORITY)
.addClassAndMethod(methodGen, sourceFile)
.addFoundAndExpectedType(refType, castType).addOptionalUniqueAnnotations(valueSource, source)
.addSourceLine(sourceLineAnnotation));
} else if (isCast && rank < 0.9
&& !valueNumber.hasFlag(ValueNumber.ARRAY_VALUE)) {
int priority = NORMAL_PRIORITY;
@CheckForNull String oldCheck = instanceOfChecks.get(valueSource);
if (DEBUG) {
System.out.println("Old check: " + oldCheck);
}
if (castName.equals(oldCheck)) {
priority += 1;
} else if ("".equals(oldCheck)) {
priority += 1;
if (!(source instanceof LocalVariableAnnotation)) {
continue;
}
}
if (rank > 0.75) {
priority += 2;
} else if (rank > 0.5) {
priority += 1;
} else if (rank > 0.25) {
priority += 0;
} else {
priority--;
}
if (DEBUG) {
System.out.println(" priority a: " + priority);
}
if (methodGen.getClassName().startsWith(refName) || methodGen.getClassName().startsWith(castName)) {
priority += 1;
}
if (DEBUG) {
System.out.println(" priority b: " + priority);
}
if (castJavaClass.isInterface() && !castToAbstractCollection) {
priority++;
}
if (DEBUG) {
System.out.println(" priority c: " + priority);
}
if (castToConcreteCollection && veryAbstractCollectionClasses.contains(refName)) {
priority--;
}
if (DEBUG) {
System.out.println(" priority d: " + priority);
}
if (priority <= LOW_PRIORITY && !castToAbstractCollection && !castToConcreteCollection
&& (refJavaClass.isInterface() || refJavaClass.isAbstract())) {
priority++;
}
if (DEBUG) {
System.out.println(" priority e: " + priority);
}
if (DEBUG) {
System.out.println(" ref name: " + refName);
}
if (methodGen.getName().equals("compareTo")) {
priority++;
} else if (methodGen.isPublic() && isParameter && !castName.equals(oldCheck)) {
priority--;
}
if (wasMethodInvocationWasGeneric && valueNumber.hasFlag(ValueNumber.RETURN_VALUE)) {
continue;
}
if (constantClass != null && pcForConstantClass +20 >= pc && valueNumber.hasFlag(ValueNumber.RETURN_VALUE)
&& ClassName.toDottedClassName(constantClass).equals(castName)) {
priority += 2;
}
if (DEBUG) {
System.out.println(" priority f: " + priority);
}
if (source instanceof MethodAnnotation) {
MethodAnnotation m = (MethodAnnotation) source;
XMethod xm = m.toXMethod();
if (xm != null && (xm.isPrivate() || xm.isStatic()) && priority == Priorities.LOW_PRIORITY) {
continue;
}
}
if (valueNumber.hasFlag(ValueNumber.RETURN_VALUE) && priority < Priorities.LOW_PRIORITY) {
priority = Priorities.LOW_PRIORITY;
}
if (DEBUG) {
System.out.println(" priority g: " + priority);
}
if (DEBUG) {
System.out.println(" priority h: " + priority);
}
if (catchSize < 15) {
return;
}
if (catchSize < 25) {
priority++;
}
if (DEBUG) {
System.out.println(" priority i: " + priority);
}
if (priority < HIGH_PRIORITY) {
priority = HIGH_PRIORITY;
}
if (priority <= LOW_PRIORITY) {
String bug = "BC_UNCONFIRMED_CAST";
if (valueNumber.hasFlag(ValueNumber.RETURN_VALUE) || valueSource instanceof MethodAnnotation) {
bug = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE";
} else if (castToConcreteCollection) {
bug = "BC_BAD_CAST_TO_CONCRETE_COLLECTION";
} else if (castToAbstractCollection) {
bug = "BC_BAD_CAST_TO_ABSTRACT_COLLECTION";