这意味着您可以将正在执行方法调用的类链接到在运行时正在接收调用的类(和方法)。 所有其他用于方法调用的JVM字节码指令,例如invokevirtual ,都将目标类型信息硬连线到编译中,即硬连线到类文件中。 让我们看一个例子。
Constant pool:
#1 = Class #2 // com/schlimm/bytecode/examples/BytecodeExamples
...
#42 = Class #43 // java/lang/String
...
#65 = Methodref #42.#66 // java/lang/String.length:()I
#66 = NameAndType #67:#68 // length:()I
#67 = Utf8 length
#68 = Utf8 ()I
...
{
...
public void virtualMethodCall();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: ldc #44 // String Hello
2: invokevirtual #65 // Method java/lang/String.length:()I
5: pop
6: return
LineNumberTable:
line 31: 0
line 32: 6
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/schlimm/bytecode/examples/BytecodeExamples;
}
上面的字节码片段显示了java.lang的invokevirtual方法调用。 第20行中的String- > length() 。它引用了contsant池表中的Item 65,它是MethodRef条目(请参见第6行)。 常量池表中的项目42和66引用类和方法描述符条目。 如您所见,invokevirtual调用的目标类型和方法已完全解析,并硬连接到字节码中。 现在,让我们回到invokedynamic !
重要的是要注意,不可能将Java代码编译为包含invokedynamic指令的字节码。 Java是静态类型的 。 这意味着Java在编译时执行类型检查。 因此,在Java中,有可能(并且想要!)将方法调用接收者的所有类型信息硬连线到调用者类文件中。 调用方知道调用目标的类型名称,如上面的示例所示。 另一方面,使用invokedynamic可使JVM在运行时准确解析该类型信息。 只有动态语言(例如JRuby或Rhino)才需要(也想要!)。
现在,假设您要在动态键入的JVM上实现一种新语言。 我不建议您在JVM上发明另一种语言,但是假设您应该,并且假设您的新语言应被动态键入。 用您的新语言,这意味着在运行时执行方法调用的调用方和接收方之间的链接。 由于Java 7,这可以使用invokedynamic指令在字节码级别上实现。
因为无法使用Java编译器创建invokedynamic指令,所以我将创建一个包含我自己的invokedynamic的类文件。 创建此类文件后,我将使用普通的Java启动器运行该类文件的main方法。 没有编译器,如何创建类文件? 这可以通过使用字节码操作框架(例如