原始博客的地址:
https://lyldalek.notion.site/JVM-0x6-fa280975b6f947e3bb8077d3e1685b5b
该项目的地址:
前面我们基本完成了方法区的实现,现在我们需要搞定方法的调用。
在Java虚拟机支持以下5条方法调用字节码指令,分别是:
只要能被invokestatic
和invokespecial
指令调用的方法,都可以在解析阶段中确定唯一的调用版本,Java语言里符合这个条件的方法共有静态方法、私有方法、实例构造器、父类方法4种,再加上被final修饰的方法(尽管它使用invokevirtual
指令调用),这5种方法调用会在类加载的时候就可以把符号引用解析为该方法的直接引用。这些方法统称为“非虚方法”(Non-Virtual Method),与之相反,其他方法就被称为“虚方法”(Virtual Method)。
假定解析符号引用后得到方法M。M必须是静态方法,否则抛出Incompatible-ClassChangeError
异常。M不能是类初始化方法。类初始化方法只能由Java虚拟机调用,不能使用invokestatic
指令调用。这一规则由class文件验证器保证,这里不做检查。如果声明M的类还没有被初始化,则要先初始化该类。
代码实现:
@Override
public void execute(StackFrame frame) {
ConstantPool constantPool = frame.getMyMethod().getMyClass().getConstantPool();
ConstantPool.Constant constant = constantPool.getConstant(operand);
MethodRef methodRef = (MethodRef) constant.value;
MyMethod resolvedMethod = methodRef.getResolvedMethod();
if (!resolvedMethod.isStatic()) {
throw new MyJvmException("java.lang.IncompatibleClassChangeError");
}
MyClass resolvedClass = methodRef.getResolvedClass();
if (!resolvedClass.isInitStarted()) {
frame.revertPc();
ClassInit.initMyClass(resolvedClass, frame.getThread());
return;
}
InvokeMethod.invokeMethod(frame, resolvedMethod);
}
这里我们封装了一些方法调用的逻辑 InvokeMethod
,它主要是处理栈帧与参数的传递。