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;
14 import java.io.IOException;
15 import java.util.Arrays;
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;
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;
48 * @param classFileBytes byte[]
49 * Actual bytes of a .class file
51 * @param fileName char[]
52 * Actual name of the file that contains the bytes, can be null
54 * @param fullyInitialize boolean
55 * Flag to fully initialize the new object
56 * @exception ClassFormatException
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;
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);
74 this.constantPoolOffsets[i] = readOffset;
75 readOffset += u2At(readOffset + 1);
76 readOffset += ConstantUtf8FixedSize;
79 this.constantPoolOffsets[i] = readOffset;
80 readOffset += ConstantIntegerFixedSize;
83 this.constantPoolOffsets[i] = readOffset;
84 readOffset += ConstantFloatFixedSize;
87 this.constantPoolOffsets[i] = readOffset;
88 readOffset += ConstantLongFixedSize;
92 this.constantPoolOffsets[i] = readOffset;
93 readOffset += ConstantDoubleFixedSize;
97 this.constantPoolOffsets[i] = readOffset;
98 readOffset += ConstantClassFixedSize;
101 this.constantPoolOffsets[i] = readOffset;
102 readOffset += ConstantStringFixedSize;
105 this.constantPoolOffsets[i] = readOffset;
106 readOffset += ConstantFieldRefFixedSize;
109 this.constantPoolOffsets[i] = readOffset;
110 readOffset += ConstantMethodRefFixedSize;
112 case InterfaceMethodRefTag :
113 this.constantPoolOffsets[i] = readOffset;
114 readOffset += ConstantInterfaceMethodRefFixedSize;
116 case NameAndTypeTag :
117 this.constantPoolOffsets[i] = readOffset;
118 readOffset += ConstantNameAndTypeFixedSize;
121 // Read and validate access flags
122 this.accessFlags = u2At(readOffset);
125 // Read the classname, use exception handlers to catch bad format
126 this.classNameIndex = u2At(readOffset);
127 this.className = getConstantClassNameAt(this.classNameIndex);
130 // Read the superclass name, can be null for java.lang.Object
131 int superclassNameIndex = u2At(readOffset);
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);
139 // Read the interfaces, use exception handlers to catch bad format
140 this.interfacesCount = u2At(readOffset);
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));
149 // Read the this.fields, use exception handlers to catch bad format
150 this.fieldsCount = u2At(readOffset);
152 if (this.fieldsCount != 0) {
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();
161 // Read the this.methods
162 this.methodsCount = u2At(readOffset);
164 if (this.methodsCount != 0) {
165 this.methods = new MethodInfo[this.methodsCount];
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();
174 // Read the attributes
175 int attributesCount = u2At(readOffset);
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;
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++) {
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;
200 if (CharOperation.equals(attributeName, SourceName)) {
201 utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
202 this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
204 if (CharOperation.equals(attributeName, SyntheticName)) {
205 this.accessFlags |= AccSynthetic;
210 readOffset += (6 + u4At(readOffset + 2));
212 if (fullyInitialize) {
215 } catch (Exception e) {
216 throw new ClassFormatException(
217 ClassFormatException.ErrTruncatedInput,
223 * @param classFileBytes Actual bytes of a .class file
224 * @param fileName Actual name of the file that contains the bytes, can be null
226 * @exception ClassFormatException
228 public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
229 this(classFileBytes, fileName, false);
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.
237 public int accessFlags() {
238 return this.accessFlags;
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.
244 * @param int constantPoolIndex
247 private char[] getConstantClassNameAt(int constantPoolIndex) {
248 int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
249 return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
252 * Answer the int array that corresponds to all the offsets of each entry in the constant pool
256 public int[] getConstantPoolOffsets() {
257 return this.constantPoolOffsets;
260 * Answer the resolved compoundName of the enclosing type
261 * or null if the receiver is a top level type.
263 public char[] getEnclosingTypeName() {
264 if (this.innerInfo != null && !this.isAnonymous()) {
265 return this.innerInfo.getEnclosingTypeName();
270 * Answer the receiver's this.fields or null if the array is empty.
271 * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
273 public IBinaryField[] getFields() {
277 * Answer the file name which defines the type.
278 * The format is unspecified.
280 public char[] getFileName() {
281 return this.classFileName;
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.
289 * public void foo() {
292 * public Runnable bar() {
293 * return new Runnable() {
294 * public void run() {}
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
304 public char[] getInnerSourceName() {
305 if (this.innerInfo != null)
306 return this.innerInfo.getSourceName();
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.
314 * For example, java.lang.String is java/lang/String.
317 public char[][] getInterfaceNames() {
318 return this.interfaceNames;
321 * Answer the receiver's nested types or null if the array is empty.
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[]
327 public IBinaryNestedType[] getMemberTypes() {
328 // we might have some member types of the current type
329 if (this.innerInfos == null) return null;
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;
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.
347 if (outerClassNameIdx != 0 && innerNameIndex != 0 && outerClassNameIdx == this.classNameIndex) {
348 memberTypes[memberTypeIndex++] = currentInnerInfo;
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.
358 (memberTypes = new IBinaryNestedType[memberTypeIndex]),
367 * Answer the receiver's this.methods or null if the array is empty.
368 * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
370 public IBinaryMethod[] getMethods() {
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
379 public int getModifiers() {
380 if (this.innerInfo != null) {
381 return this.innerInfo.getModifiers();
383 return this.accessFlags;
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.
389 * For example, java.lang.String is java/lang/String.
392 public char[] getName() {
393 return this.className;
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.
400 * For example, java.lang.String is java/lang/String.
403 public char[] getSuperclassName() {
404 return this.superclassName;
407 * Answer true if the receiver is an anonymous type, false otherwise
409 * @return <CODE>boolean</CODE>
411 public boolean isAnonymous() {
412 if (this.innerInfo == null) return false;
413 char[] sourceName = this.innerInfo.getSourceName();
414 return (sourceName == null || sourceName.length == 0);
417 * Answer whether the receiver contains the resolved binary form
418 * or the unresolved source form of the type.
421 public boolean isBinaryType() {
425 * Answer true if the receiver is a class. False otherwise.
428 public boolean isClass() {
429 return (getModifiers() & AccInterface) == 0;
432 * Answer true if the receiver is an interface. False otherwise.
435 public boolean isInterface() {
436 return (getModifiers() & AccInterface) != 0;
439 * Answer true if the receiver is a local type, false otherwise
441 * @return <CODE>boolean</CODE>
443 public boolean isLocal() {
445 this.innerInfo != null
446 && this.innerInfo.getEnclosingTypeName() == null
447 && this.innerInfo.getSourceName() != null;
450 * Answer true if the receiver is a member type, false otherwise
452 * @return <CODE>boolean</CODE>
454 public boolean isMember() {
455 return this.innerInfo != null && this.innerInfo.getEnclosingTypeName() != null;
458 * Answer true if the receiver is a nested type, false otherwise
460 * @return <CODE>boolean</CODE>
462 public boolean isNestedType() {
463 return this.innerInfo != null;
465 public static ClassFileReader read(File file) throws ClassFormatException, IOException {
466 return read(file, false);
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();
474 return classFileReader;
476 public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
477 return read(fileName, false);
479 public static ClassFileReader read(String fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException {
480 return read(new File(fileName), fullyInitialize);
482 public static ClassFileReader read(
483 java.util.zip.ZipFile zip,
485 throws ClassFormatException, java.io.IOException {
486 return read(zip, filename, false);
488 public static ClassFileReader read(
489 java.util.zip.ZipFile zip,
491 boolean fullyInitialize)
492 throws ClassFormatException, java.io.IOException {
493 java.util.zip.ZipEntry ze = zip.getEntry(filename);
496 byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
497 ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
498 if (fullyInitialize) {
499 classFileReader.initialize();
501 return classFileReader;
505 * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
509 public char[] sourceFileName() {
510 return this.sourceFileName;
512 public String toString() {
513 java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
514 java.io.PrintWriter print = new java.io.PrintWriter(out);
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$
522 return out.toString();
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
539 public boolean hasStructuralChanges(byte[] newBytes) {
540 return hasStructuralChanges(newBytes, true, true);
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
558 public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
560 ClassFileReader newClassFile =
561 new ClassFileReader(newBytes, this.classFileName);
562 // type level comparison
564 if (this.getModifiers() != newClassFile.getModifiers())
567 if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
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)
575 for (int i = 0, max = this.interfacesCount; i < max; i++)
576 if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
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)
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())
595 FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
596 int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
597 boolean compareFields = true;
598 if (this.fieldsCount == otherFieldInfosLength) {
600 for (; i < this.fieldsCount; i++)
601 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
602 if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
606 if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
609 if (this.fieldsCount != 0)
610 Arrays.sort(this.fields);
611 if (otherFieldInfosLength != 0)
612 Arrays.sort(otherFieldInfos);
614 if (excludesSynthetic) {
615 if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
618 for (int i = 0; i < this.fieldsCount; i++)
619 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
625 MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
626 int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
627 boolean compareMethods = true;
628 if (this.methodsCount == otherMethodInfosLength) {
630 for (; i < this.methodsCount; i++)
631 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
632 if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
635 if (compareMethods) {
636 if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
639 if (this.methodsCount != 0)
640 Arrays.sort(this.methods);
641 if (otherMethodInfosLength != 0)
642 Arrays.sort(otherMethodInfos);
644 if (excludesSynthetic) {
645 if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
648 for (int i = 0; i < this.methodsCount; i++)
649 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
655 } catch (ClassFormatException e) {
659 private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
660 int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
661 int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
665 end : while (index1 < length1 && index2 < length2) {
666 while (currentFieldInfos[index1].isSynthetic()) {
667 if (++index1 >= length1) break end;
669 while (otherFieldInfos[index2].isSynthetic()) {
670 if (++index2 >= length2) break end;
672 if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
676 while (index1 < length1) {
677 if (!currentFieldInfos[index1++].isSynthetic()) return true;
679 while (index2 < length2) {
680 if (!otherFieldInfos[index2++].isSynthetic()) return true;
684 private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
685 if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
687 if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
689 if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
691 if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
693 if (currentFieldInfo.hasConstant()) {
694 Constant currentConstant = currentFieldInfo.getConstant();
695 Constant otherConstant = otherFieldInfo.getConstant();
696 if (currentConstant.typeID() != otherConstant.typeID())
698 if (!currentConstant.getClass().equals(otherConstant.getClass()))
700 switch (currentConstant.typeID()) {
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;
723 private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
724 int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
725 int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
730 end : while (index1 < length1 && index2 < length2) {
731 while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
732 if (++index1 >= length1) break end;
734 while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
735 if (++index2 >= length2) break end;
737 if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
741 while (index1 < length1) {
742 if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
744 while (index2 < length2) {
745 if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
749 private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
750 if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
752 if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
754 if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
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)
764 for (int k = 0; k < currentThrownExceptionsLength; k++)
765 if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
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.
774 private void initialize() {
775 for (int i = 0, max = fieldsCount; i < max; i++) {
776 fields[i].initialize();
778 for (int i = 0, max = methodsCount; i < max; i++) {
779 methods[i].initialize();
781 if (innerInfos != null) {
782 for (int i = 0, max = innerInfos.length; i < max; i++) {
783 innerInfos[i].initialize();
788 protected void reset() {
789 this.constantPoolOffsets = null;