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