本节继续实现方法调用和返回。
返回指令
方法执行完毕之后,需要把结果返回给调用方,这一工作由返回指令完成。返回指令属于控制指令,一共6条。其中return指令用于没有返回值的情况,areturn、ireturn、lreturn、和dreturn分别用于返回引用、int、long、float和double类型的值。该系列指令实现代码位于/instructions/control/return.go文件中:
|
|
另外Thread结构体需要添加TopFrame()方法,和CurrentFrame()代码一样,用不同的名称只是为了避免混淆。
方法调用指令
本章不考虑接口的静态方法和默认方法,本章实现的4条指令并不能满足Java虚拟机规范第8版的规定,我们还在学习中。
invokestatic指令
该指令实现代码位于/instructions/references/invokestatic.go文件中:
invokespecial指令
修改/instructions/references/invokespecial.go文件,下面截取部分代码展示:
在实现invokespecial指令代码中,有一些其他部分需要完善,如下:
添加GetRefFromTop()方法,该方法位于/rtdata/operand_stack.go文件中,方法的作用是返回距离操作数栈顶ngeslot的引用变量。要注意的是,在传递参数之前,不能破坏操作数栈的状态,所以该方法的实现只是获取了引用变量,而没有从栈中弹出。
1234// 获取距离操作数栈顶n个slot的引用变量func (self *OperandStack) GetRefFromTop(n uint) *heap.Object {return self.slots[self.size-1-n].ref}修改/rtdata/heap/class.go,将getPackageName()方法改为GetPackageName(),注意refactor,因为其他文件用到了将getPackageName()方法,并添加如下方法:
123func (self *Class) SuperClass() *Class {return self.superClass}修改/rtdata/heap/class_hierarchy.go文件,将isSubClassOf()方法改为IsSubClassOf(),并且添加如下方法:
123func (self *Class) IsSuperClassOf(other *Class) bool {return other.IsSubClassOf(self)}修改/rtdata/heap/method.go文件,添加如下方法:
123func (self *Method) IsAbstract() bool {return 0 != self.accessFlags&ACC_ABSTRACT}
invokevirtual指令
该指令实现代码位于/instructions/references/invokesvirtual.go文件中,大部分代码和invokespecial指令类似,就不贴出来了,参照项目源码。
invokeinterface指令
和上面山条方法调用指令略有不同,在字节码中,invokeinterfase指令的操作吗后面跟着4个字节,而非2个字节。前两个字节的含义和其他指令相同,是个uint16运行时常量池索引。第3个字节时给方法传递参数需要的slot数,其含义和给Method结构体定义的argSlotCount字段相同,这个数是可以根据方法描述符计算出来的,它的存在仅仅是因为历史原因。第4个字节是留给Oracle的某些Java虚拟机实现用的,它的值必须是0,该字节的存在是为了保证Java虚拟机可以向后兼容。Execute()方法和之前类似,下面就不贴出来了,参照项目源码。Execute()方法中IsImplements()方法在/rtdata/heap/class_hierarchy.go文件中,也不贴出来了。
至此,4条方法调用指令都实现完毕了,最后不要忘了把factory.go文件中本章节相关指令项的注释去掉。
改进解释器
之前写的解释器只能执行单个方法,现在可以扩展它,让它支持方法调用。修改/instructions/interceptor.go文件:
修改Interpret()方法,添加logInst参数,类型为bool,作用式控制是否把指令执行信息打印到控制台。
修改/rtdata/jvm_stack.go文件,添加isEmpty()方法,修改/rtdata/thread.go文件,添加IsStackEmpty()方法:
123456789// 判断栈顶指针是否为空func (self *Stack) isEmpty() bool {return self._top == nil}// 判断虚拟机栈是否为空func (self *Thread) IsStackEmpty() bool {return self.stack.isEmpty()}修改catchErr()方法实现方式:
1234567891011121314151617func catchErr(thread *rtdata.Thread) {if r := recover(); r != nil {logFrames(thread)panic(r)}}// 打印Java虚拟机栈错误信息func logFrames(thread *rtdata.Thread) {for !thread.IsStackEmpty() {frame := thread.PopFrame()method := frame.Method()className := method.Class().Name()fmt.Printf(">> pc:%4d %v.%v%v \n",frame.NextPC(), className, method.Name(), method.Descriptor())}}修改loop()函数,也是本次改造最红要的变化之处:
123456789101112131415161718192021222324252627282930313233343536373839404142434445func loop(thread *rtdata.Thread, logInst bool) {// 实例化BytecodeReaderreader := &base.BytecodeReader{}// 遍历Java虚拟机栈,直到栈顶指针为空for {// 获取Java虚拟机栈当前帧(栈顶指针)frame := thread.CurrentFrame()// 获取下个pc寄存器地址(也就是下一条将要执行的指令所在的位置)pc := frame.NextPC()// 设置线程pc寄存器为当前帧的下一个指令地址thread.SetPC(pc)// 重置readerreader.Reset(frame.Method().Code(), pc)// 获取指令操作码opcode := reader.ReadUint8()// 根据操作码常见指令,参照factory.go文件inst := NewInstruction(opcode)// 读取操作数inst.FetchOperands(reader)// 设置下一个指令起始地址(下一条指令操作码在字节码中的位置)frame.SetNextPC(reader.PC())// 是否将执行信息打印到控制台if logInst {logInstruction(frame, inst)}// 执行指令inst.Execute(frame)// 若栈顶指针为空,表示线程执行结束if thread.IsStackEmpty() {break}}}// 打印指令执行信息func logInstruction(frame *rtdata.Frame, inst base.Instruction) {method := frame.Method()className := method.Class().Name()methodName := method.Name()pc := frame.Thread().PC()fmt.Printf("%v.%v() #%2d %T %v\n", className, methodName, pc, inst, inst)}
下节继续。。。