一直以来又长又臭的调用链简直就是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