Makefile fixup
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / classfmt / ClassFileReader.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.classfmt;
12
13 import java.io.File;
14 import java.io.IOException;
15 import java.util.Arrays;
16
17 import org.eclipse.jdt.core.compiler.CharOperation;
18 import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
19 import org.eclipse.jdt.internal.compiler.env.*;
20 import org.eclipse.jdt.internal.compiler.impl.Constant;
21 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
22 import org.eclipse.jdt.internal.compiler.util.Util;
23
24 public class ClassFileReader extends ClassFileStruct implements AttributeNamesConstants, IBinaryType {
25         private int constantPoolCount;
26         private int[] constantPoolOffsets;
27         private long version;
28         private int accessFlags;
29         private char[] className;
30         private char[] superclassName;
31         private int interfacesCount;
32         private char[][] interfaceNames;
33         private int fieldsCount;
34         private FieldInfo[] fields;
35         private int methodsCount;
36         private MethodInfo[] methods;
37         private InnerClassInfo[] innerInfos;
38         private char[] sourceFileName;
39         // initialized in case the .class file is a nested type
40         private InnerClassInfo innerInfo;
41         private char[] classFileName;
42         private int classNameIndex;
43         private int innerInfoIndex;
44 /**
45  * @param classFileBytes byte[]
46  *              Actual bytes of a .class file
47  * 
48  * @param fileName char[]
49  *              Actual name of the file that contains the bytes, can be null
50  * 
51  * @param fullyInitialize boolean
52  *              Flag to fully initialize the new object
53  * @exception ClassFormatException
54  */
55 public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
56         // This method looks ugly but is actually quite simple, the constantPool is constructed
57         // in 3 passes.  All non-primitive constant pool members that usually refer to other members
58         // by index are tweaked to have their value in inst vars, this minor cost at read-time makes
59         // all subsequent uses of the constant pool element faster.
60         super(classFileBytes, 0);
61         this.classFileName = fileName;
62         int readOffset = 10;
63         try {
64                 this.version = ((long)this.u2At(6) << 16) + this.u2At(4); // major<<16 + minor
65                 constantPoolCount = this.u2At(8);
66                 // Pass #1 - Fill in all primitive constants
67                 this.constantPoolOffsets = new int[constantPoolCount];
68                 for (int i = 1; i < constantPoolCount; i++) {
69                         int tag = this.u1At(readOffset);
70                         switch (tag) {
71                                 case Utf8Tag :
72                                         this.constantPoolOffsets[i] = readOffset;
73                                         readOffset += u2At(readOffset + 1);
74                                         readOffset += ConstantUtf8FixedSize;
75                                         break;
76                                 case IntegerTag :
77                                         this.constantPoolOffsets[i] = readOffset;
78                                         readOffset += ConstantIntegerFixedSize;
79                                         break;
80                                 case FloatTag :
81                                         this.constantPoolOffsets[i] = readOffset;
82                                         readOffset += ConstantFloatFixedSize;
83                                         break;
84                                 case LongTag :
85                                         this.constantPoolOffsets[i] = readOffset;
86                                         readOffset += ConstantLongFixedSize;
87                                         i++;
88                                         break;
89                                 case DoubleTag :
90                                         this.constantPoolOffsets[i] = readOffset;
91                                         readOffset += ConstantDoubleFixedSize;
92                                         i++;
93                                         break;
94                                 case ClassTag :
95                                         this.constantPoolOffsets[i] = readOffset;
96                                         readOffset += ConstantClassFixedSize;
97                                         break;
98                                 case StringTag :
99                                         this.constantPoolOffsets[i] = readOffset;
100                                         readOffset += ConstantStringFixedSize;
101                                         break;
102                                 case FieldRefTag :
103                                         this.constantPoolOffsets[i] = readOffset;
104                                         readOffset += ConstantFieldRefFixedSize;
105                                         break;
106                                 case MethodRefTag :
107                                         this.constantPoolOffsets[i] = readOffset;
108                                         readOffset += ConstantMethodRefFixedSize;
109                                         break;
110                                 case InterfaceMethodRefTag :
111                                         this.constantPoolOffsets[i] = readOffset;
112                                         readOffset += ConstantInterfaceMethodRefFixedSize;
113                                         break;
114                                 case NameAndTypeTag :
115                                         this.constantPoolOffsets[i] = readOffset;
116                                         readOffset += ConstantNameAndTypeFixedSize;
117                         }
118                 }
119                 // Read and validate access flags
120                 this.accessFlags = u2At(readOffset);
121                 readOffset += 2;
122
123                 // Read the classname, use exception handlers to catch bad format
124                 this.classNameIndex = u2At(readOffset);
125                 this.className = getConstantClassNameAt(this.classNameIndex);
126                 readOffset += 2;
127
128                 // Read the superclass name, can be null for java.lang.Object
129                 int superclassNameIndex = u2At(readOffset);
130                 readOffset += 2;
131                 // if superclassNameIndex is equals to 0 there is no need to set a value for the 
132                 // field this.superclassName. null is fine.
133                 if (superclassNameIndex != 0) {
134                         this.superclassName = getConstantClassNameAt(superclassNameIndex);
135                 }
136
137                 // Read the interfaces, use exception handlers to catch bad format
138                 this.interfacesCount = u2At(readOffset);
139                 readOffset += 2;
140                 if (this.interfacesCount != 0) {
141                         this.interfaceNames = new char[this.interfacesCount][];
142                         for (int i = 0; i < this.interfacesCount; i++) {
143                                 this.interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
144                                 readOffset += 2;
145                         }
146                 }
147                 // Read the this.fields, use exception handlers to catch bad format
148                 this.fieldsCount = u2At(readOffset);
149                 readOffset += 2;
150                 if (this.fieldsCount != 0) {
151                         FieldInfo field;
152                         this.fields = new FieldInfo[this.fieldsCount];
153                         for (int i = 0; i < this.fieldsCount; i++) {
154                                 field = new FieldInfo(reference, this.constantPoolOffsets, readOffset);
155                                 this.fields[i] = field;
156                                 readOffset += field.sizeInBytes();
157                         }
158                 }
159                 // Read the this.methods
160                 this.methodsCount = u2At(readOffset);
161                 readOffset += 2;
162                 if (this.methodsCount != 0) {
163                         this.methods = new MethodInfo[this.methodsCount];
164                         MethodInfo method;
165                         for (int i = 0; i < this.methodsCount; i++) {
166                                 method = new MethodInfo(reference, this.constantPoolOffsets, readOffset);
167                                 this.methods[i] = method;
168                                 readOffset += method.sizeInBytes();
169                         }
170                 }
171
172                 // Read the attributes
173                 int attributesCount = u2At(readOffset);
174                 readOffset += 2;
175
176                 for (int i = 0; i < attributesCount; i++) {
177                         int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
178                         char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
179                         if (CharOperation.equals(attributeName, DeprecatedName)) {
180                                 this.accessFlags |= AccDeprecated;
181                         } else {
182                                 if (CharOperation.equals(attributeName, InnerClassName)) {
183                                         int innerOffset = readOffset + 6;
184                                         int number_of_classes = u2At(innerOffset);
185                                         if (number_of_classes != 0) {
186                                                 innerOffset+= 2;
187                                                 this.innerInfos = new InnerClassInfo[number_of_classes];
188                                                 for (int j = 0; j < number_of_classes; j++) {
189                                                         this.innerInfos[j] = 
190                                                                 new InnerClassInfo(reference, this.constantPoolOffsets, innerOffset); 
191                                                         if (this.classNameIndex == this.innerInfos[j].innerClassNameIndex) {
192                                                                 this.innerInfo = this.innerInfos[j];
193                                                                 this.innerInfoIndex = j;
194                                                         }
195                                                         innerOffset += 8;
196                                                 }
197                                         }
198                                 } else {
199                                         if (CharOperation.equals(attributeName, SourceName)) {
200                                                 utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
201                                                 this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
202                                         } else {
203                                                 if (CharOperation.equals(attributeName, SyntheticName)) {
204                                                         this.accessFlags |= AccSynthetic;
205                                                 }
206                                         }
207                                 }
208                         }
209                         readOffset += (6 + u4At(readOffset + 2));
210                 }
211                 if (fullyInitialize) {
212                         this.initialize();
213                 }
214         } catch(ClassFormatException e) {
215                 throw e;
216         } catch (Exception e) {
217                 throw new ClassFormatException(
218                         ClassFormatException.ErrTruncatedInput, 
219                         readOffset); 
220         }
221 }
222
223 /**
224  * @param classFileBytes Actual bytes of a .class file
225  * @param fileName      Actual name of the file that contains the bytes, can be null
226  * 
227  * @exception ClassFormatException
228  */
229 public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
230         this(classFileBytes, fileName, false);
231 }
232
233 /**
234  *      Answer the receiver's access flags.  The value of the access_flags
235  *      item is a mask of modifiers used with class and interface declarations.
236  *  @return int 
237  */
238 public int accessFlags() {
239         return this.accessFlags;
240 }
241 /**
242  * Answer the char array that corresponds to the class name of the constant class.
243  * constantPoolIndex is the index in the constant pool that is a constant class entry.
244  *
245  * @param constantPoolIndex int
246  * @return char[]
247  */
248 private char[] getConstantClassNameAt(int constantPoolIndex) {
249         int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
250         return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
251 }
252 /**
253  * Answer the int array that corresponds to all the offsets of each entry in the constant pool
254  *
255  * @return int[]
256  */
257 public int[] getConstantPoolOffsets() {
258         return this.constantPoolOffsets;
259 }
260 /*
261  * Answer the resolved compoundName of the enclosing type
262  * or null if the receiver is a top level type.
263  */
264 public char[] getEnclosingTypeName() {
265         if (this.innerInfo != null && !this.isAnonymous()) {
266                 return this.innerInfo.getEnclosingTypeName();
267         }
268         return null;
269 }
270 /**
271  * Answer the receiver's this.fields or null if the array is empty.
272  * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
273  */
274 public IBinaryField[] getFields() {
275         return this.fields;
276 }
277 /**
278  * Answer the file name which defines the type.
279  * The format is unspecified.
280  */
281 public char[] getFileName() {
282         return this.classFileName;
283 }
284 /**
285  * 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.
286  * e.g.
287  * public class A {
288  *      public class B {
289  *      }
290  *      public void foo() {
291  *              class C {}
292  *      }
293  *      public Runnable bar() {
294  *              return new Runnable() {
295  *                      public void run() {}
296  *              };
297  *      }
298  * }
299  * It returns {'B'} for the member A$B
300  * It returns null for A
301  * It returns {'C'} for the local class A$1$C
302  * It returns null for the anonymous A$1
303  * @return char[]
304  */
305 public char[] getInnerSourceName() {
306         if (this.innerInfo != null)
307                 return this.innerInfo.getSourceName();
308         return null;
309 }
310 /**
311  * Answer the resolved names of the receiver's interfaces in the
312  * class file format as specified in section 4.2 of the Java 2 VM spec
313  * or null if the array is empty.
314  *
315  * For example, java.lang.String is java/lang/String.
316  * @return char[][]
317  */
318 public char[][] getInterfaceNames() {
319         return this.interfaceNames;
320 }
321 /**
322  * Answer the receiver's nested types or null if the array is empty.
323  *
324  * This nested type info is extracted from the inner class attributes.
325  * Ask the name environment to find a member type using its compound name
326  * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
327  */
328 public IBinaryNestedType[] getMemberTypes() {
329         // we might have some member types of the current type
330         if (this.innerInfos == null) return null;
331
332         int length = this.innerInfos.length;
333         int startingIndex = this.innerInfo != null ? this.innerInfoIndex + 1 : 0;
334         if (length != startingIndex) {
335                 IBinaryNestedType[] memberTypes = 
336                         new IBinaryNestedType[length - this.innerInfoIndex]; 
337                 int memberTypeIndex = 0;
338                 for (int i = startingIndex; i < length; i++) {
339                         InnerClassInfo currentInnerInfo = this.innerInfos[i];
340                         int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
341                         int innerNameIndex = currentInnerInfo.innerNameIndex;
342                         /*
343                          * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
344                          * attribute entry is a member class, but due to the bug:
345                          * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
346                          * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
347                          * 
348                          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=49879
349                          * From JavaMail 1.2, the class javax.mail.Folder contains an anonymous class in the
350                          * terminateQueue() method for which the inner attribute is boggus.
351                          * outerClassNameIdx is not 0, innerNameIndex is not 0, but the sourceName length is 0.
352                          * So I added this extra check to filter out this anonymous class from the 
353                          * member types.
354                          */
355                         if (outerClassNameIdx != 0
356                                 && innerNameIndex != 0
357                                 && outerClassNameIdx == this.classNameIndex
358                                 && currentInnerInfo.getSourceName().length != 0) {
359                                 memberTypes[memberTypeIndex++] = currentInnerInfo;
360                         }
361                 }
362                 if (memberTypeIndex == 0) return null;
363                 if (memberTypeIndex != memberTypes.length) {
364                         // we need to resize the memberTypes array. Some local or anonymous classes
365                         // are present in the current class.
366                         System.arraycopy(
367                                 memberTypes, 
368                                 0, 
369                                 (memberTypes = new IBinaryNestedType[memberTypeIndex]), 
370                                 0, 
371                                 memberTypeIndex); 
372                 }
373                 return memberTypes;
374         }
375         return null;
376 }
377 /**
378  * Answer the receiver's this.methods or null if the array is empty.
379  * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
380  */
381 public IBinaryMethod[] getMethods() {
382         return this.methods;
383 }
384 /**
385  * Answer an int whose bits are set according the access constants
386  * defined by the VM spec.
387  * Set the AccDeprecated and AccSynthetic bits if necessary
388  * @return int
389  */
390 public int getModifiers() {
391         if (this.innerInfo != null) {
392                 if ((this.accessFlags & AccDeprecated) != 0) {
393                         return this.innerInfo.getModifiers() | AccDeprecated;
394                 } else {
395                         return this.innerInfo.getModifiers();
396                 }
397         }
398         return this.accessFlags;
399 }
400 /**
401  * Answer the resolved name of the type in the
402  * class file format as specified in section 4.2 of the Java 2 VM spec.
403  *
404  * For example, java.lang.String is java/lang/String.
405  * @return char[]
406  */
407 public char[] getName() {
408         return this.className;
409 }
410 /**
411  * Answer the resolved name of the receiver's superclass in the
412  * class file format as specified in section 4.2 of the Java 2 VM spec
413  * or null if it does not have one.
414  *
415  * For example, java.lang.String is java/lang/String.
416  * @return char[]
417  */
418 public char[] getSuperclassName() {
419         return this.superclassName;
420 }
421 /**
422  * Answer the major/minor version defined in this class file according to the VM spec.
423  * as a long: (major<<16)+minor
424  * @return the major/minor version found
425  */
426 public long getVersion() {
427         return this.version;
428 }
429 /**
430  * Answer true if the receiver is an anonymous type, false otherwise
431  *
432  * @return <CODE>boolean</CODE>
433  */
434 public boolean isAnonymous() {
435         if (this.innerInfo == null) return false;
436         char[] sourceName = this.innerInfo.getSourceName();
437         return (sourceName == null || sourceName.length == 0);
438 }
439 /**
440  * Answer whether the receiver contains the resolved binary form
441  * or the unresolved source form of the type.
442  * @return boolean
443  */
444 public boolean isBinaryType() {
445         return true;
446 }
447 /**
448  * Answer true if the receiver is a class. False otherwise.
449  * @return boolean
450  */
451 public boolean isClass() {
452         return (getModifiers() & AccInterface) == 0;
453 }
454 /**
455  * Answer true if the receiver is an interface. False otherwise.
456  * @return boolean
457  */
458 public boolean isInterface() {
459         return (getModifiers() & AccInterface) != 0;
460 }
461 /**
462  * Answer true if the receiver is a local type, false otherwise
463  *
464  * @return <CODE>boolean</CODE>
465  */
466 public boolean isLocal() {
467         if (this.innerInfo == null) return false;
468         if (this.innerInfo.getEnclosingTypeName() != null) return false;
469         char[] sourceName = this.innerInfo.getSourceName();
470         return (sourceName != null && sourceName.length > 0);   
471 }
472 /**
473  * Answer true if the receiver is a member type, false otherwise
474  *
475  * @return <CODE>boolean</CODE>
476  */
477 public boolean isMember() {
478         if (this.innerInfo == null) return false;
479         if (this.innerInfo.getEnclosingTypeName() == null) return false;
480         char[] sourceName = this.innerInfo.getSourceName();
481         return (sourceName != null && sourceName.length > 0);    // protection against ill-formed attributes (67600)
482 }
483 /**
484  * Answer true if the receiver is a nested type, false otherwise
485  *
486  * @return <CODE>boolean</CODE>
487  */
488 public boolean isNestedType() {
489         return this.innerInfo != null;
490 }
491 public static ClassFileReader read(File file) throws ClassFormatException, IOException {
492         return read(file, false);
493 }
494 public static ClassFileReader read(File file, boolean fullyInitialize) throws ClassFormatException, IOException {
495         byte classFileBytes[] = Util.getFileByteContent(file);
496         ClassFileReader classFileReader = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
497         if (fullyInitialize) {
498                 classFileReader.initialize();
499         }
500         return classFileReader;
501 }
502 public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
503         return read(fileName, false);
504 }
505 public static ClassFileReader read(String fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException {
506         return read(new File(fileName), fullyInitialize);
507 }
508 public static ClassFileReader read(
509         java.util.zip.ZipFile zip, 
510         String filename)
511         throws ClassFormatException, java.io.IOException {
512                 return read(zip, filename, false);
513 }
514 public static ClassFileReader read(
515         java.util.zip.ZipFile zip, 
516         String filename,
517         boolean fullyInitialize)
518         throws ClassFormatException, java.io.IOException {
519         java.util.zip.ZipEntry ze = zip.getEntry(filename);
520         if (ze == null)
521                 return null;
522         byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
523         ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
524         if (fullyInitialize) {
525                 classFileReader.initialize();
526         }
527         return classFileReader;
528 }
529
530 /**
531  * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
532  * 
533  * @return char[]
534  */
535 public char[] sourceFileName() {
536         return this.sourceFileName;
537 }
538 public String toString() {
539         java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
540         java.io.PrintWriter print = new java.io.PrintWriter(out);
541         
542         print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
543         print.println(" this.className: " + new String(getName())); //$NON-NLS-1$
544         print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
545         print.println(" access_flags: " + ClassFileStruct.printTypeModifiers(this.accessFlags()) + "(" + this.accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
546
547         print.flush();
548         return out.toString();
549 }
550 /**
551  * Check if the receiver has structural changes compare to the byte array in argument.
552  * Structural changes are:
553  * - modifiers changes for the class, the this.fields or the this.methods
554  * - signature changes for this.fields or this.methods.
555  * - changes in the number of this.fields or this.methods
556  * - changes for field constants
557  * - changes for thrown exceptions
558  * - change for the super class or any super interfaces.
559  * - changes for member types name or modifiers
560  * If any of these changes occurs, the method returns true. false otherwise. 
561  * The synthetic fields are included and the members are not required to be sorted.
562  * @param newBytes the bytes of the .class file we want to compare the receiver to
563  * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
564  */
565 public boolean hasStructuralChanges(byte[] newBytes) {
566         return hasStructuralChanges(newBytes, true, true);
567 }
568 /**
569  * Check if the receiver has structural changes compare to the byte array in argument.
570  * Structural changes are:
571  * - modifiers changes for the class, the this.fields or the this.methods
572  * - signature changes for this.fields or this.methods.
573  * - changes in the number of this.fields or this.methods
574  * - changes for field constants
575  * - changes for thrown exceptions
576  * - change for the super class or any super interfaces.
577  * - changes for member types name or modifiers
578  * If any of these changes occurs, the method returns true. false otherwise.
579  * @param newBytes the bytes of the .class file we want to compare the receiver to
580  * @param orderRequired a boolean indicating whether the members should be sorted or not
581  * @param excludesSynthetic a boolean indicating whether the synthetic members should be used in the comparison
582  * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
583  */
584 public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
585         try {
586                 ClassFileReader newClassFile =
587                         new ClassFileReader(newBytes, this.classFileName);
588                 // type level comparison
589                 // modifiers
590                 if (this.getModifiers() != newClassFile.getModifiers())
591                         return true;
592                 // superclass
593                 if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
594                         return true;
595                 // interfaces
596                 char[][] newInterfacesNames = newClassFile.getInterfaceNames();
597                 if (this.interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
598                         int newInterfacesLength = newInterfacesNames == null ? 0 : newInterfacesNames.length;
599                         if (newInterfacesLength != this.interfacesCount)
600                                 return true;
601                         for (int i = 0, max = this.interfacesCount; i < max; i++)
602                                 if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
603                                         return true;
604                 }
605
606                 // member types
607                 IBinaryNestedType[] currentMemberTypes = this.getMemberTypes();
608                 IBinaryNestedType[] otherMemberTypes = newClassFile.getMemberTypes();
609                 if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
610                         int currentMemberTypeLength = currentMemberTypes == null ? 0 : currentMemberTypes.length;
611                         int otherMemberTypeLength = otherMemberTypes == null ? 0 : otherMemberTypes.length;
612                         if (currentMemberTypeLength != otherMemberTypeLength)
613                                 return true;
614                         for (int i = 0; i < currentMemberTypeLength; i++)
615                                 if (!CharOperation.equals(currentMemberTypes[i].getName(), otherMemberTypes[i].getName())
616                                         || currentMemberTypes[i].getModifiers() != otherMemberTypes[i].getModifiers())
617                                                 return true;
618                 }
619
620                 // fields
621                 FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
622                 int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
623                 boolean compareFields = true;
624                 if (this.fieldsCount == otherFieldInfosLength) {
625                         int i = 0;
626                         for (; i < this.fieldsCount; i++)
627                                 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
628                         if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
629                                 return true;
630                 }
631                 if (compareFields) {
632                         if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
633                                 return true;
634                         if (orderRequired) {
635                                 if (this.fieldsCount != 0)
636                                         Arrays.sort(this.fields);
637                                 if (otherFieldInfosLength != 0)
638                                         Arrays.sort(otherFieldInfos);
639                         }
640                         if (excludesSynthetic) {
641                                 if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
642                                         return true;
643                         } else {
644                                 for (int i = 0; i < this.fieldsCount; i++)
645                                         if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
646                                                 return true;
647                         }
648                 }
649                 
650                 // methods
651                 MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
652                 int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
653                 boolean compareMethods = true;
654                 if (this.methodsCount == otherMethodInfosLength) {
655                         int i = 0;
656                         for (; i < this.methodsCount; i++)
657                                 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
658                         if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
659                                 return true;
660                 }
661                 if (compareMethods) {
662                         if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
663                                 return true;
664                         if (orderRequired) {
665                                 if (this.methodsCount != 0)
666                                         Arrays.sort(this.methods);
667                                 if (otherMethodInfosLength != 0)
668                                         Arrays.sort(otherMethodInfos);  
669                         }
670                         if (excludesSynthetic) {
671                                 if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
672                                         return true;
673                         } else {
674                                 for (int i = 0; i < this.methodsCount; i++)
675                                         if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
676                                                 return true;
677                         }
678                 }
679
680                 return false;
681         } catch (ClassFormatException e) {
682                 return true;
683         }
684 }
685 private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
686         int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
687         int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
688         int index1 = 0;
689         int index2 = 0;
690
691         end : while (index1 < length1 && index2 < length2) {
692                 while (currentFieldInfos[index1].isSynthetic()) {
693                         if (++index1 >= length1) break end;
694                 }
695                 while (otherFieldInfos[index2].isSynthetic()) {
696                         if (++index2 >= length2) break end;
697                 }
698                 if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
699                         return true;
700         }
701
702         while (index1 < length1) {
703                 if (!currentFieldInfos[index1++].isSynthetic()) return true;
704         }
705         while (index2 < length2) {
706                 if (!otherFieldInfos[index2++].isSynthetic()) return true;
707         }
708         return false;
709 }
710 private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
711         if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
712                 return true;
713         if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
714                 return true;
715         if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
716                 return true;
717         if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
718                 return true;
719         if (currentFieldInfo.hasConstant()) {
720                 Constant currentConstant = currentFieldInfo.getConstant();
721                 Constant otherConstant = otherFieldInfo.getConstant();
722                 if (currentConstant.typeID() != otherConstant.typeID())
723                         return true;
724                 if (!currentConstant.getClass().equals(otherConstant.getClass()))
725                         return true;
726                 switch (currentConstant.typeID()) {
727                         case TypeIds.T_int :
728                                 return currentConstant.intValue() != otherConstant.intValue();
729                         case TypeIds.T_byte :
730                                 return currentConstant.byteValue() != otherConstant.byteValue();
731                         case TypeIds.T_short :
732                                 return currentConstant.shortValue() != otherConstant.shortValue();
733                         case TypeIds.T_char :
734                                 return currentConstant.charValue() != otherConstant.charValue();
735                         case TypeIds.T_long :
736                                 return currentConstant.longValue() != otherConstant.longValue();
737                         case TypeIds.T_float :
738                                 return currentConstant.floatValue() != otherConstant.floatValue();
739                         case TypeIds.T_double :
740                                 return currentConstant.doubleValue() != otherConstant.doubleValue();
741                         case TypeIds.T_boolean :
742                                 return currentConstant.booleanValue() != otherConstant.booleanValue();
743                         case TypeIds.T_String :
744                                 return !currentConstant.stringValue().equals(otherConstant.stringValue());
745                 }
746         }
747         return false;
748 }
749 private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
750         int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
751         int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
752         int index1 = 0;
753         int index2 = 0;
754
755         MethodInfo m;
756         end : while (index1 < length1 && index2 < length2) {
757                 while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
758                         if (++index1 >= length1) break end;
759                 }
760                 while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
761                         if (++index2 >= length2) break end;
762                 }
763                 if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
764                         return true;
765         }
766
767         while (index1 < length1) {
768                 if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
769         }
770         while (index2 < length2) {
771                 if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
772         }
773         return false;
774 }
775 private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
776         if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
777                 return true;
778         if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
779                 return true;
780         if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
781                 return true;
782
783         char[][] currentThrownExceptions = currentMethodInfo.getExceptionTypeNames();
784         char[][] otherThrownExceptions = otherMethodInfo.getExceptionTypeNames();
785         if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
786                 int currentThrownExceptionsLength = currentThrownExceptions == null ? 0 : currentThrownExceptions.length;
787                 int otherThrownExceptionsLength = otherThrownExceptions == null ? 0 : otherThrownExceptions.length;
788                 if (currentThrownExceptionsLength != otherThrownExceptionsLength)
789                         return true;
790                 for (int k = 0; k < currentThrownExceptionsLength; k++)
791                         if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
792                                 return true;
793         }
794         return false;
795 }
796 /**
797  * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
798  * will be therefore fully initialized and we can get rid of the bytes.
799  */
800 private void initialize() throws ClassFormatException {
801         try {
802                 for (int i = 0, max = fieldsCount; i < max; i++) {
803                         fields[i].initialize();
804                 }
805                 for (int i = 0, max = methodsCount; i < max; i++) {
806                         methods[i].initialize();
807                 }
808                 if (innerInfos != null) {
809                         for (int i = 0, max = innerInfos.length; i < max; i++) {
810                                 innerInfos[i].initialize();
811                         }
812                 }
813                 this.reset();
814         } catch(RuntimeException e) {
815                 ClassFormatException exception = new ClassFormatException(e, this.classFileName);
816                 throw exception;
817         }
818 }
819 protected void reset() {
820         this.constantPoolOffsets = null;
821         super.reset();
822 }
823
824 }