使用 "invokedynamic" - 幕后发生了什么?

Using "invokedynamic" - What is going on under the hood?

背景

我目前正在用 C# 编写一个 JVM 用于纯粹的学术目的(并且可能在未来构建一个混合的 .NET 和 Java/Scala 应用程序)。

上下文

我写的很简单 JAVA class:

public class test
{
    public static String hello_world(int i)
    {
        return "Hello " + i + " World!";
    }
}

并编译成test.class。 当我用我的反编译器(我将其作为 JVM 的一部分编写)对其进行反编译时,我看到了此方法的以下说明:

iload_0
invokedynamic 2
areturn

在常量池中查看索引 2 处的常量时,我​​看到一个包含以下数据的 InvokeDynamic-Constant 条目:

makeConcatWithConstants : (I)Ljava/lang/String;

我想这是有道理的(我更像是 .NET 用户而不是 JAVA 用户)。

当使用参数 1 执行我的方法 hello_world 时,在执行 invokedynamic 2 之前我有以下堆栈:

----TOP---
0x00000001
--BOTTOM--

问题

我的问题是:如何使用invokedynamic
我无法解析方法 makeConcatWithConstants,因为 InvokeDynamic-Constant 没有给我任何提示,makeConcatWithConstants 可能位于 (see documentation) 的位置。
堆栈也不包含对堆的引用,指示方法 makeConcatWithConstants 可以关联的实例类型。

我通读了 the invokedynamic docs 但我不明白(也许我对 .NET-Framework 太 "damaged" 了)。

有人可以给我一些例子,说明在执行这三个指令时 JVM 的幕后发生了什么吗? (invokedynamic 的被调用者期望什么等)?

我已经在我的 JVM 中实现了 invokestatic...但是我目前无法理解 invokedynamic

invokedynamic的想法是;当第一次遇到这个字节码时,调用一个 bootstrap 方法,该方法创建一个 Callsite 对象,该对象链接到需要调用的实际方法。

在实践中,这通常意味着您动态创建调用的实现。

如果您使用 javap -v test 查看您的程序,您将在底部看到一个 BootstrapMethods 属性:

BootstrapMethods:
  0: #15 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #16 Hello \u0001 World!

您可以在哪里看到此特定调用站点的 bootstrap 方法位于 StringConcatFactory

Method arguments 是常量参数集。

Lookup, String and MethodType的主要论点分别是;具有与调用点相同的特权、一些名称和调用点类型的查找对象。其中第一个需要在运行时由 VM 提供,后两个由 invokedynamic 常量池条目以名称和类型的形式提供:

#2 = InvokeDynamic      #0:#17         // #0:makeConcatWithConstants:(I)Ljava/lang/String;

所以要实现这个字节码,你必须有一些机制来创建查找对象,然后才能调用 bootstrap 方法。之后,您可以在返回的 Callsite object, which gives you a MethodHandle 上调用 dynamicInvoker(),然后您应该为这个特定的调用站点缓存,然后(最后)调用。

如果你想看看这是如何在 OpenJDK 中实现的,你可以在这里找到实现:http://hg.openjdk.java.net/jdk/jdk/file/tip/src/hotspot/share/interpreter/bytecodeInterpreter.cpp#l2446

我猜这在项目的早期阶段可能太棘手了,所以现在用 -XDstringConcat=inline 编译你的程序可能更容易,因为它使用了遗留的 StringBuilder连接,这应该更容易实现。