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