package me.lzb.jvm.loader;
import me.lzb.common.utils.ByteUtils;
import me.lzb.common.utils.StringUtils;
import me.lzb.jvm.attr.*;
import me.lzb.jvm.clz.AccessFlag;
import me.lzb.jvm.clz.ClassFile;
import me.lzb.jvm.clz.ClassIndex;
import me.lzb.jvm.constant.*;
import me.lzb.jvm.field.Field;
import me.lzb.jvm.method.Method;
import java.io.UnsupportedEncodingException;
/**
* 处理字class文件字节流
* Created by LZB on 2017/4/14.
*/
public class ClassFileParser {
private final byte[] data;
private int index;
public ClassFileParser(byte[] data) {
this.data = data;
}
public ClassFile parse() {
ClassFile classFile = new ClassFile();
parserMagicNumber(classFile);
if (!StringUtils.equals(classFile.getMagicNumber(), ConstantInfo.MAGIC_NUMBER)) {
throw new RuntimeException("It is not a java class file.");
}
parserVersion(classFile);
parserConstantPool(classFile);
parserAccessFlag(classFile);
parserClassIndex(classFile);
parserInterface(classFile);
parserField(classFile);
parserMethod(classFile);
return classFile;
}
private byte[] nextBytes(int nextLength) {
byte[] target = new byte[nextLength];
System.arraycopy(data, index, target, 0, nextLength);
index = index + nextLength;
return target;
}
private int nextBytesToInt(int nextLength) {
return ByteUtils.byteToInt(nextBytes(nextLength));
}
private String nextBytesToString(int nextLength) {
return ByteUtils.byteToHexString(nextBytes(nextLength));
}
private void parserMagicNumber(ClassFile classFile) {
this.index = 0;
classFile.setMagicNumber(nextBytesToString(4));
}
private void parserVersion(ClassFile classFile) {
classFile.setMinorVersion(nextBytesToInt(2));
classFile.setMajorVersion(nextBytesToInt(2));
}
private void parserConstantPool(ClassFile classFile) {
int count = nextBytesToInt(2);
ConstantPool pool = new ConstantPool();
pool.addConstantInfo(new NullConstantInfo());
for (int i = 1; i < count; i++) {
int tag = nextBytesToInt(1);
if (tag == ConstantInfo.Class_info) {
ClassInfo classInfo = new ClassInfo(pool);
classInfo.setUtf8Index(nextBytesToInt(2));
pool.addConstantInfo(classInfo);
continue;
}
if (tag == ConstantInfo.Fieldref_info) {
FieldRefInfo fieldRefInfo = new FieldRefInfo(pool);
fieldRefInfo.setClassInfoIndex(nextBytesToInt(2));
fieldRefInfo.setNameAndTypeIndex(nextBytesToInt(2));
pool.addConstantInfo(fieldRefInfo);
continue;
}
if (tag == ConstantInfo.Methodref_info) {
MethodRefInfo methodRefInfo = new MethodRefInfo(pool);
methodRefInfo.setClassInfoIndex(nextBytesToInt(2));
methodRefInfo.setNameAndTypeIndex(nextBytesToInt(2));
pool.addConstantInfo(methodRefInfo);
continue;
}
if (tag == ConstantInfo.NameAndType_info) {
NameAndTypeInfo nameAndTypeInfo = new NameAndTypeInfo(pool);
nameAndTypeInfo.setIndex1(nextBytesToInt(2));
nameAndTypeInfo.setIndex2(nextBytesToInt(2));
pool.addConstantInfo(nameAndTypeInfo);
continue;
}
if (tag == ConstantInfo.String_info) {
StringInfo stringInfo = new StringInfo(pool);
stringInfo.setIndex(nextBytesToInt(2));
pool.addConstantInfo(stringInfo);
continue;
}
if (tag == ConstantInfo.Utf8_info) {
UTF8Info utf8Info = new UTF8Info(pool);
int len = nextBytesToInt(2);
byte[] data = nextBytes(len);
String value = null;
try {
value = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
utf8Info.setLength(len);
utf8Info.setValue(value);
pool.addConstantInfo(utf8Info);
continue;
}
throw new RuntimeException("the constant pool tag " + tag + " has not been implemented yet." + i);
}
classFile.setConstantPool(pool);
}
private void parserAccessFlag(ClassFile classFile) {
AccessFlag flag = new AccessFlag(nextBytesToInt(2));
classFile.setAccessFlag(flag);
}
private void parserClassIndex(ClassFile classFile) {
ClassIndex clzIndex = new ClassIndex();
int thisClassIndex = nextBytesToInt(2);
int superClassIndex = nextBytesToInt(2);
clzIndex.setThisClassIndex(thisClassIndex);
clzIndex.setSuperClassIndex(superClassIndex);
classFile.setClzIndex(clzIndex);
}
private void parserInterface(ClassFile classFile) {
int count = nextBytesToInt(2);
//TODO 实现interface
}
private void parserField(ClassFile classFile) {
int count = nextBytesToInt(2);
for (int i = 1; i <= count; i++) {
int accessFlags = nextBytesToInt(2);
int nameIndex = nextBytesToInt(2);
int descriptorIndex = nextBytesToInt(2);
int attributesCount = nextBytesToInt(2);
if (attributesCount > 0) {
throw new RuntimeException("Field Attribute has not been implement");
}
Field field = new Field(accessFlags, nameIndex, descriptorIndex, classFile.getConstantPool());
classFile.addField(field);
}
}
private void parserMethod(ClassFile classFile) {
int count = nextBytesToInt(2);
for (int i = 1; i <= count; i++) {
int accessFlags = nextBytesToInt(2);
int nameIndex = nextBytesToInt(2);
int descriptorIndex = nextBytesToInt(2);
int attributesCount = nextBytesToInt(2);
Method method = new Method(classFile, accessFlags, nameIndex, descriptorIndex);
for (int j = 1; j <= attributesCount; j++) {
int attributeNameIndex = nextBytesToInt(2);
String attributeName = classFile.getConstantPool().getUTF8String(attributeNameIndex);
if (StringUtils.equalsIgnoreCase(attributeName, AttributeInfo.CODE)) {
parserCodeAttr(attributeNameIndex, method, classFile);
continue;
}
throw new RuntimeException("only CODE attribute is implemented.");
}
classFile.addMethod(method);
}
}
private void parserCodeAttr(int attributeNameIndex, Method method, ClassFile classFile) {
int attributeLength = nextBytesToInt(4);
int maxStack = nextBytesToInt(2);
int maxLocals = nextBytesToInt(2);
int codeLength = nextBytesToInt(4);
String code = nextBytesToString(codeLength);
//处理cmd
CommandParser commandParser = new CommandParser(code);
CodeAttr codeAttr = new CodeAttr(attributeNameIndex, attributeLength, maxStack, maxLocals, codeLength, code, commandParser.parse(classFile));
int exceptionTableLength = nextBytesToInt(2);
if (exceptionTableLength > 0) {
String exceptionTable = nextBytesToString(exceptionTableLength);
//TODO 异常
}
int subAttrCount = nextBytesToInt(2);
for (int k = 1; k <= subAttrCount; k++) {
int subAttrIndex = nextBytesToInt(2);
String subAttrName = classFile.getConstantPool().getUTF8String(subAttrIndex);
if (StringUtils.equalsIgnoreCase(subAttrName, AttributeInfo.LINE_NUM_TABLE)) {
parserLineNumberTable(codeAttr, subAttrIndex);
continue;
}
if (StringUtils.equalsIgnoreCase(subAttrName, AttributeInfo.LOCAL_VAR_TABLE)) {
parserLocalVariableTable(codeAttr, subAttrIndex);
continue;
}
if (StringUtils.equalsIgnoreCase(subAttrName, AttributeInfo.STACK_MAP_TABLE)) {
parserStackMapTable(codeAttr, subAttrIndex);
continue;
}
throw new RuntimeException("Need code to process" + subAttrName);
}
method.setCodeAttr(codeAttr);
}
private void parserLineNumberTable(CodeAttr codeAttr, int attributeNameIndex) {
// int attributeNameIndex = nextBytesToInt(2);
int attributeLength = nextBytesToInt(4);
int lineNumberTableLength = nextBytesToInt(2);
LineNumberTable table = new LineNumberTable(attributeNameIndex, attributeLength);
for (int l = 1; l <= lineNumberTableLength; l++) {
int startPc = nextBytesToInt(2);
int lineNumber = nextBytesToInt(2);
LineNumberItem item = new LineNumberItem(startPc, lineNumber);
table.addLineNumberItem(item);
}
codeAttr.setLineNumTable(table);
}
private void parserLocalVariableTable(CodeAttr codeAttr, int attributeNameIndex) {
// int attributeNameIndex = nextBytesToInt(2);
int attributeLength = nextBytesToInt(4);
int localVariableTableLength = nextBytesToInt(2);
LocalVariableTable table = new LocalVariableTable(attributeNameIndex, attributeLength);
for (int l = 1; l <= localVariableTableLength; l++) {
int startPc = nextBytesToInt(2);
int lineNumber = nextBytesToInt(2);
int nameIndex = nextBytesToInt(2);
int descriptorIndex = nextBytesToInt(2);
int index = nextBytesToInt(2);
LocalVariableItem item = new LocalVariableItem(startPc, lineNumber, nameIndex, descriptorIndex, index);
table.addLocalVariableItem(item);
}
codeAttr.setLocalVarTable(table);
}
private void parserStackMapTable(CodeAttr codeAttr, int attributeNameIndex) {
// int attributeNameIndex = nextBytesToInt(2);
int attributeLength = nextBytesToInt(4);
String code = nextBytesToString(attributeLength);
StackMapTable table = new StackMapTable(attributeNameIndex, attributeLength, code);
//后面的StackMapTable太过复杂, 不再处理, 只把原始的代码读进来保存
codeAttr.setStackMapTable(table);
}
}