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