Class DynamicObject

java.lang.Object
com.oracle.truffle.api.object.DynamicObject
All Implemented Interfaces:
TruffleObject

public abstract class DynamicObject extends Object implements TruffleObject
Represents a dynamic object, members of which can be dynamically added and removed at run time. To use it, extend DynamicObject and use nodes nested under DynamicObject such as DynamicObject.GetNode for object accesses. When constructing a DynamicObject, it has to be initialized with an empty initial shape. Initial shapes are created using Shape.newBuilder() and should ideally be shared per TruffleLanguage instance to allow shape caches to be shared across contexts. Subclasses can provide in-object dynamic field slots using the DynamicObject.DynamicField annotation and Shape.Builder.layout.

Example:

public class MyObject extends DynamicObject implements TruffleObject {
    MyObject(Shape shape) {
        super(shape);
    }

    static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
}

public abstract class MyTruffleLanguage extends TruffleLanguage<MyContext> {
    final Shape initialShape = Shape.newBuilder().build();

    public MyObject newObject() {
        return new MyObject(initialShape);
    }
}

Using DynamicObject nodes

DynamicObject nodes is the central interface for accessing and mutating properties and other state (flags, dynamic type) of DynamicObjects. All nodes provide cached and uncached variants.

Note: Property keys are always compared using object identity (==), never with equals, for efficiency reasons. If the node is not used with a fixed key, and some keys might be equals but not have the same identity (==), you must either intern the keys first, or cache the key by equality using an inline cache and use the cached key with the DynamicObject node to ensure equal keys with different identity will use the same cache entry and not overflow the cache:

abstract static class GetStringKeyNode extends Node {
    abstract Object execute(DynamicObject receiver, Object key);

    @Specialization(guards = "equalNode.execute(key, cachedKey, UTF_16)", limit = "3")
    static Object doCached(MyDynamicObjectSubclass receiver, TruffleString key,
                    @Cached("key") TruffleString cachedKey,
                    @Cached TruffleString.EqualNode equalNode,
                    @Cached DynamicObject.GetNode getNode) {
        return getNode.execute(receiver, cachedKey, NULL_VALUE);
    }
}

Usage examples:

Simple use of DynamicObject.GetNode with a pre-interned symbol key.

abstract static class GetSimpleNode extends Node {
    abstract Object execute(DynamicObject receiver, Object key);

    @Specialization
    static Object doCached(MyDynamicObjectSubclass receiver, Symbol key,
                    @Cached DynamicObject.GetNode getNode) {
        return getNode.execute(receiver, key, NULL_VALUE);
    }
}

Implementing InteropLibrary messages using DynamicObject access nodes:

@ExportLibrary(InteropLibrary.class)
static class MyDynamicObjectSubclass extends DynamicObject {
    MyDynamicObjectSubclass(Shape shape) {
        super(shape);
    }

    @ExportMessage
    Object readMember(String member,
                    @Cached DynamicObject.GetNode getNode) throws UnknownIdentifierException {
        Object result = getNode.execute(this, member, null);
        if (result == null) {
            throw UnknownIdentifierException.create(member);
        }
        return result;
    }
Member name equality check omitted for brevity.

Adding extra dynamic fields to a DynamicObject subclass:

public class MyObjectWithFields extends DynamicObject implements TruffleObject {
    @DynamicField private Object extra1;
    @DynamicField private Object extra2;
    @DynamicField private long extraLong1;
    @DynamicField private long extraLong2;

    MyObjectWithFields(Shape shape) {
        super(shape);
    }

    static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
}

Shape initialShape = Shape.newBuilder().layout(MyObject.class, MyObject.LOOKUP).build();

MyObject obj = new MyObject(initialShape);
Since:
0.8 or earlier
See Also:
  • Constructor Details

    • DynamicObject

      protected DynamicObject(Shape shape)
      Constructor for DynamicObject subclasses. Initializes the object with the provided shape. The shape must have been constructed with a layout class assignable from this class (i.e., the concrete subclass, a superclass thereof, including DynamicObject) and must not have any instance properties (but may have constant properties).

      Examples:

      Shape shape = Shape.newBuilder().build();
      DynamicObject myObject = new MyObject(shape);
      
      Shape shape = Shape.newBuilder().layout(MyObject.class, MethodHandles.lookup()).build();
      DynamicObject myObject = new MyObject(shape);
      
      Parameters:
      shape - the initial shape of this object
      Throws:
      IllegalArgumentException - if called with an illegal (incompatible) shape
      Since:
      19.0
  • Method Details