跳至主要內容

12、方法调用和返回

引领潮流大约 4 分钟手动编写jvm虚拟机jvmgo

知识扩展

静态方法和实例方法

静态方法(类方法)实例方法
通过类调用通过实例调用
静态绑定动态绑定
编译期确定运行期确定
  • 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”