blob: 19b80dcc3fbc9c994d86f4725ad066ed9debc9fa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.util;
/**
* Default implementation of IClassFileReader.
*/
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.util.ClassFormatException;
import org.eclipse.jdt.core.util.IAttributeNamesConstants;
import org.eclipse.jdt.core.util.IClassFileAttribute;
import org.eclipse.jdt.core.util.IClassFileReader;
import org.eclipse.jdt.core.util.IConstantPool;
import org.eclipse.jdt.core.util.IConstantPoolConstant;
import org.eclipse.jdt.core.util.IFieldInfo;
import org.eclipse.jdt.core.util.IInnerClassesAttribute;
import org.eclipse.jdt.core.util.IMethodInfo;
import org.eclipse.jdt.core.util.IModifierConstants;
import org.eclipse.jdt.core.util.ISourceAttribute;
import org.eclipse.jdt.internal.compiler.util.Util;
public class ClassFileReader extends ClassFileStruct implements IClassFileReader {
private static final IFieldInfo[] NO_FIELD_INFOS = new IFieldInfo[0];
private static final char[][] NO_INTERFACES_NAMES = CharOperation.NO_CHAR_CHAR;
private static final IMethodInfo[] NO_METHOD_INFOS = new IMethodInfo[0];
private int accessFlags;
private IClassFileAttribute[] attributes;
private int attributesCount;
private char[] className;
private int classNameIndex;
private IConstantPool constantPool;
private IFieldInfo[] fields;
private int fieldsCount;
private IInnerClassesAttribute innerClassesAttribute;
private int[] interfaceIndexes;
private char[][] interfaceNames;
private int interfacesCount;
private int magicNumber;
private int majorVersion;
private IMethodInfo[] methods;
private int methodsCount;
private int minorVersion;
private ISourceAttribute sourceFileAttribute;
private char[] superclassName;
private int superclassNameIndex;
/**
* Constructor for ClassFileReader.
*
* @param classFileBytes the raw bytes of the .class file
* @param decodingFlags the decoding flags
*
* @see IClassFileReader#ALL
* @see IClassFileReader#CLASSFILE_ATTRIBUTES
* @see IClassFileReader#CONSTANT_POOL
* @see IClassFileReader#FIELD_INFOS
*/
public ClassFileReader(byte[] classFileBytes, int decodingFlags) 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.
int constantPoolCount;
int[] constantPoolOffsets;
try {
this.magicNumber = (int) u4At(classFileBytes, 0, 0);
if (this.magicNumber != 0xCAFEBABE) {
throw new ClassFormatException(ClassFormatException.INVALID_MAGIC_NUMBER);
}
int readOffset = 10;
this.minorVersion = u2At(classFileBytes, 4, 0);
this.majorVersion = u2At(classFileBytes, 6, 0);
if ((decodingFlags & IClassFileReader.CONSTANT_POOL) == 0) {
// no need to go further
return;
}
constantPoolCount = u2At(classFileBytes, 8, 0);
// Pass #1 - Fill in all primitive constants
constantPoolOffsets = new int[constantPoolCount];
for (int i = 1; i < constantPoolCount; i++) {
int tag = u1At(classFileBytes, readOffset, 0);
switch (tag) {
case IConstantPoolConstant.CONSTANT_Utf8 :
constantPoolOffsets[i] = readOffset;
readOffset += u2At(classFileBytes, readOffset + 1, 0);
readOffset += IConstantPoolConstant.CONSTANT_Utf8_SIZE;
break;
case IConstantPoolConstant.CONSTANT_Integer :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_Integer_SIZE;
break;
case IConstantPoolConstant.CONSTANT_Float :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_Float_SIZE;
break;
case IConstantPoolConstant.CONSTANT_Long :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_Long_SIZE;
i++;
break;
case IConstantPoolConstant.CONSTANT_Double :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_Double_SIZE;
i++;
break;
case IConstantPoolConstant.CONSTANT_Class :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_Class_SIZE;
break;
case IConstantPoolConstant.CONSTANT_String :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_String_SIZE;
break;
case IConstantPoolConstant.CONSTANT_Fieldref :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_Fieldref_SIZE;
break;
case IConstantPoolConstant.CONSTANT_Methodref :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_Methodref_SIZE;
break;
case IConstantPoolConstant.CONSTANT_InterfaceMethodref :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_InterfaceMethodref_SIZE;
break;
case IConstantPoolConstant.CONSTANT_NameAndType :
constantPoolOffsets[i] = readOffset;
readOffset += IConstantPoolConstant.CONSTANT_NameAndType_SIZE;
break;
default:
throw new ClassFormatException(ClassFormatException.INVALID_TAG_CONSTANT);
}
}
this.constantPool = new ConstantPool(classFileBytes, constantPoolOffsets);
// Read and validate access flags
this.accessFlags = u2At(classFileBytes, readOffset, 0);
readOffset += 2;
// Read the classname, use exception handlers to catch bad format
this.classNameIndex = u2At(classFileBytes, readOffset, 0);
this.className = getConstantClassNameAt(classFileBytes, constantPoolOffsets, this.classNameIndex);
readOffset += 2;
// Read the superclass name, can be zero for java.lang.Object
this.superclassNameIndex = u2At(classFileBytes, readOffset, 0);
readOffset += 2;
// if superclassNameIndex is equals to 0 there is no need to set a value for the
// field this.superclassName. null is fine.
if (this.superclassNameIndex != 0) {
this.superclassName = getConstantClassNameAt(classFileBytes, constantPoolOffsets, this.superclassNameIndex);
}
// Read the interfaces, use exception handlers to catch bad format
this.interfacesCount = u2At(classFileBytes, readOffset, 0);
readOffset += 2;
this.interfaceNames = NO_INTERFACES_NAMES;
this.interfaceIndexes = Util.EMPTY_INT_ARRAY;
if (this.interfacesCount != 0) {
if ((decodingFlags & IClassFileReader.SUPER_INTERFACES) != IClassFileReader.CONSTANT_POOL) {
this.interfaceNames = new char[this.interfacesCount][];
this.interfaceIndexes = new int[this.interfacesCount];
for (int i = 0; i < this.interfacesCount; i++) {
this.interfaceIndexes[i] = u2At(classFileBytes, readOffset, 0);
this.interfaceNames[i] = getConstantClassNameAt(classFileBytes, constantPoolOffsets, this.interfaceIndexes[i]);
readOffset += 2;
}
} else {
readOffset += (2 * this.interfacesCount);
}
}
// Read the this.fields, use exception handlers to catch bad format
this.fieldsCount = u2At(classFileBytes, readOffset, 0);
readOffset += 2;
this.fields = NO_FIELD_INFOS;
if (this.fieldsCount != 0) {
if ((decodingFlags & IClassFileReader.FIELD_INFOS) != IClassFileReader.CONSTANT_POOL) {
FieldInfo field;
this.fields = new FieldInfo[this.fieldsCount];
for (int i = 0; i < this.fieldsCount; i++) {
field = new FieldInfo(classFileBytes, this.constantPool, readOffset);
this.fields[i] = field;
readOffset += field.sizeInBytes();
}
} else {
for (int i = 0; i < this.fieldsCount; i++) {
int attributeCountForField = u2At(classFileBytes, 6, readOffset);
readOffset += 8;
if (attributeCountForField != 0) {
for (int j = 0; j < attributeCountForField; j++) {
int attributeLength = (int) u4At(classFileBytes, 2, readOffset);
readOffset += (6 + attributeLength);
}
}
}
}
}
// Read the this.methods
this.methodsCount = u2At(classFileBytes, readOffset, 0);
readOffset += 2;
this.methods = NO_METHOD_INFOS;
if (this.methodsCount != 0) {
if ((decodingFlags & IClassFileReader.METHOD_INFOS) != IClassFileReader.CONSTANT_POOL) {
this.methods = new MethodInfo[this.methodsCount];
MethodInfo method;
for (int i = 0; i < this.methodsCount; i++) {
method = new MethodInfo(classFileBytes, this.constantPool, readOffset, decodingFlags);
this.methods[i] = method;
readOffset += method.sizeInBytes();
}
} else {
for (int i = 0; i < this.methodsCount; i++) {
int attributeCountForMethod = u2At(classFileBytes, 6, readOffset);
readOffset += 8;
if (attributeCountForMethod != 0) {
for (int j = 0; j < attributeCountForMethod; j++) {
int attributeLength = (int) u4At(classFileBytes, 2, readOffset);
readOffset += (6 + attributeLength);
}
}
}
}
}
// Read the attributes
this.attributesCount = u2At(classFileBytes, readOffset, 0);
readOffset += 2;
int attributesIndex = 0;
this.attributes = ClassFileAttribute.NO_ATTRIBUTES;
if (this.attributesCount != 0) {
if ((decodingFlags & IClassFileReader.CLASSFILE_ATTRIBUTES) != IClassFileReader.CONSTANT_POOL) {
this.attributes = new IClassFileAttribute[this.attributesCount];
for (int i = 0; i < this.attributesCount; i++) {
int utf8Offset = constantPoolOffsets[u2At(classFileBytes, readOffset, 0)];
char[] attributeName = utf8At(classFileBytes, utf8Offset + 3, 0, u2At(classFileBytes, utf8Offset + 1, 0));
if (equals(attributeName, IAttributeNamesConstants.INNER_CLASSES)) {
this.innerClassesAttribute = new InnerClassesAttribute(classFileBytes, this.constantPool, readOffset);
this.attributes[attributesIndex++] = this.innerClassesAttribute;
} else if (equals(attributeName, IAttributeNamesConstants.SOURCE)) {
this.sourceFileAttribute = new SourceFileAttribute(classFileBytes, this.constantPool, readOffset);
this.attributes[attributesIndex++] = this.sourceFileAttribute;
} else if (equals(attributeName, IAttributeNamesConstants.ENCLOSING_METHOD)) {
this.attributes[attributesIndex++] = new EnclosingMethodAttribute(classFileBytes, this.constantPool, readOffset);
} else if (equals(attributeName, IAttributeNamesConstants.SIGNATURE)) {
this.attributes[attributesIndex++] = new SignatureAttribute(classFileBytes, this.constantPool, readOffset);
} else if (equals(attributeName, IAttributeNamesConstants.RUNTIME_VISIBLE_ANNOTATIONS)) {
this.attributes[attributesIndex++] = new RuntimeVisibleAnnotationsAttribute(classFileBytes, this.constantPool, readOffset);
} else if (equals(attributeName, IAttributeNamesConstants.RUNTIME_INVISIBLE_ANNOTATIONS)) {
this.attributes[attributesIndex++] = new RuntimeInvisibleAnnotationsAttribute(classFileBytes, this.constantPool, readOffset);
} else {
this.attributes[attributesIndex++] = new ClassFileAttribute(classFileBytes, this.constantPool, readOffset);
}
readOffset += (6 + u4At(classFileBytes, readOffset + 2, 0));
}
} else {
for (int i = 0; i < this.attributesCount; i++) {
readOffset += (6 + u4At(classFileBytes, readOffset + 2, 0));
}
}
}
if (readOffset != classFileBytes.length) {
throw new ClassFormatException(ClassFormatException.TOO_MANY_BYTES);
}
} catch(ClassFormatException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new ClassFormatException(ClassFormatException.ERROR_TRUNCATED_INPUT);
}
}
/**
* @see IClassFileReader#getAccessFlags()
*/
public int getAccessFlags() {
return this.accessFlags;
}
/**
* @see IClassFileReader#getAttributeCount()
*/
public int getAttributeCount() {
return this.attributesCount;
}
/**
* @see IClassFileReader#getAttributes()
*/
public IClassFileAttribute[] getAttributes() {
return this.attributes;
}
/**
* @see IClassFileReader#getClassIndex()
*/
public int getClassIndex() {
return this.classNameIndex;
}
/**
* @see IClassFileReader#getClassName()
*/
public char[] getClassName() {
return this.className;
}
private char[] getConstantClassNameAt(byte[] classFileBytes, int[] constantPoolOffsets, int constantPoolIndex) {
int utf8Offset = constantPoolOffsets[u2At(classFileBytes, constantPoolOffsets[constantPoolIndex] + 1, 0)];
return utf8At(classFileBytes, utf8Offset + 3, 0, u2At(classFileBytes, utf8Offset + 1, 0));
}
/**
* @see IClassFileReader#getConstantPool()
*/
public IConstantPool getConstantPool() {
return this.constantPool;
}
/**
* @see IClassFileReader#getFieldInfos()
*/
public IFieldInfo[] getFieldInfos() {
return this.fields;
}
/**
* @see IClassFileReader#getFieldsCount()
*/
public int getFieldsCount() {
return this.fieldsCount;
}
/**
* @see IClassFileReader#getInnerClassesAttribute()
*/
public IInnerClassesAttribute getInnerClassesAttribute() {
return this.innerClassesAttribute;
}
/**
* @see IClassFileReader#getInterfaceIndexes()
*/
public int[] getInterfaceIndexes() {
return this.interfaceIndexes;
}
/**
* @see IClassFileReader#getInterfaceNames()
*/
public char[][] getInterfaceNames() {
return this.interfaceNames;
}
/**
* @see IClassFileReader#getMagic()
*/
public int getMagic() {
return this.magicNumber;
}
/**
* @see IClassFileReader#getMajorVersion()
*/
public int getMajorVersion() {
return this.majorVersion;
}
/**
* @see IClassFileReader#getMethodInfos()
*/
public IMethodInfo[] getMethodInfos() {
return this.methods;
}
/**
* @see IClassFileReader#getMethodsCount()
*/
public int getMethodsCount() {
return this.methodsCount;
}
/**
* @see IClassFileReader#getMinorVersion()
*/
public int getMinorVersion() {
return this.minorVersion;
}
/**
* @see IClassFileReader#getSourceFileAttribute()
*/
public ISourceAttribute getSourceFileAttribute() {
return this.sourceFileAttribute;
}
/**
* @see IClassFileReader#getSuperclassIndex()
*/
public int getSuperclassIndex() {
return this.superclassNameIndex;
}
/**
* @see IClassFileReader#getSuperclassName()
*/
public char[] getSuperclassName() {
return this.superclassName;
}
/**
* @see IClassFileReader#isClass()
*/
public boolean isClass() {
return !isInterface();
}
/**
* @see IClassFileReader#isInterface()
*/
public boolean isInterface() {
return (getAccessFlags() & IModifierConstants.ACC_INTERFACE) != 0;
}
}