package org.nanovm.converter;
//
// NanoVMTool, Converter and Upload Tool for the NanoVM
// Copyright (C) 2005 by Till Harbaum <Till@Harbaum.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Parts of this tool are based on public domain code written by Kimberley
// Burchett: http://www.kimbly.com/code/classfile/
//
import org.nanovm.converter.ClassInfo;
import java.io.*;
/**
* This class parses java class files and stores the information in a
* ClassInfo object.
*
* @see ClassInfo
*/
public class ClassFileReader implements AccessFlags {
public void read(InputStream in, ClassInfo classInfo)
throws IOException {
strClass(new DataInputStream(new BufferedInputStream(in)), classInfo);
}
private void strClass(DataInput in, ClassInfo classInfo)
throws IOException {
short count;
// Magic And Version Numbers
int magic = in.readInt();
if (magic != 0xCAFEBABE)
throw new IOException("Invalid classfile magic number" +
": expected 0xCAFEBABE, found 0x" + Integer.toHexString(magic));
short major = in.readShort(), minor = in.readShort();
System.out.println("Classfile version " + major + "." + minor);
// Constant Pool
ConstPool cp = new ConstPool();
cp.read(in);
classInfo.setConstPool(cp);
// General Class Info
short flags = in.readShort();
short classIndex = in.readShort();
short superClassIndex = in.readShort();
int classNameIndex = cp.getEntryAtIndex(classIndex).getClassNameIndex();
int superClassNameIndex = cp.getEntryAtIndex(superClassIndex).getClassNameIndex();
String className = cp.getEntryAtIndex(classNameIndex).getString();
String superClassName = cp.getEntryAtIndex(superClassNameIndex).getString();
if (Debug.strClass != null)
Debug.println(Debug.strClass,
"flags=" + flags +
"; class index=" + classIndex +
"; super class index=" + superClassIndex);
// save necessary class information
classInfo.setAccessFlags(flags);
classInfo.setName(className);
classInfo.setSuperClassName(superClassName);
// Interfaces
count = in.readShort();
if(Debug.strClass != null)
Debug.println(Debug.strClass,
"#interfaces=" + count);
Debug.indent();
for (int i = 0; i < count; i++)
classInfo.addInterface(strInterface(cp, in));
Debug.outdent();
// Fields
count = in.readShort();
if (Debug.strClass != null) Debug.println(Debug.strClass,
"#fields=" + count);
Debug.indent();
for (int i = 0; i < count; i++)
classInfo.addField(strField(cp, in));
Debug.outdent();
// Methods
count = in.readShort();
if (Debug.strClass != null)
Debug.println(Debug.strClass,
"#methods=" + count);
Debug.indent();
for (int i = 0; i < count; i++)
classInfo.addMethod(strMethod(cp, in));
Debug.outdent();
// Attributes
count = in.readShort();
if (Debug.strClass != null)
Debug.println(Debug.strClass,
"#attributes=" + count);
Debug.indent();
for (int i = 0; i < count; i++)
strClassAttribute(classInfo, cp, in);
Debug.outdent();
}
private String strInterface(ConstPool cp, DataInput in)
throws IOException {
short classIndex = in.readShort();
if (Debug.strInterface != null)
Debug.println(Debug.strInterface,
"class index=" + classIndex);
int nameIndex = cp.getEntryAtIndex(classIndex).getClassNameIndex();
return cp.getEntryAtIndex(nameIndex).getString();
}
private FieldInfo strField(ConstPool cp, DataInput in)
throws IOException {
FieldInfo result;
// General Field Info
{
short flags = in.readShort();
short nameIndex = in.readShort();
short signatureIndex = in.readShort();
if (Debug.strField != null) Debug.println(Debug.strField,
"flags=" + flags +
"; name index=" + nameIndex +
"; signature index=" + signatureIndex);
result = new FieldInfo(flags,
cp.getEntryAtIndex(nameIndex).getString(),
cp.getEntryAtIndex(signatureIndex).getString());
}
// Field Attributes
short numAttributes = in.readShort();
if (Debug.strField != null) Debug.println(Debug.strField,
"#attributes=" + numAttributes);
Debug.indent();
for (int i = 0; i < numAttributes; i++)
strFieldAttribute(result, cp, in);
Debug.outdent();
return result;
}
private MethodInfo strMethod(ConstPool cp, DataInput in)
throws IOException {
MethodInfo result;
// General Method Info
{
short flags = in.readShort();
short nameIndex = in.readShort();
short signatureIndex = in.readShort();
String methodName = cp.getEntryAtIndex(nameIndex).getString();
String methodSignature = cp.getEntryAtIndex(signatureIndex).getString();
if (Debug.strMethod != null) Debug.println(Debug.strMethod,
"flags=" + flags +
"; name index=" + nameIndex +
"; signature index=" + signatureIndex);
result = new MethodInfo(flags, methodName, methodSignature);
}
// Method Attributes
short methodAttrCount = in.readShort();
if (Debug.strMethod != null) Debug.println(Debug.strMethod,
"#attributes=" + methodAttrCount);
Debug.indent();
for (int iMethodAttr = 0; iMethodAttr < methodAttrCount; iMethodAttr++)
strMethodAttribute(result, cp, in);
Debug.outdent();
return result;
}
private void strClassAttribute(ClassInfo classInfo, ConstPool cp,
DataInput in) throws IOException {
short nameIndex = in.readShort();
int length = in.readInt();
// make sure we read the entire attribute -- if it has bad data,
// an exception might get thrown before we've read it all
byte[] bytes = new byte[length];
in.readFully(bytes);
in = new DataInputStream(new ByteArrayInputStream(bytes));
if (Debug.strClass != null) Debug.println(Debug.strClass,
"attribute name index=" + nameIndex +
"; length=" + length);
Debug.indent();
try {
String name = cp.getEntryAtIndex(nameIndex).getString();
// SourceFile Attribute
if (name.equals("SourceFile")) {
short filenameIndex = in.readShort();
if (Debug.strClass != null) Debug.println(Debug.strClass,
"filename index=" + filenameIndex);
classInfo.setSourceFile(cp.getEntryAtIndex(filenameIndex).getString());
}
else if (name.equals("InnerClasses"))
classInfo.setInnerClasses(strInnerClasses(cp, in));
else
classInfo.addAttribute(strUnknownAttribute(name, length, in));
} catch (ConstPoolEntryError e) {
if (Debug.strBadData != null) Debug.println(Debug.strBadData,
"class attribute name index=" + nameIndex);
}
Debug.outdent();
}
private void strFieldAttribute(FieldInfo fieldInfo, ConstPool cp,
DataInput in) throws IOException {
short nameIndex = in.readShort();
String name = cp.getEntryAtIndex(nameIndex).getString();
int length = in.readInt();
// make sure we read the entire attribute -- if it has bad data,
// an exception might get thrown before we've read it all
byte[] bytes = new byte[length];
in.readFully(bytes);
in = new DataInputStream(new ByteArrayInputStream(bytes));
if (Debug.strField != null) Debug.println(Debug.strField,
"attribute name index=" + nameIndex +
"; length=" + length);
Debug.indent();
try {
// ConstantValue Attribute
if (name.equals("ConstantValue")) {
short cvIndex = in.readShort();
if (Debug.strField != null) Debug.println(Debug.strField,
"constant value index=" + cvIndex);
fieldInfo.setConstantValue(cp.getEntryAtIndex(cvIndex).getPrimitiveTypeValue());
}
else if (name.equals("Synthetic")) {
if (Debug.strField != null) Debug.println(Debug.strField,
"synthetic");
fieldInfo.setSynthetic(true);
}
else
fieldInfo.addAttribute(strUnknownAttribute(name, length, in));
} catch (ConstPoolEntryError e) {
if (Debug.strBadData != null) Debug.println(Debug.strBadData,
"field attribute name index=" + nameIndex);
}
Debug.outdent();
}
private void strMethodAttribute(MethodInfo methodInfo, ConstPool cp,
DataInput in) throws IOException {
short nameIndex = in.readShort();
int length = in.readInt();
// make sure we read the entire attribute -- if it has bad data,
// an exception might get thrown before we've read it all
byte[] bytes = new byte[length];
in.readFully(bytes);
in = new DataInputStream(new ByteArrayInputStream(bytes));
if (Debug.strMethod != null) Debug.println(Debug.strMethod,
"attribute name index=" + nameIndex +
"; length=" + length);
Debug.indent();
try {
String name = cp.getEntryAtIndex(nameIndex).getString();
if (name.equals("Exceptions")) {
int count = in.readShort();
for (int i = 0; i < count; i++) {
short exceptionClassIndex = in.readShort();
int exceptionClassNameIndex = cp.getEntryAtIndex(exceptionClassIndex).getClassNameIndex();
String exceptionName = cp.getEntryAtIndex(exceptionClassNameIndex). getString();
methodInfo.addException(exceptionName);
}
}
else if (name.equals("Code"))
methodInfo.setCodeInfo(strCode(cp, in));
else if (name.equals("Deprecated")) {
if (Debug.strMethod != null) Debug.println(Debug.strMethod,
"deprecated");
methodInfo.setDeprecated(true);
}
else
methodInfo.addAttribute(strUnknownAttribute(name, length, in));
} catch (ConstPoolEntryError e) {
if (Debug.strBadData != null) Debug.println(Debug.strBadData,
"method attribute name index=" + nameIndex);
}
Debug.outdent();
}
private CodeInfo strCode(ConstPool cp, DataInput in)
throws IOException {
// General Code Info
short maxStack = in.readShort();
short maxLocals = in.readShort();
byte[] bytecode = new byte[in.readInt()];
if (Debug.strCode != null) Debug.println(Debug.strCode,
"maxStack=" + maxStack +
"; maxLocals=" + maxLocals +
"; bytecode length=" + bytecode.length);
in.readFully(bytecode);
// Exception Table
ExceptionInfo[] exceptionTable = new ExceptionInfo[in.readShort()];
if (Debug.strCode != null) Debug.println(Debug.strCode,
"exception table length=" + exceptionTable.length);
Debug.indent();
for (int i = 0; i < exceptionTable.length; i++) {
short startPC = in.readShort();
short endPC = in.readShort();
short handlerPC = in.readShort();
short catchTypeIndex = in.readShort();
if (Debug.strCode != null) Debug.println(Debug.strCode,
"startPC=" + startPC +
"; endPC=" + endPC +
"; handlerPC=" + handlerPC +
"; catchTypeIndex=" + catchTypeIndex);
String catchType = null;
if (catchTypeIndex != 0) { // index is null for finally blocks
int catchTypeNameIndex =
cp.getEntryAtIndex(catchTypeIndex).getClassNameIndex();
catchType = cp.getEntryAtIndex(catchTypeNameIndex).getString();
}
exceptionTable[i] =
new ExceptionInfo(startPC, endPC, handlerPC, catchType);
}
Debug.outdent();
CodeInfo codeInfo =
new CodeInfo(maxStack, maxLocals, bytecode, exceptionTable);
// Code Attributes
short codeAttrCount = in.readShort();
if (Debug.strCode != null) Debug.println(Debug.strCode,
"#attributes=" + codeAttrCount);
Debug.indent();
for (int iCodeAttr = 0; iCodeAttr < codeAttrCount; iCodeAttr++)
strCodeAttribute(codeInfo, cp, in);
Debug.outdent();
return codeInfo;
}
private void strCodeAttribute(CodeInfo codeInfo, ConstPool cp,
DataInput in) throws IOException {
short nameIndex = in.readShort();
int length = in.readInt();
// make sure we read the entire attribute -- if it has bad data,
// an exception might get thrown before we've read it all
byte[] bytes = new byte[length];
in.readFully(bytes);
in = new DataInputStream(new ByteArrayInputStream(bytes));
if (Debug.strCode != null)
Debug.println(Debug.strCode, "code attribute name index=" +
nameIndex + "; length=" + length);
Debug.indent();
try {
String name = cp.getEntryAtIndex(nameIndex).getString();
if (name.equals("LineNumberTable"))
codeInfo.setLineNumberTable(readLineNumberTable(in));
else if (name.equals("LocalVariableTable"))
codeInfo.setLocalVariableTable(strLocalVariablesTable(cp, in));
else
codeInfo.addAttribute(strUnknownAttribute(name, length, in));
} catch (ConstPoolEntryError e) {
if (Debug.strBadData != null) Debug.println(Debug.strBadData,
"code attribute name index=" + nameIndex);
}
Debug.outdent();
}
private InnerClassInfo[] strInnerClasses(ConstPool cp, DataInput in)
throws IOException {
short rows = in.readShort();
if (Debug.strInnerClasses != null) Debug.println(Debug.strInnerClasses,
"#inner classes=" + rows);
InnerClassInfo[] classes = new InnerClassInfo[rows];
for (int i = 0; i < rows; i++) {
short innerClassIndex = in.readShort();
short outerClassIndex = in.readShort();
short simpleNameIndex = in.readShort();
short flags = in.readShort();
if (Debug.strInnerClasses != null)
Debug.println(Debug.strInnerClasses,
"inner class index=" + innerClassIndex +
"; outer class index=" + outerClassIndex +
"; simple name index=" + simpleNameIndex +
"; flags=" + flags);
int innerClassNameIndex = cp.getEntryAtIndex(innerClassIndex).getClassNameIndex();
String innerClassName = cp.getEntryAtIndex(innerClassNameIndex).getString();
String outerClassName = null;
if (outerClassIndex != 0) {
int outerClassNameIndex = cp.getEntryAtIndex(outerClassIndex).getClassNameIndex();
outerClassName = cp.getEntryAtIndex(outerClassNameIndex).getString();
}
String simpleName = null;
if (simpleNameIndex != 0)
simpleName = cp.getEntryAtIndex(simpleNameIndex).getString();
classes[i] = new InnerClassInfo(innerClassName, outerClassName, simpleName, flags);
}
return classes;
}
private LocalVariableInfo[] strLocalVariablesTable(ConstPool cp, DataInput in)
throws IOException
{
LocalVariableInfo[] table = new LocalVariableInfo[in.readShort()];
if (Debug.strLocalVariables != null)
Debug.println(Debug.strLocalVariables,
"#local variables=" + table.length);
for (int i = 0; i < table.length; i++) {
short startPC = in.readShort();
short length = in.readShort();
short nameIndex = in.readShort();
short signatureIndex = in.readShort();
short slot= in.readShort();
if (Debug.strLocalVariables != null)
Debug.println(Debug.strLocalVariables,
"start PC=" + startPC +
"; length=" + length +
"; name index=" + nameIndex +
"; signature index=" + signatureIndex +
"; slot=" + slot);
String name = cp.getEntryAtIndex(nameIndex).getString();
String signature = cp.getEntryAtIndex(signatureIndex).getString();
table[i] = new LocalVariableInfo(startPC, length, name, signature, slot);
}
return table;
}
private LineNumberInfo[] readLineNumberTable(DataInput in)
throws IOException {
LineNumberInfo[] table = new LineNumberInfo[in.readShort()];
if (Debug.strLineNumbers != null)
Debug.println(Debug.strLineNumbers,
"#line numbers=" + table.length);
for (int i = 0; i < table.length; i++) {
short startPC = in.readShort();
short lineNumber = in.readShort();
if (Debug.strLineNumbers != null)
Debug.println(Debug.strLineNumbers,
"start PC=" + startPC +
"; line number=" + lineNumber);
table[i] = new LineNumberInfo(startPC, lineNumber);
}
return table;
}
private AttributeInfo strUnknownAttribute(String name, int length, DataInput in)
throws IOException {
if (Debug.strUnknownAttribute != null)
Debug.println(Debug.strUnknownAttribute,
"attribute name=\"" + name + "\"");
byte[] data = new byte[length];
in.readFully(data);
return new AttributeInfo(name, data);
}
}