package com.coderising.jvm.loader;
import org.junit.Assert;
import com.coderising.jvm.attr.AttributeInfo;
import com.coderising.jvm.attr.CodeAttr;
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.ConstantPool;
import com.coderising.jvm.constant.FieldRefInfo;
import com.coderising.jvm.constant.MethodRefInfo;
import com.coderising.jvm.constant.NULLConstantInfo;
import com.coderising.jvm.constant.NameAndTypeInfo;
import com.coderising.jvm.constant.StringInfo;
import com.coderising.jvm.constant.UTF8Info;
import com.coderising.jvm.field.Field;
import com.coderising.jvm.method.Method;
public class ClassFileParser {
public ClassFile parse(byte[] codes) {
if(codes == null || codes.length == 0){
throw new RuntimeException("illegality byteCode");
}
ByteCodeIterator it = new ByteCodeIterator(codes);
ClassFile clzFile = new ClassFile();
it.skip(4); //CAFE BABE
clzFile.setMinorVersion(it.next2ByteToInt());
clzFile.setMajorVersion(it.next2ByteToInt());
//constant_pool
clzFile.setConstantPool(parseConstantPool(it));
//access_flag
clzFile.setAccessFlag(parseAccessFlag(it));
//classIndex
clzFile.setClassIndex(parseClassInfex(it));
//interfaces TODO
int interfaceCount = it.next2ByteToInt();
Assert.assertEquals("no have ", 0, interfaceCount);
//fields
parseField(it, clzFile);
//methods
parseMethod(it, clzFile);
return clzFile;
}
private void parseMethod(ByteCodeIterator it, ClassFile clzFile) {
int methodCount = it.next2ByteToInt();
while(methodCount > 0) {
Method method = new Method();
int accessFlag = it.next2ByteToInt();
int nameIndex = it.next2ByteToInt();
int descIndex = it.next2ByteToInt();
int attrCount = it.next2ByteToInt();
method.setAccessFlag(accessFlag);
method.setNameIndex(nameIndex);
method.setDescIndex(descIndex);
method.setConstantPool(clzFile.getConstantPool());
while(attrCount > 0) {
int attrNameIndex = it.next2ByteToInt();
String attrName = clzFile.getConstantPool().getUTF8String(attrNameIndex);
if(AttributeInfo.ATTR_CODE.equals(attrName)) { //code
it.back(2);
CodeAttr codeAttr = CodeAttr.parse(it, clzFile.getConstantPool());
method.setCodeAttr(codeAttr);
} else {
throw new RuntimeException("no implements attributeName: " + attrName);
}
attrCount--;
}
clzFile.addMethod(method);
methodCount--;
}
}
private void parseField(ByteCodeIterator it, ClassFile clzFile) {
int fieldCount = it.next2ByteToInt();
while(fieldCount > 0) {
Field field = new Field();
int accessFlag = it.next2ByteToInt();
int nameIndex = it.next2ByteToInt();
int descIndex = it.next2ByteToInt();
int attrCount = it.next2ByteToInt();
Assert.assertEquals("no have ", 0, attrCount); //TODO
field.setAccessFlag(accessFlag);
field.setNameIndex(nameIndex);
field.setDescIndex(descIndex);
field.setConstantPool(clzFile.getConstantPool());
clzFile.addFiled(field);
fieldCount--;
}
}
private AccessFlag parseAccessFlag(ByteCodeIterator iter) {
int value = iter.next2ByteToInt();
AccessFlag accessFlag = new AccessFlag(value);
return accessFlag;
}
private ClassIndex parseClassInfex(ByteCodeIterator iter) {
int thisClassIndex = iter.next2ByteToInt();
int superClassIndex = iter.next2ByteToInt();
ClassIndex classIndex = new ClassIndex(thisClassIndex, superClassIndex);
return classIndex;
}
private ConstantPool parseConstantPool(ByteCodeIterator iter) {
int poolCount = iter.next2ByteToInt() - 1;
ConstantPool constantPool = new ConstantPool();
constantPool.setSize(poolCount);
constantPool.addConstantInfo(new NULLConstantInfo());
while(poolCount > 0) {
int tag = iter.next1ByteToInt();
if(tag == ConstantPool.C_CLASS_INFO) {
int nameIndex = iter.next2ByteToInt();
ClassInfo classInfo = new ClassInfo(nameIndex, constantPool);
constantPool.addConstantInfo(classInfo);
} else if(tag == ConstantPool.C_UTF8_INFO) {
int length = iter.next2ByteToInt();
String str = iter.nextLengthByteToString(length);
UTF8Info utf8Info = new UTF8Info(str);
constantPool.addConstantInfo(utf8Info);
} else if(tag == ConstantPool.C_METHODREF_INFO) {
int classInfoIndex = iter.next2ByteToInt();
int nameAndTypeIndex = iter.next2ByteToInt();
MethodRefInfo methodRefInfo = new MethodRefInfo(classInfoIndex, nameAndTypeIndex);
constantPool.addConstantInfo(methodRefInfo);
} else if(tag == ConstantPool.C_NAME_AND_TYPE_INFO) {
int index1 = iter.next2ByteToInt();
int index2 = iter.next2ByteToInt();
NameAndTypeInfo nameAndTypeInfo = new NameAndTypeInfo(index1 , index2);
constantPool.addConstantInfo(nameAndTypeInfo);
} else if(tag == ConstantPool.C_FIELDREF_INFO) {
int classInfoIndex = iter.next2ByteToInt();
int nameAndTypeIndex = iter.next2ByteToInt();
FieldRefInfo fieldRefInfo = new FieldRefInfo(classInfoIndex, nameAndTypeIndex) ;
constantPool.addConstantInfo(fieldRefInfo);
} else if(tag == ConstantPool.C_STRING_INFO) {
int index = iter.next2ByteToInt();
StringInfo stringInfo = new StringInfo(index);
constantPool.addConstantInfo(stringInfo);
} else {
throw new RuntimeException("no implement tag = " + tag);
}
poolCount--;
}
return constantPool;
}
}