X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Feclipse%2Fjdt%2Finternal%2Fcompiler%2Fclassfmt%2FClassFileReader.java;fp=src%2Forg%2Feclipse%2Fjdt%2Finternal%2Fcompiler%2Fclassfmt%2FClassFileReader.java;h=6899fa0631d8a4f491fdf55be54c5acc2aa22652;hb=040fa5af2cd00017cf3575950cdaade34a6d7f6c;hp=0000000000000000000000000000000000000000;hpb=a580fb8376d315d05e4d6bfdff9ff1101a151cd6;p=org.ibex.tool.git
diff --git a/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java b/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java
new file mode 100644
index 0000000..6899fa0
--- /dev/null
+++ b/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java
@@ -0,0 +1,824 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.classfmt;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
+import org.eclipse.jdt.internal.compiler.env.*;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+public class ClassFileReader extends ClassFileStruct implements AttributeNamesConstants, IBinaryType {
+ private int constantPoolCount;
+ private int[] constantPoolOffsets;
+ private long version;
+ private int accessFlags;
+ private char[] className;
+ private char[] superclassName;
+ private int interfacesCount;
+ private char[][] interfaceNames;
+ private int fieldsCount;
+ private FieldInfo[] fields;
+ private int methodsCount;
+ private MethodInfo[] methods;
+ private InnerClassInfo[] innerInfos;
+ private char[] sourceFileName;
+ // initialized in case the .class file is a nested type
+ private InnerClassInfo innerInfo;
+ private char[] classFileName;
+ private int classNameIndex;
+ private int innerInfoIndex;
+/**
+ * @param classFileBytes byte[]
+ * Actual bytes of a .class file
+ *
+ * @param fileName char[]
+ * Actual name of the file that contains the bytes, can be null
+ *
+ * @param fullyInitialize boolean
+ * Flag to fully initialize the new object
+ * @exception ClassFormatException
+ */
+public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
+ // This method looks ugly but is actually quite simple, the constantPool is constructed
+ // in 3 passes. All non-primitive constant pool members that usually refer to other members
+ // by index are tweaked to have their value in inst vars, this minor cost at read-time makes
+ // all subsequent uses of the constant pool element faster.
+ super(classFileBytes, 0);
+ this.classFileName = fileName;
+ int readOffset = 10;
+ try {
+ this.version = ((long)this.u2At(6) << 16) + this.u2At(4); // major<<16 + minor
+ constantPoolCount = this.u2At(8);
+ // Pass #1 - Fill in all primitive constants
+ this.constantPoolOffsets = new int[constantPoolCount];
+ for (int i = 1; i < constantPoolCount; i++) {
+ int tag = this.u1At(readOffset);
+ switch (tag) {
+ case Utf8Tag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += u2At(readOffset + 1);
+ readOffset += ConstantUtf8FixedSize;
+ break;
+ case IntegerTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantIntegerFixedSize;
+ break;
+ case FloatTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantFloatFixedSize;
+ break;
+ case LongTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantLongFixedSize;
+ i++;
+ break;
+ case DoubleTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantDoubleFixedSize;
+ i++;
+ break;
+ case ClassTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantClassFixedSize;
+ break;
+ case StringTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantStringFixedSize;
+ break;
+ case FieldRefTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantFieldRefFixedSize;
+ break;
+ case MethodRefTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantMethodRefFixedSize;
+ break;
+ case InterfaceMethodRefTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantInterfaceMethodRefFixedSize;
+ break;
+ case NameAndTypeTag :
+ this.constantPoolOffsets[i] = readOffset;
+ readOffset += ConstantNameAndTypeFixedSize;
+ }
+ }
+ // Read and validate access flags
+ this.accessFlags = u2At(readOffset);
+ readOffset += 2;
+
+ // Read the classname, use exception handlers to catch bad format
+ this.classNameIndex = u2At(readOffset);
+ this.className = getConstantClassNameAt(this.classNameIndex);
+ readOffset += 2;
+
+ // Read the superclass name, can be null for java.lang.Object
+ int superclassNameIndex = u2At(readOffset);
+ readOffset += 2;
+ // if superclassNameIndex is equals to 0 there is no need to set a value for the
+ // field this.superclassName. null is fine.
+ if (superclassNameIndex != 0) {
+ this.superclassName = getConstantClassNameAt(superclassNameIndex);
+ }
+
+ // Read the interfaces, use exception handlers to catch bad format
+ this.interfacesCount = u2At(readOffset);
+ readOffset += 2;
+ if (this.interfacesCount != 0) {
+ this.interfaceNames = new char[this.interfacesCount][];
+ for (int i = 0; i < this.interfacesCount; i++) {
+ this.interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
+ readOffset += 2;
+ }
+ }
+ // Read the this.fields, use exception handlers to catch bad format
+ this.fieldsCount = u2At(readOffset);
+ readOffset += 2;
+ if (this.fieldsCount != 0) {
+ FieldInfo field;
+ this.fields = new FieldInfo[this.fieldsCount];
+ for (int i = 0; i < this.fieldsCount; i++) {
+ field = new FieldInfo(reference, this.constantPoolOffsets, readOffset);
+ this.fields[i] = field;
+ readOffset += field.sizeInBytes();
+ }
+ }
+ // Read the this.methods
+ this.methodsCount = u2At(readOffset);
+ readOffset += 2;
+ if (this.methodsCount != 0) {
+ this.methods = new MethodInfo[this.methodsCount];
+ MethodInfo method;
+ for (int i = 0; i < this.methodsCount; i++) {
+ method = new MethodInfo(reference, this.constantPoolOffsets, readOffset);
+ this.methods[i] = method;
+ readOffset += method.sizeInBytes();
+ }
+ }
+
+ // Read the attributes
+ int attributesCount = u2At(readOffset);
+ readOffset += 2;
+
+ for (int i = 0; i < attributesCount; i++) {
+ int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
+ char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
+ if (CharOperation.equals(attributeName, DeprecatedName)) {
+ this.accessFlags |= AccDeprecated;
+ } else {
+ if (CharOperation.equals(attributeName, InnerClassName)) {
+ int innerOffset = readOffset + 6;
+ int number_of_classes = u2At(innerOffset);
+ if (number_of_classes != 0) {
+ innerOffset+= 2;
+ this.innerInfos = new InnerClassInfo[number_of_classes];
+ for (int j = 0; j < number_of_classes; j++) {
+ this.innerInfos[j] =
+ new InnerClassInfo(reference, this.constantPoolOffsets, innerOffset);
+ if (this.classNameIndex == this.innerInfos[j].innerClassNameIndex) {
+ this.innerInfo = this.innerInfos[j];
+ this.innerInfoIndex = j;
+ }
+ innerOffset += 8;
+ }
+ }
+ } else {
+ if (CharOperation.equals(attributeName, SourceName)) {
+ utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
+ this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
+ } else {
+ if (CharOperation.equals(attributeName, SyntheticName)) {
+ this.accessFlags |= AccSynthetic;
+ }
+ }
+ }
+ }
+ readOffset += (6 + u4At(readOffset + 2));
+ }
+ if (fullyInitialize) {
+ this.initialize();
+ }
+ } catch(ClassFormatException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ClassFormatException(
+ ClassFormatException.ErrTruncatedInput,
+ readOffset);
+ }
+}
+
+/**
+ * @param classFileBytes Actual bytes of a .class file
+ * @param fileName Actual name of the file that contains the bytes, can be null
+ *
+ * @exception ClassFormatException
+ */
+public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
+ this(classFileBytes, fileName, false);
+}
+
+/**
+ * Answer the receiver's access flags. The value of the access_flags
+ * item is a mask of modifiers used with class and interface declarations.
+ * @return int
+ */
+public int accessFlags() {
+ return this.accessFlags;
+}
+/**
+ * Answer the char array that corresponds to the class name of the constant class.
+ * constantPoolIndex is the index in the constant pool that is a constant class entry.
+ *
+ * @param constantPoolIndex int
+ * @return char[]
+ */
+private char[] getConstantClassNameAt(int constantPoolIndex) {
+ int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
+ return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
+}
+/**
+ * Answer the int array that corresponds to all the offsets of each entry in the constant pool
+ *
+ * @return int[]
+ */
+public int[] getConstantPoolOffsets() {
+ return this.constantPoolOffsets;
+}
+/*
+ * Answer the resolved compoundName of the enclosing type
+ * or null if the receiver is a top level type.
+ */
+public char[] getEnclosingTypeName() {
+ if (this.innerInfo != null && !this.isAnonymous()) {
+ return this.innerInfo.getEnclosingTypeName();
+ }
+ return null;
+}
+/**
+ * Answer the receiver's this.fields or null if the array is empty.
+ * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
+ */
+public IBinaryField[] getFields() {
+ return this.fields;
+}
+/**
+ * Answer the file name which defines the type.
+ * The format is unspecified.
+ */
+public char[] getFileName() {
+ return this.classFileName;
+}
+/**
+ * Answer the source name if the receiver is a inner type. Return null if it is an anonymous class or if the receiver is a top-level class.
+ * e.g.
+ * public class A {
+ * public class B {
+ * }
+ * public void foo() {
+ * class C {}
+ * }
+ * public Runnable bar() {
+ * return new Runnable() {
+ * public void run() {}
+ * };
+ * }
+ * }
+ * It returns {'B'} for the member A$B
+ * It returns null for A
+ * It returns {'C'} for the local class A$1$C
+ * It returns null for the anonymous A$1
+ * @return char[]
+ */
+public char[] getInnerSourceName() {
+ if (this.innerInfo != null)
+ return this.innerInfo.getSourceName();
+ return null;
+}
+/**
+ * Answer the resolved names of the receiver's interfaces in the
+ * class file format as specified in section 4.2 of the Java 2 VM spec
+ * or null if the array is empty.
+ *
+ * For example, java.lang.String is java/lang/String.
+ * @return char[][]
+ */
+public char[][] getInterfaceNames() {
+ return this.interfaceNames;
+}
+/**
+ * Answer the receiver's nested types or null if the array is empty.
+ *
+ * This nested type info is extracted from the inner class attributes.
+ * Ask the name environment to find a member type using its compound name
+ * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
+ */
+public IBinaryNestedType[] getMemberTypes() {
+ // we might have some member types of the current type
+ if (this.innerInfos == null) return null;
+
+ int length = this.innerInfos.length;
+ int startingIndex = this.innerInfo != null ? this.innerInfoIndex + 1 : 0;
+ if (length != startingIndex) {
+ IBinaryNestedType[] memberTypes =
+ new IBinaryNestedType[length - this.innerInfoIndex];
+ int memberTypeIndex = 0;
+ for (int i = startingIndex; i < length; i++) {
+ InnerClassInfo currentInnerInfo = this.innerInfos[i];
+ int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
+ int innerNameIndex = currentInnerInfo.innerNameIndex;
+ /*
+ * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
+ * attribute entry is a member class, but due to the bug:
+ * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
+ * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
+ *
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=49879
+ * From JavaMail 1.2, the class javax.mail.Folder contains an anonymous class in the
+ * terminateQueue() method for which the inner attribute is boggus.
+ * outerClassNameIdx is not 0, innerNameIndex is not 0, but the sourceName length is 0.
+ * So I added this extra check to filter out this anonymous class from the
+ * member types.
+ */
+ if (outerClassNameIdx != 0
+ && innerNameIndex != 0
+ && outerClassNameIdx == this.classNameIndex
+ && currentInnerInfo.getSourceName().length != 0) {
+ memberTypes[memberTypeIndex++] = currentInnerInfo;
+ }
+ }
+ if (memberTypeIndex == 0) return null;
+ if (memberTypeIndex != memberTypes.length) {
+ // we need to resize the memberTypes array. Some local or anonymous classes
+ // are present in the current class.
+ System.arraycopy(
+ memberTypes,
+ 0,
+ (memberTypes = new IBinaryNestedType[memberTypeIndex]),
+ 0,
+ memberTypeIndex);
+ }
+ return memberTypes;
+ }
+ return null;
+}
+/**
+ * Answer the receiver's this.methods or null if the array is empty.
+ * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
+ */
+public IBinaryMethod[] getMethods() {
+ return this.methods;
+}
+/**
+ * Answer an int whose bits are set according the access constants
+ * defined by the VM spec.
+ * Set the AccDeprecated and AccSynthetic bits if necessary
+ * @return int
+ */
+public int getModifiers() {
+ if (this.innerInfo != null) {
+ if ((this.accessFlags & AccDeprecated) != 0) {
+ return this.innerInfo.getModifiers() | AccDeprecated;
+ } else {
+ return this.innerInfo.getModifiers();
+ }
+ }
+ return this.accessFlags;
+}
+/**
+ * Answer the resolved name of the type in the
+ * class file format as specified in section 4.2 of the Java 2 VM spec.
+ *
+ * For example, java.lang.String is java/lang/String.
+ * @return char[]
+ */
+public char[] getName() {
+ return this.className;
+}
+/**
+ * Answer the resolved name of the receiver's superclass in the
+ * class file format as specified in section 4.2 of the Java 2 VM spec
+ * or null if it does not have one.
+ *
+ * For example, java.lang.String is java/lang/String.
+ * @return char[]
+ */
+public char[] getSuperclassName() {
+ return this.superclassName;
+}
+/**
+ * Answer the major/minor version defined in this class file according to the VM spec.
+ * as a long: (major<<16)+minor
+ * @return the major/minor version found
+ */
+public long getVersion() {
+ return this.version;
+}
+/**
+ * Answer true if the receiver is an anonymous type, false otherwise
+ *
+ * @return boolean
+ */
+public boolean isAnonymous() {
+ if (this.innerInfo == null) return false;
+ char[] sourceName = this.innerInfo.getSourceName();
+ return (sourceName == null || sourceName.length == 0);
+}
+/**
+ * Answer whether the receiver contains the resolved binary form
+ * or the unresolved source form of the type.
+ * @return boolean
+ */
+public boolean isBinaryType() {
+ return true;
+}
+/**
+ * Answer true if the receiver is a class. False otherwise.
+ * @return boolean
+ */
+public boolean isClass() {
+ return (getModifiers() & AccInterface) == 0;
+}
+/**
+ * Answer true if the receiver is an interface. False otherwise.
+ * @return boolean
+ */
+public boolean isInterface() {
+ return (getModifiers() & AccInterface) != 0;
+}
+/**
+ * Answer true if the receiver is a local type, false otherwise
+ *
+ * @return boolean
+ */
+public boolean isLocal() {
+ if (this.innerInfo == null) return false;
+ if (this.innerInfo.getEnclosingTypeName() != null) return false;
+ char[] sourceName = this.innerInfo.getSourceName();
+ return (sourceName != null && sourceName.length > 0);
+}
+/**
+ * Answer true if the receiver is a member type, false otherwise
+ *
+ * @return boolean
+ */
+public boolean isMember() {
+ if (this.innerInfo == null) return false;
+ if (this.innerInfo.getEnclosingTypeName() == null) return false;
+ char[] sourceName = this.innerInfo.getSourceName();
+ return (sourceName != null && sourceName.length > 0); // protection against ill-formed attributes (67600)
+}
+/**
+ * Answer true if the receiver is a nested type, false otherwise
+ *
+ * @return boolean
+ */
+public boolean isNestedType() {
+ return this.innerInfo != null;
+}
+public static ClassFileReader read(File file) throws ClassFormatException, IOException {
+ return read(file, false);
+}
+public static ClassFileReader read(File file, boolean fullyInitialize) throws ClassFormatException, IOException {
+ byte classFileBytes[] = Util.getFileByteContent(file);
+ ClassFileReader classFileReader = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
+ if (fullyInitialize) {
+ classFileReader.initialize();
+ }
+ return classFileReader;
+}
+public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
+ return read(fileName, false);
+}
+public static ClassFileReader read(String fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException {
+ return read(new File(fileName), fullyInitialize);
+}
+public static ClassFileReader read(
+ java.util.zip.ZipFile zip,
+ String filename)
+ throws ClassFormatException, java.io.IOException {
+ return read(zip, filename, false);
+}
+public static ClassFileReader read(
+ java.util.zip.ZipFile zip,
+ String filename,
+ boolean fullyInitialize)
+ throws ClassFormatException, java.io.IOException {
+ java.util.zip.ZipEntry ze = zip.getEntry(filename);
+ if (ze == null)
+ return null;
+ byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
+ ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
+ if (fullyInitialize) {
+ classFileReader.initialize();
+ }
+ return classFileReader;
+}
+
+/**
+ * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
+ *
+ * @return char[]
+ */
+public char[] sourceFileName() {
+ return this.sourceFileName;
+}
+public String toString() {
+ java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
+ java.io.PrintWriter print = new java.io.PrintWriter(out);
+
+ print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
+ print.println(" this.className: " + new String(getName())); //$NON-NLS-1$
+ print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
+ print.println(" access_flags: " + ClassFileStruct.printTypeModifiers(this.accessFlags()) + "(" + this.accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
+
+ print.flush();
+ return out.toString();
+}
+/**
+ * Check if the receiver has structural changes compare to the byte array in argument.
+ * Structural changes are:
+ * - modifiers changes for the class, the this.fields or the this.methods
+ * - signature changes for this.fields or this.methods.
+ * - changes in the number of this.fields or this.methods
+ * - changes for field constants
+ * - changes for thrown exceptions
+ * - change for the super class or any super interfaces.
+ * - changes for member types name or modifiers
+ * If any of these changes occurs, the method returns true. false otherwise.
+ * The synthetic fields are included and the members are not required to be sorted.
+ * @param newBytes the bytes of the .class file we want to compare the receiver to
+ * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
+ */
+public boolean hasStructuralChanges(byte[] newBytes) {
+ return hasStructuralChanges(newBytes, true, true);
+}
+/**
+ * Check if the receiver has structural changes compare to the byte array in argument.
+ * Structural changes are:
+ * - modifiers changes for the class, the this.fields or the this.methods
+ * - signature changes for this.fields or this.methods.
+ * - changes in the number of this.fields or this.methods
+ * - changes for field constants
+ * - changes for thrown exceptions
+ * - change for the super class or any super interfaces.
+ * - changes for member types name or modifiers
+ * If any of these changes occurs, the method returns true. false otherwise.
+ * @param newBytes the bytes of the .class file we want to compare the receiver to
+ * @param orderRequired a boolean indicating whether the members should be sorted or not
+ * @param excludesSynthetic a boolean indicating whether the synthetic members should be used in the comparison
+ * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
+ */
+public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
+ try {
+ ClassFileReader newClassFile =
+ new ClassFileReader(newBytes, this.classFileName);
+ // type level comparison
+ // modifiers
+ if (this.getModifiers() != newClassFile.getModifiers())
+ return true;
+ // superclass
+ if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
+ return true;
+ // interfaces
+ char[][] newInterfacesNames = newClassFile.getInterfaceNames();
+ if (this.interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
+ int newInterfacesLength = newInterfacesNames == null ? 0 : newInterfacesNames.length;
+ if (newInterfacesLength != this.interfacesCount)
+ return true;
+ for (int i = 0, max = this.interfacesCount; i < max; i++)
+ if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
+ return true;
+ }
+
+ // member types
+ IBinaryNestedType[] currentMemberTypes = this.getMemberTypes();
+ IBinaryNestedType[] otherMemberTypes = newClassFile.getMemberTypes();
+ if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
+ int currentMemberTypeLength = currentMemberTypes == null ? 0 : currentMemberTypes.length;
+ int otherMemberTypeLength = otherMemberTypes == null ? 0 : otherMemberTypes.length;
+ if (currentMemberTypeLength != otherMemberTypeLength)
+ return true;
+ for (int i = 0; i < currentMemberTypeLength; i++)
+ if (!CharOperation.equals(currentMemberTypes[i].getName(), otherMemberTypes[i].getName())
+ || currentMemberTypes[i].getModifiers() != otherMemberTypes[i].getModifiers())
+ return true;
+ }
+
+ // fields
+ FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
+ int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
+ boolean compareFields = true;
+ if (this.fieldsCount == otherFieldInfosLength) {
+ int i = 0;
+ for (; i < this.fieldsCount; i++)
+ if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
+ if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
+ return true;
+ }
+ if (compareFields) {
+ if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
+ return true;
+ if (orderRequired) {
+ if (this.fieldsCount != 0)
+ Arrays.sort(this.fields);
+ if (otherFieldInfosLength != 0)
+ Arrays.sort(otherFieldInfos);
+ }
+ if (excludesSynthetic) {
+ if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
+ return true;
+ } else {
+ for (int i = 0; i < this.fieldsCount; i++)
+ if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
+ return true;
+ }
+ }
+
+ // methods
+ MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
+ int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
+ boolean compareMethods = true;
+ if (this.methodsCount == otherMethodInfosLength) {
+ int i = 0;
+ for (; i < this.methodsCount; i++)
+ if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
+ if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
+ return true;
+ }
+ if (compareMethods) {
+ if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
+ return true;
+ if (orderRequired) {
+ if (this.methodsCount != 0)
+ Arrays.sort(this.methods);
+ if (otherMethodInfosLength != 0)
+ Arrays.sort(otherMethodInfos);
+ }
+ if (excludesSynthetic) {
+ if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
+ return true;
+ } else {
+ for (int i = 0; i < this.methodsCount; i++)
+ if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
+ return true;
+ }
+ }
+
+ return false;
+ } catch (ClassFormatException e) {
+ return true;
+ }
+}
+private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
+ int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
+ int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
+ int index1 = 0;
+ int index2 = 0;
+
+ end : while (index1 < length1 && index2 < length2) {
+ while (currentFieldInfos[index1].isSynthetic()) {
+ if (++index1 >= length1) break end;
+ }
+ while (otherFieldInfos[index2].isSynthetic()) {
+ if (++index2 >= length2) break end;
+ }
+ if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
+ return true;
+ }
+
+ while (index1 < length1) {
+ if (!currentFieldInfos[index1++].isSynthetic()) return true;
+ }
+ while (index2 < length2) {
+ if (!otherFieldInfos[index2++].isSynthetic()) return true;
+ }
+ return false;
+}
+private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
+ if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
+ return true;
+ if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
+ return true;
+ if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
+ return true;
+ if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
+ return true;
+ if (currentFieldInfo.hasConstant()) {
+ Constant currentConstant = currentFieldInfo.getConstant();
+ Constant otherConstant = otherFieldInfo.getConstant();
+ if (currentConstant.typeID() != otherConstant.typeID())
+ return true;
+ if (!currentConstant.getClass().equals(otherConstant.getClass()))
+ return true;
+ switch (currentConstant.typeID()) {
+ case TypeIds.T_int :
+ return currentConstant.intValue() != otherConstant.intValue();
+ case TypeIds.T_byte :
+ return currentConstant.byteValue() != otherConstant.byteValue();
+ case TypeIds.T_short :
+ return currentConstant.shortValue() != otherConstant.shortValue();
+ case TypeIds.T_char :
+ return currentConstant.charValue() != otherConstant.charValue();
+ case TypeIds.T_long :
+ return currentConstant.longValue() != otherConstant.longValue();
+ case TypeIds.T_float :
+ return currentConstant.floatValue() != otherConstant.floatValue();
+ case TypeIds.T_double :
+ return currentConstant.doubleValue() != otherConstant.doubleValue();
+ case TypeIds.T_boolean :
+ return currentConstant.booleanValue() != otherConstant.booleanValue();
+ case TypeIds.T_String :
+ return !currentConstant.stringValue().equals(otherConstant.stringValue());
+ }
+ }
+ return false;
+}
+private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
+ int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
+ int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
+ int index1 = 0;
+ int index2 = 0;
+
+ MethodInfo m;
+ end : while (index1 < length1 && index2 < length2) {
+ while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
+ if (++index1 >= length1) break end;
+ }
+ while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
+ if (++index2 >= length2) break end;
+ }
+ if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
+ return true;
+ }
+
+ while (index1 < length1) {
+ if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
+ }
+ while (index2 < length2) {
+ if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
+ }
+ return false;
+}
+private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
+ if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
+ return true;
+ if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
+ return true;
+ if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
+ return true;
+
+ char[][] currentThrownExceptions = currentMethodInfo.getExceptionTypeNames();
+ char[][] otherThrownExceptions = otherMethodInfo.getExceptionTypeNames();
+ if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
+ int currentThrownExceptionsLength = currentThrownExceptions == null ? 0 : currentThrownExceptions.length;
+ int otherThrownExceptionsLength = otherThrownExceptions == null ? 0 : otherThrownExceptions.length;
+ if (currentThrownExceptionsLength != otherThrownExceptionsLength)
+ return true;
+ for (int k = 0; k < currentThrownExceptionsLength; k++)
+ if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
+ return true;
+ }
+ return false;
+}
+/**
+ * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
+ * will be therefore fully initialized and we can get rid of the bytes.
+ */
+private void initialize() throws ClassFormatException {
+ try {
+ for (int i = 0, max = fieldsCount; i < max; i++) {
+ fields[i].initialize();
+ }
+ for (int i = 0, max = methodsCount; i < max; i++) {
+ methods[i].initialize();
+ }
+ if (innerInfos != null) {
+ for (int i = 0, max = innerInfos.length; i < max; i++) {
+ innerInfos[i].initialize();
+ }
+ }
+ this.reset();
+ } catch(RuntimeException e) {
+ ClassFormatException exception = new ClassFormatException(e, this.classFileName);
+ throw exception;
+ }
+}
+protected void reset() {
+ this.constantPoolOffsets = null;
+ super.reset();
+}
+
+}