1 19 20 package edu.umd.cs.findbugs.ba; 21 22 import java.util.HashMap ; 23 import java.util.HashSet ; 24 import java.util.Map ; 25 import java.util.Set ; 26 27 import org.apache.bcel.Constants; 28 import org.apache.bcel.Repository; 29 import org.apache.bcel.classfile.ExceptionTable; 30 import org.apache.bcel.classfile.Field; 31 import org.apache.bcel.classfile.JavaClass; 32 import org.apache.bcel.classfile.Method; 33 import org.apache.bcel.generic.ArrayType; 34 import org.apache.bcel.generic.ConstantPoolGen; 35 import org.apache.bcel.generic.FieldInstruction; 36 import org.apache.bcel.generic.INVOKESTATIC; 37 import org.apache.bcel.generic.Instruction; 38 import org.apache.bcel.generic.InvokeInstruction; 39 import org.apache.bcel.generic.ObjectType; 40 import org.apache.bcel.generic.ReferenceType; 41 import org.apache.bcel.generic.Type; 42 43 import edu.umd.cs.findbugs.SystemProperties; 44 import edu.umd.cs.findbugs.annotations.NonNull; 45 import edu.umd.cs.findbugs.ba.type.TypeFrame; 46 47 56 public class Hierarchy { 57 private static final boolean DEBUG_METHOD_LOOKUP = 58 SystemProperties.getBoolean("hier.lookup.debug"); 59 60 63 public static final ObjectType EXCEPTION_TYPE = ObjectTypeFactory.getInstance("java.lang.Exception"); 64 67 public static final ObjectType ERROR_TYPE = ObjectTypeFactory.getInstance("java.lang.Error"); 68 71 public static final ObjectType RUNTIME_EXCEPTION_TYPE = ObjectTypeFactory.getInstance("java.lang.RuntimeException"); 72 73 82 public static boolean isSubtype(String clsName, String possibleSupertypeClassName) throws ClassNotFoundException { 83 ObjectType cls = ObjectTypeFactory.getInstance(clsName); 84 ObjectType superCls = ObjectTypeFactory.getInstance(possibleSupertypeClassName); 85 return isSubtype(cls, superCls); 86 } 87 88 96 public static boolean isSubtype(ReferenceType t, ReferenceType possibleSupertype) throws ClassNotFoundException { 97 Map <ReferenceType, Boolean > subtypes = subtypeCache.get(possibleSupertype); 98 if (subtypes == null) { 99 subtypes = new HashMap <ReferenceType, Boolean >(); 100 subtypeCache.put(possibleSupertype, subtypes); 101 } 102 Boolean result = subtypes.get(t); 103 if (result == null) { 104 result = Boolean.valueOf(t.isAssignmentCompatibleWith(possibleSupertype)); 105 subtypes.put(t, result); 106 } 107 return result; 108 } 109 110 static Map <ReferenceType, Map <ReferenceType, Boolean >> subtypeCache = new HashMap <ReferenceType, Map <ReferenceType, Boolean >> (); 111 120 public static boolean isUniversalExceptionHandler(ObjectType catchType) { 121 return catchType == null || catchType.equals(Type.THROWABLE); 122 } 123 124 128 public static boolean isUncheckedException(ObjectType type) throws ClassNotFoundException { 129 return isSubtype(type, RUNTIME_EXCEPTION_TYPE) || isSubtype(type, ERROR_TYPE); 130 } 131 132 140 public static boolean isMonitorWait(String methodName, String methodSig) { 141 return methodName.equals("wait") && 142 (methodSig.equals("()V") || methodSig.equals("(J)V") || methodSig.equals("(JI)V")); 143 } 144 145 153 public static boolean isMonitorWait(Instruction ins, ConstantPoolGen cpg) { 154 if (!(ins instanceof InvokeInstruction)) 155 return false; 156 if (ins.getOpcode() == Constants.INVOKESTATIC) 157 return false; 158 159 InvokeInstruction inv = (InvokeInstruction) ins; 160 String methodName = inv.getMethodName(cpg); 161 String methodSig = inv.getSignature(cpg); 162 163 return isMonitorWait(methodName, methodSig); 164 } 165 166 174 public static boolean isMonitorNotify(String methodName, String methodSig) { 175 return (methodName.equals("notify") || methodName.equals("notifyAll")) && 176 methodSig.equals("()V"); 177 } 178 186 public static boolean isMonitorNotify(Instruction ins, ConstantPoolGen cpg) { 187 if (!(ins instanceof InvokeInstruction)) 188 return false; 189 if (ins.getOpcode() == Constants.INVOKESTATIC) 190 return false; 191 192 InvokeInstruction inv = (InvokeInstruction) ins; 193 String methodName = inv.getMethodName(cpg); 194 String methodSig = inv.getSignature(cpg); 195 196 return isMonitorNotify(methodName, methodSig); 197 } 198 199 208 public static JavaClassAndMethod findExactMethod(InvokeInstruction inv, ConstantPoolGen cpg) throws ClassNotFoundException { 209 return findExactMethod(inv, cpg, ANY_METHOD); 210 } 211 212 222 public static JavaClassAndMethod findExactMethod( 223 InvokeInstruction inv, 224 ConstantPoolGen cpg, 225 JavaClassAndMethodChooser chooser) throws ClassNotFoundException { 226 String className = inv.getClassName(cpg); 227 String methodName = inv.getName(cpg); 228 String methodSig = inv.getSignature(cpg); 229 230 JavaClass jclass = Repository.lookupClass(className); 231 return findMethod(jclass, methodName, methodSig, chooser); 232 } 233 234 242 public static JavaClassAndMethod visitSuperClassMethods( 243 JavaClassAndMethod method, JavaClassAndMethodChooser chooser) throws ClassNotFoundException { 244 return findMethod( 245 method.getJavaClass().getSuperClasses(), 246 method.getMethod().getName(), 247 method.getMethod().getSignature(), 248 chooser); 249 } 250 251 259 public static JavaClassAndMethod visitSuperInterfaceMethods( 260 JavaClassAndMethod method, JavaClassAndMethodChooser chooser) throws ClassNotFoundException { 261 return findMethod( 262 method.getJavaClass().getAllInterfaces(), 263 method.getMethod().getName(), 264 method.getMethod().getSignature(), 265 chooser); 266 } 267 268 293 public static JavaClassAndMethod findInvocationLeastUpperBound( 294 InvokeInstruction inv, ConstantPoolGen cpg) 295 throws ClassNotFoundException { 296 return findInvocationLeastUpperBound(inv, cpg, ANY_METHOD); 297 } 298 299 public static JavaClassAndMethod findInvocationLeastUpperBound( 300 InvokeInstruction inv, ConstantPoolGen cpg, JavaClassAndMethodChooser methodChooser) 301 throws ClassNotFoundException { 302 JavaClassAndMethod result; 303 304 if (DEBUG_METHOD_LOOKUP) { 305 System.out.println("Find prototype method for " + 306 SignatureConverter.convertMethodSignature(inv,cpg)); 307 } 308 309 short opcode = inv.getOpcode(); 310 311 if (methodChooser != ANY_METHOD) { 312 methodChooser = new CompoundMethodChooser(new JavaClassAndMethodChooser[]{ 313 methodChooser, opcode == Constants.INVOKESTATIC ? STATIC_METHOD : INSTANCE_METHOD 314 }); 315 } 316 317 if (opcode == Constants.INVOKESPECIAL) { 319 result = findExactMethod(inv, cpg, methodChooser); 321 } else { 322 String className = inv.getClassName(cpg); 323 String methodName = inv.getName(cpg); 324 String methodSig = inv.getSignature(cpg); 325 if (DEBUG_METHOD_LOOKUP) { 326 System.out.println("[Class name is " + className + "]"); 327 System.out.println("[Method name is " + methodName + "]"); 328 System.out.println("[Method signature is " + methodSig + "]"); 329 } 330 331 if (className.startsWith("[")) { 332 className= "java.lang.Object"; 334 } 335 336 if (opcode == Constants.INVOKEVIRTUAL || opcode == Constants.INVOKESTATIC) { 337 if (DEBUG_METHOD_LOOKUP) { 338 System.out.println("[invokevirtual or invokestatic]"); 339 } 340 result = findMethod(Repository.lookupClass(className), methodName, methodSig, methodChooser); 343 if (result == null) { 344 if (DEBUG_METHOD_LOOKUP) { 345 System.out.println("[not in class, checking superclasses...]"); 346 } 347 JavaClass[] superClassList = Repository.getSuperClasses(className); 348 result = findMethod(superClassList, methodName, methodSig, methodChooser); 349 } 350 } else { 351 result = findMethod(Repository.lookupClass(className), methodName, methodSig, methodChooser); 353 if (result == null) { 354 JavaClass[] interfaceList = Repository.getInterfaces(className); 355 result = findMethod(interfaceList, methodName, methodSig, methodChooser); 356 } 357 } 358 } 359 360 return result; 361 } 362 363 372 public static ObjectType[] findDeclaredExceptions(InvokeInstruction inv, ConstantPoolGen cpg) 373 throws ClassNotFoundException { 374 JavaClassAndMethod method = findInvocationLeastUpperBound(inv, cpg); 375 376 if (method == null) 377 return null; 378 379 ExceptionTable exTable = method.getMethod().getExceptionTable(); 380 if (exTable == null) 381 return new ObjectType[0]; 382 383 String [] exNameList = exTable.getExceptionNames(); 384 ObjectType[] result = new ObjectType[exNameList.length]; 385 for (int i = 0; i < exNameList.length; ++i) { 386 result[i] = ObjectTypeFactory.getInstance(exNameList[i]); 387 } 388 return result; 389 } 390 391 399 public static JavaClassAndMethod findMethod(JavaClass javaClass, String methodName, String methodSig) { 400 return findMethod(javaClass, methodName, methodSig, ANY_METHOD); 401 } 402 403 413 public static JavaClassAndMethod findMethod( 414 JavaClass javaClass, 415 String methodName, 416 String methodSig, 417 JavaClassAndMethodChooser chooser) { 418 if (DEBUG_METHOD_LOOKUP) { 419 System.out.println("Check " + javaClass.getClassName()); 420 } 421 Method[] methodList = javaClass.getMethods(); 422 for (Method method : methodList) { 423 JavaClassAndMethod javaClassAndMethod = new JavaClassAndMethod(javaClass, method); 424 if (method.getName().equals(methodName) 425 && method.getSignature().equals(methodSig) 426 && chooser.choose(javaClassAndMethod)) { 427 if (DEBUG_METHOD_LOOKUP) { 428 System.out.println("\t==> FOUND: " + method); 429 } 430 return new JavaClassAndMethod(javaClass, method); 431 } 432 } 433 if (DEBUG_METHOD_LOOKUP) { 434 System.out.println("\t==> NOT FOUND"); 435 } 436 return null; 437 } 438 439 448 public static XMethod findXMethod(JavaClass javaClass, String methodName, String methodSig, 449 JavaClassAndMethodChooser chooser) { 450 JavaClassAndMethod result = findMethod(javaClass, methodName, methodSig, chooser); 451 return result == null ? null : XFactory.createXMethod(result.getJavaClass(), result.getMethod()); 452 } 453 454 457 public static final JavaClassAndMethodChooser ANY_METHOD = new JavaClassAndMethodChooser() { 458 public boolean choose(JavaClassAndMethod javaClassAndMethod) { 459 return true; 460 } 461 }; 462 463 467 public static final JavaClassAndMethodChooser CONCRETE_METHOD = new JavaClassAndMethodChooser() { 468 public boolean choose(JavaClassAndMethod javaClassAndMethod) { 469 Method method = javaClassAndMethod.getMethod(); 470 int accessFlags = method.getAccessFlags(); 471 return (accessFlags & Constants.ACC_ABSTRACT) == 0 472 && (accessFlags & Constants.ACC_NATIVE) == 0; 473 } 474 }; 475 476 479 public static final JavaClassAndMethodChooser STATIC_METHOD = new JavaClassAndMethodChooser() { 480 public boolean choose(JavaClassAndMethod javaClassAndMethod) { 481 return javaClassAndMethod.getMethod().isStatic(); 482 } 483 }; 484 485 488 public static final JavaClassAndMethodChooser INSTANCE_METHOD = new JavaClassAndMethodChooser() { 489 public boolean choose(JavaClassAndMethod javaClassAndMethod) { 490 return !javaClassAndMethod.getMethod().isStatic(); 491 } 492 }; 493 494 503 public static JavaClassAndMethod findMethod(JavaClass[] classList, String methodName, String methodSig) { 504 return findMethod(classList, methodName, methodSig, ANY_METHOD); 505 } 506 507 518 public static JavaClassAndMethod findMethod(JavaClass[] classList, String methodName, String methodSig, 519 JavaClassAndMethodChooser chooser) { 520 JavaClassAndMethod m = null; 521 522 for (JavaClass cls : classList) { 523 if ((m = findMethod(cls, methodName, methodSig, chooser)) != null) 524 break; 525 } 526 527 return m; 528 } 529 530 539 public static XMethod findXMethod(JavaClass[] classList, String methodName, String methodSig) { 540 return findXMethod(classList, methodName, methodSig, ANY_METHOD); 541 } 542 543 554 public static XMethod findXMethod(JavaClass[] classList, String methodName, String methodSig, 555 JavaClassAndMethodChooser chooser) { 556 for (JavaClass cls : classList) { 557 JavaClassAndMethod m; 558 if ((m = findMethod(cls, methodName, methodSig)) != null && chooser.choose(m)) { 559 return XFactory.createXMethod(cls, m.getMethod()); 560 } 561 } 562 return null; 563 } 564 565 576 public static Set <JavaClassAndMethod> resolveMethodCallTargets( 577 InvokeInstruction invokeInstruction, 578 TypeFrame typeFrame, 579 ConstantPoolGen cpg) throws DataflowAnalysisException, ClassNotFoundException { 580 581 short opcode = invokeInstruction.getOpcode(); 582 583 if (opcode == Constants.INVOKESTATIC) { 584 HashSet <JavaClassAndMethod> result = new HashSet <JavaClassAndMethod>(); 585 JavaClassAndMethod targetMethod = findInvocationLeastUpperBound(invokeInstruction, cpg, CONCRETE_METHOD); 586 if (targetMethod != null) { 587 result.add(targetMethod); 588 } 589 return result; 590 } 591 592 if (!typeFrame.isValid()) { 593 return new HashSet <JavaClassAndMethod>(); 594 } 595 596 Type receiverType; 597 boolean receiverTypeIsExact; 598 599 if (opcode == Constants.INVOKESPECIAL) { 600 receiverType = ObjectTypeFactory.getInstance(invokeInstruction.getClassName(cpg)); 603 receiverTypeIsExact = false; } else { 605 int instanceStackLocation = typeFrame.getInstanceStackLocation(invokeInstruction, cpg); 610 receiverType = typeFrame.getStackValue(instanceStackLocation); 611 if (!(receiverType instanceof ReferenceType)) { 612 return new HashSet <JavaClassAndMethod>(); 613 } 614 receiverTypeIsExact = typeFrame.isExact(instanceStackLocation); 615 } 616 if (DEBUG_METHOD_LOOKUP) { 617 System.out.println("[receiver type is " + receiverType + ", " + 618 (receiverTypeIsExact ? "exact]" : " not exact]")); 619 } 620 621 return resolveMethodCallTargets((ReferenceType) receiverType, invokeInstruction, cpg, receiverTypeIsExact); 622 } 623 624 635 public static Set <JavaClassAndMethod> resolveMethodCallTargets( 636 ReferenceType receiverType, 637 InvokeInstruction invokeInstruction, 638 ConstantPoolGen cpg 639 ) throws ClassNotFoundException { 640 return resolveMethodCallTargets(receiverType, invokeInstruction, cpg, false); 641 } 642 643 654 public static Set <JavaClassAndMethod> resolveMethodCallTargets( 655 ReferenceType receiverType, 656 InvokeInstruction invokeInstruction, 657 ConstantPoolGen cpg, 658 boolean receiverTypeIsExact 659 ) throws ClassNotFoundException { 660 HashSet <JavaClassAndMethod> result = new HashSet <JavaClassAndMethod>(); 661 662 if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC) 663 throw new IllegalArgumentException (); 664 665 String methodName = invokeInstruction.getName(cpg); 666 String methodSig = invokeInstruction.getSignature(cpg); 667 668 if (receiverType instanceof ArrayType) { 671 JavaClass javaLangObject = AnalysisContext.currentAnalysisContext().lookupClass("java.lang.Object"); 672 JavaClassAndMethod classAndMethod = findMethod(javaLangObject, methodName, methodSig, INSTANCE_METHOD); 673 if (classAndMethod != null) 674 result.add(classAndMethod); 675 return result; 676 } 677 678 AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext(); 679 680 JavaClass receiverClass = analysisContext.lookupClass( 682 ((ObjectType) receiverType).getClassName()); 683 684 JavaClassAndMethod upperBound = findMethod(receiverClass, methodName, methodSig, CONCRETE_METHOD); 687 if (upperBound == null) { 688 JavaClass[] superClassList = receiverClass.getSuperClasses(); 690 upperBound = findMethod(superClassList, methodName, methodSig, CONCRETE_METHOD); 691 } 692 if (upperBound != null) { 693 if (DEBUG_METHOD_LOOKUP) { 694 System.out.println("Adding upper bound: " + 695 SignatureConverter.convertMethodSignature(upperBound.getJavaClass(), upperBound.getMethod())); 696 } 697 result.add(upperBound); 698 } 699 700 boolean virtualCall = 702 invokeInstruction.getOpcode() != Constants.INVOKESPECIAL 703 && !receiverTypeIsExact; 704 705 if (virtualCall) { 706 Set <JavaClass> subTypeSet = analysisContext.getSubtypes().getTransitiveSubtypes(receiverClass); 709 for (JavaClass subtype : subTypeSet) { 710 JavaClassAndMethod concreteSubtypeMethod = findMethod(subtype, methodName, methodSig, CONCRETE_METHOD); 711 if (concreteSubtypeMethod != null) { 712 result.add(concreteSubtypeMethod); 713 } 714 } 715 } 716 717 return result; 718 } 719 732 739 public static Field findField(String className, String fieldName) throws ClassNotFoundException { 740 JavaClass jclass = Repository.lookupClass(className); 741 742 while (jclass != null) { 743 Field[] fieldList = jclass.getFields(); 744 for (Field field : fieldList) { 745 if (field.getName().equals(fieldName)) { 746 return field; 747 } 748 } 749 750 jclass = jclass.getSuperClass(); 751 } 752 753 return null; 754 } 755 756 777 778 790 public static XField findXField(String className, String fieldName, String fieldSig) 791 throws ClassNotFoundException { 792 793 JavaClass classDefiningField = Repository.lookupClass(className); 794 795 Field field = null; 796 loop: 797 while (classDefiningField != null) { 798 Field[] fieldList = classDefiningField.getFields(); 799 for (Field aFieldList : fieldList) { 800 field = aFieldList; 801 if (field.getName().equals(fieldName) && field.getSignature().equals(fieldSig)) { 802 break loop; 803 } 804 } 805 806 classDefiningField = classDefiningField.getSuperClass(); 807 } 808 809 if (classDefiningField == null) 810 return null; 811 else { 812 String realClassName = classDefiningField.getClassName(); 813 int accessFlags = field.getAccessFlags(); 814 return field.isStatic() 815 ? (XField) new StaticField(realClassName, fieldName, fieldSig, accessFlags) 816 : (XField) new InstanceField(realClassName, fieldName, fieldSig, accessFlags); 817 } 818 } 819 820 829 public static XField findXField(FieldInstruction fins, @NonNull ConstantPoolGen cpg) 830 throws ClassNotFoundException { 831 832 String className = fins.getClassName(cpg); 833 String fieldName = fins.getFieldName(cpg); 834 String fieldSig = fins.getSignature(cpg); 835 836 XField xfield = findXField(className, fieldName, fieldSig); 837 short opcode = fins.getOpcode(); 838 if (xfield != null && 839 xfield.isStatic() == (opcode == Constants.GETSTATIC || opcode == Constants.PUTSTATIC)) 840 return xfield; 841 else 842 return null; 843 } 844 845 852 public static boolean isInnerClassAccess(INVOKESTATIC inv, ConstantPoolGen cpg) { 853 String methodName = inv.getName(cpg); 854 return methodName.startsWith("access$"); 855 } 856 857 865 public static InnerClassAccess getInnerClassAccess(INVOKESTATIC inv, ConstantPoolGen cpg) 866 throws ClassNotFoundException { 867 868 String className = inv.getClassName(cpg); 869 String methodName = inv.getName(cpg); 870 String methodSig = inv.getSignature(cpg); 871 872 InnerClassAccess access = AnalysisContext.currentAnalysisContext() 873 .getInnerClassAccessMap().getInnerClassAccess(className, methodName); 874 return (access != null && access.getMethodSignature().equals(methodSig)) 875 ? access 876 : null; 877 } 878 } 879 880 | Popular Tags |