blob: dea13cc34123e616c17d2f4244f0ce31764ad062 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 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.equinox.p2.internal.repository.comparator.java;
import java.util.Arrays;
public class ClassFileReader extends ClassFileStruct {
/*
* This value should be used to read completely each part of a .class file.
*/
public static final int ALL = 0xFFFF;
/*
* This value should be used to read only the constant pool entries of a .class file.
*/
public static final int CONSTANT_POOL = 0x0001;
/*
* This value should be used to read the constant pool entries and
* the method infos of a .class file.
*/
public static final int METHOD_INFOS = 0x0002 + CONSTANT_POOL;
/*
* This value should be used to read the constant pool entries and
* the field infos of a .class file.
*/
public static final int FIELD_INFOS = 0x0004 + CONSTANT_POOL;
/*
* This value should be used to read the constant pool entries and
* the super interface names of a .class file.
*/
public static final int SUPER_INTERFACES = 0x0008 + CONSTANT_POOL;
/*
* This value should be used to read the constant pool entries and
* the attributes of a .class file.
*/
public static final int CLASSFILE_ATTRIBUTES = 0x0010 + CONSTANT_POOL;
/*
* This value should be used to read the method bodies.
* It has to be used with METHOD_INFOS.
*/
public static final int METHOD_BODIES = 0x0020;
/*
* This value should be used to read the whole contents of the .class file except the
* method bodies.
*/
public static final int ALL_BUT_METHOD_BODIES = ALL & ~METHOD_BODIES;
private static final FieldInfo[] NO_FIELD_INFOS = new FieldInfo[0];
private static final char[][] NO_INTERFACES_NAMES = CharOperation.NO_CHAR_CHAR;
private static final MethodInfo[] NO_METHOD_INFOS = new MethodInfo[0];
private int accessFlags;
private ClassFileAttribute[] attributes;
private int attributesCount;
private char[] className;
private int classNameIndex;
private ConstantPool constantPool;
private FieldInfo[] fields;
private int fieldsCount;
private InnerClassesAttribute innerClassesAttribute;
private int[] interfaceIndexes;
private char[][] interfaceNames;
private int interfacesCount;
private int magicNumber;
private int majorVersion;
private MethodInfo[] methods;
private int methodsCount;
private int minorVersion;
private SourceFileAttribute 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 & 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 ConstantPoolConstant.CONSTANT_Utf8 :
constantPoolOffsets[i] = readOffset;
readOffset += u2At(classFileBytes, readOffset + 1, 0);
readOffset += ConstantPoolConstant.CONSTANT_Utf8_SIZE;
break;
case ConstantPoolConstant.CONSTANT_Integer :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_Integer_SIZE;
break;
case ConstantPoolConstant.CONSTANT_Float :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_Float_SIZE;
break;
case ConstantPoolConstant.CONSTANT_Long :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_Long_SIZE;
i++;
break;
case ConstantPoolConstant.CONSTANT_Double :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_Double_SIZE;
i++;
break;
case ConstantPoolConstant.CONSTANT_Class :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_Class_SIZE;
break;
case ConstantPoolConstant.CONSTANT_String :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_String_SIZE;
break;
case ConstantPoolConstant.CONSTANT_Fieldref :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_Fieldref_SIZE;
break;
case ConstantPoolConstant.CONSTANT_Methodref :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_Methodref_SIZE;
break;
case ConstantPoolConstant.CONSTANT_InterfaceMethodref :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.CONSTANT_InterfaceMethodref_SIZE;
break;
case ConstantPoolConstant.CONSTANT_NameAndType :
constantPoolOffsets[i] = readOffset;
readOffset += ConstantPoolConstant.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 = Utility.EMPTY_INT_ARRAY;
if (this.interfacesCount != 0) {
if ((decodingFlags & SUPER_INTERFACES) != 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 & FIELD_INFOS) != 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 & METHOD_INFOS) != 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 & CLASSFILE_ATTRIBUTES) != CONSTANT_POOL) {
this.attributes = new ClassFileAttribute[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 (Arrays.equals(attributeName, AttributeNamesConstants.INNER_CLASSES)) {
this.innerClassesAttribute = new InnerClassesAttribute(classFileBytes, this.constantPool, readOffset);
this.attributes[attributesIndex++] = this.innerClassesAttribute;
} else if (Arrays.equals(attributeName, AttributeNamesConstants.SOURCE)) {
this.sourceFileAttribute = new SourceFileAttribute(classFileBytes, this.constantPool, readOffset);
this.attributes[attributesIndex++] = this.sourceFileAttribute;
} else if (Arrays.equals(attributeName, AttributeNamesConstants.ENCLOSING_METHOD)) {
this.attributes[attributesIndex++] = new EnclosingMethodAttribute(classFileBytes, this.constantPool, readOffset);
} else if (Arrays.equals(attributeName, AttributeNamesConstants.SIGNATURE)) {
this.attributes[attributesIndex++] = new SignatureAttribute(classFileBytes, this.constantPool, readOffset);
} else if (Arrays.equals(attributeName, AttributeNamesConstants.RUNTIME_VISIBLE_ANNOTATIONS)) {
this.attributes[attributesIndex++] = new RuntimeVisibleAnnotationsAttribute(classFileBytes, this.constantPool, readOffset);
} else if (Arrays.equals(attributeName, AttributeNamesConstants.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 ClassFileAttribute[] 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 ConstantPool getConstantPool() {
return this.constantPool;
}
/*
* @see IClassFileReader#getFieldInfos()
*/
public FieldInfo[] getFieldInfos() {
return this.fields;
}
/*
* @see IClassFileReader#getFieldsCount()
*/
public int getFieldsCount() {
return this.fieldsCount;
}
/*
* @see IClassFileReader#getInnerClassesAttribute()
*/
public InnerClassesAttribute 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 MethodInfo[] 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 SourceFileAttribute 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;
}
}