| 1 19 20 package edu.umd.cs.findbugs.detect; 21 22 import java.util.Arrays ; 23 import java.util.BitSet ; 24 import java.util.HashMap ; 25 import java.util.Iterator ; 26 import java.util.Map ; 27 28 import org.apache.bcel.Constants; 29 import org.apache.bcel.Repository; 30 import org.apache.bcel.classfile.Attribute; 31 import org.apache.bcel.classfile.ConstantCP; 32 import org.apache.bcel.classfile.ConstantMethodref; 33 import org.apache.bcel.classfile.ConstantNameAndType; 34 import org.apache.bcel.classfile.JavaClass; 35 import org.apache.bcel.classfile.Method; 36 import org.apache.bcel.classfile.Synthetic; 37 import org.apache.bcel.generic.ArrayType; 38 import org.apache.bcel.generic.BasicType; 39 import org.apache.bcel.generic.ConstantPoolGen; 40 import org.apache.bcel.generic.Instruction; 41 import org.apache.bcel.generic.InstructionHandle; 42 import org.apache.bcel.generic.InvokeInstruction; 43 import org.apache.bcel.generic.MethodGen; 44 import org.apache.bcel.generic.ObjectType; 45 import org.apache.bcel.generic.ReferenceType; 46 import org.apache.bcel.generic.Type; 47 48 import edu.umd.cs.findbugs.BugAccumulator; 49 import edu.umd.cs.findbugs.BugInstance; 50 import edu.umd.cs.findbugs.BugReporter; 51 import edu.umd.cs.findbugs.Detector; 52 import edu.umd.cs.findbugs.SourceLineAnnotation; 53 import edu.umd.cs.findbugs.SystemProperties; 54 import edu.umd.cs.findbugs.annotations.CheckForNull; 55 import edu.umd.cs.findbugs.ba.CFG; 56 import edu.umd.cs.findbugs.ba.CFGBuilderException; 57 import edu.umd.cs.findbugs.ba.ClassContext; 58 import edu.umd.cs.findbugs.ba.DataflowAnalysisException; 59 import edu.umd.cs.findbugs.ba.IncompatibleTypes; 60 import edu.umd.cs.findbugs.ba.Location; 61 import edu.umd.cs.findbugs.ba.MethodUnprofitableException; 62 import edu.umd.cs.findbugs.ba.generic.GenericObjectType; 63 import edu.umd.cs.findbugs.ba.generic.GenericUtilities; 64 import edu.umd.cs.findbugs.ba.generic.GenericUtilities.TypeCategory; 65 import edu.umd.cs.findbugs.ba.type.TopType; 66 import edu.umd.cs.findbugs.ba.type.TypeDataflow; 67 import edu.umd.cs.findbugs.ba.type.TypeFrame; 68 69 72 public class FindUnrelatedTypesInGenericContainer implements Detector { 73 74 private BugReporter bugReporter; 75 76 private static final boolean DEBUG = SystemProperties.getBoolean("gc.debug"); 77 78 87 private Map <String , int []> collectionsMap = new HashMap <String , int[]>(); 88 89 98 public static String getCollectionsMapKey(String ...triplet) { 99 return triplet[0] + "??" + triplet[1] + "???" + triplet[2]; 100 } 101 102 private void addToCollectionsMap(String className, String methodName, 103 String methodSignature, int... argumentParameterIndex) { 104 collectionsMap.put( 105 getCollectionsMapKey(className, methodName, methodSignature), 106 argumentParameterIndex); 107 } 108 109 private void addToCollectionsMap(String [] classNames, String methodName, 110 String methodSignature, int... argumentParameterIndex) { 111 for (String className : classNames) 112 addToCollectionsMap( 113 className, methodName, methodSignature, 114 argumentParameterIndex); 115 } 116 117 String [] collectionMembers = new String [] { 118 "java.util.Collection", 119 "java.util.AbstractCollection", 120 "java.util.List", 121 "java.util.AbstractList", 122 "java.util.ArrayList", 123 "java.util.LinkedList", 124 "java.util.Set", 125 "java.util.SortedSet", 126 "java.util.LinkedHashSet", 127 "java.util.HashSet", 128 "java.util.TreeSet" 129 }; 130 131 String [] mapMembers = new String [] { 132 "java.util.Map", 133 "java.util.AbstractMap", 134 "java.util.SortedMap", 135 "java.util.TreeMap", 136 "java.util.HashMap", 137 "java.util.LinkedHashMap", 138 "java.util.concurrent.ConcurrentHashMap", 139 "java.util.EnumMap", 140 "java.util.Hashtable", 141 "java.util.IdentityHashMap", 142 "java.util.WeakHashMap" 143 }; 144 145 String [] listMembers = new String [] { 146 "java.util.List", 147 "java.util.AbstractList", 148 "java.util.ArrayList", 149 "java.util.LinkedList" 150 }; 151 152 public FindUnrelatedTypesInGenericContainer(BugReporter bugReporter) { 153 this.bugReporter = bugReporter; 154 String basicSignature = "(Ljava/lang/Object;)Z"; 155 String collectionSignature = "(Ljava/util/Collection<*>;)Z"; 156 String indexSignature = "(Ljava/lang/Object;)I"; 157 158 addToCollectionsMap(collectionMembers, "contains", basicSignature, 0); 160 addToCollectionsMap(collectionMembers, "remove", basicSignature, 0); 162 163 167 addToCollectionsMap(listMembers, "indexOf", indexSignature, 0); 169 addToCollectionsMap(listMembers, "lastIndexOf", indexSignature, 0); 170 171 addToCollectionsMap(mapMembers, "containsKey", basicSignature, 0); 173 addToCollectionsMap(mapMembers, "containsValue", basicSignature, 1); 174 175 addToCollectionsMap(mapMembers, "get", basicSignature, 0); 177 addToCollectionsMap(mapMembers, "remove", basicSignature, 0); 178 } 179 180 184 public void visitClassContext(ClassContext classContext) { 185 JavaClass javaClass = classContext.getJavaClass(); 186 Method[] methodList = javaClass.getMethods(); 187 188 for (Method method : methodList) { 189 if (method.getCode() == null) 190 continue; 191 192 try { 193 analyzeMethod(classContext, method); 194 } catch (MethodUnprofitableException e) { 195 assert true; } catch (CFGBuilderException e) { 197 String msg = "Detector " + this.getClass().getName() 198 + " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature(); 199 bugReporter.logError(msg , e); 200 } catch (DataflowAnalysisException e) { 201 String msg = "Detector " + this.getClass().getName() 202 + " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature(); 203 bugReporter.logError(msg, e); 204 } 205 } 206 } 207 208 211 public boolean prescreen(ClassContext classContext, Method method) { 212 BitSet bytecodeSet = classContext.getBytecodeSet(method); 213 return bytecodeSet != null && ( 214 bytecodeSet.get(Constants.INVOKEINTERFACE) || 215 bytecodeSet.get(Constants.INVOKEVIRTUAL) || 216 bytecodeSet.get(Constants.INVOKESPECIAL) || 217 bytecodeSet.get(Constants.INVOKESTATIC) || 218 bytecodeSet.get(Constants.INVOKENONVIRTUAL) 219 ); 220 } 221 222 226 private boolean isSynthetic(Method m) { 227 Attribute[] attrs = m.getAttributes(); 228 for (Attribute attr : attrs) { 229 if (attr instanceof Synthetic) 230 return true; 231 } 232 return false; 233 } 234 235 private void analyzeMethod(ClassContext classContext, Method method) 236 throws CFGBuilderException, DataflowAnalysisException { 237 if (isSynthetic(method) || !prescreen(classContext, method)) 238 return; 239 240 BugAccumulator accumulator = new BugAccumulator(bugReporter); 241 242 CFG cfg = classContext.getCFG(method); 243 TypeDataflow typeDataflow = classContext.getTypeDataflow(method); 244 245 ConstantPoolGen cpg = classContext.getConstantPoolGen(); 246 MethodGen methodGen = classContext.getMethodGen(method); 247 if (methodGen == null) return; 248 String fullMethodName = 249 methodGen.getClassName() + "." + methodGen.getName(); 250 251 String sourceFile = classContext.getJavaClass().getSourceFileName(); 252 if (DEBUG) { 253 System.out.println("Checking " + fullMethodName); 254 } 255 256 for (Iterator <Location> iter = cfg.locationIterator(); iter.hasNext();) { 258 Location location = iter.next(); 259 InstructionHandle handle = location.getHandle(); 260 Instruction ins = handle.getInstruction(); 261 262 if (!(ins instanceof InvokeInstruction)) 264 continue; 265 266 InvokeInstruction inv = (InvokeInstruction)ins; 267 268 String [] itriplet = getInstructionTriplet(inv, cpg); 270 String [] triplet = getRelevantTriplet(itriplet); 271 if (triplet == null) 272 continue; 273 274 int [] argumentParameterIndex = 276 collectionsMap.get( getCollectionsMapKey(triplet) ); 277 278 TypeFrame frame = typeDataflow.getFactAtLocation(location); 279 if (!frame.isValid()) { 280 continue; 282 } 283 284 Type operandType = frame.getTopValue(); 285 if (operandType.equals(TopType.instance())) { 286 continue; 288 } 289 290 Type objectType = frame.getInstance(inv, cpg); 292 if (!(objectType instanceof GenericObjectType)) 293 continue; 294 295 GenericObjectType operand = (GenericObjectType) objectType; 296 297 if (!operand.hasParameters()) continue; 299 300 int numArguments = frame.getNumArguments(inv, cpg); 301 302 if (numArguments <= 0 || argumentParameterIndex.length != numArguments) 303 continue; 304 305 boolean match = true; 307 IncompatibleTypes [] matches = new IncompatibleTypes [numArguments]; 308 for (int i=0; i<numArguments; i++) matches[i] = IncompatibleTypes.SEEMS_OK; 309 310 for (int ii=0; ii < numArguments; ii++) { 311 if (argumentParameterIndex[ii] < 0) continue; if (argumentParameterIndex[ii] >= operand.getNumParameters()) 313 continue; 315 Type parmType = operand.getParameterAt(argumentParameterIndex[ii]); 316 Type argType = frame.getArgument(inv, cpg, ii, numArguments); 317 matches[ii] = compareTypes(parmType, argType); 318 319 if (matches[ii] != IncompatibleTypes.SEEMS_OK) match = false; 320 } 321 322 if (match) 323 continue; 325 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation 327 .fromVisitedInstruction(classContext, methodGen, sourceFile, handle); 328 329 for (int i=0; i<numArguments; i++) { 331 if (matches[i] == IncompatibleTypes.SEEMS_OK) continue; 332 333 Type parmType = operand.getParameterAt(argumentParameterIndex[i]); 334 if (parmType instanceof GenericObjectType) 335 parmType = ((GenericObjectType)parmType).getUpperBound(); 336 Type argType = frame.getArgument(inv, cpg, i, numArguments); 337 338 accumulator.accumulateBug(new BugInstance(this, 339 "GC_UNRELATED_TYPES", matches[i].getPriority()) 340 .addClassAndMethod(methodGen, sourceFile) 341 .addType(parmType.getSignature()) .addType(argType.getSignature()) .addCalledMethod(methodGen, (InvokeInstruction) ins) 346 ,sourceLineAnnotation); 347 } 348 349 } 350 351 accumulator.reportAccumulatedBugs(); 352 } 353 354 358 private String [] getInstructionTriplet(InvokeInstruction inv, ConstantPoolGen cpg) { 359 360 ConstantCP ref = (ConstantCP) cpg.getConstant( inv.getIndex() ); 362 String className = ref.getClass(cpg.getConstantPool()); 363 364 ConstantNameAndType refNT = 366 (ConstantNameAndType) cpg.getConstant( ref.getNameAndTypeIndex() ); 367 String methodName = refNT.getName(cpg.getConstantPool()); 368 369 String methodSignature = refNT.getSignature(cpg.getConstantPool()); 371 372 return new String [] { className, methodName, methodSignature }; 373 } 374 375 381 private @CheckForNull String [] getRelevantTriplet(String [] instructionTriplet) { 382 if (collectionsMap.containsKey( getCollectionsMapKey(instructionTriplet) )) 383 return instructionTriplet; 384 385 if (Arrays.asList(mapMembers).contains(instructionTriplet[0])) { 388 if ( "get" .equals(instructionTriplet[1]) || 389 "remove".equals(instructionTriplet[1]) ) { 390 addToCollectionsMap(instructionTriplet[0], 391 instructionTriplet[1], instructionTriplet[2], 0); 392 return instructionTriplet; 393 } 394 } 395 396 398 return null; 400 } 401 402 410 private IncompatibleTypes compareTypes(Type parmType, Type argType) { 411 String parmString = GenericUtilities.getString(parmType); 415 String argString = GenericUtilities.getString(argType); 416 if (parmString.equals(argString)) return IncompatibleTypes.SEEMS_OK; 417 418 String objString = GenericUtilities.getString(Type.OBJECT); 421 if ( parmString.equals(objString) || 422 argString.equals(objString) ) { 423 return IncompatibleTypes.SEEMS_OK; 424 } 425 426 TypeCategory parmCat = GenericUtilities.getTypeCategory(parmType); 428 TypeCategory argCat = GenericUtilities.getTypeCategory(argType); 429 430 if ( parmCat == TypeCategory.PLAIN_OBJECT_TYPE && 432 argCat == TypeCategory.PLAIN_OBJECT_TYPE ) 433 434 435 return IncompatibleTypes.getPriorityForAssumingCompatible(parmType, argType); 436 437 if ( parmCat == TypeCategory.WILDCARD_EXTENDS || 439 parmCat == TypeCategory.WILDCARD_SUPER ) 440 return compareTypes( 441 ((GenericObjectType)parmType).getExtension(), argType); 442 443 444 if ( parmCat == TypeCategory.TYPE_VARIABLE || 446 argCat == TypeCategory.TYPE_VARIABLE ) 447 return IncompatibleTypes.SEEMS_OK; 448 449 450 if ( parmCat == TypeCategory.ARRAY_TYPE && 452 argCat == TypeCategory.ARRAY_TYPE ) { 453 ArrayType parmArray = (ArrayType) parmType; 454 ArrayType argArray = (ArrayType) argType; 455 456 if (parmArray.getDimensions() != argArray.getDimensions()) 457 return IncompatibleTypes.ARRAY_AND_NON_ARRAY; 458 459 return compareTypes(parmArray.getBasicType(), argArray.getBasicType()); 460 } 461 if ( parmCat == TypeCategory.ARRAY_TYPE ^ 464 argCat == TypeCategory.ARRAY_TYPE ) { 465 return IncompatibleTypes.ARRAY_AND_NON_ARRAY; 466 } 467 468 if ( parmCat == TypeCategory.PARAMETERS && 470 argCat == TypeCategory.PARAMETERS ) { 471 GenericObjectType parmGeneric = (GenericObjectType) parmType; 472 GenericObjectType argGeneric = (GenericObjectType) argType; 473 474 return compareTypes( parmGeneric.getObjectType(), 476 argGeneric.getObjectType()); 477 478 } 480 if (false) { 483 if ( parmCat == TypeCategory.PARAMETERS ^ 485 argCat == TypeCategory.PARAMETERS ) { 486 return IncompatibleTypes.SEEMS_OK; } 488 } 489 490 if (parmCat == TypeCategory.WILDCARD) return IncompatibleTypes.SEEMS_OK; 493 494 if (parmType instanceof BasicType || argType instanceof BasicType) { 498 throw new IllegalArgumentException ("checking for compatibility of " + parmType + " with " + argType); 501 } 502 503 return IncompatibleTypes.SEEMS_OK; 504 505 } 506 507 private boolean compareTypesOld(Type parmType, Type argType) { 509 if (GenericUtilities.getString(parmType) 513 .equals(GenericUtilities.getString(argType))) 514 return true; 515 516 if (parmType instanceof GenericObjectType) { 517 GenericObjectType o = (GenericObjectType) parmType; 518 if (o.getTypeCategory() == GenericUtilities.TypeCategory.WILDCARD_EXTENDS) { 519 return compareTypesOld(o.getExtension(), argType); 520 } 521 } 522 if (parmType instanceof GenericObjectType && 524 !((GenericObjectType) parmType).hasParameters()) return true; 525 if (argType instanceof GenericObjectType && 526 !((GenericObjectType) argType).hasParameters()) return true; 527 528 if (parmType instanceof GenericObjectType && argType instanceof GenericObjectType) { 530 return true; 531 } else { 532 if (!(parmType instanceof ReferenceType && argType instanceof ReferenceType)) 534 return true; 535 536 if (!(parmType instanceof ObjectType && argType instanceof ObjectType)) 538 return true; 539 540 try { 542 return Repository.instanceOf( 543 ((ObjectType)argType).getClassName(), 544 ((ObjectType)parmType).getClassName()); 545 } catch (ClassNotFoundException e) {} 546 } 547 548 return true; 549 } 550 551 555 public void report() { 556 } 557 558 } 559 | Popular Tags |