Index: third_party/jmake/src/org/pantsbuild/jmake/ClassInfo.java |
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/ClassInfo.java b/third_party/jmake/src/org/pantsbuild/jmake/ClassInfo.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9bfcd5a0b218988c2acd36a61690de45e7f9becc |
--- /dev/null |
+++ b/third_party/jmake/src/org/pantsbuild/jmake/ClassInfo.java |
@@ -0,0 +1,746 @@ |
+/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved |
+ * |
+ * This program is distributed under the terms of |
+ * the GNU General Public License Version 2. See the LICENSE file |
+ * at the top of the source tree. |
+ */ |
+package org.pantsbuild.jmake; |
+ |
+import java.io.Serializable; |
+import java.lang.reflect.Modifier; |
+import java.util.ArrayList; |
+import java.util.LinkedHashSet; |
+import java.util.List; |
+import java.util.Set; |
+ |
+/** |
+ * A reflection of a class, in the form that allows fast checks and information obtaining. |
+ * |
+ * @author Misha Dmitriev |
+ * 5 April 2004 |
+ */ |
+@SuppressWarnings("serial") |
+public class ClassInfo implements Serializable { |
+ |
+ public static final int VER_OLD = 0; // Old version |
+ public static final int VER_NEW = 1; // New version |
+ public static final int NO_VERSIONS = 2; // Non-project class, no change tracking |
+ private transient PCDManager pcdm; |
+ transient int verCode; // Version code for this ClassInfo - one of the above. |
+ String name = null; |
+ transient String packageName; // Package name; restored when database is reloaded |
+ int javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST; // Can have values from Utils.JAVAC_TARGET_RELEASE_xxx |
+ String cpoolRefsToClasses[]; // Directly referenced class names trimmed of array and 'L' prefixes and ';' suffixes |
+ boolean isRefClassArray[]; // Indicates if a directly referenced class is actually an array class |
+ // In all signatures we replace the 'L' and ';' symbols that enclose non-primitive type names with '@' and '#' respectively, |
+ // so that class names inside signatures can be located fast and unambiguously. |
+ String cpoolRefsToFieldClasses[]; // Defining classes of referenced fields, trimmed of enclosing 'L' and ';' symbols |
+ String cpoolRefsToFieldNames[]; // Names of referenced fields |
+ String cpoolRefsToFieldSignatures[]; // Signatures of referenced fields |
+ String cpoolRefsToMethodClasses[]; // Defining classes of referenced methods, trimmed of enclosing 'L' and ';' symbols |
+ String cpoolRefsToMethodNames[]; // Names of referenced methods |
+ String cpoolRefsToMethodSignatures[]; // Signatures of referenced methods |
+ char accessFlags; // isInterface flag included |
+ boolean isNonMemberNestedClass = false; // True if this is a non-member nested class |
+ String superName; |
+ String interfaces[]; |
+ String fieldNames[]; |
+ String fieldSignatures[]; |
+ char fieldAccessFlags[]; |
+ Object primitiveConstantInitValues[]; |
+ String methodNames[]; |
+ String methodSignatures[]; |
+ char methodAccessFlags[]; |
+ String checkedExceptions[][]; |
+ transient ClassInfo directSubclasses[]; // Direct subclasses. Created lazily and not preserved on disk. |
+ transient String directlyEnclosingClass; // Directly enclosing class name; restored when database is reloaded |
+ transient String topLevelEnclosingClass; // Top-level enclosing class name; restored when database is reloaded |
+ String nestedClasses[]; // Names of all nested classes. Don't make transient - it's used to check |
+ // if nested classes for this class were added/deleted in new version |
+ transient char nestedClassAccessFlags[]; // No need to store this information permanently |
+ transient boolean nestedClassNonMember[]; // Ditto |
+ |
+ /** Creates new ClassInfo out of a class file. The last parameter is needed only to produce sensible error reports.*/ |
+ public ClassInfo(byte[] classFileBytes, int verCode, PCDManager pcdm, String classFileFullPath) { |
+ this.pcdm = pcdm; |
+ this.verCode = verCode; |
+ pcdm.getClassFileReader().readClassFile(classFileBytes, this, classFileFullPath, true); |
+ packageName = Utils.getPackageName(name); |
+ directlyEnclosingClass = |
+ Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease); |
+ topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name); |
+ } |
+ |
+ /** |
+ * Create a "lightweight" ClassInfo, that contains just the class name, super name, interfaces, flags and verCode. |
+ * Used for non-project classes, that don't change themselves, for which we are only interested in type hierarchy structure. |
+ */ |
+ public ClassInfo(byte[] classFileBytes, PCDManager pcdm, String classFileFullPath) { |
+ this.pcdm = pcdm; |
+ this.verCode = NO_VERSIONS; |
+ pcdm.getClassFileReader().readClassFile(classFileBytes, this, classFileFullPath, false); |
+ packageName = Utils.getPackageName(name); |
+ directlyEnclosingClass = |
+ Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease); |
+ topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name); |
+ } |
+ |
+ /** Even more lightweight variant - created for a deleted non-project class, to enable minimum possible checks. */ |
+ public ClassInfo(String name, PCDManager pcdm) { |
+ this.pcdm = pcdm; |
+ this.verCode = NO_VERSIONS; |
+ this.name = name; |
+ packageName = Utils.getPackageName(name); |
+ directlyEnclosingClass = Utils.getDirectlyEnclosingClass(name, 0); |
+ topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name); |
+ } |
+ |
+ public ClassInfo() { |
+ } |
+ |
+ /** Initialize transient data that can be initialized immediately after this ClassInfo is read from the project database */ |
+ public void initializeImmediateTransientFields() { |
+ verCode = VER_OLD; |
+ |
+ packageName = Utils.getPackageName(name); |
+ |
+ directlyEnclosingClass = |
+ Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease); |
+ topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name); |
+ } |
+ |
+ /** |
+ * Called to restore the pointer to the current PCDManager after this ClassInfo is brought back |
+ * from the store. |
+ */ |
+ public void restorePCDM(PCDManager pcdm) { |
+ this.pcdm = pcdm; |
+ } |
+ |
+ public boolean isInterface() { |
+ return Modifier.isInterface(accessFlags); |
+ } |
+ |
+ public boolean isAbstract() { |
+ return Modifier.isAbstract(accessFlags); |
+ } |
+ |
+ public boolean isPublic() { |
+ return Modifier.isPublic(accessFlags); |
+ } |
+ |
+ /** |
+ * Returns the names of the superclasses of the given class (transitively), that belong |
+ * to the same project, plus those of the superclasses that can be found on the class path |
+ * supplied to jmake, and on the boot class path. |
+ */ |
+ public List<String> getAllSuperclassNames() { |
+ List<String> res = new ArrayList<String>(); |
+ String superName = this.superName; |
+ while (superName != null && !"java/lang/Object".equals(superName)) { |
+ res.add(superName); |
+ ClassInfo classInfo = pcdm.getClassInfoForName(verCode, superName); |
+ if (classInfo == null) { // Class not in project (or deleted?). Try to find it and further superclasses in non-project classes |
+ ClassPath.getSuperclasses(superName, res, pcdm); |
+ break; |
+ } |
+ superName = classInfo.superName; |
+ } |
+ return res; |
+ } |
+ |
+ /** |
+ * Returns the set of names of the interfaces transitively implemented by the given |
+ * class, that belong to the same project. |
+ */ |
+ public Set<String> getAllImplementedIntfNames() { |
+ Set<String> res = new LinkedHashSet<String>(); |
+ addImplementedInterfaceNames(false, res); |
+ return res; |
+ } |
+ |
+ /** Add to the given set the names of direct/all interfaces implemented by the given class. */ |
+ private void addImplementedInterfaceNames(boolean directOnly, |
+ Set<String> intfSet) { |
+ if (interfaces != null) { |
+ for (int i = 0; i < interfaces.length; i++) { |
+ String superIntfName = interfaces[i]; |
+ intfSet.add(superIntfName); |
+ if (directOnly) { |
+ continue; |
+ } |
+ ClassInfo superIntfInfo = |
+ pcdm.getClassInfoForName(verCode, superIntfName); |
+ if (superIntfInfo == null) { // Class not in project |
+ ClassPath.addAllImplementedInterfaceNames(superIntfName, intfSet, pcdm); |
+ } else { |
+ superIntfInfo.addImplementedInterfaceNames(false, intfSet); |
+ } |
+ } |
+ } |
+ |
+ if (directOnly || superName == null || |
+ "java/lang/Object".equals(superName)) { |
+ return; |
+ } |
+ ClassInfo superInfo = pcdm.getClassInfoForName(verCode, superName); |
+ if (superInfo == null) { // Class not in project |
+ ClassPath.addAllImplementedInterfaceNames(superName, intfSet, pcdm); |
+ } else { |
+ superInfo.addImplementedInterfaceNames(false, intfSet); |
+ } |
+ } |
+ |
+ /** Returns the array of all direct subclasses of this class (array of zero length if there are none). */ |
+ public ClassInfo[] getDirectSubclasses() { |
+ if (directSubclasses != null) { |
+ return directSubclasses; |
+ } |
+ |
+ List<ClassInfo> listRes = new ArrayList<ClassInfo>(); |
+ |
+ for (PCDEntry entry : pcdm.entries()) { |
+ ClassInfo classInfo = pcdm.getClassInfoForPCDEntry(verCode, entry); |
+ if (classInfo == null) { |
+ continue; // New or deleted class, depending on verCode |
+ } |
+ if (classInfo.superName.equals(name)) { |
+ listRes.add(classInfo); |
+ } |
+ } |
+ |
+ directSubclasses = listRes.toArray(new ClassInfo[listRes.size()]); |
+ return directSubclasses; |
+ } |
+ |
+ /** Check if the initial values for the given primitive constatnts in two classes are the same. */ |
+ public static boolean constFieldInitValuesEqual(ClassInfo oldClassInfo, int oldFieldNo, |
+ ClassInfo newClassInfo, int newFieldNo) { |
+ Object oldInitValue = oldClassInfo.primitiveConstantInitValues == null ? null |
+ : oldClassInfo.primitiveConstantInitValues[oldFieldNo]; |
+ Object newInitValue = newClassInfo.primitiveConstantInitValues == null ? null |
+ : newClassInfo.primitiveConstantInitValues[newFieldNo]; |
+ if (oldInitValue == newInitValue) { |
+ return true; |
+ } |
+ if (oldInitValue == null || newInitValue == null) { |
+ return false; |
+ } |
+ |
+ if (oldInitValue instanceof Integer) { |
+ if (((Integer) oldInitValue).intValue() == ((Integer) newInitValue).intValue()) { |
+ return true; |
+ } else { |
+ return false; |
+ } |
+ } else if (oldInitValue instanceof String) { |
+ if ( ((String) oldInitValue).equals((String) newInitValue) ) { |
+ return true; |
+ } else { |
+ return false; |
+ } |
+ } else if (oldInitValue instanceof Long) { |
+ if (((Long) oldInitValue).longValue() == ((Long) newInitValue).longValue()) { |
+ return true; |
+ } else { |
+ return false; |
+ } |
+ } else if (oldInitValue instanceof Float) { |
+ if (((Float) oldInitValue).floatValue() == ((Float) newInitValue).floatValue()) { |
+ return true; |
+ } else { |
+ return false; |
+ } |
+ } else if (oldInitValue instanceof Double) { |
+ if (((Double) oldInitValue).doubleValue() == ((Double) newInitValue).doubleValue()) { |
+ return true; |
+ } else { |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+ } |
+ |
+ public boolean implementsInterfaceDirectly(String intfName) { |
+ if (interfaces == null) { |
+ return false; |
+ } |
+ for (int i = 0; i < interfaces.length; i++) { |
+ if (intfName.equals(interfaces[i])) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** Check if this class implements interface I or any subinterface of I directly */ |
+ public boolean implementsIntfOrSubintfDirectly(String intfName) { |
+ if (interfaces == null) { |
+ return false; |
+ } |
+ for (int i = 0; i < interfaces.length; i++) { |
+ if (intfName.equals(interfaces[i])) { |
+ return true; |
+ } |
+ // An interface can have multiple superinterfaces, all of which are listed in its "interfaces" array |
+ // (although in the .java source it "extends" them all). |
+ ClassInfo superIntfInfo = |
+ pcdm.getClassInfoForName(verCode, interfaces[i]); |
+ if (superIntfInfo == null) { |
+ continue; // Class not in project |
+ } |
+ if (superIntfInfo.implementsIntfOrSubintfDirectly(intfName)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Class C implements interface I indirectly, if C or some superclass of C directly implements I |
+ * or some subinterface of I. |
+ */ |
+ public boolean implementsInterfaceDirectlyOrIndirectly(String intfName) { |
+ if (interfaces == null) { |
+ return false; |
+ } |
+ |
+ if (implementsIntfOrSubintfDirectly(intfName)) { |
+ return true; |
+ } |
+ |
+ if (superName != null) { |
+ ClassInfo superInfo = pcdm.getClassInfoForName(verCode, superName); |
+ if (superInfo == null) { |
+ return false; // Class not in project |
+ } |
+ return superInfo.implementsInterfaceDirectlyOrIndirectly(intfName); |
+ } |
+ |
+ return false; |
+ } |
+ |
+ /** |
+ * Returns true if this class declares a field with the same name and type as |
+ * the field number fieldNo in class classInfo. |
+ */ |
+ public boolean declaresField(ClassInfo classInfo, int fieldNo) { |
+ if (fieldNames == null) { |
+ return false; |
+ } |
+ String fieldName = classInfo.fieldNames[fieldNo]; |
+ String fieldSignature = classInfo.fieldSignatures[fieldNo]; |
+ |
+ for (int i = 0; i < fieldNames.length; i++) { |
+ if (fieldName.equals(fieldNames[i]) && |
+ fieldSignature.equals(fieldSignatures[i])) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** Returns true if this class declares a field with the given name, signature and access */ |
+ public boolean declaresField(String name, String signature, boolean isStatic) { |
+ if (fieldNames == null) { |
+ return false; |
+ } |
+ signature = ("@" + signature + "#").intern(); |
+ for (int i = 0; i < fieldNames.length; i++) { |
+ if (name.equals(fieldNames[i]) && |
+ signature.equals(fieldSignatures[i]) && |
+ Modifier.isStatic(fieldAccessFlags[i]) == isStatic) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Returns true if this class declares a method with the same name and signature as |
+ * the method number methodNo in class classInfo. |
+ */ |
+ public boolean declaresMethod(ClassInfo classInfo, int methodNo) { |
+ if (methodNames == null) { |
+ return false; |
+ } |
+ String methodName = classInfo.methodNames[methodNo]; |
+ String methodSignature = classInfo.methodSignatures[methodNo]; |
+ |
+ for (int i = 0; i < methodNames.length; i++) { |
+ if (methodName.equals(methodNames[i]) && |
+ methodSignature.equals(methodSignatures[i])) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * If this class declares a method with the same name and signature as the given method, |
+ * return its position. Otherwise, return -1. |
+ */ |
+ public int getDeclaredMethodPos(ClassInfo classInfo, int methodNo) { |
+ if (methodNames == null) { |
+ return -1; |
+ } |
+ String methodName = classInfo.methodNames[methodNo]; |
+ String methodSignature = classInfo.methodSignatures[methodNo]; |
+ |
+ for (int i = 0; i < methodNames.length; i++) { |
+ if (methodName.equals(methodNames[i]) && |
+ methodSignature.equals(methodSignatures[i])) { |
+ return i; |
+ } |
+ } |
+ return -1; |
+ } |
+ |
+ /** |
+ * Returns a nonnegative number (position in the method array) if this class declares a method with the |
+ * name methodName, and -1 otherwise. |
+ */ |
+ public int declaresSameNameMethod(String methodName) { |
+ if (methodNames == null) { |
+ return -1; |
+ } |
+ for (int j = 0; j < methodNames.length; j++) { |
+ if (methodName.equals(methodNames[j])) { |
+ return j; |
+ } |
+ } |
+ return -1; |
+ } |
+ |
+ /** |
+ * Check if this class references the given class in different ways, depending on thorDegree parameter. |
+ * thorDegree = 0: the given class (but not its array class) directly from the constantpool. |
+ * |
+ * thorDegree = 1: the given class or its array class directly from the constantpool, as a |
+ * type of a data field, as a type in a method signature or a thrown exception, as a directly |
+ * implemented interface or a direct superclass |
+ * |
+ * thorDegree = 2: the given class or its array class directly or indirectly from the |
+ * constantpool, as a type of a data field, as a type in a method signature or a thrown exception, |
+ * as a directly/indirectly implemented interface or a direct/indirect superclass. |
+ * |
+ * isRefTypeInterface indicates whether className is an interface. |
+ */ |
+ public boolean referencesClass(String className, boolean isRefTypeInterface, int thorDegree) { |
+ int i; |
+ |
+ if (thorDegree == 0) { |
+ if (cpoolRefsToClasses == null) { |
+ return false; |
+ } |
+ for (i = 0; i < cpoolRefsToClasses.length; i++) { |
+ if (!isRefClassArray[i] && |
+ className.equals(cpoolRefsToClasses[i])) { |
+ return true; |
+ } |
+ } |
+ } else { |
+ if (isSubclassOf(className, (thorDegree == 1))) { |
+ return true; |
+ } |
+ if (isRefTypeInterface) { |
+ if (thorDegree == 1) { |
+ if (implementsInterfaceDirectly(className)) { |
+ return true; |
+ } |
+ } else { |
+ // Check for indirectly implemented interfaces |
+ if (implementsInterfaceDirectlyOrIndirectly(className)) { |
+ return true; |
+ } |
+ } |
+ } |
+ |
+ if (cpoolRefsToClasses != null) { |
+ for (i = 0; i < cpoolRefsToClasses.length; i++) { |
+ if (className.equals(cpoolRefsToClasses[i])) { |
+ return true; |
+ } |
+ } |
+ } |
+ if (thorDegree == 2) { |
+ // Check for indirect references from the constantpool |
+ if (cpoolRefsToFieldSignatures != null) { |
+ for (i = 0; i < cpoolRefsToFieldSignatures.length; i++) { |
+ if (signatureIncludesClassName(cpoolRefsToFieldSignatures[i], className)) { |
+ return true; |
+ } |
+ } |
+ } |
+ if (cpoolRefsToMethodNames != null) { |
+ for (i = 0; i < cpoolRefsToMethodSignatures.length; i++) { |
+ if (signatureIncludesClassName(cpoolRefsToMethodSignatures[i], className)) { |
+ return true; |
+ } |
+ } |
+ } |
+ } |
+ |
+ if (fieldSignatures != null) { |
+ for (i = 0; i < fieldSignatures.length; i++) { |
+ if (signatureIncludesClassName(fieldSignatures[i], className)) { |
+ return true; |
+ } |
+ } |
+ } |
+ if (methodSignatures != null) { |
+ for (i = 0; i < methodSignatures.length; i++) { |
+ if (signatureIncludesClassName(methodSignatures[i], className)) { |
+ return true; |
+ } |
+ } |
+ } |
+ if (checkedExceptions != null) { |
+ for (i = 0; i < checkedExceptions.length; i++) { |
+ if (checkedExceptions[i] != null) { |
+ String excArray[] = checkedExceptions[i]; |
+ for (int j = 0; j < excArray.length; j++) { |
+ if (className.equals(excArray[j])) { |
+ return true; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ return false; |
+ } |
+ |
+ private static boolean signatureIncludesClassName(String signature, String className) { |
+ int stIndex = signature.indexOf(className); |
+ if (stIndex == -1) { |
+ return false; |
+ } |
+ return ((stIndex != 0 && signature.charAt(stIndex - 1) == '@' && signature.charAt(stIndex + className.length()) == '#') || |
+ (stIndex == 0 && signature.length() == className.length())); |
+ } |
+ |
+ public boolean isSubclassOf(String className, boolean directOnly) { |
+ if (className.equals(superName)) { |
+ return true; |
+ } |
+ if (directOnly) { |
+ return false; |
+ } |
+ String superName = this.superName; |
+ while (superName != null) { |
+ if (className.equals(superName)) { |
+ return true; |
+ } |
+ ClassInfo classInfo = pcdm.getClassInfoForName(verCode, superName); |
+ if (classInfo == null) { |
+ break; // Class not in project |
+ } |
+ superName = classInfo.superName; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check if this class references field number fieldNo of class fieldDefClassInfo. Let us call |
+ * this field C.f. Actual reference contained in the constant pool may be not to C.f itself, |
+ * but to Csub.f, where Csub is some subclass of C such that neither Csub nor any other class |
+ * located between C and Csub in the class hierarchy redeclares f. We look up both "real" |
+ * references C.f and "fake" references such as Csub.f. |
+ */ |
+ public boolean referencesField(ClassInfo fieldDefClassInfo, int fieldNo) { |
+ if (cpoolRefsToFieldNames == null) { |
+ return false; |
+ } |
+ String fieldDefClassName = fieldDefClassInfo.name; |
+ String fieldName = fieldDefClassInfo.fieldNames[fieldNo]; |
+ String fieldSig = fieldDefClassInfo.fieldSignatures[fieldNo]; |
+ for (int i = 0; i < cpoolRefsToFieldNames.length; i++) { |
+ if (fieldName.equals(cpoolRefsToFieldNames[i]) && |
+ fieldSig.equals(cpoolRefsToFieldSignatures[i]) ) { |
+ if (fieldDefClassName.equals(cpoolRefsToFieldClasses[i]) ) { |
+ return true; // "real" reference |
+ } else { // Check if this is a "fake" reference that resolves to the above "real" reference |
+ ClassInfo classInThisCpool = |
+ pcdm.getClassInfoForName(verCode, cpoolRefsToFieldClasses[i]); |
+ if (classInThisCpool == null) { |
+ continue; // Class not in project |
+ } |
+ if (!classInThisCpool.isSubclassOf(fieldDefClassInfo.name, false)) { |
+ continue; |
+ } |
+ |
+ // Ok, now check that this field is not actually redeclared in fieldDefClassInfo or |
+ // somewhere in between it and classInThisCpool |
+ boolean redeclared = false; |
+ ClassInfo curClass = classInThisCpool; |
+ do { |
+ if (curClass.declaresField(fieldDefClassInfo, fieldNo)) { |
+ redeclared = true; |
+ break; |
+ } |
+ String superName = curClass.superName; |
+ curClass = pcdm.getClassInfoForName(verCode, superName); |
+ if (curClass == null) { |
+ break; |
+ } |
+ } while (curClass != fieldDefClassInfo); |
+ if (!redeclared) { |
+ return true; |
+ } |
+ } |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check if this class references method number methodNo of class methodDefClassInfo. Let us |
+ * call this method C.m. Actual reference contained in the constant pool may be not to C.m |
+ * itself, but to Csub.m, where Csub is some subclass of C such that neither Csub nor any |
+ * other class located between C and Csub in the class hierarchy redeclares m. We look up |
+ * both "real" references C.m and "fake" references such as Csub.m. |
+ */ |
+ public boolean referencesMethod(ClassInfo methodDefClassInfo, int methodNo) { |
+ if (cpoolRefsToMethodNames == null) { |
+ return false; |
+ } |
+ String methodDefClassName = methodDefClassInfo.name; |
+ String methodName = methodDefClassInfo.methodNames[methodNo]; |
+ String methodSig = methodDefClassInfo.methodSignatures[methodNo]; |
+ for (int i = 0; i < cpoolRefsToMethodNames.length; i++) { |
+ if (methodName.equals(cpoolRefsToMethodNames[i]) && |
+ methodSig.equals(cpoolRefsToMethodSignatures[i])) { |
+ if (methodDefClassName.equals(cpoolRefsToMethodClasses[i])) { |
+ return true; // "real" reference |
+ } else { // Check if this is a "fake" reference that resolves to the above "real" reference |
+ // Be careful - class in the cpool may be not a project class (e.g. a core class). |
+ ClassInfo classInThisCpool = |
+ pcdm.getClassInfoForName(verCode, cpoolRefsToMethodClasses[i]); |
+ if (classInThisCpool == null) { |
+ continue; // Class not in project |
+ } |
+ if (classInThisCpool.isSubclassOf(methodDefClassInfo.name, false)) { |
+ // Ok, now check that this method is not actually redeclared in classInThisCpool (which is |
+ // lower in the hierarchy) or somewhere in between it and classInThisCpool |
+ boolean redeclared = false; |
+ ClassInfo curClass = classInThisCpool; |
+ do { |
+ if (curClass.declaresMethod(methodDefClassInfo, methodNo)) { |
+ redeclared = true; |
+ break; |
+ } |
+ String superName = curClass.superName; |
+ curClass = |
+ pcdm.getClassInfoForName(verCode, superName); |
+ if (curClass == null) { |
+ break; |
+ } |
+ } while (curClass != methodDefClassInfo); |
+ if (!redeclared) { |
+ return true; |
+ } |
+ } else if (methodDefClassInfo.isInterface() && classInThisCpool.implementsIntfOrSubintfDirectly(methodDefClassName)) { |
+ return true; |
+ } |
+ } |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * If this class has a method that throws the given exception, return its index. Otherwise return -1. |
+ * The search starts from method with index startMethodIdx. |
+ */ |
+ public int hasMethodThrowingException(ClassInfo excClassInfo, int startMethodIdx) { |
+ if (checkedExceptions == null) { |
+ return -1; |
+ } |
+ if (startMethodIdx >= checkedExceptions.length) { |
+ return -1; |
+ } |
+ String excName = excClassInfo.name; |
+ for (int i = startMethodIdx; i < checkedExceptions.length; i++) { |
+ if (checkedExceptions[i] == null) { |
+ continue; |
+ } |
+ String[] exc = checkedExceptions[i]; |
+ for (int j = 0; j < exc.length; j++) { |
+ if (exc[j].equals(excName)) { |
+ return i; |
+ } |
+ } |
+ } |
+ return -1; |
+ } |
+ |
+ public static abstract class MethodHandler { |
+ |
+ abstract void handleMethod(ClassInfo ci, int methodIdx); |
+ } |
+ |
+ /** |
+ * Check this class and all its superclasses (if includeSuperclasses == true) and superinterfaces (if includeInterfaces == true) |
+ * for a method with the given name. If such a method is found, call h.handleMethod(classInfo, methodIdx). |
+ */ |
+ public void findExistingSameNameMethods(String methodName, boolean includeSuperclasses, boolean includeInterfaces, MethodHandler h) { |
+ String className = name; |
+ ClassInfo classInfo; |
+ while (className != null) { |
+ classInfo = pcdm.getClassInfoForName(verCode, className); |
+ if (classInfo == null) { |
+ break; // Class not in project |
+ } |
+ String mNames[] = classInfo.methodNames; |
+ int mNamesLen = mNames != null ? mNames.length : 0; |
+ for (int i = 0; i < mNamesLen; i++) { |
+ if (methodName.equals(mNames[i])) { |
+ h.handleMethod(classInfo, i); |
+ } |
+ } |
+ if (includeInterfaces && classInfo.interfaces != null) { |
+ String intfNames[] = classInfo.interfaces; |
+ for (int i = 0; i < intfNames.length; i++) { |
+ ClassInfo superIntfInfo = |
+ pcdm.getClassInfoForName(verCode, intfNames[i]); |
+ if (superIntfInfo == null) { |
+ continue; // Class not in project |
+ } |
+ superIntfInfo.findExistingSameNameMethods(methodName, true, includeInterfaces, h); |
+ } |
+ } |
+ if (includeSuperclasses) { |
+ className = classInfo.superName; |
+ } else { |
+ return; |
+ } |
+ } |
+ } |
+ |
+ public static boolean isPrimitiveFieldSig(String fieldSig) { |
+ return fieldSig.indexOf('@') == -1; |
+ } |
+ |
+ /** |
+ * Check if the given signature is of a class type, and that class does not belong to the project. |
+ * It used to be a check for just a core type name, but sometimes people use JDK sources as e.g. a test |
+ * case - so better perform a universal (and entirely correct, unlike just a core type name) test here. |
+ */ |
+ public boolean isNonProjectClassTypeFieldSig(String fieldSig) { |
+ int stPos = fieldSig.indexOf('@'); |
+ if (stPos == -1) { |
+ return false; |
+ } |
+ int endPos = fieldSig.indexOf('#'); |
+ String className = fieldSig.substring(stPos + 1, endPos); |
+ return (!pcdm.isProjectClass(verCode, className)); |
+ } |
+ |
+ /** For debugging. */ |
+ public String toString() { |
+ return name + (verCode == VER_OLD ? " OLD" : " NEW"); |
+ } |
+} |