一直以来又长又臭的调用链简直就是Java语言的标志性特色,方法调用可谓是Java世界里表达一切逻辑的基石。现在我们终于具备了实现它的基础。

JVM中的5条方法调用指令

在JVM中触发方法调用的指令有5条,分别是:

  • invokestatic

调用静态方法

  • invokespecial

调用构造方法

  • invokeinterface

调用接口方法

  • invokevirtual

调用对象方法

  • invokedynamic
CONSTANT_Methodref_infoCONSTANT_InvokeDynamic_info


invokestatic指令的实现

invokestatic
invokestatic byte1 byte2
CONSTANT_Methodref_info
// 方法引用常量
type MethodRefConstInfo struct {
    Tag uint8
    ClassIndex uint16
    NameAndTypeIndex uint16
}
NameAndTypeNameAndType
type NameAndTypeConst struct {
    Tag uint8
    NameIndex uint16
    DescIndex uint16
}
NameIndexUTF8sayHello
DescIndexUTF8(ILjava/lang/String;)Ljava/lang/String;(ILjava/lang/String;)IintLjava/lang/String;L;Ljava/lang/String;StringV()(ILjava/lang/String;)Ljava/lang/String;intStringString
String foo(int, String) {}

有了方法名、方法签名、类名,我们就可以唯一确定一个方法了。在调用之前还要注意参数顺序问题,调用前javac会生成一系列参数压栈的指令,但是我们在取参数出栈的时候,由于栈先进先出的性质,弹出参数的顺序跟实际顺序是相反的,这一点一定要小心。

UTF8
type Utf8InfoConst struct {
    Tag uint8
    Length uint16
    Bytes []byte
}
Bytes



byte1 byte2
methodRefCpIndex := (indexbyte1 << 8) | indexbyte2

然后取出方法引用常量、取出方法名、方法描述符、方法所在的class全名:

/ 取出引用的方法
    methodRef := def.ConstPool[methodRefCpIndex].(*class.MethodRefConstInfo)
    // 取出方法名
    nameAndType := def.ConstPool[methodRef.NameAndTypeIndex].(*class.NameAndTypeConst)
    methodName := def.ConstPool[nameAndType.NameIndex].(*class.Utf8InfoConst).String()
    // 描述符
    descriptor := def.ConstPool[nameAndType.DescIndex].(*class.Utf8InfoConst).String()
    // 取出方法所在的class
    classRef := def.ConstPool[methodRef.ClassIndex].(*class.ClassInfoConstInfo)
    // 取出目标class全名
    targetClassFullName := def.ConstPool[classRef.FullClassNameIndex].(*class.Utf8InfoConst).String()

加载class:

// 加载
    targetDef, err := i.miniJvm.findDefClass(targetClassFullName)
    if nil != err {
        return fmt.Errorf("failed to load class for '%s': %w", targetClassFullName, err)
    }
findDefClass()classpath.classDefClass

然后就可以直接调用方法了:

// 调用
    return i.ExecuteWithFrame(targetDef, methodName, descriptor, frame)
ExecuteWithFrame



invokestaticnative