| 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");
|
| + }
|
| +}
|
|
|