1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.classfmt;
14 import java.io.IOException;
15 import java.util.Arrays;
17 import org.eclipse.jdt.core.Signature;
18 import org.eclipse.jdt.core.compiler.CharOperation;
19 import org.eclipse.jdt.internal.compiler.ast.Annotation;
20 import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
21 import org.eclipse.jdt.internal.compiler.env.*;
22 import org.eclipse.jdt.internal.compiler.impl.Constant;
23 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
24 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
25 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
26 import org.eclipse.jdt.internal.compiler.util.Util;
28 public class ClassFileReader extends ClassFileStruct implements AttributeNamesConstants, IBinaryType {
29 public static ClassFileReader read(File file) throws ClassFormatException, IOException {
30 return read(file, false);
32 public static ClassFileReader read(File file, boolean fullyInitialize) throws ClassFormatException, IOException {
33 byte classFileBytes[] = Util.getFileByteContent(file);
34 ClassFileReader classFileReader = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
35 if (fullyInitialize) {
36 classFileReader.initialize();
38 return classFileReader;
40 public static ClassFileReader read(
41 java.util.zip.ZipFile zip,
43 throws ClassFormatException, java.io.IOException {
44 return read(zip, filename, false);
46 public static ClassFileReader read(
47 java.util.zip.ZipFile zip,
49 boolean fullyInitialize)
50 throws ClassFormatException, java.io.IOException {
51 java.util.zip.ZipEntry ze = zip.getEntry(filename);
54 byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
55 ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
56 if (fullyInitialize) {
57 classFileReader.initialize();
59 return classFileReader;
61 public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
62 return read(fileName, false);
64 public static ClassFileReader read(String fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException {
65 return read(new File(fileName), fullyInitialize);
67 private int accessFlags;
68 private char[] classFileName;
69 private char[] className;
70 private int classNameIndex;
71 private int constantPoolCount;
72 private int[] constantPoolOffsets;
73 private FieldInfo[] fields;
74 private int fieldsCount;
75 // initialized in case the .class file is a nested type
76 private InnerClassInfo innerInfo;
77 private int innerInfoIndex;
78 private InnerClassInfo[] innerInfos;
79 private char[][] interfaceNames;
80 private int interfacesCount;
81 private MethodInfo[] methods;
82 private int methodsCount;
83 private char[] signature;
84 private char[] sourceFileName;
85 private char[] superclassName;
90 * @param classFileBytes Actual bytes of a .class file
91 * @param fileName Actual name of the file that contains the bytes, can be null
93 * @exception ClassFormatException
95 public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
96 this(classFileBytes, fileName, false);
100 * @param classFileBytes byte[]
101 * Actual bytes of a .class file
103 * @param fileName char[]
104 * Actual name of the file that contains the bytes, can be null
106 * @param fullyInitialize boolean
107 * Flag to fully initialize the new object
108 * @exception ClassFormatException
110 public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
111 // This method looks ugly but is actually quite simple, the constantPool is constructed
112 // in 3 passes. All non-primitive constant pool members that usually refer to other members
113 // by index are tweaked to have their value in inst vars, this minor cost at read-time makes
114 // all subsequent uses of the constant pool element faster.
115 super(classFileBytes, 0);
116 this.classFileName = fileName;
119 this.version = ((long)this.u2At(6) << 16) + this.u2At(4); // major<<16 + minor
120 constantPoolCount = this.u2At(8);
121 // Pass #1 - Fill in all primitive constants
122 this.constantPoolOffsets = new int[constantPoolCount];
123 for (int i = 1; i < constantPoolCount; i++) {
124 int tag = this.u1At(readOffset);
127 this.constantPoolOffsets[i] = readOffset;
128 readOffset += u2At(readOffset + 1);
129 readOffset += ConstantUtf8FixedSize;
132 this.constantPoolOffsets[i] = readOffset;
133 readOffset += ConstantIntegerFixedSize;
136 this.constantPoolOffsets[i] = readOffset;
137 readOffset += ConstantFloatFixedSize;
140 this.constantPoolOffsets[i] = readOffset;
141 readOffset += ConstantLongFixedSize;
145 this.constantPoolOffsets[i] = readOffset;
146 readOffset += ConstantDoubleFixedSize;
150 this.constantPoolOffsets[i] = readOffset;
151 readOffset += ConstantClassFixedSize;
154 this.constantPoolOffsets[i] = readOffset;
155 readOffset += ConstantStringFixedSize;
158 this.constantPoolOffsets[i] = readOffset;
159 readOffset += ConstantFieldRefFixedSize;
162 this.constantPoolOffsets[i] = readOffset;
163 readOffset += ConstantMethodRefFixedSize;
165 case InterfaceMethodRefTag :
166 this.constantPoolOffsets[i] = readOffset;
167 readOffset += ConstantInterfaceMethodRefFixedSize;
169 case NameAndTypeTag :
170 this.constantPoolOffsets[i] = readOffset;
171 readOffset += ConstantNameAndTypeFixedSize;
174 // Read and validate access flags
175 this.accessFlags = u2At(readOffset);
178 // Read the classname, use exception handlers to catch bad format
179 this.classNameIndex = u2At(readOffset);
180 this.className = getConstantClassNameAt(this.classNameIndex);
183 // Read the superclass name, can be null for java.lang.Object
184 int superclassNameIndex = u2At(readOffset);
186 // if superclassNameIndex is equals to 0 there is no need to set a value for the
187 // field this.superclassName. null is fine.
188 if (superclassNameIndex != 0) {
189 this.superclassName = getConstantClassNameAt(superclassNameIndex);
192 // Read the interfaces, use exception handlers to catch bad format
193 this.interfacesCount = u2At(readOffset);
195 if (this.interfacesCount != 0) {
196 this.interfaceNames = new char[this.interfacesCount][];
197 for (int i = 0; i < this.interfacesCount; i++) {
198 this.interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
202 // Read the this.fields, use exception handlers to catch bad format
203 this.fieldsCount = u2At(readOffset);
205 if (this.fieldsCount != 0) {
207 this.fields = new FieldInfo[this.fieldsCount];
208 for (int i = 0; i < this.fieldsCount; i++) {
209 field = new FieldInfo(reference, this.constantPoolOffsets, readOffset);
210 this.fields[i] = field;
211 readOffset += field.sizeInBytes();
214 // Read the this.methods
215 this.methodsCount = u2At(readOffset);
217 if (this.methodsCount != 0) {
218 this.methods = new MethodInfo[this.methodsCount];
220 for (int i = 0; i < this.methodsCount; i++) {
221 method = new MethodInfo(reference, this.constantPoolOffsets, readOffset);
222 this.methods[i] = method;
223 readOffset += method.sizeInBytes();
227 // Read the attributes
228 int attributesCount = u2At(readOffset);
231 for (int i = 0; i < attributesCount; i++) {
232 int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
233 char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
234 if (attributeName.length == 0) {
235 readOffset += (6 + u4At(readOffset + 2));
238 switch(attributeName[0] ) {
240 if (CharOperation.equals(attributeName, DeprecatedName)) {
241 this.accessFlags |= AccDeprecated;
245 if (CharOperation.equals(attributeName, InnerClassName)) {
246 int innerOffset = readOffset + 6;
247 int number_of_classes = u2At(innerOffset);
248 if (number_of_classes != 0) {
250 this.innerInfos = new InnerClassInfo[number_of_classes];
251 for (int j = 0; j < number_of_classes; j++) {
253 new InnerClassInfo(reference, this.constantPoolOffsets, innerOffset);
254 if (this.classNameIndex == this.innerInfos[j].innerClassNameIndex) {
255 this.innerInfo = this.innerInfos[j];
256 this.innerInfoIndex = j;
264 if (attributeName.length > 2) {
265 switch(attributeName[1]) {
267 if (CharOperation.equals(attributeName, SourceName)) {
268 utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
269 this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
273 if (CharOperation.equals(attributeName, SyntheticName)) {
274 this.accessFlags |= AccSynthetic;
278 if (CharOperation.equals(attributeName, SignatureName)) {
279 utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
280 this.signature = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
286 if (CharOperation.equals(attributeName, RuntimeVisibleAnnotationsName)) {
287 decodeStandardAnnotations(readOffset);
291 readOffset += (6 + u4At(readOffset + 2));
293 if (fullyInitialize) {
296 } catch(ClassFormatException e) {
298 } catch (Exception e) {
299 throw new ClassFormatException(
300 ClassFormatException.ErrTruncatedInput,
306 * Answer the receiver's access flags. The value of the access_flags
307 * item is a mask of modifiers used with class and interface declarations.
310 public int accessFlags() {
311 return this.accessFlags;
313 private int decodeAnnotation(int offset) {
314 int readOffset = offset;
315 int utf8Offset = this.constantPoolOffsets[u2At(offset)];
316 char[] typeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
317 typeName = Signature.toCharArray(typeName);
318 CharOperation.replace(typeName, '/', '.');
319 char[][] qualifiedTypeName = CharOperation.splitOn('.', typeName);
320 int numberOfPairs = u2At(offset + 2);
322 switch(qualifiedTypeName.length) {
324 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_DEPRECATED)) {
325 this.tagBits |= TagBits.AnnotationDeprecated;
330 char[] lastPart = qualifiedTypeName[3];
331 if (lastPart.length > 0) {
332 switch(lastPart[0]) {
334 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_ANNOTATION_RETENTION)) {
335 for (int i = 0; i < numberOfPairs; i++) {
337 readOffset = decodeElementValueForJavaLangAnnotationRetention(readOffset);
343 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_ANNOTATION_TARGET)) {
344 for (int i = 0; i < numberOfPairs; i++) {
346 readOffset = decodeElementValueForJavaLangAnnotationTarget(readOffset);
352 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_ANNOTATION_DOCUMENTED)) {
353 this.tagBits |= TagBits.AnnotationDocumented;
358 if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_ANNOTATION_INHERITED)) {
359 this.tagBits |= TagBits.AnnotationInherited;
365 for (int i = 0; i < numberOfPairs; i++) {
367 readOffset = decodeElementValue(readOffset);
371 private int decodeElementValue(int offset) {
372 int readOffset = offset;
373 int tag = u1At(readOffset);
394 readOffset += decodeAnnotation(readOffset);
397 int numberOfValues = u2At(readOffset);
399 for (int i = 0; i < numberOfValues; i++) {
400 readOffset = decodeElementValueForJavaLangAnnotationTarget(readOffset);
406 private int decodeElementValueForJavaLangAnnotationTarget(int offset) {
407 int readOffset = offset;
408 int tag = u1At(readOffset);
424 int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
425 char[] constName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
427 this.tagBits |= Annotation.getTargetElementType(constName);
433 readOffset += decodeAnnotation(readOffset);
436 int numberOfValues = u2At(readOffset);
438 if (numberOfValues == 0) {
439 this.tagBits |= TagBits.AnnotationTarget;
441 for (int i = 0; i < numberOfValues; i++) {
442 readOffset = decodeElementValueForJavaLangAnnotationTarget(readOffset);
449 private int decodeElementValueForJavaLangAnnotationRetention(int offset) {
450 int readOffset = offset;
451 int tag = u1At(readOffset);
467 int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
468 char[] constName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
470 this.tagBits |= Annotation.getRetentionPolicy(constName);
476 readOffset += decodeAnnotation(readOffset);
479 int numberOfValues = u2At(readOffset);
481 for (int i = 0; i < numberOfValues; i++) {
482 readOffset = decodeElementValueForJavaLangAnnotationRetention(readOffset);
489 * @param offset the offset is located at the beginning of the runtime visible
490 * annotation attribute.
492 private void decodeStandardAnnotations(int offset) {
493 int numberOfAnnotations = u2At(offset + 6);
494 int readOffset = offset + 8;
495 for (int i = 0; i < numberOfAnnotations; i++) {
496 readOffset = decodeAnnotation(readOffset);
500 * Answer the char array that corresponds to the class name of the constant class.
501 * constantPoolIndex is the index in the constant pool that is a constant class entry.
503 * @param constantPoolIndex int
506 private char[] getConstantClassNameAt(int constantPoolIndex) {
507 int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
508 return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
511 * Answer the int array that corresponds to all the offsets of each entry in the constant pool
515 public int[] getConstantPoolOffsets() {
516 return this.constantPoolOffsets;
519 * Answer the resolved compoundName of the enclosing type
520 * or null if the receiver is a top level type.
522 public char[] getEnclosingTypeName() {
523 if (this.innerInfo != null && !this.isAnonymous()) {
524 return this.innerInfo.getEnclosingTypeName();
529 * Answer the receiver's this.fields or null if the array is empty.
530 * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
532 public IBinaryField[] getFields() {
536 * @see org.eclipse.jdt.internal.compiler.env.IDependent#getFileName()
538 public char[] getFileName() {
539 return this.classFileName;
541 public char[] getGenericSignature() {
542 return this.signature;
545 * 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.
550 * public void foo() {
553 * public Runnable bar() {
554 * return new Runnable() {
555 * public void run() {}
559 * It returns {'B'} for the member A$B
560 * It returns null for A
561 * It returns {'C'} for the local class A$1$C
562 * It returns null for the anonymous A$1
565 public char[] getInnerSourceName() {
566 if (this.innerInfo != null)
567 return this.innerInfo.getSourceName();
571 * Answer the resolved names of the receiver's interfaces in the
572 * class file format as specified in section 4.2 of the Java 2 VM spec
573 * or null if the array is empty.
575 * For example, java.lang.String is java/lang/String.
578 public char[][] getInterfaceNames() {
579 return this.interfaceNames;
583 * @see org.eclipse.jdt.internal.compiler.env.IGenericType#getKind()
585 public int getKind() {
586 int modifiers = getModifiers();
587 if ((modifiers & AccInterface) != 0) {
588 if ((modifiers & AccAnnotation) != 0) return IGenericType.ANNOTATION_TYPE_DECL;
589 return IGenericType.INTERFACE_DECL;
591 if ((modifiers & AccEnum) != 0) return IGenericType.ENUM_DECL;
592 return IGenericType.CLASS_DECL;
595 * Answer the receiver's nested types or null if the array is empty.
597 * This nested type info is extracted from the inner class attributes. Ask the
598 * name environment to find a member type using its compound name
600 * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
602 public IBinaryNestedType[] getMemberTypes() {
603 // we might have some member types of the current type
604 if (this.innerInfos == null) return null;
606 int length = this.innerInfos.length;
607 int startingIndex = this.innerInfo != null ? this.innerInfoIndex + 1 : 0;
608 if (length != startingIndex) {
609 IBinaryNestedType[] memberTypes =
610 new IBinaryNestedType[length - this.innerInfoIndex];
611 int memberTypeIndex = 0;
612 for (int i = startingIndex; i < length; i++) {
613 InnerClassInfo currentInnerInfo = this.innerInfos[i];
614 int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
615 int innerNameIndex = currentInnerInfo.innerNameIndex;
617 * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
618 * attribute entry is a member class, but due to the bug:
619 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
620 * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
622 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=49879
623 * From JavaMail 1.2, the class javax.mail.Folder contains an anonymous class in the
624 * terminateQueue() method for which the inner attribute is boggus.
625 * outerClassNameIdx is not 0, innerNameIndex is not 0, but the sourceName length is 0.
626 * So I added this extra check to filter out this anonymous class from the
629 if (outerClassNameIdx != 0
630 && innerNameIndex != 0
631 && outerClassNameIdx == this.classNameIndex
632 && currentInnerInfo.getSourceName().length != 0) {
633 memberTypes[memberTypeIndex++] = currentInnerInfo;
636 if (memberTypeIndex == 0) return null;
637 if (memberTypeIndex != memberTypes.length) {
638 // we need to resize the memberTypes array. Some local or anonymous classes
639 // are present in the current class.
643 (memberTypes = new IBinaryNestedType[memberTypeIndex]),
652 * Answer the receiver's this.methods or null if the array is empty.
653 * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
655 public IBinaryMethod[] getMethods() {
659 * Answer an int whose bits are set according the access constants
660 * defined by the VM spec.
661 * Set the AccDeprecated and AccSynthetic bits if necessary
664 public int getModifiers() {
665 if (this.innerInfo != null) {
666 return this.innerInfo.getModifiers() | (this.accessFlags & AccDeprecated);
668 return this.accessFlags;
671 * Answer the resolved name of the type in the
672 * class file format as specified in section 4.2 of the Java 2 VM spec.
674 * For example, java.lang.String is java/lang/String.
677 public char[] getName() {
678 return this.className;
681 * Answer the resolved name of the receiver's superclass in the
682 * class file format as specified in section 4.2 of the Java 2 VM spec
683 * or null if it does not have one.
685 * For example, java.lang.String is java/lang/String.
688 public char[] getSuperclassName() {
689 return this.superclassName;
691 public long getTagBits() {
695 * Answer the major/minor version defined in this class file according to the VM spec.
696 * as a long: (major<<16)+minor
697 * @return the major/minor version found
699 public long getVersion() {
702 private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
703 int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
704 int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
708 end : while (index1 < length1 && index2 < length2) {
709 while (currentFieldInfos[index1].isSynthetic()) {
710 if (++index1 >= length1) break end;
712 while (otherFieldInfos[index2].isSynthetic()) {
713 if (++index2 >= length2) break end;
715 if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
719 while (index1 < length1) {
720 if (!currentFieldInfos[index1++].isSynthetic()) return true;
722 while (index2 < length2) {
723 if (!otherFieldInfos[index2++].isSynthetic()) return true;
727 private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
728 int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
729 int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
734 end : while (index1 < length1 && index2 < length2) {
735 while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
736 if (++index1 >= length1) break end;
738 while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
739 if (++index2 >= length2) break end;
741 if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
745 while (index1 < length1) {
746 if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
748 while (index2 < length2) {
749 if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
754 * Check if the receiver has structural changes compare to the byte array in argument.
755 * Structural changes are:
756 * - modifiers changes for the class, the this.fields or the this.methods
757 * - signature changes for this.fields or this.methods.
758 * - changes in the number of this.fields or this.methods
759 * - changes for field constants
760 * - changes for thrown exceptions
761 * - change for the super class or any super interfaces.
762 * - changes for member types name or modifiers
763 * If any of these changes occurs, the method returns true. false otherwise.
764 * The synthetic fields are included and the members are not required to be sorted.
765 * @param newBytes the bytes of the .class file we want to compare the receiver to
766 * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
768 public boolean hasStructuralChanges(byte[] newBytes) {
769 return hasStructuralChanges(newBytes, true, true);
772 * Check if the receiver has structural changes compare to the byte array in argument.
773 * Structural changes are:
774 * - modifiers changes for the class, the this.fields or the this.methods
775 * - signature changes for this.fields or this.methods.
776 * - changes in the number of this.fields or this.methods
777 * - changes for field constants
778 * - changes for thrown exceptions
779 * - change for the super class or any super interfaces.
780 * - changes for member types name or modifiers
781 * If any of these changes occurs, the method returns true. false otherwise.
782 * @param newBytes the bytes of the .class file we want to compare the receiver to
783 * @param orderRequired a boolean indicating whether the members should be sorted or not
784 * @param excludesSynthetic a boolean indicating whether the synthetic members should be used in the comparison
785 * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
787 public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
789 ClassFileReader newClassFile =
790 new ClassFileReader(newBytes, this.classFileName);
791 // type level comparison
793 if (this.getModifiers() != newClassFile.getModifiers())
797 if ((this.getTagBits() & TagBits.AnnotationTargetMASK|TagBits.AnnotationDeprecated|TagBits.AnnotationRetentionMASK) != (newClassFile.getTagBits() & TagBits.AnnotationTargetMASK|TagBits.AnnotationDeprecated|TagBits.AnnotationRetentionMASK))
801 if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
804 char[][] newInterfacesNames = newClassFile.getInterfaceNames();
805 if (this.interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
806 int newInterfacesLength = newInterfacesNames == null ? 0 : newInterfacesNames.length;
807 if (newInterfacesLength != this.interfacesCount)
809 for (int i = 0, max = this.interfacesCount; i < max; i++)
810 if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
815 IBinaryNestedType[] currentMemberTypes = this.getMemberTypes();
816 IBinaryNestedType[] otherMemberTypes = newClassFile.getMemberTypes();
817 if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
818 int currentMemberTypeLength = currentMemberTypes == null ? 0 : currentMemberTypes.length;
819 int otherMemberTypeLength = otherMemberTypes == null ? 0 : otherMemberTypes.length;
820 if (currentMemberTypeLength != otherMemberTypeLength)
822 for (int i = 0; i < currentMemberTypeLength; i++)
823 if (!CharOperation.equals(currentMemberTypes[i].getName(), otherMemberTypes[i].getName())
824 || currentMemberTypes[i].getModifiers() != otherMemberTypes[i].getModifiers())
829 FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
830 int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
831 boolean compareFields = true;
832 if (this.fieldsCount == otherFieldInfosLength) {
834 for (; i < this.fieldsCount; i++)
835 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
836 if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
840 if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
843 if (this.fieldsCount != 0)
844 Arrays.sort(this.fields);
845 if (otherFieldInfosLength != 0)
846 Arrays.sort(otherFieldInfos);
848 if (excludesSynthetic) {
849 if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
852 for (int i = 0; i < this.fieldsCount; i++)
853 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
859 MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
860 int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
861 boolean compareMethods = true;
862 if (this.methodsCount == otherMethodInfosLength) {
864 for (; i < this.methodsCount; i++)
865 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
866 if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
869 if (compareMethods) {
870 if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
873 if (this.methodsCount != 0)
874 Arrays.sort(this.methods);
875 if (otherMethodInfosLength != 0)
876 Arrays.sort(otherMethodInfos);
878 if (excludesSynthetic) {
879 if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
882 for (int i = 0; i < this.methodsCount; i++)
883 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
889 } catch (ClassFormatException e) {
893 private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
894 if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
896 if ((currentFieldInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherFieldInfo.getTagBits() & TagBits.AnnotationDeprecated))
898 if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
900 if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
902 if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
904 if (currentFieldInfo.hasConstant()) {
905 Constant currentConstant = currentFieldInfo.getConstant();
906 Constant otherConstant = otherFieldInfo.getConstant();
907 if (currentConstant.typeID() != otherConstant.typeID())
909 if (!currentConstant.getClass().equals(otherConstant.getClass()))
911 switch (currentConstant.typeID()) {
913 return currentConstant.intValue() != otherConstant.intValue();
914 case TypeIds.T_byte :
915 return currentConstant.byteValue() != otherConstant.byteValue();
916 case TypeIds.T_short :
917 return currentConstant.shortValue() != otherConstant.shortValue();
918 case TypeIds.T_char :
919 return currentConstant.charValue() != otherConstant.charValue();
920 case TypeIds.T_long :
921 return currentConstant.longValue() != otherConstant.longValue();
922 case TypeIds.T_float :
923 return currentConstant.floatValue() != otherConstant.floatValue();
924 case TypeIds.T_double :
925 return currentConstant.doubleValue() != otherConstant.doubleValue();
926 case TypeIds.T_boolean :
927 return currentConstant.booleanValue() != otherConstant.booleanValue();
928 case TypeIds.T_JavaLangString :
929 return !currentConstant.stringValue().equals(otherConstant.stringValue());
934 private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
935 if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
937 if ((currentMethodInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherMethodInfo.getTagBits() & TagBits.AnnotationDeprecated))
939 if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
941 if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
943 if (!CharOperation.equals(currentMethodInfo.getGenericSignature(), otherMethodInfo.getGenericSignature()))
946 char[][] currentThrownExceptions = currentMethodInfo.getExceptionTypeNames();
947 char[][] otherThrownExceptions = otherMethodInfo.getExceptionTypeNames();
948 if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
949 int currentThrownExceptionsLength = currentThrownExceptions == null ? 0 : currentThrownExceptions.length;
950 int otherThrownExceptionsLength = otherThrownExceptions == null ? 0 : otherThrownExceptions.length;
951 if (currentThrownExceptionsLength != otherThrownExceptionsLength)
953 for (int k = 0; k < currentThrownExceptionsLength; k++)
954 if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
960 * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
961 * will be therefore fully initialized and we can get rid of the bytes.
963 private void initialize() throws ClassFormatException {
965 for (int i = 0, max = fieldsCount; i < max; i++) {
966 fields[i].initialize();
968 for (int i = 0, max = methodsCount; i < max; i++) {
969 methods[i].initialize();
971 if (innerInfos != null) {
972 for (int i = 0, max = innerInfos.length; i < max; i++) {
973 innerInfos[i].initialize();
977 } catch(RuntimeException e) {
978 ClassFormatException exception = new ClassFormatException(e, this.classFileName);
983 * Answer true if the receiver is an anonymous type, false otherwise
985 * @return <CODE>boolean</CODE>
987 public boolean isAnonymous() {
988 if (this.innerInfo == null) return false;
989 char[] sourceName = this.innerInfo.getSourceName();
990 return (sourceName == null || sourceName.length == 0);
993 * Answer whether the receiver contains the resolved binary form
994 * or the unresolved source form of the type.
997 public boolean isBinaryType() {
1002 * Answer true if the receiver is a local type, false otherwise
1004 * @return <CODE>boolean</CODE>
1006 public boolean isLocal() {
1007 if (this.innerInfo == null) return false;
1008 if (this.innerInfo.getEnclosingTypeName() != null) return false;
1009 char[] sourceName = this.innerInfo.getSourceName();
1010 return (sourceName != null && sourceName.length > 0);
1013 * Answer true if the receiver is a member type, false otherwise
1015 * @return <CODE>boolean</CODE>
1017 public boolean isMember() {
1018 if (this.innerInfo == null) return false;
1019 if (this.innerInfo.getEnclosingTypeName() == null) return false;
1020 char[] sourceName = this.innerInfo.getSourceName();
1021 return (sourceName != null && sourceName.length > 0); // protection against ill-formed attributes (67600)
1024 * Answer true if the receiver is a nested type, false otherwise
1026 * @return <CODE>boolean</CODE>
1028 public boolean isNestedType() {
1029 return this.innerInfo != null;
1031 protected void reset() {
1032 this.constantPoolOffsets = null;
1036 * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
1040 public char[] sourceFileName() {
1041 return this.sourceFileName;
1043 public String toString() {
1044 java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
1045 java.io.PrintWriter print = new java.io.PrintWriter(out);
1046 print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
1047 print.println(" this.className: " + new String(getName())); //$NON-NLS-1$
1048 print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
1049 print.println(" access_flags: " + ClassFileStruct.printTypeModifiers(this.accessFlags()) + "(" + this.accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
1051 return out.toString();