- 面向 JDK 23 的 GraalVM(最新版本)
- 面向 JDK 24 的 GraalVM(抢先体验版)
- 面向 JDK 21 的 GraalVM
- 面向 JDK 17 的 GraalVM
- 存档
- 开发版本
- Truffle 语言实现框架
- Truffle 分支检测
- 动态对象模型
- 静态对象模型
- 面向解释器代码的主机优化
- Truffle 对函数内联的处理方法
- 分析 Truffle 解释器
- Truffle 互操作 2.0
- 语言实现
- 使用 Truffle 实现新语言
- Truffle 语言和仪器迁移到 Java 模块
- Truffle 本机函数接口
- 优化 Truffle 解释器
- 选项
- 栈上替换
- Truffle 字符串指南
- 专用化直方图
- 测试 DSL 专用化
- 基于多语言 API 的 TCK
- Truffle 对编译队列的处理方法
- Truffle 库指南
- Truffle AOT 概述
- Truffle AOT 编译
- 辅助引擎缓存
- Truffle 语言安全点教程
- 单态化
- 拆分算法
- 单态化用例
- 向运行时报告多态专用化
动态对象模型
本指南演示了如何开始使用 GraalVM 20.2.0 中引入的 DynamicObject 和 DynamicObjectLibrary API。完整文档可以在 Javadoc 中找到。
动机 #
在实现动态语言时,用户定义的对象/类的对象布局通常无法静态推断,并且需要适应动态添加的成员和更改的类型。这就是动态对象 API 的用武之地:它负责对象布局,并根据对象的形状(即其属性及其值的类型)对对象进行分类。访问节点随后可以缓存遇到的形状,避免代价高昂的检查,并更有效地访问对象属性。
入门 #
客座语言应该有一个用于所有语言对象的公共基类,该基类扩展 DynamicObject
并实现 TruffleObject
。例如
@ExportLibrary(InteropLibrary.class)
public class BasicObject extends DynamicObject implements TruffleObject {
public BasicObject(Shape shape) {
super(shape);
}
@ExportMessage
boolean hasLanguage() {
return true;
}
// ...
}
在此类中导出常见的 InteropLibrary
消息也是有意义的。
内置对象类随后可以扩展此基类并导出其他消息,以及通常的额外 Java 字段和方法
@ExportLibrary(InteropLibrary.class)
public class Array extends BasicObject {
private final Object[] elements;
public Array(Shape shape, Object[] elements) {
super(shape);
this.elements = elements;
}
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
long getArraySize() {
return elements.length;
}
// ...
}
可以使用 DynamicObjectLibrary
访问动态对象成员,可以使用 Truffle DSL 的 @CachedLibrary
注解和 DynamicObjectLibrary.getFactory()
+ getUncached()
、create(DynamicObject)
和 createDispatched(int)
获取。以下是如何使用它来实现 InteropLibrary
消息的示例
@ExportLibrary(InteropLibrary.class)
public class SimpleObject extends BasicObject {
public UserObject(Shape shape) {
super(shape);
}
@ExportMessage
boolean hasMembers() {
return true;
}
@ExportMessage
Object readMember(String name,
@CachedLibrary("this") DynamicObjectLibrary objectLibrary)
throws UnknownIdentifierException {
Object result = objectLibrary.getOrDefault(this, name, null);
if (result == null) {
/* Property does not exist. */
throw UnknownIdentifierException.create(name);
}
return result;
}
@ExportMessage
void writeMember(String name, Object value,
@CachedLibrary("this") DynamicObjectLibrary objectLibrary) {
objectLibrary.put(this, name, value);
}
@ExportMessage
boolean isMemberReadable(String member,
@CachedLibrary("this") DynamicObjectLibrary objectLibrary) {
return objectLibrary.containsKey(this, member);
}
// ...
}
为了构造这些对象的实例,您首先需要一个可以传递给 DynamicObject
构造函数的 Shape
。此形状使用 Shape.newBuilder().build()
创建。返回的形状描述了对象的初始形状,并构成新形状树的根。当您使用 DynamicObjectLibrary#put
添加新属性时,对象将在该形状树中变异为其他形状。
注意:您应该重用相同的初始形状,因为形状在内部按根形状进行缓存。建议您将初始形状存储在 TruffleLanguage
实例中,以便它们可以在同一个引擎的不同上下文之间共享。应避免静态形状,除非是单例(例如 null
值)。
例如
@TruffleLanguage.Registration(...)
public final class MyLanguage extends TruffleLanguage<MyContext> {
private final Shape initialObjectShape;
private final Shape initialArrayShape;
public MyLanguage() {
this.initialObjectShape = Shape.newBuilder().layout(ExtendedObject.class).build();
this.initialArrayShape = Shape.newBuilder().build();
}
public createObject() {
return new MyObject(initialObjectShape);
}
//...
}
扩展的对象布局 #
您可以使用额外的动态字段扩展默认的对象布局,这些字段通过在子类中添加 @DynamicField
注释的类型为 Object
或 long
的字段声明并使用 Shape.newBuilder().layout(ExtendedObject.class).build();
指定布局类来传递给动态对象模型。在此类及其超类中声明的动态字段随后将自动用于存储动态对象属性,并允许更快地访问适合该保留空间的属性。注意:您不得直接访问动态字段。始终为此目的使用 DynamicObjectLibrary
。
@ExportLibrary(InteropLibrary.class)
public class ExtendedObject extends SimpleObject {
@DynamicField private Object _obj0;
@DynamicField private Object _obj1;
@DynamicField private Object _obj2;
@DynamicField private long _long0;
@DynamicField private long _long1;
@DynamicField private long _long2;
public ExtendedObject(Shape shape) {
super(shape);
}
}
缓存注意事项 #
为了确保最佳缓存,请避免对多个独立的操作(get
、put
等)重用同一个缓存的 DynamicObjectLibrary
。尝试最大程度地减少每个缓存的库实例看到的不同形状和属性键的数量。当属性键在静态(编译最终)时,始终为每个属性键使用单独的 DynamicObjectLibrary
。在连续放置多个属性时使用分派库(@CachedLibrary(limit=...)
)。例如
public abstract class MakePairNode extends BinaryExpressionNode {
@Specialization
Object makePair(Object left, Object right,
@CachedLanguage MyLanguage language,
@CachedLibrary(limit = "3") DynamicObjectLibrary putLeft,
@CachedLibrary(limit = "3") DynamicObjectLibrary putRight) {
MyObject obj = language.createObject();
putLeft.put(obj, "left", left);
putRight.put(obj, "right", right);
return obj;
}
}
进一步阅读 #
对象模型的高级描述已在 面向 Truffle 语言实现框架的对象存储模型 中发布。
有关 Truffle 和 GraalVM 的更多演示文稿和出版物,请参阅 Truffle 出版物。