1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.classfmt;
14 import java.io.IOException;
15 import java.util.Arrays;
17 import org.eclipse.jdt.core.compiler.CharOperation;
18 import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
19 import org.eclipse.jdt.internal.compiler.env.*;
20 import org.eclipse.jdt.internal.compiler.impl.Constant;
21 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
22 import org.eclipse.jdt.internal.compiler.util.Util;
24 public class ClassFileReader extends ClassFileStruct implements AttributeNamesConstants, IBinaryType {
25 private int constantPoolCount;
26 private int[] constantPoolOffsets;
28 private int accessFlags;
29 private char[] className;
30 private char[] superclassName;
31 private int interfacesCount;
32 private char[][] interfaceNames;
33 private int fieldsCount;
34 private FieldInfo[] fields;
35 private int methodsCount;
36 private MethodInfo[] methods;
37 private InnerClassInfo[] innerInfos;
38 private char[] sourceFileName;
39 // initialized in case the .class file is a nested type
40 private InnerClassInfo innerInfo;
41 private char[] classFileName;
42 private int classNameIndex;
43 private int innerInfoIndex;
45 * @param classFileBytes byte[]
46 * Actual bytes of a .class file
48 * @param fileName char[]
49 * Actual name of the file that contains the bytes, can be null
51 * @param fullyInitialize boolean
52 * Flag to fully initialize the new object
53 * @exception ClassFormatException
55 public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
56 // This method looks ugly but is actually quite simple, the constantPool is constructed
57 // in 3 passes. All non-primitive constant pool members that usually refer to other members
58 // by index are tweaked to have their value in inst vars, this minor cost at read-time makes
59 // all subsequent uses of the constant pool element faster.
60 super(classFileBytes, 0);
61 this.classFileName = fileName;
64 this.version = ((long)this.u2At(6) << 16) + this.u2At(4); // major<<16 + minor
65 constantPoolCount = this.u2At(8);
66 // Pass #1 - Fill in all primitive constants
67 this.constantPoolOffsets = new int[constantPoolCount];
68 for (int i = 1; i < constantPoolCount; i++) {
69 int tag = this.u1At(readOffset);
72 this.constantPoolOffsets[i] = readOffset;
73 readOffset += u2At(readOffset + 1);
74 readOffset += ConstantUtf8FixedSize;
77 this.constantPoolOffsets[i] = readOffset;
78 readOffset += ConstantIntegerFixedSize;
81 this.constantPoolOffsets[i] = readOffset;
82 readOffset += ConstantFloatFixedSize;
85 this.constantPoolOffsets[i] = readOffset;
86 readOffset += ConstantLongFixedSize;
90 this.constantPoolOffsets[i] = readOffset;
91 readOffset += ConstantDoubleFixedSize;
95 this.constantPoolOffsets[i] = readOffset;
96 readOffset += ConstantClassFixedSize;
99 this.constantPoolOffsets[i] = readOffset;
100 readOffset += ConstantStringFixedSize;
103 this.constantPoolOffsets[i] = readOffset;
104 readOffset += ConstantFieldRefFixedSize;
107 this.constantPoolOffsets[i] = readOffset;
108 readOffset += ConstantMethodRefFixedSize;
110 case InterfaceMethodRefTag :
111 this.constantPoolOffsets[i] = readOffset;
112 readOffset += ConstantInterfaceMethodRefFixedSize;
114 case NameAndTypeTag :
115 this.constantPoolOffsets[i] = readOffset;
116 readOffset += ConstantNameAndTypeFixedSize;
119 // Read and validate access flags
120 this.accessFlags = u2At(readOffset);
123 // Read the classname, use exception handlers to catch bad format
124 this.classNameIndex = u2At(readOffset);
125 this.className = getConstantClassNameAt(this.classNameIndex);
128 // Read the superclass name, can be null for java.lang.Object
129 int superclassNameIndex = u2At(readOffset);
131 // if superclassNameIndex is equals to 0 there is no need to set a value for the
132 // field this.superclassName. null is fine.
133 if (superclassNameIndex != 0) {
134 this.superclassName = getConstantClassNameAt(superclassNameIndex);
137 // Read the interfaces, use exception handlers to catch bad format
138 this.interfacesCount = u2At(readOffset);
140 if (this.interfacesCount != 0) {
141 this.interfaceNames = new char[this.interfacesCount][];
142 for (int i = 0; i < this.interfacesCount; i++) {
143 this.interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
147 // Read the this.fields, use exception handlers to catch bad format
148 this.fieldsCount = u2At(readOffset);
150 if (this.fieldsCount != 0) {
152 this.fields = new FieldInfo[this.fieldsCount];
153 for (int i = 0; i < this.fieldsCount; i++) {
154 field = new FieldInfo(reference, this.constantPoolOffsets, readOffset);
155 this.fields[i] = field;
156 readOffset += field.sizeInBytes();
159 // Read the this.methods
160 this.methodsCount = u2At(readOffset);
162 if (this.methodsCount != 0) {
163 this.methods = new MethodInfo[this.methodsCount];
165 for (int i = 0; i < this.methodsCount; i++) {
166 method = new MethodInfo(reference, this.constantPoolOffsets, readOffset);
167 this.methods[i] = method;
168 readOffset += method.sizeInBytes();
172 // Read the attributes
173 int attributesCount = u2At(readOffset);
176 for (int i = 0; i < attributesCount; i++) {
177 int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
178 char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
179 if (CharOperation.equals(attributeName, DeprecatedName)) {
180 this.accessFlags |= AccDeprecated;
182 if (CharOperation.equals(attributeName, InnerClassName)) {
183 int innerOffset = readOffset + 6;
184 int number_of_classes = u2At(innerOffset);
185 if (number_of_classes != 0) {
187 this.innerInfos = new InnerClassInfo[number_of_classes];
188 for (int j = 0; j < number_of_classes; j++) {
190 new InnerClassInfo(reference, this.constantPoolOffsets, innerOffset);
191 if (this.classNameIndex == this.innerInfos[j].innerClassNameIndex) {
192 this.innerInfo = this.innerInfos[j];
193 this.innerInfoIndex = j;
199 if (CharOperation.equals(attributeName, SourceName)) {
200 utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
201 this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
203 if (CharOperation.equals(attributeName, SyntheticName)) {
204 this.accessFlags |= AccSynthetic;
209 readOffset += (6 + u4At(readOffset + 2));
211 if (fullyInitialize) {
214 } catch(ClassFormatException e) {
216 } catch (Exception e) {
217 throw new ClassFormatException(
218 ClassFormatException.ErrTruncatedInput,
224 * @param classFileBytes Actual bytes of a .class file
225 * @param fileName Actual name of the file that contains the bytes, can be null
227 * @exception ClassFormatException
229 public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
230 this(classFileBytes, fileName, false);
234 * Answer the receiver's access flags. The value of the access_flags
235 * item is a mask of modifiers used with class and interface declarations.
238 public int accessFlags() {
239 return this.accessFlags;
242 * Answer the char array that corresponds to the class name of the constant class.
243 * constantPoolIndex is the index in the constant pool that is a constant class entry.
245 * @param constantPoolIndex int
248 private char[] getConstantClassNameAt(int constantPoolIndex) {
249 int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
250 return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
253 * Answer the int array that corresponds to all the offsets of each entry in the constant pool
257 public int[] getConstantPoolOffsets() {
258 return this.constantPoolOffsets;
261 * Answer the resolved compoundName of the enclosing type
262 * or null if the receiver is a top level type.
264 public char[] getEnclosingTypeName() {
265 if (this.innerInfo != null && !this.isAnonymous()) {
266 return this.innerInfo.getEnclosingTypeName();
271 * Answer the receiver's this.fields or null if the array is empty.
272 * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
274 public IBinaryField[] getFields() {
278 * Answer the file name which defines the type.
279 * The format is unspecified.
281 public char[] getFileName() {
282 return this.classFileName;
285 * Answer the source name if the receiver is a inner type. Return null if it is an anonymous class or if the receiver is a top-level class.
290 * public void foo() {
293 * public Runnable bar() {
294 * return new Runnable() {
295 * public void run() {}
299 * It returns {'B'} for the member A$B
300 * It returns null for A
301 * It returns {'C'} for the local class A$1$C
302 * It returns null for the anonymous A$1
305 public char[] getInnerSourceName() {
306 if (this.innerInfo != null)
307 return this.innerInfo.getSourceName();
311 * Answer the resolved names of the receiver's interfaces in the
312 * class file format as specified in section 4.2 of the Java 2 VM spec
313 * or null if the array is empty.
315 * For example, java.lang.String is java/lang/String.
318 public char[][] getInterfaceNames() {
319 return this.interfaceNames;
322 * Answer the receiver's nested types or null if the array is empty.
324 * This nested type info is extracted from the inner class attributes.
325 * Ask the name environment to find a member type using its compound name
326 * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
328 public IBinaryNestedType[] getMemberTypes() {
329 // we might have some member types of the current type
330 if (this.innerInfos == null) return null;
332 int length = this.innerInfos.length;
333 int startingIndex = this.innerInfo != null ? this.innerInfoIndex + 1 : 0;
334 if (length != startingIndex) {
335 IBinaryNestedType[] memberTypes =
336 new IBinaryNestedType[length - this.innerInfoIndex];
337 int memberTypeIndex = 0;
338 for (int i = startingIndex; i < length; i++) {
339 InnerClassInfo currentInnerInfo = this.innerInfos[i];
340 int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
341 int innerNameIndex = currentInnerInfo.innerNameIndex;
343 * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
344 * attribute entry is a member class, but due to the bug:
345 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
346 * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
348 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=49879
349 * From JavaMail 1.2, the class javax.mail.Folder contains an anonymous class in the
350 * terminateQueue() method for which the inner attribute is boggus.
351 * outerClassNameIdx is not 0, innerNameIndex is not 0, but the sourceName length is 0.
352 * So I added this extra check to filter out this anonymous class from the
355 if (outerClassNameIdx != 0
356 && innerNameIndex != 0
357 && outerClassNameIdx == this.classNameIndex
358 && currentInnerInfo.getSourceName().length != 0) {
359 memberTypes[memberTypeIndex++] = currentInnerInfo;
362 if (memberTypeIndex == 0) return null;
363 if (memberTypeIndex != memberTypes.length) {
364 // we need to resize the memberTypes array. Some local or anonymous classes
365 // are present in the current class.
369 (memberTypes = new IBinaryNestedType[memberTypeIndex]),
378 * Answer the receiver's this.methods or null if the array is empty.
379 * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
381 public IBinaryMethod[] getMethods() {
385 * Answer an int whose bits are set according the access constants
386 * defined by the VM spec.
387 * Set the AccDeprecated and AccSynthetic bits if necessary
390 public int getModifiers() {
391 if (this.innerInfo != null) {
392 if ((this.accessFlags & AccDeprecated) != 0) {
393 return this.innerInfo.getModifiers() | AccDeprecated;
395 return this.innerInfo.getModifiers();
398 return this.accessFlags;
401 * Answer the resolved name of the type in the
402 * class file format as specified in section 4.2 of the Java 2 VM spec.
404 * For example, java.lang.String is java/lang/String.
407 public char[] getName() {
408 return this.className;
411 * Answer the resolved name of the receiver's superclass in the
412 * class file format as specified in section 4.2 of the Java 2 VM spec
413 * or null if it does not have one.
415 * For example, java.lang.String is java/lang/String.
418 public char[] getSuperclassName() {
419 return this.superclassName;
422 * Answer the major/minor version defined in this class file according to the VM spec.
423 * as a long: (major<<16)+minor
424 * @return the major/minor version found
426 public long getVersion() {
430 * Answer true if the receiver is an anonymous type, false otherwise
432 * @return <CODE>boolean</CODE>
434 public boolean isAnonymous() {
435 if (this.innerInfo == null) return false;
436 char[] sourceName = this.innerInfo.getSourceName();
437 return (sourceName == null || sourceName.length == 0);
440 * Answer whether the receiver contains the resolved binary form
441 * or the unresolved source form of the type.
444 public boolean isBinaryType() {
448 * Answer true if the receiver is a class. False otherwise.
451 public boolean isClass() {
452 return (getModifiers() & AccInterface) == 0;
455 * Answer true if the receiver is an interface. False otherwise.
458 public boolean isInterface() {
459 return (getModifiers() & AccInterface) != 0;
462 * Answer true if the receiver is a local type, false otherwise
464 * @return <CODE>boolean</CODE>
466 public boolean isLocal() {
467 if (this.innerInfo == null) return false;
468 if (this.innerInfo.getEnclosingTypeName() != null) return false;
469 char[] sourceName = this.innerInfo.getSourceName();
470 return (sourceName != null && sourceName.length > 0);
473 * Answer true if the receiver is a member type, false otherwise
475 * @return <CODE>boolean</CODE>
477 public boolean isMember() {
478 if (this.innerInfo == null) return false;
479 if (this.innerInfo.getEnclosingTypeName() == null) return false;
480 char[] sourceName = this.innerInfo.getSourceName();
481 return (sourceName != null && sourceName.length > 0); // protection against ill-formed attributes (67600)
484 * Answer true if the receiver is a nested type, false otherwise
486 * @return <CODE>boolean</CODE>
488 public boolean isNestedType() {
489 return this.innerInfo != null;
491 public static ClassFileReader read(File file) throws ClassFormatException, IOException {
492 return read(file, false);
494 public static ClassFileReader read(File file, boolean fullyInitialize) throws ClassFormatException, IOException {
495 byte classFileBytes[] = Util.getFileByteContent(file);
496 ClassFileReader classFileReader = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
497 if (fullyInitialize) {
498 classFileReader.initialize();
500 return classFileReader;
502 public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
503 return read(fileName, false);
505 public static ClassFileReader read(String fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException {
506 return read(new File(fileName), fullyInitialize);
508 public static ClassFileReader read(
509 java.util.zip.ZipFile zip,
511 throws ClassFormatException, java.io.IOException {
512 return read(zip, filename, false);
514 public static ClassFileReader read(
515 java.util.zip.ZipFile zip,
517 boolean fullyInitialize)
518 throws ClassFormatException, java.io.IOException {
519 java.util.zip.ZipEntry ze = zip.getEntry(filename);
522 byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
523 ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
524 if (fullyInitialize) {
525 classFileReader.initialize();
527 return classFileReader;
531 * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
535 public char[] sourceFileName() {
536 return this.sourceFileName;
538 public String toString() {
539 java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
540 java.io.PrintWriter print = new java.io.PrintWriter(out);
542 print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
543 print.println(" this.className: " + new String(getName())); //$NON-NLS-1$
544 print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
545 print.println(" access_flags: " + ClassFileStruct.printTypeModifiers(this.accessFlags()) + "(" + this.accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
548 return out.toString();
551 * Check if the receiver has structural changes compare to the byte array in argument.
552 * Structural changes are:
553 * - modifiers changes for the class, the this.fields or the this.methods
554 * - signature changes for this.fields or this.methods.
555 * - changes in the number of this.fields or this.methods
556 * - changes for field constants
557 * - changes for thrown exceptions
558 * - change for the super class or any super interfaces.
559 * - changes for member types name or modifiers
560 * If any of these changes occurs, the method returns true. false otherwise.
561 * The synthetic fields are included and the members are not required to be sorted.
562 * @param newBytes the bytes of the .class file we want to compare the receiver to
563 * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
565 public boolean hasStructuralChanges(byte[] newBytes) {
566 return hasStructuralChanges(newBytes, true, true);
569 * Check if the receiver has structural changes compare to the byte array in argument.
570 * Structural changes are:
571 * - modifiers changes for the class, the this.fields or the this.methods
572 * - signature changes for this.fields or this.methods.
573 * - changes in the number of this.fields or this.methods
574 * - changes for field constants
575 * - changes for thrown exceptions
576 * - change for the super class or any super interfaces.
577 * - changes for member types name or modifiers
578 * If any of these changes occurs, the method returns true. false otherwise.
579 * @param newBytes the bytes of the .class file we want to compare the receiver to
580 * @param orderRequired a boolean indicating whether the members should be sorted or not
581 * @param excludesSynthetic a boolean indicating whether the synthetic members should be used in the comparison
582 * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
584 public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
586 ClassFileReader newClassFile =
587 new ClassFileReader(newBytes, this.classFileName);
588 // type level comparison
590 if (this.getModifiers() != newClassFile.getModifiers())
593 if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
596 char[][] newInterfacesNames = newClassFile.getInterfaceNames();
597 if (this.interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
598 int newInterfacesLength = newInterfacesNames == null ? 0 : newInterfacesNames.length;
599 if (newInterfacesLength != this.interfacesCount)
601 for (int i = 0, max = this.interfacesCount; i < max; i++)
602 if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
607 IBinaryNestedType[] currentMemberTypes = this.getMemberTypes();
608 IBinaryNestedType[] otherMemberTypes = newClassFile.getMemberTypes();
609 if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
610 int currentMemberTypeLength = currentMemberTypes == null ? 0 : currentMemberTypes.length;
611 int otherMemberTypeLength = otherMemberTypes == null ? 0 : otherMemberTypes.length;
612 if (currentMemberTypeLength != otherMemberTypeLength)
614 for (int i = 0; i < currentMemberTypeLength; i++)
615 if (!CharOperation.equals(currentMemberTypes[i].getName(), otherMemberTypes[i].getName())
616 || currentMemberTypes[i].getModifiers() != otherMemberTypes[i].getModifiers())
621 FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
622 int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
623 boolean compareFields = true;
624 if (this.fieldsCount == otherFieldInfosLength) {
626 for (; i < this.fieldsCount; i++)
627 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
628 if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
632 if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
635 if (this.fieldsCount != 0)
636 Arrays.sort(this.fields);
637 if (otherFieldInfosLength != 0)
638 Arrays.sort(otherFieldInfos);
640 if (excludesSynthetic) {
641 if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
644 for (int i = 0; i < this.fieldsCount; i++)
645 if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
651 MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
652 int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
653 boolean compareMethods = true;
654 if (this.methodsCount == otherMethodInfosLength) {
656 for (; i < this.methodsCount; i++)
657 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
658 if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
661 if (compareMethods) {
662 if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
665 if (this.methodsCount != 0)
666 Arrays.sort(this.methods);
667 if (otherMethodInfosLength != 0)
668 Arrays.sort(otherMethodInfos);
670 if (excludesSynthetic) {
671 if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
674 for (int i = 0; i < this.methodsCount; i++)
675 if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
681 } catch (ClassFormatException e) {
685 private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
686 int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
687 int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
691 end : while (index1 < length1 && index2 < length2) {
692 while (currentFieldInfos[index1].isSynthetic()) {
693 if (++index1 >= length1) break end;
695 while (otherFieldInfos[index2].isSynthetic()) {
696 if (++index2 >= length2) break end;
698 if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
702 while (index1 < length1) {
703 if (!currentFieldInfos[index1++].isSynthetic()) return true;
705 while (index2 < length2) {
706 if (!otherFieldInfos[index2++].isSynthetic()) return true;
710 private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
711 if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
713 if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
715 if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
717 if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
719 if (currentFieldInfo.hasConstant()) {
720 Constant currentConstant = currentFieldInfo.getConstant();
721 Constant otherConstant = otherFieldInfo.getConstant();
722 if (currentConstant.typeID() != otherConstant.typeID())
724 if (!currentConstant.getClass().equals(otherConstant.getClass()))
726 switch (currentConstant.typeID()) {
728 return currentConstant.intValue() != otherConstant.intValue();
729 case TypeIds.T_byte :
730 return currentConstant.byteValue() != otherConstant.byteValue();
731 case TypeIds.T_short :
732 return currentConstant.shortValue() != otherConstant.shortValue();
733 case TypeIds.T_char :
734 return currentConstant.charValue() != otherConstant.charValue();
735 case TypeIds.T_long :
736 return currentConstant.longValue() != otherConstant.longValue();
737 case TypeIds.T_float :
738 return currentConstant.floatValue() != otherConstant.floatValue();
739 case TypeIds.T_double :
740 return currentConstant.doubleValue() != otherConstant.doubleValue();
741 case TypeIds.T_boolean :
742 return currentConstant.booleanValue() != otherConstant.booleanValue();
743 case TypeIds.T_String :
744 return !currentConstant.stringValue().equals(otherConstant.stringValue());
749 private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
750 int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
751 int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
756 end : while (index1 < length1 && index2 < length2) {
757 while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
758 if (++index1 >= length1) break end;
760 while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
761 if (++index2 >= length2) break end;
763 if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
767 while (index1 < length1) {
768 if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
770 while (index2 < length2) {
771 if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
775 private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
776 if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
778 if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
780 if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
783 char[][] currentThrownExceptions = currentMethodInfo.getExceptionTypeNames();
784 char[][] otherThrownExceptions = otherMethodInfo.getExceptionTypeNames();
785 if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
786 int currentThrownExceptionsLength = currentThrownExceptions == null ? 0 : currentThrownExceptions.length;
787 int otherThrownExceptionsLength = otherThrownExceptions == null ? 0 : otherThrownExceptions.length;
788 if (currentThrownExceptionsLength != otherThrownExceptionsLength)
790 for (int k = 0; k < currentThrownExceptionsLength; k++)
791 if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
797 * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
798 * will be therefore fully initialized and we can get rid of the bytes.
800 private void initialize() throws ClassFormatException {
802 for (int i = 0, max = fieldsCount; i < max; i++) {
803 fields[i].initialize();
805 for (int i = 0, max = methodsCount; i < max; i++) {
806 methods[i].initialize();
808 if (innerInfos != null) {
809 for (int i = 0, max = innerInfos.length; i < max; i++) {
810 innerInfos[i].initialize();
814 } catch(RuntimeException e) {
815 ClassFormatException exception = new ClassFormatException(e, this.classFileName);
819 protected void reset() {
820 this.constantPoolOffsets = null;