blob: cbdfa91584fda93200e8cf9bb73e528fffa7a71d [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.classfmt;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.util.*;
import org.eclipse.jdt.internal.compiler.codegen.*;
import java.io.*;
public class ClassFileReader extends ClassFileStruct implements AttributeNamesConstants, IBinaryType {
private int constantPoolCount;
private int[] constantPoolOffsets;
private int accessFlags;
private char[] className;
private char[] superclassName;
private int interfacesCount;
private char[][] interfaceNames;
private int fieldsCount;
private FieldInfo[] fields;
private int methodsCount;
private MethodInfo[] methods;
private InnerClassInfo[] innerInfos;
private char[] sourceFileName;
// initialized in case the .class file is a nested type
private InnerClassInfo innerInfo;
private char[] classFileName;
private int classNameIndex;
private int innerInfoIndex;
/**
* @param classFileBytes actual bytes of a .class file
* @param fileName actual name of the file that contains the bytes, can be null
*/
public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
// This method looks ugly but is actually quite simple, the constantPool is constructed
// in 3 passes. All non-primitive constant pool members that usually refer to other members
// by index are tweaked to have their value in inst vars, this minor cost at read-time makes
// all subsequent uses of the constant pool element faster.
super(classFileBytes, 0);
classFileName = fileName;
int readOffset = 10;
try {
constantPoolCount = this.u2At(8);
// Pass #1 - Fill in all primitive constants
constantPoolOffsets = new int[constantPoolCount];
for (int i = 1; i < constantPoolCount; i++) {
int tag = this.u1At(readOffset);
switch (tag) {
case Utf8Tag :
constantPoolOffsets[i] = readOffset;
readOffset += u2At(readOffset + 1);
readOffset += ConstantUtf8FixedSize;
break;
case IntegerTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantIntegerFixedSize;
break;
case FloatTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantFloatFixedSize;
break;
case LongTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantLongFixedSize;
i++;
break;
case DoubleTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantDoubleFixedSize;
i++;
break;
case ClassTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantClassFixedSize;
break;
case StringTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantStringFixedSize;
break;
case FieldRefTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantFieldRefFixedSize;
break;
case MethodRefTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantMethodRefFixedSize;
break;
case InterfaceMethodRefTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantInterfaceMethodRefFixedSize;
break;
case NameAndTypeTag :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantNameAndTypeFixedSize;
}
}
// Read and validate access flags
accessFlags = u2At(readOffset);
readOffset += 2;
// Read the classname, use exception handlers to catch bad format
classNameIndex = u2At(readOffset);
className = getConstantClassNameAt(classNameIndex);
readOffset += 2;
// Read the superclass name, can be null for java.lang.Object
int superclassNameIndex = u2At(readOffset);
readOffset += 2;
// if superclassNameIndex is equals to 0 there is no need to set a value for the
// field superclassName. null is fine.
if (superclassNameIndex != 0) {
superclassName = getConstantClassNameAt(superclassNameIndex);
}
// Read the interfaces, use exception handlers to catch bad format
interfacesCount = u2At(readOffset);
readOffset += 2;
if (interfacesCount != 0) {
interfaceNames = new char[interfacesCount][];
for (int i = 0; i < interfacesCount; i++) {
interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
readOffset += 2;
}
}
// Read the fields, use exception handlers to catch bad format
fieldsCount = u2At(readOffset);
readOffset += 2;
if (fieldsCount != 0) {
FieldInfo field;
fields = new FieldInfo[fieldsCount];
for (int i = 0; i < fieldsCount; i++) {
field = new FieldInfo(reference, constantPoolOffsets, readOffset);
fields[i] = field;
readOffset += field.sizeInBytes();
}
}
// Read the methods
methodsCount = u2At(readOffset);
readOffset += 2;
if (methodsCount != 0) {
methods = new MethodInfo[methodsCount];
MethodInfo method;
for (int i = 0; i < methodsCount; i++) {
method = new MethodInfo(reference, constantPoolOffsets, readOffset);
methods[i] = method;
readOffset += method.sizeInBytes();
}
}
// Read the attributes
int attributesCount = u2At(readOffset);
readOffset += 2;
for (int i = 0; i < attributesCount; i++) {
int utf8Offset = constantPoolOffsets[u2At(readOffset)];
char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
if (CharOperation.equals(attributeName, DeprecatedName)) {
accessFlags |= AccDeprecated;
} else {
if (CharOperation.equals(attributeName, InnerClassName)) {
int innerOffset = readOffset + 6;
int number_of_classes = u2At(innerOffset);
if (number_of_classes != 0) {
innerInfos = new InnerClassInfo[number_of_classes];
for (int j = 0; j < number_of_classes; j++) {
innerInfos[j] =
new InnerClassInfo(reference, constantPoolOffsets, innerOffset + 2);
if (classNameIndex == innerInfos[j].innerClassNameIndex) {
innerInfo = innerInfos[j];
innerInfoIndex = j;
}
innerOffset += 8;
}
}
} else {
if (CharOperation.equals(attributeName, SourceName)) {
utf8Offset = constantPoolOffsets[u2At(readOffset + 6)];
sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
} else {
if (CharOperation.equals(attributeName, SyntheticName)) {
accessFlags |= AccSynthetic;
}
}
}
}
readOffset += (6 + u4At(readOffset + 2));
}
} catch (Exception e) {
throw new ClassFormatException(
ClassFormatException.ErrTruncatedInput,
readOffset);
}
}
/**
* Answer the receiver's access flags. The value of the access_flags
* item is a mask of modifiers used with class and interface declarations.
* @return int
*/
public int accessFlags() {
return accessFlags;
}
/**
* (c)1998 Object Technology International.
* (c)1998 International Business Machines Corporation.
*
* Answer the char array that corresponds to the class name of the constant class.
* constantPoolIndex is the index in the constant pool that is a constant class entry.
*
* @param int constantPoolIndex
* @return char[]
*/
private char[] getConstantClassNameAt(int constantPoolIndex) {
int utf8Offset = constantPoolOffsets[u2At(constantPoolOffsets[constantPoolIndex] + 1)];
return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
}
/**
* Answer the int array that corresponds to all the offsets of each entry in the constant pool
*
* @return int[]
*/
public int[] getConstantPoolOffsets() {
return constantPoolOffsets;
}
/*
* Answer the resolved compoundName of the enclosing type
* or null if the receiver is a top level type.
*/
public char[] getEnclosingTypeName() {
if (innerInfo != null) {
return innerInfo.getEnclosingTypeName();
}
return null;
}
/**
* Answer the receiver's fields or null if the array is empty.
* @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
*/
public IBinaryField[] getFields() {
return fields;
}
/**
* Answer the file name which defines the type.
* The format is unspecified.
*/
public char[] getFileName() {
return classFileName;
}
/**
* 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.
* e.g.
* public class A {
* public class B {
* }
* public void foo() {
* class C {}
* }
* public Runnable bar() {
* return new Runnable() {
* public void run() {}
* };
* }
* }
* It returns {'B'} for the member A$B
* It returns null for A
* It returns {'C'} for the local class A$1$C
* It returns null for the anonymous A$1
* @return char[]
*/
public char[] getInnerSourceName() {
if (innerInfo != null)
return innerInfo.getSourceName();
return null;
}
/**
* Answer the resolved names of the receiver's interfaces in the
* class file format as specified in section 4.2 of the Java 2 VM spec
* or null if the array is empty.
*
* For example, java.lang.String is java/lang/String.
* @return char[][]
*/
public char[][] getInterfaceNames() {
return interfaceNames;
}
/**
* Answer the receiver's nested types or null if the array is empty.
*
* This nested type info is extracted from the inner class attributes.
* Ask the name environment to find a member type using its compound name
* @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
*/
public IBinaryNestedType[] getMemberTypes() {
// we might have some member types of the current type
if (innerInfos == null) return null;
int length = innerInfos.length;
int startingIndex = innerInfo != null ? innerInfoIndex + 1 : 0;
if (length != startingIndex) {
IBinaryNestedType[] memberTypes =
new IBinaryNestedType[length - innerInfoIndex];
int memberTypeIndex = 0;
for (int i = startingIndex; i < length; i++) {
InnerClassInfo currentInnerInfo = innerInfos[i];
int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
if (outerClassNameIdx != 0 && outerClassNameIdx == classNameIndex) {
memberTypes[memberTypeIndex++] = currentInnerInfo;
}
}
if (memberTypeIndex == 0) return null;
if (memberTypeIndex != memberTypes.length) {
// we need to resize the memberTypes array. Some local or anonymous classes
// are present in the current class.
System.arraycopy(
memberTypes,
0,
(memberTypes = new IBinaryNestedType[memberTypeIndex]),
0,
memberTypeIndex);
}
return memberTypes;
}
return null;
}
/**
* Answer the receiver's methods or null if the array is empty.
* @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
*/
public IBinaryMethod[] getMethods() {
return methods;
}
/**
* Answer an int whose bits are set according the access constants
* defined by the VM spec.
* Set the AccDeprecated and AccSynthetic bits if necessary
* @return int
*/
public int getModifiers() {
if (innerInfo != null) {
return innerInfo.getModifiers();
}
return accessFlags;
}
/**
* Answer the resolved name of the type in the
* class file format as specified in section 4.2 of the Java 2 VM spec.
*
* For example, java.lang.String is java/lang/String.
* @return char[]
*/
public char[] getName() {
return className;
}
/**
* Answer the resolved name of the receiver's superclass in the
* class file format as specified in section 4.2 of the Java 2 VM spec
* or null if it does not have one.
*
* For example, java.lang.String is java/lang/String.
* @return char[]
*/
public char[] getSuperclassName() {
return superclassName;
}
/**
* Answer true if the receiver is an anonymous type, false otherwise
*
* @return <CODE>boolean</CODE>
*/
public boolean isAnonymous() {
return innerInfo != null && innerInfo.getEnclosingTypeName() == null && innerInfo.getSourceName() == null;
}
/**
* Answer whether the receiver contains the resolved binary form
* or the unresolved source form of the type.
* @return boolean
*/
public boolean isBinaryType() {
return true;
}
/**
* Answer true if the receiver is a class. False otherwise.
* @return boolean
*/
public boolean isClass() {
return (getModifiers() & AccInterface) == 0;
}
/**
* Answer true if the receiver is an interface. False otherwise.
* @return boolean
*/
public boolean isInterface() {
return (getModifiers() & AccInterface) != 0;
}
/**
* Answer true if the receiver is a local type, false otherwise
*
* @return <CODE>boolean</CODE>
*/
public boolean isLocal() {
return innerInfo != null && innerInfo.getEnclosingTypeName() == null && innerInfo.getSourceName() != null;
}
/**
* Answer true if the receiver is a member type, false otherwise
*
* @return <CODE>boolean</CODE>
*/
public boolean isMember() {
return innerInfo != null && innerInfo.getEnclosingTypeName() != null;
}
/**
* Answer true if the receiver is a nested type, false otherwise
*
* @return <CODE>boolean</CODE>
*/
public boolean isNestedType() {
return innerInfo != null;
}
/**
* (c)1998 Object Technology International.
* (c)1998 International Business Machines Corporation.
*
* @param file The file you want to read
* @return org.eclipse.jdt.internal.compiler.classfmt.DietClassFile
*/
public static ClassFileReader read(java.io.File file) throws ClassFormatException, java.io.IOException {
int fileLength;
byte classFileBytes[] = new byte[fileLength = (int) file.length()];
java.io.FileInputStream stream = new java.io.FileInputStream(file);
int bytesRead = 0;
int lastReadSize = 0;
while ((lastReadSize != -1) && (bytesRead != fileLength)) {
lastReadSize = stream.read(classFileBytes, bytesRead, fileLength - bytesRead);
bytesRead += lastReadSize;
}
ClassFileReader classFile = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
stream.close();
return classFile;
}
/**
* (c)1998 Object Technology International.
* (c)1998 International Business Machines Corporation.
*
* @param String fileName
*/
public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
int fileLength;
File file = new File(fileName);
byte classFileBytes[] = new byte[fileLength = (int) file.length()];
java.io.FileInputStream stream = new java.io.FileInputStream(file);
int bytesRead = 0;
int lastReadSize = 0;
while ((lastReadSize != -1) && (bytesRead != fileLength)) {
lastReadSize = stream.read(classFileBytes, bytesRead, fileLength - bytesRead);
bytesRead += lastReadSize;
}
ClassFileReader classFile = new ClassFileReader(classFileBytes, fileName.toCharArray());
stream.close();
return classFile;
}
/**
* (c)1998 Object Technology International.
* (c)1998 International Business Machines Corporation.
*
* @param java.util.zip.ZipFile zip
* @param java.lang.String filename
* @return org.eclipse.jdt.internal.compiler.classfmt.DietClassFile
*/
public static ClassFileReader read(
java.util.zip.ZipFile zip,
String filename)
throws ClassFormatException, java.io.IOException {
java.util.zip.ZipEntry ze = zip.getEntry(filename);
if (ze == null)
return null;
java.io.InputStream zipInputStream = zip.getInputStream(ze);
byte classFileBytes[] = new byte[(int) ze.getSize()];
int length = classFileBytes.length;
int len = 0;
int readSize = 0;
while ((readSize != -1) && (len != length)) {
readSize = zipInputStream.read(classFileBytes, len, length - len);
len += readSize;
}
zipInputStream.close();
return new ClassFileReader(classFileBytes, filename.toCharArray());
}
/**
* (c)1998 Object Technology International.
* (c)1998 International Business Machines Corporation.
*
* Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
*
* @return char[]
*/
public char[] sourceFileName() {
return sourceFileName;
}
/**
* (c)1998 Object Technology International.
* (c)1998 International Business Machines Corporation.
*
*
*/
public String toString() {
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
java.io.PrintWriter print = new java.io.PrintWriter(out);
print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
print.println(" className: " + new String(getName())); //$NON-NLS-1$
print.println(" superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
print.println(" access_flags: " + ClassFileStruct.printTypeModifiers(accessFlags()) + "(" + accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
print.flush();
return out.toString();
}
}