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