12、方法调用和返回
大约 4 分钟
知识扩展
静态方法和实例方法
| 静态方法(类方法) | 实例方法 |
|---|---|
| 通过类调用 | 通过实例调用 |
| 静态绑定 | 动态绑定 |
| 编译期确定 | 运行期确定 |
- invokestatic 静态方法
- invokespecial 调用无须绑定的实例方法,例如:构造方法,私有方法,super
- invokeinterface 动态绑定- 调用接口
- invokevirtual 动态绑定 -其他
- invokedynamic 动态绑定 扩展
关键流程
解析符号引用 找方法
// 从当前类及其父类中查找方法
func lookupMethod(class *Class, name string, descriptor string) *Method {
method := LookupMethodInClass(class, name, descriptor)
if method == nil {
method = lookupMethodInInterfaces(class.interfaces, name, descriptor)
}
return method
}
核心方法调用
方法调用
// invokerFrame - 调用当前的方法的哪一个方法栈帧
// method - 当前方法,即被 invokerFrame 调用的方法
func InvokeMethod(invokerFrame *rtda.Frame, method *heap.Method) {
// 1.使用同一个线程为当前方法创建栈帧并压入线程栈顶
thread := invokerFrame.Thread()
newFrame := thread.NewFrame(method)
thread.PushFrame(newFrame)
// 2. 获取当前方法需要的参数个数,并从调用者 invokerFrame 的操作数栈中弹出制定个数个参数,放到当前方法的栈帧的本地变量中
argSlotCount := int(method.ArgSlotCount())
if argSlotCount > 0 {
for i := argSlotCount - 1; i >= 0; i-- {
slot := invokerFrame.OperandStack().PopSlot()
newFrame.LocalVars().SetSlot(uint(i), slot)
}
}
//hack
if method.IsNative() {
if method.Name() == "registerNatives" {
thread.PopFrame()
} else {
panic(fmt.Sprintf("native method: %v.%v%v
",
method.Class().Name(), method.Name(), method.Descriptor()))
}
}
}
返回指令
// 将当前栈帧的返回值(操作数栈顶)移除并推入调用者栈帧的操作数栈顶
// 将当前栈帧从线程中移除
func (self *ARETURN) Execute(frame *rtda.Frame) {
thread := frame.Thread()
currentFrame := thread.PopFrame() // 当前方法的栈帧
invokerFrame := thread.TopFrame() // 调用当前方法的前一个方法的栈帧
retVal := currentFrame.OperandStack().PopRef()
invokerFrame.OperandStack().PushRef(retVal)
}
func (self *IRETURN) Execute(frame *rtda.Frame) {
thread := frame.Thread()
currentFrame := thread.PopFrame() // 当前方法的栈帧
invokerFrame := thread.TopFrame() // 调用当前方法的前一个方法的栈帧
retVal := currentFrame.OperandStack().PopInt()
invokerFrame.OperandStack().PushInt(retVal)
}
方法调用指令
invokestatic 静态方法
func (self *INVOKE_STATIC) Execute(frame *rtda.Frame) {
cp := frame.Method().Class().ConstantPool()
methodRef := cp.GetConstant(self.Index).(*heap.MethodRef)
method := methodRef.ResolveMethod() // 根据方法符号引用:方法name、方法描述符descriptor、所包含的类指针 查找方法
if !method.IsStatic() {
panic("java.lang.IncompatibleClassChangeError")
}
class := methodRef.ResolvedClass()
if !class.InitStarted() {
frame.RevertNextPC()
base.InitClass(frame.Thread(), class)
return
}
base.InvokeMethod(frame, method)
}
invokespecial 构造方法
func (self *INVOKE_SPECIAL) Execute(frame *rtda.Frame) {
currentClass := frame.Method().Class()
cp := currentClass.ConstantPool()
methodRef := cp.GetConstant(self.Index).(*heap.MethodRef)
resolvedClass := methodRef.ResolvedClass()
resolveMethod := methodRef.ResolveMethod()
//构造方法
if resolveMethod.Name() == "<init>" && resolveMethod.Class() != resolvedClass {
panic("java.lang.NoSuchMethodError")
}
if resolveMethod.IsStatic() {
panic("java.lang.IncompatibleClassChangeError")
}
//todo count -1避免数组越界
ref := frame.OperandStack().GetRefFromTop(resolveMethod.ArgSlotCount()-1) // 弹出 this 引用
if ref == nil {
panic("java.lang.NullPointerException")
}
base.InvokeMethod(frame, resolveMethod)
}
invokeinterface
func (self *INVOKE_INTERFACE) Execute(frame *rtda.Frame) {
cp := frame.Method().Class().ConstantPool()
methodRef := cp.GetConstant(self.index).(*heap.InterfaceMethodRef)
resolvedMethod := methodRef.ResolvedInterfaceMethod()
if resolvedMethod.IsStatic() || resolvedMethod.IsPrivate() {
panic("java.lang.IncompatibleClassChangeError")
}
ref := frame.OperandStack().GetRefFromTop(resolvedMethod.ArgSlotCount() - 1)
if ref == nil {
panic("java.lang.NullPointerException") // todo
}
if !ref.Class().IsImplements(methodRef.ResolvedClass()) {
panic("java.lang.IncompatibleClassChangeError")
}
methodToBeInvoked := heap.LookupMethodInClass(ref.Class(),
methodRef.Name(), methodRef.Descriptor())
if methodToBeInvoked == nil || methodToBeInvoked.IsAbstract() {
panic("java.lang.AbstractMethodError")
}
if !methodToBeInvoked.IsPublic() {
panic("java.lang.IllegalAccessError")
}
base.InvokeMethod(frame, methodToBeInvoked)
}
编写解释器interpreter
func Interpret(method *heap.Method, logInst bool) {
thread := rtda.NewTread() //创建线程
frame := thread.NewFrame(method) //创建栈帧
thread.PushFrame(frame) //将栈帧push线程stack中
defer catchErr(thread)
loop(thread, logInst)
}
//执行指令
func loop(thread *rtda.Thread, logInst bool) {
reader := &base.ByteCodeReader{}
for {
//获取当前栈
frame := thread.CurrentFrame()
pc := frame.NextPC()
thread.SetPC(pc)
//decode
reader.Reset(frame.Method().Code(), pc)
//读取指令opcode
opcode := reader.ReadUint8() // 读取操作码 opCode(指令类型)
inst := instructions.NewInstruction(opcode) // 根据opCode创建相应的指令
inst.FetchOperands(reader) // 从字节码中读取操作数
frame.SetNextPC(reader.PC()) // 将当前读取到的字节码的位置设置到 frame 的 nextPc 中,用于执行下一条指令
if logInst {
logInstruction(frame, inst)
}
//执行栈帧
inst.Execute(frame)
//线程中栈帧执行完毕退出
if thread.IsStackEmpty() {
break
}
}
}
测试demo
//函数调用与返回
public class InvokeDemo implements Runnable {
public static void main(String[] args) {
new InvokeDemo().test();
}
public void test() {
InvokeDemo.staticMethod(); // invokestatic
InvokeDemo demo = new InvokeDemo(); // invokespecial
demo.instanceMethod(); // invokespecial
super.equals(null); // invokespecial
this.run(); // invokevirtual
((Runnable) demo).run(); // invokeinterface
}
public static void staticMethod() {}
private void instanceMethod() {}
@Override public void run() {}
}
go 测试分支
//测试函数调用与返回
func parseReturn(cmd *Cmd) {
cp := classpath.Parse(cmd.XjreOption, cmd.cpOption)
//获得classLoader
classLoader := heap.NewClassLoader(cp, cmd.verboseClassFlag)
//获得加载类名字
className := strings.Replace(cmd.class, ".", "/", -1)
mainClass := classLoader.LoadClass(className)
//获得main方法
mainMethod := mainClass.GetMainMethod()
if mainMethod != nil {
Interpret(mainMethod, cmd.verboseInstFlag)
} else {
fmt.Printf("Main method not found in class %s
", cmd.class)
}
}
shell脚本
#测试函数调用返回
go run main -verbose:class -verbose:inst -test "return" -cp test/lib/example.jar jvmgo.book.ch07.InvokeDemo
go run main -verbose:class -verbose:inst -test "return" -cp test/lib/example.jar jvmgo.book.ch07.FibonacciTest
实战项目地址
https://gitee.com/yinlingchaoliu/jvmgo.git
提交代码标记 “return”