Skip to content

Commit bc4057a

Browse files
committed
Add DynamicObject API migration guide.
1 parent dcd86f3 commit bc4057a

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Migrating from DynamicObjectLibrary to DynamicObject Nodes
2+
3+
`DynamicObjectLibrary` has been deprecated in 25.1, and replaced with new, lighter-weight node-based APIs, found as subclasses of `DynamicObject`.
4+
Each `DynamicObjectLibrary` message has an equivalent node replacement.
5+
6+
| `DynamicObjectLibrary` message | `DynamicObject` node equivalent | Purpose |
7+
|--------------------------------------------|------------------------------------------------------------------------|-------------------------------------------------------------------------------|
8+
| `getOrDefault(obj, key, def)` | `DynamicObject.GetNode.execute(obj, key, def)` | Gets the value of a property or a default value if absent |
9+
| `getIntOrDefault(obj, key, def)` | `DynamicObject.GetNode.executeInt(obj, key, def)` | Gets the value of a property as int or throws UnexpectedResultException |
10+
| `getLongOrDefault(obj, key, def)` | `DynamicObject.GetNode.executeLong(obj, key, def)` | Gets the value of a property as long or throws UnexpectedResultException |
11+
| `getDoubleOrDefault(obj, key, def)` | `DynamicObject.GetNode.executeDouble(obj, key, def)` | Gets the value of a property as double or throws UnexpectedResultException |
12+
| `put(obj, key, val)` | `DynamicObject.PutNode.execute(obj, key, val)` | Adds a new property of sets the value of an existing property |
13+
| `putWithFlags(obj, key, val, flags)` | `DynamicObject.PutNode.executeWithFlags(obj, key, val, flags)` | Adds a new property or sets the value and flags of an existing property |
14+
| `putIfPresent(obj, key, val)` | `DynamicObject.PutNode.executeIfPresent(obj, key, val)` | Sets the value of a property, if present |
15+
| `putConstant(obj, key, val, flags)` | `DynamicObject.PutConstantNode.executeWithFlags(obj, key, val, flags)` | Adds or replaces a property with a constant shape-bound value, use sparingly |
16+
| `containsKey(obj, key)` | `DynamicObject.ContainsKeyNode.execute(obj, key)` | Checks for the existence of a property |
17+
| `removeKey(obj, key)` | `DynamicObject.RemoveKeyNode.execute(obj, key)` | Removes a property |
18+
| `getPropertyFlagsOrDefault(obj, key, def)` | `DynamicObject.GetPropertyFlagsNode.execute(obj, key, def)` | Gets the flags of a property or a default value if absent |
19+
| `setPropertyFlags(obj, key, flags)` | `DynamicObject.SetPropertyFlagsNode.execute(obj, key, flags)` | Updates the flags or a property |
20+
| `getKeyArray(obj)` | `DynamicObject.GetKeyArrayNode.execute(obj)` | Returns an array of the keys of all the non-hidden properties |
21+
| `getProperty(obj, key)` | `DynamicObject.GetPropertyNode.execute(obj, key)` | Gets the property descriptor or null if absent |
22+
| `getPropertyArray(obj)` | `DynamicObject.GetPropertyArrayNode.execute(obj)` | Returns an array of the property descriptors of all the non-hidden properties |
23+
| `getShapeFlags(obj)` | `DynamicObject.GetShapeFlagsNode.execute(obj)` | Gets the language-specific shape flags |
24+
| `setShapeFlags(obj, flags)` | `DynamicObject.SetShapeFlagsNode.execute(obj, flags)` | Sets the language-specific shape flags |
25+
| `getDynamicType(obj)` | `DynamicObject.GetDynamicTypeNode.execute(obj)` | Gets the language-specific dynamic type identifier |
26+
| `setDynamicType(obj, type)` | `DynamicObject.SetDynamicTypeNode.execute(obj, type)` | Sets the language-specific dynamic type identifier |
27+
| `updateShape(obj)` | `DynamicObject.UpdateShapeNode.execute(obj)` | Updates the shape if the object has an obsolete shape |
28+
| `resetShape(obj, rootShape)` | `DynamicObject.ResetShapeNode.execute(obj, rootShape)` | Resets the object to an empty shape |
29+
| `markShared(obj)` | `DynamicObject.MarkSharedNode.execute(obj)` | Marks the object as shared |
30+
| `isShared(obj)` | `DynamicObject.IsSharedNode.execute(obj)` | Queries the object's shared state |
31+
| `getShape(obj)` | `obj.getShape()` | No node equivalent, use direct API |
32+
33+
Note: Unlike `DynamicObjectLibrary`, cached property keys are always compared by identity (`==`) rather than equality (`equals`).
34+
If you rely on key equality, cache the key using an `equals` guard and pass the cached canonical key to the node.
35+
36+
## Code examples
37+
38+
### Reading a property
39+
40+
#### Getting the value of a property with a fixed key or a dynamic key that is already unique or interned
41+
42+
```java
43+
abstract class GetUniqueKeyNode extends Node {
44+
45+
abstract Object execute(DynamicObject receiver, Object key);
46+
47+
@Specialization
48+
static Object doCached(MyDynamicObject receiver, Symbol key,
49+
@Cached DynamicObject.GetNode getNode) {
50+
return getNode.execute(receiver, key, NULL_VALUE);
51+
}
52+
}
53+
```
54+
55+
#### Getting the value of a property with a dynamic key and key equality
56+
57+
```java
58+
@ImportStatic(TruffleString.Encoding.class)
59+
abstract class GetStringKeyNode extends Node {
60+
61+
abstract Object execute(DynamicObject receiver, Object key);
62+
63+
@Specialization(guards = "equalNode.execute(key, cachedKey, UTF_16)", limit = "3")
64+
static Object doCached(MyDynamicObject receiver, TruffleString key,
65+
@Cached("key") TruffleString cachedKey,
66+
@Cached TruffleString.EqualNode equalNode,
67+
@Shared @Cached DynamicObject.GetNode getNode) {
68+
return getNode.execute(receiver, cachedKey, NULL_VALUE);
69+
}
70+
71+
@Specialization(replaces = "doCached")
72+
static Object doGeneric(MyDynamicObject receiver, TruffleString key,
73+
@Shared @Cached DynamicObject.GetNode getNode) {
74+
return getNode.execute(receiver, key, NULL_VALUE);
75+
}
76+
}
77+
```
78+
79+
Note that key interning is not required since only the cached code path compares keys by identity (`==`), the generic code path still uses `equals` to compare the property keys.
80+
81+
#### Getting the value of a property with boxing elimination
82+
83+
```java
84+
abstract class GetUnboxedNode extends Node {
85+
86+
abstract int executeInt(DynamicObject receiver, Object key) throws UnexpectedResultException;
87+
88+
abstract Object execute(DynamicObject receiver, Object key);
89+
90+
@Specialization(rewriteOn = UnexpectedResultException.class)
91+
static int doInt(MyDynamicObject receiver, Symbol key,
92+
@Shared @Cached DynamicObject.GetNode getNode) throws UnexpectedResultException {
93+
return getNode.executeInt(receiver, key, NULL_VALUE);
94+
}
95+
96+
@Specialization(replaces = "doInt")
97+
static Object doGeneric(MyDynamicObject receiver, Symbol key,
98+
@Shared @Cached DynamicObject.GetNode getNode) {
99+
return getNode.execute(receiver, key, NULL_VALUE);
100+
}
101+
}
102+
```
103+
104+
### Writing a property
105+
106+
#### Adding a property or setting the value of a property with a fixed key or a dynamic key that is already unique or interned
107+
108+
```java
109+
abstract class SetUniqueKeyNode extends Node {
110+
111+
abstract void execute(DynamicObject receiver, Object key, Object value);
112+
113+
@Specialization
114+
static void doCached(MyDynamicObject receiver, Symbol key, Object value,
115+
@Cached DynamicObject.PutNode putNode) {
116+
putNode.execute(receiver, key, value);
117+
}
118+
}
119+
```
120+
121+
#### Setting the value of a property with a dynamic key and key equality
122+
123+
```java
124+
@ImportStatic(TruffleString.Encoding.class)
125+
abstract class SetStringKeyNode extends Node {
126+
127+
abstract void execute(DynamicObject receiver, Object key, Object value);
128+
129+
@Specialization(guards = "equalNode.execute(key, cachedKey, UTF_16)", limit = "3")
130+
static void doCached(MyDynamicObject receiver, TruffleString key, Object value,
131+
@Cached("key") TruffleString cachedKey,
132+
@Cached TruffleString.EqualNode equalNode,
133+
@Cached DynamicObject.PutNode putNode) {
134+
putNode.execute(receiver, cachedKey, value);
135+
}
136+
137+
@Specialization(replaces = "doCached")
138+
static void doGeneric(MyDynamicObject receiver, TruffleString key,
139+
@Shared @Cached DynamicObject.PutNode getNode) {
140+
putNode.execute(receiver, key, value);
141+
}
142+
}
143+
```
144+
145+
Note that key interning is not required since only the cached code path compares keys by identity (`==`), the generic code path still uses `equals` to compare the property keys.

0 commit comments

Comments
 (0)