0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.classfmt;
0011:
0012: import java.io.File;
0013: import java.io.IOException;
0014: import java.io.InputStream;
0015: import java.util.Arrays;
0016:
0017: import org.eclipse.jdt.core.compiler.CharOperation;
0018: import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
0019: import org.eclipse.jdt.internal.compiler.env.*;
0020: import org.eclipse.jdt.internal.compiler.impl.Constant;
0021: import org.eclipse.jdt.internal.compiler.lookup.TagBits;
0022: import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
0023: import org.eclipse.jdt.internal.compiler.util.Util;
0024:
0025: public class ClassFileReader extends ClassFileStruct implements
0026: IBinaryType {
0027: private static String printTypeModifiers(int modifiers) {
0028: java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
0029: java.io.PrintWriter print = new java.io.PrintWriter(out);
0030:
0031: if ((modifiers & ClassFileConstants.AccPublic) != 0)
0032: print.print("public "); //$NON-NLS-1$
0033: if ((modifiers & ClassFileConstants.AccPrivate) != 0)
0034: print.print("private "); //$NON-NLS-1$
0035: if ((modifiers & ClassFileConstants.AccFinal) != 0)
0036: print.print("final "); //$NON-NLS-1$
0037: if ((modifiers & ClassFileConstants.AccSuper) != 0)
0038: print.print("super "); //$NON-NLS-1$
0039: if ((modifiers & ClassFileConstants.AccInterface) != 0)
0040: print.print("interface "); //$NON-NLS-1$
0041: if ((modifiers & ClassFileConstants.AccAbstract) != 0)
0042: print.print("abstract "); //$NON-NLS-1$
0043: print.flush();
0044: return out.toString();
0045: }
0046:
0047: public static ClassFileReader read(InputStream stream,
0048: String fileName) throws ClassFormatException, IOException {
0049: return read(stream, fileName, false);
0050: }
0051:
0052: public static ClassFileReader read(File file)
0053: throws ClassFormatException, IOException {
0054: return read(file, false);
0055: }
0056:
0057: public static ClassFileReader read(InputStream stream,
0058: String fileName, boolean fullyInitialize)
0059: throws ClassFormatException, IOException {
0060: byte classFileBytes[] = Util.getInputStreamAsByteArray(stream,
0061: -1);
0062: ClassFileReader classFileReader = new ClassFileReader(
0063: classFileBytes, fileName.toCharArray());
0064: if (fullyInitialize) {
0065: classFileReader.initialize();
0066: }
0067: return classFileReader;
0068: }
0069:
0070: public static ClassFileReader read(File file,
0071: boolean fullyInitialize) throws ClassFormatException,
0072: IOException {
0073: byte classFileBytes[] = Util.getFileByteContent(file);
0074: ClassFileReader classFileReader = new ClassFileReader(
0075: classFileBytes, file.getAbsolutePath().toCharArray());
0076: if (fullyInitialize) {
0077: classFileReader.initialize();
0078: }
0079: return classFileReader;
0080: }
0081:
0082: public static ClassFileReader read(java.util.zip.ZipFile zip,
0083: String filename) throws ClassFormatException,
0084: java.io.IOException {
0085: return read(zip, filename, false);
0086: }
0087:
0088: public static ClassFileReader read(java.util.zip.ZipFile zip,
0089: String filename, boolean fullyInitialize)
0090: throws ClassFormatException, java.io.IOException {
0091: java.util.zip.ZipEntry ze = zip.getEntry(filename);
0092: if (ze == null)
0093: return null;
0094: byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
0095: ClassFileReader classFileReader = new ClassFileReader(
0096: classFileBytes, filename.toCharArray());
0097: if (fullyInitialize) {
0098: classFileReader.initialize();
0099: }
0100: return classFileReader;
0101: }
0102:
0103: public static ClassFileReader read(String fileName)
0104: throws ClassFormatException, java.io.IOException {
0105: return read(fileName, false);
0106: }
0107:
0108: public static ClassFileReader read(String fileName,
0109: boolean fullyInitialize) throws ClassFormatException,
0110: java.io.IOException {
0111: return read(new File(fileName), fullyInitialize);
0112: }
0113:
0114: private int accessFlags;
0115: private char[] classFileName;
0116: private char[] className;
0117: private int classNameIndex;
0118: private int constantPoolCount;
0119: private AnnotationInfo[] annotations;
0120: private FieldInfo[] fields;
0121: private int fieldsCount;
0122: // initialized in case the .class file is a nested type
0123: private InnerClassInfo innerInfo;
0124: private int innerInfoIndex;
0125: private InnerClassInfo[] innerInfos;
0126: private char[][] interfaceNames;
0127: private int interfacesCount;
0128: private MethodInfo[] methods;
0129: private int methodsCount;
0130: private char[] signature;
0131: private char[] sourceName;
0132: private char[] sourceFileName;
0133: private char[] super className;
0134: private long tagBits;
0135: private long version;
0136:
0137: private char[] enclosingTypeName;
0138:
0139: /**
0140: * @param classFileBytes Actual bytes of a .class file
0141: * @param fileName Actual name of the file that contains the bytes, can be null
0142: *
0143: * @exception ClassFormatException
0144: */
0145: public ClassFileReader(byte classFileBytes[], char[] fileName)
0146: throws ClassFormatException {
0147: this (classFileBytes, fileName, false);
0148: }
0149:
0150: /**
0151: * @param classFileBytes byte[]
0152: * Actual bytes of a .class file
0153: *
0154: * @param fileName char[]
0155: * Actual name of the file that contains the bytes, can be null
0156: *
0157: * @param fullyInitialize boolean
0158: * Flag to fully initialize the new object
0159: * @exception ClassFormatException
0160: */
0161: public ClassFileReader(byte[] classFileBytes, char[] fileName,
0162: boolean fullyInitialize) throws ClassFormatException {
0163: // This method looks ugly but is actually quite simple, the constantPool is constructed
0164: // in 3 passes. All non-primitive constant pool members that usually refer to other members
0165: // by index are tweaked to have their value in inst vars, this minor cost at read-time makes
0166: // all subsequent uses of the constant pool element faster.
0167: super (classFileBytes, null, 0);
0168: this .classFileName = fileName;
0169: int readOffset = 10;
0170: try {
0171: this .version = ((long) this .u2At(6) << 16) + this .u2At(4); // major<<16 + minor
0172: constantPoolCount = this .u2At(8);
0173: // Pass #1 - Fill in all primitive constants
0174: this .constantPoolOffsets = new int[constantPoolCount];
0175: for (int i = 1; i < constantPoolCount; i++) {
0176: int tag = this .u1At(readOffset);
0177: switch (tag) {
0178: case ClassFileConstants.Utf8Tag:
0179: this .constantPoolOffsets[i] = readOffset;
0180: readOffset += u2At(readOffset + 1);
0181: readOffset += ClassFileConstants.ConstantUtf8FixedSize;
0182: break;
0183: case ClassFileConstants.IntegerTag:
0184: this .constantPoolOffsets[i] = readOffset;
0185: readOffset += ClassFileConstants.ConstantIntegerFixedSize;
0186: break;
0187: case ClassFileConstants.FloatTag:
0188: this .constantPoolOffsets[i] = readOffset;
0189: readOffset += ClassFileConstants.ConstantFloatFixedSize;
0190: break;
0191: case ClassFileConstants.LongTag:
0192: this .constantPoolOffsets[i] = readOffset;
0193: readOffset += ClassFileConstants.ConstantLongFixedSize;
0194: i++;
0195: break;
0196: case ClassFileConstants.DoubleTag:
0197: this .constantPoolOffsets[i] = readOffset;
0198: readOffset += ClassFileConstants.ConstantDoubleFixedSize;
0199: i++;
0200: break;
0201: case ClassFileConstants.ClassTag:
0202: this .constantPoolOffsets[i] = readOffset;
0203: readOffset += ClassFileConstants.ConstantClassFixedSize;
0204: break;
0205: case ClassFileConstants.StringTag:
0206: this .constantPoolOffsets[i] = readOffset;
0207: readOffset += ClassFileConstants.ConstantStringFixedSize;
0208: break;
0209: case ClassFileConstants.FieldRefTag:
0210: this .constantPoolOffsets[i] = readOffset;
0211: readOffset += ClassFileConstants.ConstantFieldRefFixedSize;
0212: break;
0213: case ClassFileConstants.MethodRefTag:
0214: this .constantPoolOffsets[i] = readOffset;
0215: readOffset += ClassFileConstants.ConstantMethodRefFixedSize;
0216: break;
0217: case ClassFileConstants.InterfaceMethodRefTag:
0218: this .constantPoolOffsets[i] = readOffset;
0219: readOffset += ClassFileConstants.ConstantInterfaceMethodRefFixedSize;
0220: break;
0221: case ClassFileConstants.NameAndTypeTag:
0222: this .constantPoolOffsets[i] = readOffset;
0223: readOffset += ClassFileConstants.ConstantNameAndTypeFixedSize;
0224: }
0225: }
0226: // Read and validate access flags
0227: this .accessFlags = u2At(readOffset);
0228: readOffset += 2;
0229:
0230: // Read the classname, use exception handlers to catch bad format
0231: this .classNameIndex = u2At(readOffset);
0232: this .className = getConstantClassNameAt(this .classNameIndex);
0233: readOffset += 2;
0234:
0235: // Read the superclass name, can be null for java.lang.Object
0236: int super classNameIndex = u2At(readOffset);
0237: readOffset += 2;
0238: // if superclassNameIndex is equals to 0 there is no need to set a value for the
0239: // field this.superclassName. null is fine.
0240: if (super classNameIndex != 0) {
0241: this .super className = getConstantClassNameAt(super classNameIndex);
0242: }
0243:
0244: // Read the interfaces, use exception handlers to catch bad format
0245: this .interfacesCount = u2At(readOffset);
0246: readOffset += 2;
0247: if (this .interfacesCount != 0) {
0248: this .interfaceNames = new char[this .interfacesCount][];
0249: for (int i = 0; i < this .interfacesCount; i++) {
0250: this .interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
0251: readOffset += 2;
0252: }
0253: }
0254: // Read the fields, use exception handlers to catch bad format
0255: this .fieldsCount = u2At(readOffset);
0256: readOffset += 2;
0257: if (this .fieldsCount != 0) {
0258: FieldInfo field;
0259: this .fields = new FieldInfo[this .fieldsCount];
0260: for (int i = 0; i < this .fieldsCount; i++) {
0261: field = FieldInfo.createField(reference,
0262: this .constantPoolOffsets, readOffset);
0263: this .fields[i] = field;
0264: readOffset += field.sizeInBytes();
0265: }
0266: }
0267: // Read the methods
0268: this .methodsCount = u2At(readOffset);
0269: readOffset += 2;
0270: if (this .methodsCount != 0) {
0271: this .methods = new MethodInfo[this .methodsCount];
0272: boolean isAnnotationType = (this .accessFlags & ClassFileConstants.AccAnnotation) != 0;
0273: for (int i = 0; i < this .methodsCount; i++) {
0274: this .methods[i] = isAnnotationType ? AnnotationMethodInfo
0275: .createAnnotationMethod(reference,
0276: this .constantPoolOffsets,
0277: readOffset)
0278: : MethodInfo.createMethod(reference,
0279: this .constantPoolOffsets,
0280: readOffset);
0281: readOffset += this .methods[i].sizeInBytes();
0282: }
0283: }
0284:
0285: // Read the attributes
0286: int attributesCount = u2At(readOffset);
0287: readOffset += 2;
0288:
0289: for (int i = 0; i < attributesCount; i++) {
0290: int utf8Offset = this .constantPoolOffsets[u2At(readOffset)];
0291: char[] attributeName = utf8At(utf8Offset + 3,
0292: u2At(utf8Offset + 1));
0293: if (attributeName.length == 0) {
0294: readOffset += (6 + u4At(readOffset + 2));
0295: continue;
0296: }
0297: switch (attributeName[0]) {
0298: case 'E':
0299: if (CharOperation
0300: .equals(
0301: attributeName,
0302: AttributeNamesConstants.EnclosingMethodName)) {
0303: utf8Offset = constantPoolOffsets[u2At(constantPoolOffsets[u2At(readOffset + 6)]
0304: - structOffset + 1)]
0305: - structOffset;
0306: this .enclosingTypeName = utf8At(utf8Offset + 3,
0307: u2At(utf8Offset + 1));
0308: }
0309: break;
0310: case 'D':
0311: if (CharOperation.equals(attributeName,
0312: AttributeNamesConstants.DeprecatedName)) {
0313: this .accessFlags |= ClassFileConstants.AccDeprecated;
0314: }
0315: break;
0316: case 'I':
0317: if (CharOperation.equals(attributeName,
0318: AttributeNamesConstants.InnerClassName)) {
0319: int innerOffset = readOffset + 6;
0320: int number_of_classes = u2At(innerOffset);
0321: if (number_of_classes != 0) {
0322: innerOffset += 2;
0323: this .innerInfos = new InnerClassInfo[number_of_classes];
0324: for (int j = 0; j < number_of_classes; j++) {
0325: this .innerInfos[j] = new InnerClassInfo(
0326: reference,
0327: this .constantPoolOffsets,
0328: innerOffset);
0329: if (this .classNameIndex == this .innerInfos[j].innerClassNameIndex) {
0330: this .innerInfo = this .innerInfos[j];
0331: this .innerInfoIndex = j;
0332: }
0333: innerOffset += 8;
0334: }
0335: if (this .innerInfo != null) {
0336: char[] enclosingType = this .innerInfo
0337: .getEnclosingTypeName();
0338: if (enclosingType != null) {
0339: this .enclosingTypeName = enclosingType;
0340: }
0341: }
0342: }
0343: } else if (CharOperation
0344: .equals(
0345: attributeName,
0346: AttributeNamesConstants.InconsistentHierarchy)) {
0347: this .tagBits |= TagBits.HasInconsistentHierarchy;
0348: }
0349: break;
0350: case 'S':
0351: if (attributeName.length > 2) {
0352: switch (attributeName[1]) {
0353: case 'o':
0354: if (CharOperation.equals(attributeName,
0355: AttributeNamesConstants.SourceName)) {
0356: utf8Offset = this .constantPoolOffsets[u2At(readOffset + 6)];
0357: this .sourceFileName = utf8At(
0358: utf8Offset + 3,
0359: u2At(utf8Offset + 1));
0360: }
0361: break;
0362: case 'y':
0363: if (CharOperation
0364: .equals(
0365: attributeName,
0366: AttributeNamesConstants.SyntheticName)) {
0367: this .accessFlags |= ClassFileConstants.AccSynthetic;
0368: }
0369: break;
0370: case 'i':
0371: if (CharOperation
0372: .equals(
0373: attributeName,
0374: AttributeNamesConstants.SignatureName)) {
0375: utf8Offset = this .constantPoolOffsets[u2At(readOffset + 6)];
0376: this .signature = utf8At(utf8Offset + 3,
0377: u2At(utf8Offset + 1));
0378: }
0379: }
0380: }
0381: break;
0382: case 'R':
0383: if (CharOperation
0384: .equals(
0385: attributeName,
0386: AttributeNamesConstants.RuntimeVisibleAnnotationsName)) {
0387: decodeAnnotations(readOffset, true);
0388: } else if (CharOperation
0389: .equals(
0390: attributeName,
0391: AttributeNamesConstants.RuntimeInvisibleAnnotationsName)) {
0392: decodeAnnotations(readOffset, false);
0393: }
0394: break;
0395: }
0396: readOffset += (6 + u4At(readOffset + 2));
0397: }
0398: if (fullyInitialize) {
0399: this .initialize();
0400: }
0401: } catch (ClassFormatException e) {
0402: throw e;
0403: } catch (Exception e) {
0404: throw new ClassFormatException(
0405: ClassFormatException.ErrTruncatedInput, readOffset);
0406: }
0407: }
0408:
0409: /**
0410: * Answer the receiver's access flags. The value of the access_flags
0411: * item is a mask of modifiers used with class and interface declarations.
0412: * @return int
0413: */
0414: public int accessFlags() {
0415: return this .accessFlags;
0416: }
0417:
0418: private void decodeAnnotations(int offset, boolean runtimeVisible) {
0419: int numberOfAnnotations = u2At(offset + 6);
0420: if (numberOfAnnotations > 0) {
0421: int readOffset = offset + 8;
0422: AnnotationInfo[] newInfos = null;
0423: int newInfoCount = 0;
0424: for (int i = 0; i < numberOfAnnotations; i++) {
0425: // With the last parameter being 'false', the data structure will not be flushed out
0426: AnnotationInfo newInfo = new AnnotationInfo(
0427: this .reference, this .constantPoolOffsets,
0428: readOffset, runtimeVisible, false);
0429: readOffset += newInfo.readOffset;
0430: long standardTagBits = newInfo.standardAnnotationTagBits;
0431: if (standardTagBits != 0) {
0432: this .tagBits |= standardTagBits;
0433: } else {
0434: if (newInfos == null)
0435: newInfos = new AnnotationInfo[numberOfAnnotations
0436: - i];
0437: newInfos[newInfoCount++] = newInfo;
0438: }
0439: }
0440: if (newInfos == null)
0441: return; // nothing to record in this.annotations
0442:
0443: if (this .annotations == null) {
0444: if (newInfoCount != newInfos.length)
0445: System
0446: .arraycopy(
0447: newInfos,
0448: 0,
0449: newInfos = new AnnotationInfo[newInfoCount],
0450: 0, newInfoCount);
0451: this .annotations = newInfos;
0452: } else {
0453: int length = this .annotations.length;
0454: AnnotationInfo[] temp = new AnnotationInfo[length
0455: + newInfoCount];
0456: System.arraycopy(this .annotations, 0, temp, 0, length);
0457: System.arraycopy(newInfos, 0, temp, length,
0458: newInfoCount);
0459: this .annotations = temp;
0460: }
0461: }
0462: }
0463:
0464: /**
0465: * Answer the char array that corresponds to the class name of the constant class.
0466: * constantPoolIndex is the index in the constant pool that is a constant class entry.
0467: *
0468: * @param constantPoolIndex int
0469: * @return char[]
0470: */
0471: private char[] getConstantClassNameAt(int constantPoolIndex) {
0472: int utf8Offset = this .constantPoolOffsets[u2At(this .constantPoolOffsets[constantPoolIndex] + 1)];
0473: return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
0474: }
0475:
0476: /**
0477: * Answer the int array that corresponds to all the offsets of each entry in the constant pool
0478: *
0479: * @return int[]
0480: */
0481: public int[] getConstantPoolOffsets() {
0482: return this .constantPoolOffsets;
0483: }
0484:
0485: /*
0486: * Answer the resolved compoundName of the enclosing type
0487: * or null if the receiver is a top level type.
0488: */
0489: public char[] getEnclosingTypeName() {
0490: return this .enclosingTypeName;
0491: }
0492:
0493: /**
0494: * Answer the receiver's this.fields or null if the array is empty.
0495: * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
0496: */
0497: public IBinaryField[] getFields() {
0498: return this .fields;
0499: }
0500:
0501: /**
0502: * @see org.eclipse.jdt.internal.compiler.env.IDependent#getFileName()
0503: */
0504: public char[] getFileName() {
0505: return this .classFileName;
0506: }
0507:
0508: public char[] getGenericSignature() {
0509: return this .signature;
0510: }
0511:
0512: /**
0513: * 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.
0514: * e.g.
0515: * public class A {
0516: * public class B {
0517: * }
0518: * public void foo() {
0519: * class C {}
0520: * }
0521: * public Runnable bar() {
0522: * return new Runnable() {
0523: * public void run() {}
0524: * };
0525: * }
0526: * }
0527: * It returns {'B'} for the member A$B
0528: * It returns null for A
0529: * It returns {'C'} for the local class A$1$C
0530: * It returns null for the anonymous A$1
0531: * @return char[]
0532: */
0533: public char[] getInnerSourceName() {
0534: if (this .innerInfo != null)
0535: return this .innerInfo.getSourceName();
0536: return null;
0537: }
0538:
0539: /**
0540: * Answer the resolved names of the receiver's interfaces in the
0541: * class file format as specified in section 4.2 of the Java 2 VM spec
0542: * or null if the array is empty.
0543: *
0544: * For example, java.lang.String is java/lang/String.
0545: * @return char[][]
0546: */
0547: public char[][] getInterfaceNames() {
0548: return this .interfaceNames;
0549: }
0550:
0551: /**
0552: * Answer the receiver's nested types or null if the array is empty.
0553: *
0554: * This nested type info is extracted from the inner class attributes. Ask the
0555: * name environment to find a member type using its compound name
0556: *
0557: * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
0558: */
0559: public IBinaryNestedType[] getMemberTypes() {
0560: // we might have some member types of the current type
0561: if (this .innerInfos == null)
0562: return null;
0563:
0564: int length = this .innerInfos.length;
0565: int startingIndex = this .innerInfo != null ? this .innerInfoIndex + 1
0566: : 0;
0567: if (length != startingIndex) {
0568: IBinaryNestedType[] memberTypes = new IBinaryNestedType[length
0569: - this .innerInfoIndex];
0570: int memberTypeIndex = 0;
0571: for (int i = startingIndex; i < length; i++) {
0572: InnerClassInfo currentInnerInfo = this .innerInfos[i];
0573: int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
0574: int innerNameIndex = currentInnerInfo.innerNameIndex;
0575: /*
0576: * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
0577: * attribute entry is a member class, but due to the bug:
0578: * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
0579: * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
0580: *
0581: * https://bugs.eclipse.org/bugs/show_bug.cgi?id=49879
0582: * From JavaMail 1.2, the class javax.mail.Folder contains an anonymous class in the
0583: * terminateQueue() method for which the inner attribute is boggus.
0584: * outerClassNameIdx is not 0, innerNameIndex is not 0, but the sourceName length is 0.
0585: * So I added this extra check to filter out this anonymous class from the
0586: * member types.
0587: */
0588: if (outerClassNameIdx != 0 && innerNameIndex != 0
0589: && outerClassNameIdx == this .classNameIndex
0590: && currentInnerInfo.getSourceName().length != 0) {
0591: memberTypes[memberTypeIndex++] = currentInnerInfo;
0592: }
0593: }
0594: if (memberTypeIndex == 0)
0595: return null;
0596: if (memberTypeIndex != memberTypes.length) {
0597: // we need to resize the memberTypes array. Some local or anonymous classes
0598: // are present in the current class.
0599: System
0600: .arraycopy(
0601: memberTypes,
0602: 0,
0603: (memberTypes = new IBinaryNestedType[memberTypeIndex]),
0604: 0, memberTypeIndex);
0605: }
0606: return memberTypes;
0607: }
0608: return null;
0609: }
0610:
0611: /**
0612: * Answer the receiver's this.methods or null if the array is empty.
0613: * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
0614: */
0615: public IBinaryMethod[] getMethods() {
0616: return this .methods;
0617: }
0618:
0619: /**
0620: * @return the annotations or null if there is none.
0621: */
0622: public IBinaryAnnotation[] getAnnotations() {
0623: return this .annotations;
0624: }
0625:
0626: /**
0627: * Answer an int whose bits are set according the access constants
0628: * defined by the VM spec.
0629: * Set the AccDeprecated and AccSynthetic bits if necessary
0630: * @return int
0631: */
0632: public int getModifiers() {
0633: if (this .innerInfo != null) {
0634: return this .innerInfo.getModifiers()
0635: | (this .accessFlags & ClassFileConstants.AccDeprecated);
0636: }
0637: return this .accessFlags;
0638: }
0639:
0640: /**
0641: * Answer the resolved name of the type in the
0642: * class file format as specified in section 4.2 of the Java 2 VM spec.
0643: *
0644: * For example, java.lang.String is java/lang/String.
0645: * @return char[]
0646: */
0647: public char[] getName() {
0648: return this .className;
0649: }
0650:
0651: public char[] getSourceName() {
0652: if (this .sourceName != null)
0653: return this .sourceName;
0654:
0655: char[] name = getInnerSourceName(); // member or local scenario
0656: if (name == null) {
0657: name = getName(); // extract from full name
0658: int start;
0659: if (isAnonymous()) {
0660: start = CharOperation.indexOf('$', name, CharOperation
0661: .lastIndexOf('/', name) + 1) + 1;
0662: } else {
0663: start = CharOperation.lastIndexOf('/', name) + 1;
0664: }
0665: if (start > 0) {
0666: char[] newName = new char[name.length - start];
0667: System.arraycopy(name, start, newName, 0,
0668: newName.length);
0669: name = newName;
0670: }
0671: }
0672: return this .sourceName = name;
0673: }
0674:
0675: /**
0676: * Answer the resolved name of the receiver's superclass in the
0677: * class file format as specified in section 4.2 of the Java 2 VM spec
0678: * or null if it does not have one.
0679: *
0680: * For example, java.lang.String is java/lang/String.
0681: * @return char[]
0682: */
0683: public char[] getSuperclassName() {
0684: return this .super className;
0685: }
0686:
0687: public long getTagBits() {
0688: return this .tagBits;
0689: }
0690:
0691: /**
0692: * Answer the major/minor version defined in this class file according to the VM spec.
0693: * as a long: (major<<16)+minor
0694: * @return the major/minor version found
0695: */
0696: public long getVersion() {
0697: return this .version;
0698: }
0699:
0700: private boolean hasNonSyntheticFieldChanges(
0701: FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
0702: int length1 = currentFieldInfos == null ? 0
0703: : currentFieldInfos.length;
0704: int length2 = otherFieldInfos == null ? 0
0705: : otherFieldInfos.length;
0706: int index1 = 0;
0707: int index2 = 0;
0708:
0709: end: while (index1 < length1 && index2 < length2) {
0710: while (currentFieldInfos[index1].isSynthetic()) {
0711: if (++index1 >= length1)
0712: break end;
0713: }
0714: while (otherFieldInfos[index2].isSynthetic()) {
0715: if (++index2 >= length2)
0716: break end;
0717: }
0718: if (hasStructuralFieldChanges(currentFieldInfos[index1++],
0719: otherFieldInfos[index2++]))
0720: return true;
0721: }
0722:
0723: while (index1 < length1) {
0724: if (!currentFieldInfos[index1++].isSynthetic())
0725: return true;
0726: }
0727: while (index2 < length2) {
0728: if (!otherFieldInfos[index2++].isSynthetic())
0729: return true;
0730: }
0731: return false;
0732: }
0733:
0734: private boolean hasNonSyntheticMethodChanges(
0735: MethodInfo[] currentMethodInfos,
0736: MethodInfo[] otherMethodInfos) {
0737: int length1 = currentMethodInfos == null ? 0
0738: : currentMethodInfos.length;
0739: int length2 = otherMethodInfos == null ? 0
0740: : otherMethodInfos.length;
0741: int index1 = 0;
0742: int index2 = 0;
0743:
0744: MethodInfo m;
0745: end: while (index1 < length1 && index2 < length2) {
0746: while ((m = currentMethodInfos[index1]).isSynthetic()
0747: || m.isClinit()) {
0748: if (++index1 >= length1)
0749: break end;
0750: }
0751: while ((m = otherMethodInfos[index2]).isSynthetic()
0752: || m.isClinit()) {
0753: if (++index2 >= length2)
0754: break end;
0755: }
0756: if (hasStructuralMethodChanges(
0757: currentMethodInfos[index1++],
0758: otherMethodInfos[index2++]))
0759: return true;
0760: }
0761:
0762: while (index1 < length1) {
0763: if (!((m = currentMethodInfos[index1++]).isSynthetic() || m
0764: .isClinit()))
0765: return true;
0766: }
0767: while (index2 < length2) {
0768: if (!((m = otherMethodInfos[index2++]).isSynthetic() || m
0769: .isClinit()))
0770: return true;
0771: }
0772: return false;
0773: }
0774:
0775: /**
0776: * Check if the receiver has structural changes compare to the byte array in argument.
0777: * Structural changes are:
0778: * - modifiers changes for the class, the this.fields or the this.methods
0779: * - signature changes for this.fields or this.methods.
0780: * - changes in the number of this.fields or this.methods
0781: * - changes for field constants
0782: * - changes for thrown exceptions
0783: * - change for the super class or any super interfaces.
0784: * - changes for member types name or modifiers
0785: * If any of these changes occurs, the method returns true. false otherwise.
0786: * The synthetic fields are included and the members are not required to be sorted.
0787: * @param newBytes the bytes of the .class file we want to compare the receiver to
0788: * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
0789: */
0790: public boolean hasStructuralChanges(byte[] newBytes) {
0791: return hasStructuralChanges(newBytes, true, true);
0792: }
0793:
0794: /**
0795: * Check if the receiver has structural changes compare to the byte array in argument.
0796: * Structural changes are:
0797: * - modifiers changes for the class, the this.fields or the this.methods
0798: * - signature changes for this.fields or this.methods.
0799: * - changes in the number of this.fields or this.methods
0800: * - changes for field constants
0801: * - changes for thrown exceptions
0802: * - change for the super class or any super interfaces.
0803: * - changes for member types name or modifiers
0804: * If any of these changes occurs, the method returns true. false otherwise.
0805: * @param newBytes the bytes of the .class file we want to compare the receiver to
0806: * @param orderRequired a boolean indicating whether the members should be sorted or not
0807: * @param excludesSynthetic a boolean indicating whether the synthetic members should be used in the comparison
0808: * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
0809: */
0810: public boolean hasStructuralChanges(byte[] newBytes,
0811: boolean orderRequired, boolean excludesSynthetic) {
0812: try {
0813: ClassFileReader newClassFile = new ClassFileReader(
0814: newBytes, this .classFileName);
0815: // type level comparison
0816: // modifiers
0817: if (this .getModifiers() != newClassFile.getModifiers())
0818: return true;
0819:
0820: // only consider a portion of the tagbits which indicate a structural change for dependents
0821: // e.g. @Override change has no influence outside
0822: long OnlyStructuralTagBits = TagBits.AnnotationTargetMASK // different @Target status ?
0823: | TagBits.AnnotationDeprecated // different @Deprecated status ?
0824: | TagBits.AnnotationRetentionMASK // different @Retention status ?
0825: | TagBits.HasInconsistentHierarchy; // different hierarchy status ?
0826:
0827: // meta-annotations
0828: if ((this .getTagBits() & OnlyStructuralTagBits) != (newClassFile
0829: .getTagBits() & OnlyStructuralTagBits))
0830: return true;
0831:
0832: // generic signature
0833: if (!CharOperation.equals(this .getGenericSignature(),
0834: newClassFile.getGenericSignature()))
0835: return true;
0836: // superclass
0837: if (!CharOperation.equals(this .getSuperclassName(),
0838: newClassFile.getSuperclassName()))
0839: return true;
0840: // interfaces
0841: char[][] newInterfacesNames = newClassFile
0842: .getInterfaceNames();
0843: if (this .interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
0844: int newInterfacesLength = newInterfacesNames == null ? 0
0845: : newInterfacesNames.length;
0846: if (newInterfacesLength != this .interfacesCount)
0847: return true;
0848: for (int i = 0, max = this .interfacesCount; i < max; i++)
0849: if (!CharOperation.equals(this .interfaceNames[i],
0850: newInterfacesNames[i]))
0851: return true;
0852: }
0853:
0854: // member types
0855: IBinaryNestedType[] currentMemberTypes = this
0856: .getMemberTypes();
0857: IBinaryNestedType[] otherMemberTypes = newClassFile
0858: .getMemberTypes();
0859: if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
0860: int currentMemberTypeLength = currentMemberTypes == null ? 0
0861: : currentMemberTypes.length;
0862: int otherMemberTypeLength = otherMemberTypes == null ? 0
0863: : otherMemberTypes.length;
0864: if (currentMemberTypeLength != otherMemberTypeLength)
0865: return true;
0866: for (int i = 0; i < currentMemberTypeLength; i++)
0867: if (!CharOperation.equals(currentMemberTypes[i]
0868: .getName(), otherMemberTypes[i].getName())
0869: || currentMemberTypes[i].getModifiers() != otherMemberTypes[i]
0870: .getModifiers())
0871: return true;
0872: }
0873:
0874: // fields
0875: FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile
0876: .getFields();
0877: int otherFieldInfosLength = otherFieldInfos == null ? 0
0878: : otherFieldInfos.length;
0879: boolean compareFields = true;
0880: if (this .fieldsCount == otherFieldInfosLength) {
0881: int i = 0;
0882: for (; i < this .fieldsCount; i++)
0883: if (hasStructuralFieldChanges(this .fields[i],
0884: otherFieldInfos[i]))
0885: break;
0886: if ((compareFields = i != this .fieldsCount)
0887: && !orderRequired && !excludesSynthetic)
0888: return true;
0889: }
0890: if (compareFields) {
0891: if (this .fieldsCount != otherFieldInfosLength
0892: && !excludesSynthetic)
0893: return true;
0894: if (orderRequired) {
0895: if (this .fieldsCount != 0)
0896: Arrays.sort(this .fields);
0897: if (otherFieldInfosLength != 0)
0898: Arrays.sort(otherFieldInfos);
0899: }
0900: if (excludesSynthetic) {
0901: if (hasNonSyntheticFieldChanges(this .fields,
0902: otherFieldInfos))
0903: return true;
0904: } else {
0905: for (int i = 0; i < this .fieldsCount; i++)
0906: if (hasStructuralFieldChanges(this .fields[i],
0907: otherFieldInfos[i]))
0908: return true;
0909: }
0910: }
0911:
0912: // methods
0913: MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile
0914: .getMethods();
0915: int otherMethodInfosLength = otherMethodInfos == null ? 0
0916: : otherMethodInfos.length;
0917: boolean compareMethods = true;
0918: if (this .methodsCount == otherMethodInfosLength) {
0919: int i = 0;
0920: for (; i < this .methodsCount; i++)
0921: if (hasStructuralMethodChanges(this .methods[i],
0922: otherMethodInfos[i]))
0923: break;
0924: if ((compareMethods = i != this .methodsCount)
0925: && !orderRequired && !excludesSynthetic)
0926: return true;
0927: }
0928: if (compareMethods) {
0929: if (this .methodsCount != otherMethodInfosLength
0930: && !excludesSynthetic)
0931: return true;
0932: if (orderRequired) {
0933: if (this .methodsCount != 0)
0934: Arrays.sort(this .methods);
0935: if (otherMethodInfosLength != 0)
0936: Arrays.sort(otherMethodInfos);
0937: }
0938: if (excludesSynthetic) {
0939: if (hasNonSyntheticMethodChanges(this .methods,
0940: otherMethodInfos))
0941: return true;
0942: } else {
0943: for (int i = 0; i < this .methodsCount; i++)
0944: if (hasStructuralMethodChanges(this .methods[i],
0945: otherMethodInfos[i]))
0946: return true;
0947: }
0948: }
0949:
0950: return false;
0951: } catch (ClassFormatException e) {
0952: return true;
0953: }
0954: }
0955:
0956: private boolean hasStructuralFieldChanges(
0957: FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
0958: // generic signature
0959: if (!CharOperation.equals(currentFieldInfo
0960: .getGenericSignature(), otherFieldInfo
0961: .getGenericSignature()))
0962: return true;
0963: if (currentFieldInfo.getModifiers() != otherFieldInfo
0964: .getModifiers())
0965: return true;
0966: if ((currentFieldInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherFieldInfo
0967: .getTagBits() & TagBits.AnnotationDeprecated))
0968: return true;
0969: if (!CharOperation.equals(currentFieldInfo.getName(),
0970: otherFieldInfo.getName()))
0971: return true;
0972: if (!CharOperation.equals(currentFieldInfo.getTypeName(),
0973: otherFieldInfo.getTypeName()))
0974: return true;
0975: if (currentFieldInfo.hasConstant() != otherFieldInfo
0976: .hasConstant())
0977: return true;
0978: if (currentFieldInfo.hasConstant()) {
0979: Constant currentConstant = currentFieldInfo.getConstant();
0980: Constant otherConstant = otherFieldInfo.getConstant();
0981: if (currentConstant.typeID() != otherConstant.typeID())
0982: return true;
0983: if (!currentConstant.getClass().equals(
0984: otherConstant.getClass()))
0985: return true;
0986: switch (currentConstant.typeID()) {
0987: case TypeIds.T_int:
0988: return currentConstant.intValue() != otherConstant
0989: .intValue();
0990: case TypeIds.T_byte:
0991: return currentConstant.byteValue() != otherConstant
0992: .byteValue();
0993: case TypeIds.T_short:
0994: return currentConstant.shortValue() != otherConstant
0995: .shortValue();
0996: case TypeIds.T_char:
0997: return currentConstant.charValue() != otherConstant
0998: .charValue();
0999: case TypeIds.T_long:
1000: return currentConstant.longValue() != otherConstant
1001: .longValue();
1002: case TypeIds.T_float:
1003: return currentConstant.floatValue() != otherConstant
1004: .floatValue();
1005: case TypeIds.T_double:
1006: return currentConstant.doubleValue() != otherConstant
1007: .doubleValue();
1008: case TypeIds.T_boolean:
1009: return currentConstant.booleanValue() != otherConstant
1010: .booleanValue();
1011: case TypeIds.T_JavaLangString:
1012: return !currentConstant.stringValue().equals(
1013: otherConstant.stringValue());
1014: }
1015: }
1016: return false;
1017: }
1018:
1019: private boolean hasStructuralMethodChanges(
1020: MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
1021: // generic signature
1022: if (!CharOperation.equals(currentMethodInfo
1023: .getGenericSignature(), otherMethodInfo
1024: .getGenericSignature()))
1025: return true;
1026: if (currentMethodInfo.getModifiers() != otherMethodInfo
1027: .getModifiers())
1028: return true;
1029: if ((currentMethodInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherMethodInfo
1030: .getTagBits() & TagBits.AnnotationDeprecated))
1031: return true;
1032: if (!CharOperation.equals(currentMethodInfo.getSelector(),
1033: otherMethodInfo.getSelector()))
1034: return true;
1035: if (!CharOperation.equals(currentMethodInfo
1036: .getMethodDescriptor(), otherMethodInfo
1037: .getMethodDescriptor()))
1038: return true;
1039: if (!CharOperation.equals(currentMethodInfo
1040: .getGenericSignature(), otherMethodInfo
1041: .getGenericSignature()))
1042: return true;
1043:
1044: char[][] currentThrownExceptions = currentMethodInfo
1045: .getExceptionTypeNames();
1046: char[][] otherThrownExceptions = otherMethodInfo
1047: .getExceptionTypeNames();
1048: if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
1049: int currentThrownExceptionsLength = currentThrownExceptions == null ? 0
1050: : currentThrownExceptions.length;
1051: int otherThrownExceptionsLength = otherThrownExceptions == null ? 0
1052: : otherThrownExceptions.length;
1053: if (currentThrownExceptionsLength != otherThrownExceptionsLength)
1054: return true;
1055: for (int k = 0; k < currentThrownExceptionsLength; k++)
1056: if (!CharOperation.equals(currentThrownExceptions[k],
1057: otherThrownExceptions[k]))
1058: return true;
1059: }
1060: return false;
1061: }
1062:
1063: /**
1064: * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
1065: * will be therefore fully initialized and we can get rid of the bytes.
1066: */
1067: private void initialize() throws ClassFormatException {
1068: try {
1069: for (int i = 0, max = fieldsCount; i < max; i++) {
1070: fields[i].initialize();
1071: }
1072: for (int i = 0, max = methodsCount; i < max; i++) {
1073: methods[i].initialize();
1074: }
1075: if (innerInfos != null) {
1076: for (int i = 0, max = innerInfos.length; i < max; i++) {
1077: innerInfos[i].initialize();
1078: }
1079: }
1080: if (annotations != null) {
1081: for (int i = 0, max = annotations.length; i < max; i++) {
1082: annotations[i].initialize();
1083: }
1084: }
1085: this .reset();
1086: } catch (RuntimeException e) {
1087: ClassFormatException exception = new ClassFormatException(
1088: e, this .classFileName);
1089: throw exception;
1090: }
1091: }
1092:
1093: /**
1094: * Answer true if the receiver is an anonymous type, false otherwise
1095: *
1096: * @return <CODE>boolean</CODE>
1097: */
1098: public boolean isAnonymous() {
1099: if (this .innerInfo == null)
1100: return false;
1101: char[] innerSourceName = this .innerInfo.getSourceName();
1102: return (innerSourceName == null || innerSourceName.length == 0);
1103: }
1104:
1105: /**
1106: * Answer whether the receiver contains the resolved binary form
1107: * or the unresolved source form of the type.
1108: * @return boolean
1109: */
1110: public boolean isBinaryType() {
1111: return true;
1112: }
1113:
1114: /**
1115: * Answer true if the receiver is a local type, false otherwise
1116: *
1117: * @return <CODE>boolean</CODE>
1118: */
1119: public boolean isLocal() {
1120: if (this .innerInfo == null)
1121: return false;
1122: if (this .innerInfo.getEnclosingTypeName() != null)
1123: return false;
1124: char[] innerSourceName = this .innerInfo.getSourceName();
1125: return (innerSourceName != null && innerSourceName.length > 0);
1126: }
1127:
1128: /**
1129: * Answer true if the receiver is a member type, false otherwise
1130: *
1131: * @return <CODE>boolean</CODE>
1132: */
1133: public boolean isMember() {
1134: if (this .innerInfo == null)
1135: return false;
1136: if (this .innerInfo.getEnclosingTypeName() == null)
1137: return false;
1138: char[] innerSourceName = this .innerInfo.getSourceName();
1139: return (innerSourceName != null && innerSourceName.length > 0); // protection against ill-formed attributes (67600)
1140: }
1141:
1142: /**
1143: * Answer true if the receiver is a nested type, false otherwise
1144: *
1145: * @return <CODE>boolean</CODE>
1146: */
1147: public boolean isNestedType() {
1148: return this .innerInfo != null;
1149: }
1150:
1151: /**
1152: * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
1153: *
1154: * @return char[]
1155: */
1156: public char[] sourceFileName() {
1157: return this .sourceFileName;
1158: }
1159:
1160: public String toString() {
1161: java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
1162: java.io.PrintWriter print = new java.io.PrintWriter(out);
1163: print.println(this .getClass().getName() + "{"); //$NON-NLS-1$
1164: print.println(" this.className: " + new String(getName())); //$NON-NLS-1$
1165: print
1166: .println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
1167: print
1168: .println(" access_flags: " + printTypeModifiers(this .accessFlags()) + "(" + this .accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
1169: print.flush();
1170: return out.toString();
1171: }
1172:
1173: /*
1174: public static void main(String[] args) throws ClassFormatException, IOException {
1175: if (args == null || args.length != 1) {
1176: System.err.println("ClassFileReader <filename>"); //$NON-NLS-1$
1177: System.exit(1);
1178: }
1179: File file = new File(args[0]);
1180: ClassFileReader reader = read(file, true);
1181: if (reader.annotations != null) {
1182: System.err.println();
1183: for (int i = 0; i < reader.annotations.length; i++)
1184: System.err.println(reader.annotations[i]);
1185: }
1186: System.err.print("class "); //$NON-NLS-1$
1187: System.err.print(reader.getName());
1188: char[] superclass = reader.getSuperclassName();
1189: if (superclass != null) {
1190: System.err.print(" extends "); //$NON-NLS-1$
1191: System.err.print(superclass);
1192: }
1193: System.err.println();
1194: char[][] interfaces = reader.getInterfaceNames();
1195: if (interfaces != null && interfaces.length > 0) {
1196: System.err.print(" implements "); //$NON-NLS-1$
1197: for (int i = 0; i < interfaces.length; i++) {
1198: if (i != 0) System.err.print(", "); //$NON-NLS-1$
1199: System.err.println(interfaces[i]);
1200: }
1201: }
1202: System.err.println();
1203: System.err.println('{');
1204: if (reader.fields != null) {
1205: for (int i = 0; i < reader.fields.length; i++) {
1206: System.err.println(reader.fields[i]);
1207: System.err.println();
1208: }
1209: }
1210: if (reader.methods != null) {
1211: for (int i = 0; i < reader.methods.length; i++) {
1212: System.err.println(reader.methods[i]);
1213: System.err.println();
1214: }
1215: }
1216: System.err.println();
1217: System.err.println('}');
1218: }
1219: */
1220: }
|