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