/*******************************************************************************
* 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;
}
}