1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v0.5 
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v05.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  ******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.compiler.classfmt;
 
  13 import net.sourceforge.phpdt.internal.compiler.codegen.*;
 
  14 import net.sourceforge.phpdt.internal.compiler.env.*;
 
  15 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
 
  16 import net.sourceforge.phpdt.internal.compiler.impl.NullConstant;
 
  17 import net.sourceforge.phpdt.internal.compiler.lookup.TypeIds;
 
  18 import net.sourceforge.phpdt.internal.compiler.util.*;
 
  21 import java.util.Arrays;
 
  23 public class ClassFileReader extends ClassFileStruct implements AttributeNamesConstants, IBinaryType {
 
  24         private int constantPoolCount;
 
  25         private int[] constantPoolOffsets;
 
  26         private int accessFlags;
 
  27         private char[] className;
 
  28         private char[] superclassName;
 
  29         private int interfacesCount;
 
  30         private char[][] interfaceNames;
 
  31         private int fieldsCount;
 
  32         private FieldInfo[] fields;
 
  33         private int methodsCount;
 
  34         private MethodInfo[] methods;
 
  35         private InnerClassInfo[] innerInfos;
 
  36         private char[] sourceFileName;
 
  37         // initialized in case the .class file is a nested type
 
  38         private InnerClassInfo innerInfo;
 
  39         private char[] classFileName;
 
  40         private int classNameIndex;
 
  41         private int innerInfoIndex;
 
  43  * @param classFileBytes byte[]
 
  44  *              Actual bytes of a .class file
 
  46  * @param fileName char[]
 
  47  *              Actual name of the file that contains the bytes, can be null
 
  49  * @param fullyInitialize boolean
 
  50  *              Flag to fully initialize the new object
 
  51  * @exception ClassFormatException
 
  53 public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
 
  54         // This method looks ugly but is actually quite simple, the constantPool is constructed
 
  55         // in 3 passes.  All non-primitive constant pool members that usually refer to other members
 
  56         // by index are tweaked to have their value in inst vars, this minor cost at read-time makes
 
  57         // all subsequent uses of the constant pool element faster.
 
  58         super(classFileBytes, 0);
 
  59         this.classFileName = fileName;
 
  62                 constantPoolCount = this.u2At(8);
 
  63                 // Pass #1 - Fill in all primitive constants
 
  64                 this.constantPoolOffsets = new int[constantPoolCount];
 
  65                 for (int i = 1; i < constantPoolCount; i++) {
 
  66                         int tag = this.u1At(readOffset);
 
  69                                         this.constantPoolOffsets[i] = readOffset;
 
  70                                         readOffset += u2At(readOffset + 1);
 
  71                                         readOffset += ConstantUtf8FixedSize;
 
  74                                         this.constantPoolOffsets[i] = readOffset;
 
  75                                         readOffset += ConstantIntegerFixedSize;
 
  78                                         this.constantPoolOffsets[i] = readOffset;
 
  79                                         readOffset += ConstantFloatFixedSize;
 
  82                                         this.constantPoolOffsets[i] = readOffset;
 
  83                                         readOffset += ConstantLongFixedSize;
 
  87                                         this.constantPoolOffsets[i] = readOffset;
 
  88                                         readOffset += ConstantDoubleFixedSize;
 
  92                                         this.constantPoolOffsets[i] = readOffset;
 
  93                                         readOffset += ConstantClassFixedSize;
 
  96                                         this.constantPoolOffsets[i] = readOffset;
 
  97                                         readOffset += ConstantStringFixedSize;
 
 100                                         this.constantPoolOffsets[i] = readOffset;
 
 101                                         readOffset += ConstantFieldRefFixedSize;
 
 104                                         this.constantPoolOffsets[i] = readOffset;
 
 105                                         readOffset += ConstantMethodRefFixedSize;
 
 107                                 case InterfaceMethodRefTag :
 
 108                                         this.constantPoolOffsets[i] = readOffset;
 
 109                                         readOffset += ConstantInterfaceMethodRefFixedSize;
 
 111                                 case NameAndTypeTag :
 
 112                                         this.constantPoolOffsets[i] = readOffset;
 
 113                                         readOffset += ConstantNameAndTypeFixedSize;
 
 116                 // Read and validate access flags
 
 117                 this.accessFlags = u2At(readOffset);
 
 120                 // Read the classname, use exception handlers to catch bad format
 
 121                 this.classNameIndex = u2At(readOffset);
 
 122                 this.className = getConstantClassNameAt(this.classNameIndex);
 
 125                 // Read the superclass name, can be null for java.lang.Object
 
 126                 int superclassNameIndex = u2At(readOffset);
 
 128                 // if superclassNameIndex is equals to 0 there is no need to set a value for the 
 
 129                 // field this.superclassName. null is fine.
 
 130                 if (superclassNameIndex != 0) {
 
 131                         this.superclassName = getConstantClassNameAt(superclassNameIndex);
 
 134                 // Read the interfaces, use exception handlers to catch bad format
 
 135                 this.interfacesCount = u2At(readOffset);
 
 137                 if (this.interfacesCount != 0) {
 
 138                         this.interfaceNames = new char[this.interfacesCount][];
 
 139                         for (int i = 0; i < this.interfacesCount; i++) {
 
 140                                 this.interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
 
 144                 // Read the this.fields, use exception handlers to catch bad format
 
 145                 this.fieldsCount = u2At(readOffset);
 
 147                 if (this.fieldsCount != 0) {
 
 149                         this.fields = new FieldInfo[this.fieldsCount];
 
 150                         for (int i = 0; i < this.fieldsCount; i++) {
 
 151                                 field = new FieldInfo(reference, this.constantPoolOffsets, readOffset);
 
 152                                 this.fields[i] = field;
 
 153                                 readOffset += field.sizeInBytes();
 
 156                 // Read the this.methods
 
 157                 this.methodsCount = u2At(readOffset);
 
 159                 if (this.methodsCount != 0) {
 
 160                         this.methods = new MethodInfo[this.methodsCount];
 
 162                         for (int i = 0; i < this.methodsCount; i++) {
 
 163                                 method = new MethodInfo(reference, this.constantPoolOffsets, readOffset);
 
 164                                 this.methods[i] = method;
 
 165                                 readOffset += method.sizeInBytes();
 
 169                 // Read the attributes
 
 170                 int attributesCount = u2At(readOffset);
 
 173                 for (int i = 0; i < attributesCount; i++) {
 
 174                         int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
 
 175                         char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
 
 176                         if (CharOperation.equals(attributeName, DeprecatedName)) {
 
 177                                 this.accessFlags |= AccDeprecated;
 
 179                                 if (CharOperation.equals(attributeName, InnerClassName)) {
 
 180                                         int innerOffset = readOffset + 6;
 
 181                                         int number_of_classes = u2At(innerOffset);
 
 182                                         if (number_of_classes != 0) {
 
 183                                                 this.innerInfos = new InnerClassInfo[number_of_classes];
 
 184                                                 for (int j = 0; j < number_of_classes; j++) {
 
 186                                                                 new InnerClassInfo(reference, this.constantPoolOffsets, innerOffset + 2); 
 
 187                                                         if (this.classNameIndex == this.innerInfos[j].innerClassNameIndex) {
 
 188                                                                 this.innerInfo = this.innerInfos[j];
 
 189                                                                 this.innerInfoIndex = j;
 
 195                                         if (CharOperation.equals(attributeName, SourceName)) {
 
 196                                                 utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
 
 197                                                 this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
 
 199                                                 if (CharOperation.equals(attributeName, SyntheticName)) {
 
 200                                                         this.accessFlags |= AccSynthetic;
 
 205                         readOffset += (6 + u4At(readOffset + 2));
 
 207                 if (fullyInitialize) {
 
 210         } catch (Exception e) {
 
 211                 throw new ClassFormatException(
 
 212                         ClassFormatException.ErrTruncatedInput, 
 
 218  * @param classFileBytes Actual bytes of a .class file
 
 219  * @param fileName      Actual name of the file that contains the bytes, can be null
 
 221  * @exception ClassFormatException
 
 223 public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
 
 224         this(classFileBytes, fileName, false);
 
 228  *      Answer the receiver's access flags.  The value of the access_flags
 
 229  *      item is a mask of modifiers used with class and interface declarations.
 
 232 public int accessFlags() {
 
 233         return this.accessFlags;
 
 236  * Answer the char array that corresponds to the class name of the constant class.
 
 237  * constantPoolIndex is the index in the constant pool that is a constant class entry.
 
 239  * @param int constantPoolIndex
 
 242 private char[] getConstantClassNameAt(int constantPoolIndex) {
 
 243         int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
 
 244         return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
 
 247  * Answer the int array that corresponds to all the offsets of each entry in the constant pool
 
 251 public int[] getConstantPoolOffsets() {
 
 252         return this.constantPoolOffsets;
 
 255  * Answer the resolved compoundName of the enclosing type
 
 256  * or null if the receiver is a top level type.
 
 258 public char[] getEnclosingTypeName() {
 
 259         if (this.innerInfo != null && !this.isAnonymous()) {
 
 260                 return this.innerInfo.getEnclosingTypeName();
 
 265  * Answer the receiver's this.fields or null if the array is empty.
 
 266  * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
 
 268 public IBinaryField[] getFields() {
 
 272  * Answer the file name which defines the type.
 
 273  * The format is unspecified.
 
 275 public char[] getFileName() {
 
 276         return this.classFileName;
 
 279  * 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.
 
 284  *      public void foo() {
 
 287  *      public Runnable bar() {
 
 288  *              return new Runnable() {
 
 289  *                      public void run() {}
 
 293  * It returns {'B'} for the member A$B
 
 294  * It returns null for A
 
 295  * It returns {'C'} for the local class A$1$C
 
 296  * It returns null for the anonymous A$1
 
 299 public char[] getInnerSourceName() {
 
 300         if (this.innerInfo != null)
 
 301                 return this.innerInfo.getSourceName();
 
 305  * Answer the resolved names of the receiver's interfaces in the
 
 306  * class file format as specified in section 4.2 of the Java 2 VM spec
 
 307  * or null if the array is empty.
 
 309  * For example, java.lang.String is java/lang/String.
 
 312 public char[][] getInterfaceNames() {
 
 313         return this.interfaceNames;
 
 316  * Answer the receiver's nested types or null if the array is empty.
 
 318  * This nested type info is extracted from the inner class attributes.
 
 319  * Ask the name environment to find a member type using its compound name
 
 320  * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
 
 322 public IBinaryNestedType[] getMemberTypes() {
 
 323         // we might have some member types of the current type
 
 324         if (this.innerInfos == null) return null;
 
 326         int length = this.innerInfos.length;
 
 327         int startingIndex = this.innerInfo != null ? this.innerInfoIndex + 1 : 0;
 
 328         if (length != startingIndex) {
 
 329                 IBinaryNestedType[] memberTypes = 
 
 330                         new IBinaryNestedType[length - this.innerInfoIndex]; 
 
 331                 int memberTypeIndex = 0;
 
 332                 for (int i = startingIndex; i < length; i++) {
 
 333                         InnerClassInfo currentInnerInfo = this.innerInfos[i];
 
 334                         int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
 
 335                         int innerNameIndex = currentInnerInfo.innerNameIndex;
 
 337                          * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
 
 338                          * attribute entry is a member class, but due to the bug:
 
 339                          * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
 
 340                          * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
 
 342                         if (outerClassNameIdx != 0 && innerNameIndex != 0 && outerClassNameIdx == this.classNameIndex) {
 
 343                                 memberTypes[memberTypeIndex++] = currentInnerInfo;
 
 346                 if (memberTypeIndex == 0) return null;
 
 347                 if (memberTypeIndex != memberTypes.length) {
 
 348                         // we need to resize the memberTypes array. Some local or anonymous classes
 
 349                         // are present in the current class.
 
 353                                 (memberTypes = new IBinaryNestedType[memberTypeIndex]), 
 
 362  * Answer the receiver's this.methods or null if the array is empty.
 
 363  * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
 
 365 public IBinaryMethod[] getMethods() {
 
 369  * Answer an int whose bits are set according the access constants
 
 370  * defined by the VM spec.
 
 371  * Set the AccDeprecated and AccSynthetic bits if necessary
 
 374 public int getModifiers() {
 
 375         if (this.innerInfo != null) {
 
 376                 return this.innerInfo.getModifiers();
 
 378         return this.accessFlags;
 
 381  * Answer the resolved name of the type in the
 
 382  * class file format as specified in section 4.2 of the Java 2 VM spec.
 
 384  * For example, java.lang.String is java/lang/String.
 
 387 public char[] getName() {
 
 388         return this.className;
 
 391  * Answer the resolved name of the receiver's superclass in the
 
 392  * class file format as specified in section 4.2 of the Java 2 VM spec
 
 393  * or null if it does not have one.
 
 395  * For example, java.lang.String is java/lang/String.
 
 398 public char[] getSuperclassName() {
 
 399         return this.superclassName;
 
 402  * Answer true if the receiver is an anonymous type, false otherwise
 
 404  * @return <CODE>boolean</CODE>
 
 406 public boolean isAnonymous() {
 
 407         if (this.innerInfo == null) return false;
 
 408         char[] sourceName = this.innerInfo.getSourceName();
 
 409         return (sourceName == null || sourceName.length == 0);
 
 412  * Answer whether the receiver contains the resolved binary form
 
 413  * or the unresolved source form of the type.
 
 416 public boolean isBinaryType() {
 
 420  * Answer true if the receiver is a class. False otherwise.
 
 423 public boolean isClass() {
 
 424         return (getModifiers() & AccInterface) == 0;
 
 427  * Answer true if the receiver is an interface. False otherwise.
 
 430 public boolean isInterface() {
 
 431         return (getModifiers() & AccInterface) != 0;
 
 434  * Answer true if the receiver is a local type, false otherwise
 
 436  * @return <CODE>boolean</CODE>
 
 438 public boolean isLocal() {
 
 440                 this.innerInfo != null 
 
 441                 && this.innerInfo.getEnclosingTypeName() == null 
 
 442                 && this.innerInfo.getSourceName() != null;
 
 445  * Answer true if the receiver is a member type, false otherwise
 
 447  * @return <CODE>boolean</CODE>
 
 449 public boolean isMember() {
 
 450         return this.innerInfo != null && this.innerInfo.getEnclosingTypeName() != null;
 
 453  * Answer true if the receiver is a nested type, false otherwise
 
 455  * @return <CODE>boolean</CODE>
 
 457 public boolean isNestedType() {
 
 458         return this.innerInfo != null;
 
 460 public static ClassFileReader read(File file) throws ClassFormatException, IOException {
 
 461         return read(file, false);
 
 463 public static ClassFileReader read(File file, boolean fullyInitialize) throws ClassFormatException, IOException {
 
 464         byte classFileBytes[] = Util.getFileByteContent(file);
 
 465         ClassFileReader classFileReader = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
 
 466         if (fullyInitialize) {
 
 467                 classFileReader.initialize();
 
 469         return classFileReader;
 
 471 public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
 
 472         return read(fileName, false);
 
 474 public static ClassFileReader read(String fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException {
 
 475         return read(new File(fileName), fullyInitialize);
 
 477 public static ClassFileReader read(
 
 478         java.util.zip.ZipFile zip, 
 
 480         throws ClassFormatException, java.io.IOException {
 
 481                 return read(zip, filename, false);
 
 483 public static ClassFileReader read(
 
 484         java.util.zip.ZipFile zip, 
 
 486         boolean fullyInitialize)
 
 487         throws ClassFormatException, java.io.IOException {
 
 488         java.util.zip.ZipEntry ze = zip.getEntry(filename);
 
 491         byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
 
 492         ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
 
 493         if (fullyInitialize) {
 
 494                 classFileReader.initialize();
 
 496         return classFileReader;
 
 500  * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
 
 504 public char[] sourceFileName() {
 
 505         return this.sourceFileName;
 
 507 public String toString() {
 
 508         java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
 
 509         java.io.PrintWriter print = new java.io.PrintWriter(out);
 
 511         print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
 
 512         print.println(" this.className: " + new String(getName())); //$NON-NLS-1$
 
 513         print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
 
 514         print.println(" access_flags: " + ClassFileStruct.printTypeModifiers(this.accessFlags()) + "(" + this.accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
 
 517         return out.toString();
 
 520  * Check if the receiver has structural changes compare to the byte array in argument.
 
 521  * Structural changes are:
 
 522  * - modifiers changes for the class, the this.fields or the this.methods
 
 523  * - signature changes for this.fields or this.methods.
 
 524  * - changes in the number of this.fields or this.methods
 
 525  * - changes for field constants
 
 526  * - changes for thrown exceptions
 
 527  * - change for the super class or any super interfaces.
 
 528  * - changes for member types name or modifiers
 
 529  * If any of these changes occurs, the method returns true. false otherwise. 
 
 530  * The synthetic fields are included and the members are not required to be sorted.
 
 531  * @param newBytes the bytes of the .class file we want to compare the receiver to
 
 532  * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
 
 534 public boolean hasStructuralChanges(byte[] newBytes) {
 
 535         return hasStructuralChanges(newBytes, true, true);
 
 538  * Check if the receiver has structural changes compare to the byte array in argument.
 
 539  * Structural changes are:
 
 540  * - modifiers changes for the class, the this.fields or the this.methods
 
 541  * - signature changes for this.fields or this.methods.
 
 542  * - changes in the number of this.fields or this.methods
 
 543  * - changes for field constants
 
 544  * - changes for thrown exceptions
 
 545  * - change for the super class or any super interfaces.
 
 546  * - changes for member types name or modifiers
 
 547  * If any of these changes occurs, the method returns true. false otherwise.
 
 548  * @param newBytes the bytes of the .class file we want to compare the receiver to
 
 549  * @param orderRequired a boolean indicating whether the members should be sorted or not
 
 550  * @param excludesSynthetics a boolean indicating whether the synthetic members should be used in the comparison
 
 551  * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
 
 553 public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
 
 555                 ClassFileReader newClassFile =
 
 556                         new ClassFileReader(newBytes, this.classFileName);
 
 557                 // type level comparison
 
 559                 if (this.getModifiers() != newClassFile.getModifiers())
 
 562                 if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
 
 565                 char[][] newInterfacesNames = newClassFile.getInterfaceNames();
 
 566                 if (this.interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
 
 567                         int newInterfacesLength = newInterfacesNames == null ? 0 : newInterfacesNames.length;
 
 568                         if (newInterfacesLength != this.interfacesCount)
 
 570                         for (int i = 0, max = this.interfacesCount; i < max; i++)
 
 571                                 if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
 
 576                 IBinaryNestedType[] currentMemberTypes = (IBinaryNestedType[]) this.getMemberTypes();
 
 577                 IBinaryNestedType[] otherMemberTypes = (IBinaryNestedType[]) newClassFile.getMemberTypes();
 
 578                 if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
 
 579                         int currentMemberTypeLength = currentMemberTypes == null ? 0 : currentMemberTypes.length;
 
 580                         int otherMemberTypeLength = otherMemberTypes == null ? 0 : otherMemberTypes.length;
 
 581                         if (currentMemberTypeLength != otherMemberTypeLength)
 
 583                         for (int i = 0; i < currentMemberTypeLength; i++)
 
 584                                 if (!CharOperation.equals(currentMemberTypes[i].getName(), otherMemberTypes[i].getName())
 
 585                                         || currentMemberTypes[i].getModifiers() != otherMemberTypes[i].getModifiers())
 
 590                 FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
 
 591                 int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
 
 592                 boolean compareFields = true;
 
 593                 if (this.fieldsCount == otherFieldInfosLength) {
 
 595                         for (; i < this.fieldsCount; i++)
 
 596                                 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
 
 597                         if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
 
 601                         if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
 
 604                                 if (this.fieldsCount != 0)
 
 605                                         Arrays.sort(this.fields);
 
 606                                 if (otherFieldInfosLength != 0)
 
 607                                         Arrays.sort(otherFieldInfos);
 
 609                         if (excludesSynthetic) {
 
 610                                 if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
 
 613                                 for (int i = 0; i < this.fieldsCount; i++)
 
 614                                         if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
 
 620                 MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
 
 621                 int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
 
 622                 boolean compareMethods = true;
 
 623                 if (this.methodsCount == otherMethodInfosLength) {
 
 625                         for (; i < this.methodsCount; i++)
 
 626                                 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
 
 627                         if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
 
 630                 if (compareMethods) {
 
 631                         if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
 
 634                                 if (this.methodsCount != 0)
 
 635                                         Arrays.sort(this.methods);
 
 636                                 if (otherMethodInfosLength != 0)
 
 637                                         Arrays.sort(otherMethodInfos);  
 
 639                         if (excludesSynthetic) {
 
 640                                 if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
 
 643                                 for (int i = 0; i < this.methodsCount; i++)
 
 644                                         if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
 
 650         } catch (ClassFormatException e) {
 
 654 private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
 
 655         int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
 
 656         int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
 
 660         end : while (index1 < length1 && index2 < length2) {
 
 661                 while (currentFieldInfos[index1].isSynthetic()) {
 
 662                         if (++index1 >= length1) break end;
 
 664                 while (otherFieldInfos[index2].isSynthetic()) {
 
 665                         if (++index2 >= length2) break end;
 
 667                 if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
 
 671         while (index1 < length1) {
 
 672                 if (!currentFieldInfos[index1++].isSynthetic()) return true;
 
 674         while (index2 < length2) {
 
 675                 if (!otherFieldInfos[index2++].isSynthetic()) return true;
 
 679 private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
 
 680         if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
 
 682         if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
 
 684         if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
 
 686         if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
 
 688         if (currentFieldInfo.hasConstant()) {
 
 689                 Constant currentConstant = currentFieldInfo.getConstant();
 
 690                 Constant otherConstant = otherFieldInfo.getConstant();
 
 691                 if (currentConstant.typeID() != otherConstant.typeID())
 
 693                 if (!currentConstant.getClass().equals(otherConstant.getClass()))
 
 695                 switch (currentConstant.typeID()) {
 
 697                                 return currentConstant.intValue() != otherConstant.intValue();
 
 698                         case TypeIds.T_byte :
 
 699                                 return currentConstant.byteValue() != otherConstant.byteValue();
 
 700                         case TypeIds.T_short :
 
 701                                 return currentConstant.shortValue() != otherConstant.shortValue();
 
 702                         case TypeIds.T_char :
 
 703                                 return currentConstant.charValue() != otherConstant.charValue();
 
 704                         case TypeIds.T_float :
 
 705                                 return currentConstant.floatValue() != otherConstant.floatValue();
 
 706                         case TypeIds.T_double :
 
 707                                 return currentConstant.doubleValue() != otherConstant.doubleValue();
 
 708                         case TypeIds.T_boolean :
 
 709                                 return currentConstant.booleanValue() != otherConstant.booleanValue();
 
 710                         case TypeIds.T_String :
 
 711                                 return !currentConstant.stringValue().equals(otherConstant.stringValue());
 
 712                         case TypeIds.T_null :
 
 713                                 return otherConstant != NullConstant.Default;
 
 718 private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
 
 719         int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
 
 720         int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
 
 725         end : while (index1 < length1 && index2 < length2) {
 
 726                 while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
 
 727                         if (++index1 >= length1) break end;
 
 729                 while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
 
 730                         if (++index2 >= length2) break end;
 
 732                 if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
 
 736         while (index1 < length1) {
 
 737                 if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
 
 739         while (index2 < length2) {
 
 740                 if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
 
 744 private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
 
 745         if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
 
 747         if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
 
 749         if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
 
 752         char[][] currentThrownExceptions = currentMethodInfo.getExceptionTypeNames();
 
 753         char[][] otherThrownExceptions = otherMethodInfo.getExceptionTypeNames();
 
 754         if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
 
 755                 int currentThrownExceptionsLength = currentThrownExceptions == null ? 0 : currentThrownExceptions.length;
 
 756                 int otherThrownExceptionsLength = otherThrownExceptions == null ? 0 : otherThrownExceptions.length;
 
 757                 if (currentThrownExceptionsLength != otherThrownExceptionsLength)
 
 759                 for (int k = 0; k < currentThrownExceptionsLength; k++)
 
 760                         if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
 
 766  * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
 
 767  * will be therefore fully initialized and we can get rid of the bytes.
 
 769 private void initialize() {
 
 770         for (int i = 0, max = fieldsCount; i < max; i++) {
 
 771                 fields[i].initialize();
 
 773         for (int i = 0, max = methodsCount; i < max; i++) {
 
 774                 methods[i].initialize();
 
 776         if (innerInfos != null) {
 
 777                 for (int i = 0, max = innerInfos.length; i < max; i++) {
 
 778                         innerInfos[i].initialize();
 
 783 protected void reset() {
 
 784         this.constantPoolOffsets = null;