写在前面
本文看下如何模拟解释器执行指令码。需要一些前置内容:
用Java手写jvm之系列 中的前4篇文章,当然如果你已经了解了这部分内容,也可以不看。
1:正文
既然是模拟解释器,我们肯定要先来定义一个解释器类了:
/**
* 解释器,负责基于指令码来执行具体程序
*/
public class Interpreter {
/**
* 解释器构造函数,直接就开始干活了!!!
*
* @param m
*/
public Interpreter(MemberInfo m) {
// 获取代码信息
CodeAttribute codeAttr = m.codeAttribute();
// 获取局部变量表大小
int maxLocals = codeAttr.maxLocals();
// 获取操作数栈大小
int maxStack = codeAttr.maxStack();
// 获取字节码数组,也就是我们的代码在class文件中的形式
byte[] byteCode = codeAttr.data();
// 模拟启动一个要执行代码的线程
Thread thread = new Thread();
// 创建一个执行方法对应的栈帧
Frame frame = thread.newFrame(maxLocals, maxStack);
// 当栈帧推到线程栈顶,成为当前栈帧
thread.pushFrame(frame);
// 开始执行代码了
loop(thread, byteCode);
}
private void loop(Thread thread, byte[] byteCode) {
Frame frame = thread.popFrame();
// 字节码读取器,负责维护程序计数器,以及读取指令码
BytecodeReader reader = new BytecodeReader();
while (true) {
// 获取当前的程序计数器位置,并读取该位置的指令码
int pc = frame.nextPC();
// 设置程序计数器位置
thread.setPC(pc);
// 充值字节码读取器的字节码信息和位置信息
reader.reset(byteCode, pc);
// 获取指令码
byte opcode = reader.readByte();
// 根据指令码获取对应的执行指令的指令类
Instruction inst = Factory.newInstruction(opcode);
if (null == inst) {
System.out.println("寄存器(指令)尚未实现 " + byteToHexString(new byte[]{
opcode}));
break;
}
// 获取操作数,比如IADD指令就需要从操作数的栈顶获取2个操作数,但并不是所有的指令都需要该操作
// 比如iconst_0就不需要
inst.fetchOperands(reader);
// 设置程序计数器到方法栈帧中
frame.setNextPC(reader.pc());
System.out.println("寄存器(指令):" + byteToHexString(new byte[]{
opcode}) + " -> " + inst.getClass(