package assignment0326.jvm.loader;
import assignment0326.jvm.clz.AccessFlag;
import assignment0326.jvm.clz.ClassFile;
import assignment0326.jvm.clz.ClassIndex;
import assignment0326.jvm.constant.*;
public class ClassFileParser {
private ConstantPool constantPool;
public ClassFile parse(byte[] codes) {
ClassFile classFile = new ClassFile();
ByteCodeIterator iterator = new ByteCodeIterator(codes);
magicNumberVerify(iterator);
classFile.setMinorVersion(iterator.next2BytesToInt());
classFile.setMajorVersion(iterator.next2BytesToInt());
classFile.setConstPool(parseConstantPool(iterator));
classFile.setAccessFlag(parseAccessFlag(iterator));
classFile.setClassIndex(parseClassIndex(iterator));
return classFile;
}
private void magicNumberVerify(ByteCodeIterator iterator) {
String magicNumber=Integer.toHexString(iterator.nextByteToInt());
for (int i = 0; i < 3; i++) {
magicNumber += Integer.toHexString(iterator.nextByteToInt());
}
if (!"cafebabe".equals(magicNumber)) {
throw new RuntimeException("Illegal class file.");
}
}
private AccessFlag parseAccessFlag(ByteCodeIterator iter) {
AccessFlag flag = new AccessFlag(iter.next2BytesToInt());
return flag;
}
private ClassIndex parseClassIndex(ByteCodeIterator iter) {
ClassIndex classIndex = new ClassIndex();
classIndex.setThisClassIndex(iter.next2BytesToInt());
classIndex.setSuperClassIndex(iter.next2BytesToInt());
return classIndex;
}
private ConstantPool parseConstantPool(ByteCodeIterator iter) {
constantPool = new ConstantPool();
int size = iter.next2BytesToInt();
constantPool.addConstantInfo(new NullConstantInfo());
for (int i = 0; i < size - 1; i++) {
parseConstant(iter);
}
return constantPool;
}
private void parseConstant(ByteCodeIterator iter) {
int tag = iter.nextByteToInt();
ConstantInfo constantInfo;
switch (tag) {
case ConstantInfo.UTF8_INFO:
constantInfo = parseUTF8Info(iter);
break;
case ConstantInfo.CLASS_INFO:
constantInfo = parseClassInfo(iter);
break;
case ConstantInfo.STRING_INFO:
constantInfo = parseStringInfo(iter);
break;
case ConstantInfo.FIELD_INFO:
constantInfo = parseFieldInfo(iter);
break;
case ConstantInfo.METHOD_INFO:
constantInfo = parseMethodInfo(iter);
break;
case ConstantInfo.NAME_AND_TYPE_INFO:
constantInfo = parseNameAndTypeInfo(iter);
break;
default:
throw new RuntimeException("Unsupported tag");
}
constantPool.addConstantInfo(constantInfo);
}
private ConstantInfo parseUTF8Info(ByteCodeIterator iter) {
UTF8Info utf8Info = new UTF8Info(constantPool);
int length = iter.next2BytesToInt();
String value = new String(iter.nextNBytes(length));
utf8Info.setLength(length);
utf8Info.setValue(value);
return utf8Info;
}
private ConstantInfo parseClassInfo(ByteCodeIterator iter) {
ClassInfo classInfo = new ClassInfo(constantPool);
classInfo.setUtf8Index(iter.next2BytesToInt());
return classInfo;
}
private ConstantInfo parseStringInfo(ByteCodeIterator iter) {
StringInfo stringInfo = new StringInfo(constantPool);
stringInfo.setIndex(iter.next2BytesToInt());
return stringInfo;
}
private ConstantInfo parseFieldInfo(ByteCodeIterator iter) {
FieldRefInfo fieldRefInfo = new FieldRefInfo(constantPool);
fieldRefInfo.setClassInfoIndex(iter.next2BytesToInt());
fieldRefInfo.setNameAndTypeIndex(iter.next2BytesToInt());
return fieldRefInfo;
}
private ConstantInfo parseMethodInfo(ByteCodeIterator iter) {
MethodRefInfo methodRefInfo = new MethodRefInfo(constantPool);
methodRefInfo.setClassInfoIndex(iter.next2BytesToInt());
methodRefInfo.setNameAndTypeIndex(iter.next2BytesToInt());
return methodRefInfo;
}
private ConstantInfo parseNameAndTypeInfo(ByteCodeIterator iter) {
NameAndTypeInfo nameAndTypeInfo = new NameAndTypeInfo(constantPool);
nameAndTypeInfo.setIndex1(iter.next2BytesToInt());
nameAndTypeInfo.setIndex2(iter.next2BytesToInt());
return nameAndTypeInfo;
}
}