package com.coderising.jvm.loader;
import com.coderising.jvm.clz.AccessFlag;
import com.coderising.jvm.clz.ClassFile;
import com.coderising.jvm.clz.ClassIndex;
import com.coderising.jvm.constant.ClassInfo;
import com.coderising.jvm.constant.ConstantInfo;
import com.coderising.jvm.constant.ConstantPool;
import com.coderising.jvm.constant.FieldRefInfo;
import com.coderising.jvm.constant.MethodRefInfo;
import com.coderising.jvm.constant.NameAndTypeInfo;
import com.coderising.jvm.constant.NullConstantInfo;
import com.coderising.jvm.constant.StringInfo;
import com.coderising.jvm.constant.UTF8Info;
import com.coderising.jvm.field.Field;
import com.coderising.jvm.method.Method;
import java.io.UnsupportedEncodingException;
public class ClassFileParser {
public ClassFile parse(byte[] codes) {
ClassFile classFile = new ClassFile();
ByteCodeIterator iterator = new ByteCodeIterator(codes);
String magicNumber = iterator.nextU4ToHexString();
if (!"cafebabe".equals(magicNumber)) {
return null;
}
classFile.setMinorVersion(iterator.nextU2ToInt());
classFile.setMajorVersion(iterator.nextU2ToInt());
classFile.setConstPool(parseConstantPool(iterator));
classFile.setAccessFlag(parseAccessFlag(iterator));
classFile.setClassIndex(parseClassInfex(iterator));
parseInterfaces(classFile, iterator);
parseFields(classFile, iterator);
parseMethods(classFile, iterator);
return classFile;
}
private AccessFlag parseAccessFlag(ByteCodeIterator iter) {
AccessFlag accessFlag = new AccessFlag(iter.nextU2ToInt());
return accessFlag;
}
private ClassIndex parseClassInfex(ByteCodeIterator iter) {
ClassIndex classIndex = new ClassIndex();
classIndex.setThisClassIndex(iter.nextU2ToInt());
classIndex.setSuperClassIndex(iter.nextU2ToInt());
return classIndex;
}
private ConstantPool parseConstantPool(ByteCodeIterator iter) {
int constantPoolCount = iter.nextU2ToInt();
ConstantPool pool = new ConstantPool();
pool.addConstantInfo(new NullConstantInfo());
for (int i = 1; i < constantPoolCount; i++) {
int type = iter.nextU1toInt();
if (type == ConstantInfo.CLASS_INFO) {
int utf8Index = iter.nextU2ToInt();
ClassInfo clzInfo = new ClassInfo(pool);
clzInfo.setUtf8Index(utf8Index);
pool.addConstantInfo(clzInfo);
} else if (type == ConstantInfo.UTF8_INFO) {
int len = iter.nextU2ToInt();
byte[] data = iter.getBytes(len);
String value = null;
try {
value = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
// ignore
}
UTF8Info utf8Info = new UTF8Info(pool);
utf8Info.setLength(len);
utf8Info.setValue(value);
pool.addConstantInfo(utf8Info);
} else if (type == ConstantInfo.STRING_INFO) {
StringInfo stringInfo = new StringInfo(pool);
stringInfo.setIndex(iter.nextU2ToInt());
pool.addConstantInfo(stringInfo);
} else if (type == ConstantInfo.FIELD_INFO) {
FieldRefInfo fieldRefInfo = new FieldRefInfo(pool);
fieldRefInfo.setClassInfoIndex(iter.nextU2ToInt());
fieldRefInfo.setNameAndTypeIndex(iter.nextU2ToInt());
pool.addConstantInfo(fieldRefInfo);
} else if (type == ConstantInfo.METHOD_INFO) {
MethodRefInfo methodRefInfo = new MethodRefInfo(pool);
methodRefInfo.setClassInfoIndex(iter.nextU2ToInt());
methodRefInfo.setNameAndTypeIndex(iter.nextU2ToInt());
pool.addConstantInfo(methodRefInfo);
} else if (type == ConstantInfo.NAME_AND_TYPE_INFO) {
NameAndTypeInfo nameAndTypeInfo = new NameAndTypeInfo(pool);
nameAndTypeInfo.setIndex1(iter.nextU2ToInt());
nameAndTypeInfo.setIndex2(iter.nextU2ToInt());
pool.addConstantInfo(nameAndTypeInfo);
} else {
throw new RuntimeException("the constant pool type " + type + " hasn't been implemented yet!");
}
}
return pool;
}
private void parseInterfaces(ClassFile clzFile, ByteCodeIterator iter) {
int interfaceCount = iter.nextU2ToInt();
System.out.println("Interface Count: " + interfaceCount);
}
private void parseFields(ClassFile clzFile, ByteCodeIterator iter) {
int filedCount = iter.nextU2ToInt();
System.out.println("Field Count: " + filedCount);
for (int i = 0; i < filedCount; i++) {
Field field = Field.parse(clzFile.getConstantPool(), iter);
clzFile.addField(field);
}
}
private void parseMethods(ClassFile clzFile, ByteCodeIterator iter) {
int methodCount = iter.nextU2ToInt();
System.out.println("Method Count: " + methodCount);
for (int i = 0; i < methodCount; i++) {
Method method = Method.parse(clzFile, iter);
clzFile.addMethod(method);
}
}
}