模块 jdk.dynalink
Dynalink 是一个用于动态链接对象高级操作的库。这些操作包括“读一个属性”、“写一个属性”、“调用一个函数”等等。 Dynalink 主要用于实现至少某些表达式具有动态类型(即无法静态确定的类型)的编程语言,并且对动态类型的操作表示为 呼叫站点 。这些调用站点将在运行时根据表达式评估值的实际类型链接到适当的目标 方法句柄。这些可以在调用之间改变,需要多次重新链接调用站点以适应新类型; Dynalink 处理所有这些以及更多。
Dynalink 支持使用与 JVM 基于类的模型不同(甚至根本不同)的对象模型并具有自定义类型转换的编程语言的实现。
Dynalink 与 java.lang.invoke
包密切相关并依赖于它。
虽然 java.lang.invoke
为 invokedynamic
调用站点的动态链接提供了一个低级 API,但它没有提供一种方法来表达对对象的高级操作,也没有提供实现它们的方法。这些操作是面向对象环境中的常见操作:属性访问、集合元素的访问、方法和构造函数的调用(可能具有多重分派,例如 Java 重载方法解析的链接和运行时等价物)。这些都是 JVM 语言通常需要的所有功能。如果一种语言是静态类型的并且它的类型系统与 JVM 的类型系统相匹配,它可以通过使用通常的调用、字段访问等指令(例如 invokevirtual
、 getfield
)来实现。但是,如果语言是动态的(因此,某些表达式的类型在运行时评估之前是未知的),或者它的对象模型或类型系统与 JVM 的对象模型或类型系统不匹配,那么它应该使用 invokedynamic
调用站点,而不是让 Dynalink 管理它们。
示例
Dynalink 可能最好通过显示其用法的示例来解释。假设您有一个程序使用一种语言编写,您不必声明对象的类型并且想要访问它的属性:var color = obj.color;如果您生成了一个 Java 类来表示上述单行程序,它的字节码看起来像这样:
aload 2 // load "obj" on stack invokedynamic "GET:PROPERTY:color"(Object)Object // invoke property getter on object of unknown type astore 3 // store the return value into local variable "color"为了链接
invokedynamic
指令,我们需要一个引导方法。使用 Dynalink 的极简引导方法可能如下所示:
import java.lang.invoke.*; import jdk.dynalink.*; import jdk.dynalink.support.*; class MyLanguageRuntime { private static final DynamicLinker dynamicLinker = new DynamicLinkerFactory().createLinker(); public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) { return dynamicLinker.link( new SimpleRelinkableCallSite( new CallSiteDescriptor(lookup, parseOperation(name), type))); } private static Operation parseOperation(String name) { ... } }上面的代码片段中有几个重要的对象:
DynamicLinker
是 Dynalink 中的主要对象,它协调调用站点与实现其中指定操作的方法句柄的链接。它是使用DynamicLinkerFactory
配置和创建的。- 调用 bootstrap 方法时,需要创建一个
CallSite
对象。在 Dynalink 中,这些调用站点需要额外实现RelinkableCallSite
接口。这里的“Relinkable”指的是如果调用点在运行时遇到不同类型的对象,它的目标将被更改为一个方法句柄,可以对新遇到的类型执行操作。SimpleRelinkableCallSite
和ChainedCallSite
(上例中未使用)是库已经提供的两个实现。 - Dynalink 使用
CallSiteDescriptor
对象来保存引导方法的参数:查找和方法类型,因为它在需要重新链接调用站点时需要它们。 - Dynalink 使用
Operation
对象来表达动态操作。不过,它没有规定您将如何对调用站点中的操作进行编码。这就是为什么在上面的示例中parseOperation
函数留空,并且您需要提供代码以将调用站点名称中的字符串"GET:PROPERTY:color"
解析为命名属性 getter 操作对象作为StandardOperation.GET.withNamespace(StandardNamespace.PROPERTY).named("color")
。
您已经可以使用上述设置做什么? DynamicLinkerFactory
默认情况下创建一个 DynamicLinker
可以将 Java 对象与通常的 Java 语义链接起来。如果你有这三个简单的类:
public class A { public String color; public A(String color) { this.color = color; } } public class B { private String color; public B(String color) { this.color = color; } public String getColor() { return color; } } public class C { private int color; public C(int color) { this.color = color; } public int getColor() { return color; } }并且您以某种方式创建它们的实例并使用您的编程语言将它们传递到您的调用站点:
for each(var obj in [new A("red"), new B("green"), new C(0x0000ff)]) { print(obj.color); }然后在第一次调用时,Dynalink 会将
.color
getter 操作链接到 A.color
的字段获取器,在第二次调用时它将重新链接到 B.getColor()
返回一个String
,最后在第三次调用时它将重新链接到 C.getColor()
并返回 int
。我们上面使用的 SimpleRelinkableCallSite
只记住最后遇到的类型的链接(它实现了所谓的单态内联缓存).另一个已经提供的实现,ChainedCallSite
会记住几种不同类型的链接(这是一个多态内联缓存) 并且在严肃的应用中可能是更好的选择。
动态链接和字节码创建
CallSite
对象通常作为字节码中引导 invokedynamic
指令的一部分创建。因此,Dynalink 通常用作将程序编译为 Java .class
字节码格式的语言运行时的一部分。 Dynalink 没有解决创建字节码类或将它们加载到 JVM 中的问题。也就是说,Dynalink 也可以在没有字节码编译的情况下使用(例如在语言解释器中),方法是显式创建 CallSite
对象并将它们与解释程序中的动态操作表示相关联(例如,典型的表示是语法树中的一些节点对象)。
可用操作
Dynalink 在其StandardOperation
类中定义了几个标准操作。 Java 对象的链接器可以链接所有这些操作,我们鼓励您在您的语言中至少支持和使用这些操作。标准操作 GET
和 SET
需要与至少一个 Namespace
结合使用才能发挥作用,例如,要表达属性获取器,您可以使用 StandardOperation.GET.withNamespace(StandardNamespace.PROPERTY)
。 Dynalink 在StandardNamespace
类中定义了三个标准命名空间。要将固定名称与操作相关联,您可以像前面的示例一样使用 NamedOperation
:StandardOperation.GET.withNamespace(StandardNamespace.PROPERTY).named("color")
表示名为“color”的属性的 getter。
对多个命名空间的操作
某些语言可能没有针对属性、元素和方法的对象的单独命名空间,并且源语言构造可能同时处理其中的几个。 Dynalink 支持使用NamespaceOperation
指定多个 Namespace
对象。
特定于语言的链接器
定义自己的对象模型不同于 JVM 基于类的模型和/或使用自己的类型转换的语言将需要创建自己的特定于语言的链接器。请参阅jdk.dynalink.linker
包,特别是 GuardingDynamicLinker
接口以开始使用。
动态链接和 Java 对象
DynamicLinkerFactory
创建的 DynamicLinker
对象默认包含 BeansLinker
的内部实例,它是一个特定于语言的链接器,它为所有上述操作实现了通常的 Java 语义,并且可以链接任何其他特定于语言的链接器无法连接的 Java 对象关联。这样,所有语言运行时都具有与普通 Java 对象的内置互操作性。有关它如何链接各种操作的详细信息,请参阅 BeansLinker
。
跨语言互操作性
DynamicLinkerFactory
可以配置为 类加载器。它将尝试实例化该类加载器可见的所有 GuardingDynamicLinkerExporter
类,并将它们提供的链接器组合到它创建的 DynamicLinker
中。这允许语言之间的互操作性:如果您在 JVM 中部署了两个语言运行时 A 和 B,并且它们通过上述机制导出了它们的链接器,则语言运行时 A 将在它们的 DynamicLinker
对象中具有来自 B 的特定于语言的链接器实例,反之亦然.这意味着如果来自语言运行时 B 的对象被传递给来自语言运行时 A 的代码,则来自 B 的链接器在遇到来自 B 的对象时将有机会链接 A 中的调用站点。
-
包
输出包描述包含用于链接invokedynamic
调用站点的接口和类。包含普通 Java 对象的链接器。包含语言运行时所需的接口和类,以实现它们自己的特定于语言的对象模型和类型转换。包含的类通过提供某些类的基本实现以及各种实用程序,使语言运行时更方便地实现自己的特定于语言的对象模型和类型转换。包含通过提供某些类的基本实现以及各种实用程序使使用 Dynalink 更加方便的类。 -
服务
用途