Class DynamicObjectLibrary

All Implemented Interfaces:
NodeInterface, Cloneable

public abstract class DynamicObjectLibrary extends Library
DynamicObject access library. This is the central interface for accessing and mutating properties and other state (flags, dynamic type) of DynamicObjects.

It is recommended that you use the CachedLibrary annotation in Truffle DSL nodes. You can also use the library either without caching or create a manually or automatically dispatched cached library. Cached libraries must be adopted before use. The cached library instances dispatch by object shape and, if applicable, automatically by property key.

Property keys are compared using object identity (==) first and then Object.equals(Object). It is therefore recommended to use the same string/key instances for each access of the property in order to avoid pulling in equals. Keys must not be null.

Note: cached library nodes may not profile the class of the object parameter; it is therefore the caller's responsibility to do any desired profiling and ensure accurate type information.

Usage examples:

 @Specialization(limit = "3")
 static Object read(DynamicObject receiver, Object key,
                 @CachedLibrary("receiver") DynamicObjectLibrary objLib) {
     return objLib.getOrDefault(receiver, key, NULL_VALUE);
 }
 
 @ExportMessage
 Object readMember(String name,
                 @CachedLibrary("this") DynamicObjectLibrary objLib) throws UnknownIdentifierException {
     Object result = objLib.getOrDefault(this, name, null);
     if (result == null) {
         throw UnknownIdentifierException.create(name);
     }
     return result;
 }
 
Since:
20.2.0
  • Constructor Details

    • DynamicObjectLibrary

      protected DynamicObjectLibrary()
      Since:
      20.2.0
  • Method Details

    • getFactory

      public static LibraryFactory<DynamicObjectLibrary> getFactory()
      Returns the library factory for DynamicObjectLibrary.
      Since:
      20.2.0
    • getUncached

      public static DynamicObjectLibrary getUncached()
      Gets the shared DynamicObjectLibrary instance for uncached accesses. Equivalent to DynamicObjectLibrary.getFactory().getUncached().
      Returns:
      an uncached automatically dispatched version of the library
      Since:
      20.2.0
    • getShape

      public abstract Shape getShape(DynamicObject object)
      Gets the shape of the object. Returns the cached shape if the library is a cached library instance.
      Returns:
      the object's current Shape
      Since:
      20.2.0
      See Also:
    • getOrDefault

      public abstract Object getOrDefault(DynamicObject object, Object key, Object defaultValue)
      Gets the value of an existing property or returns the provided default value if no such property exists.

      Usage example:

       @Specialization(limit = "3")
       static Object read(DynamicObject receiver, Object key,
                       @CachedLibrary("receiver") DynamicObjectLibrary objLib) {
           return objLib.getOrDefault(receiver, key, NULL_VALUE);
       }
       
      Parameters:
      key - the property key
      defaultValue - value to be returned if the property does not exist
      Returns:
      the property's value if it exists, else defaultValue.
      Since:
      20.2.0
    • getIntOrDefault

      public int getIntOrDefault(DynamicObject object, Object key, Object defaultValue) throws UnexpectedResultException
      Gets the value of an existing property or returns the provided default value if no such property exists.
      Parameters:
      key - the property key
      defaultValue - the value to be returned if the property does not exist
      Returns:
      the property's value if it exists, else defaultValue.
      Throws:
      UnexpectedResultException - if the (default) value is not an int
      Since:
      20.2.0
      See Also:
    • getDoubleOrDefault

      public double getDoubleOrDefault(DynamicObject object, Object key, Object defaultValue) throws UnexpectedResultException
      Gets the value of an existing property or returns the provided default value if no such property exists.
      Parameters:
      key - the property key
      defaultValue - the value to be returned if the property does not exist
      Returns:
      the property's value if it exists, else defaultValue.
      Throws:
      UnexpectedResultException - if the (default) value is not a double
      Since:
      20.2.0
      See Also:
    • getLongOrDefault

      public long getLongOrDefault(DynamicObject object, Object key, Object defaultValue) throws UnexpectedResultException
      Gets the value of an existing property or returns the provided default value if no such property exists.
      Parameters:
      key - the property key
      defaultValue - the value to be returned if the property does not exist
      Returns:
      the property's value if it exists, else defaultValue.
      Throws:
      UnexpectedResultException - if the (default) value is not a long
      Since:
      20.2.0
      See Also:
    • put

      public abstract void put(DynamicObject object, Object key, Object value)
      Sets the value of an existing property or adds a new property if no such property exists. A newly added property will have flags 0; flags of existing properties will not be changed. Use putWithFlags(com.oracle.truffle.api.object.DynamicObject, java.lang.Object, java.lang.Object, int) to set property flags as well.

      Usage example:

       @ExportMessage
       Object writeMember(String member, Object value,
                       @CachedLibrary("this") DynamicObjectLibrary objLib) {
           objLib.put(this, member, value);
       }
       
      Parameters:
      key - the property key
      value - the value to be set
      Since:
      20.2.0
      See Also:
    • putInt

      public void putInt(DynamicObject object, Object key, int value)
      Since:
      20.2.0
      See Also:
    • putDouble

      public void putDouble(DynamicObject object, Object key, double value)
      Since:
      20.2.0
      See Also:
    • putLong

      public void putLong(DynamicObject object, Object key, long value)
      Since:
      20.2.0
      See Also:
    • putIfPresent

      public abstract boolean putIfPresent(DynamicObject object, Object key, Object value)
      Sets the value of the property if present, otherwise returns false.
      Parameters:
      key - property identifier
      value - value to be set
      Returns:
      true if the property was present and the value set, otherwise false
      Since:
      20.2.0
      See Also:
    • putWithFlags

      public abstract void putWithFlags(DynamicObject object, Object key, Object value, int flags)
      Like put(com.oracle.truffle.api.object.DynamicObject, java.lang.Object, java.lang.Object), but additionally assigns flags to the property. If the property already exists, its flags will be updated before the value is set.
      Parameters:
      key - property identifier
      value - value to be set
      flags - flags to be set
      Since:
      20.2.0
      See Also:
    • putConstant

      public abstract void putConstant(DynamicObject object, Object key, Object value, int flags)
      Adds a property with a constant value or replaces an existing one. If the property already exists, its flags will be updated. The constant value is stored in the shape rather than the object instance and a new shape will be allocated if it does not already exist. A typical use case for this method is setting the initial default value of a declared, but yet uninitialized, property. This defers storage allocation and type speculation until the first actual value is set.

      Warning: this method will lead to a shape transition every time a new value is set and should be used sparingly (with at most one constant value per property) since it could cause an excessive amount of shapes to be created.

      Note: the value will be strongly referenced from the shape and should be a value type or light-weight object without any references to guest language objects in order to prevent potential memory leaks.

      Usage example:

       // declare property
       objLib.putConstant(receiver, key, NULL_VALUE, 0);
      
       // initialize property
       objLib.put(receiver, key, value);
       
      Parameters:
      key - property identifier
      value - the constant value to be set
      flags - property flags or 0
      Since:
      20.2.0
      See Also:
    • removeKey

      public abstract boolean removeKey(DynamicObject object, Object key)
      Removes the property with the given key from the object.
      Parameters:
      key - the property key
      Returns:
      true if the property was removed or false if property was not found
      Since:
      20.2.0
    • setDynamicType

      public abstract boolean setDynamicType(DynamicObject object, Object type)
      Sets the object's dynamic type identifier. What this type represents is completely up to the language. For example, it could be a guest-language class. The type object is strongly referenced from the shape. It is important that this be a singleton or light-weight object without any references to guest language objects in order to keep the memory footprint low and prevent potential memory leaks. Type objects are always compared by object identity, never equals.
      Parameters:
      type - a non-null type identifier defined by the guest language.
      Returns:
      true if the type (and the object's shape) changed
      Since:
      20.2.0
      See Also:
    • getDynamicType

      public abstract Object getDynamicType(DynamicObject object)
      Gets the dynamic type identifier currently associated with this object. What this type represents is completely up to the language. For example, it could be a guest-language class.
      Returns:
      the object type
      Since:
      20.2.0
      See Also:
    • containsKey

      public abstract boolean containsKey(DynamicObject object, Object key)
      Returns true if this object contains a property with the given key.

      Usage example:

       @ExportMessage
       boolean isMemberReadable(String name,
                       @CachedLibrary("this") DynamicObjectLibrary objLib) {
           return objLib.containsKey(this, name);
       }
       
      Parameters:
      key - the property key
      Returns:
      true if the object contains a property with this key, else false
      Since:
      20.2.0
    • getShapeFlags

      public abstract int getShapeFlags(DynamicObject object)
      Gets the language-specific object shape flags previously set using setShapeFlags(DynamicObject, int) or Shape.Builder.shapeFlags(int). If no shape flags were explicitly set, the default of 0 is returned. These flags may be used to tag objects that possess characteristics that need to be queried efficiently on fast and slow paths. For example, they can be used to mark objects as frozen.

      Usage example:

       @ExportMessage
       Object writeMember(String member, Object value,
                       @CachedLibrary("this") DynamicObjectLibrary objLib)
                       throws UnsupportedMessageException {
           if ((objLib.getShapeFlags(receiver) & FROZEN) != 0) {
               throw UnsupportedMessageException.create();
           }
           objLib.put(this, member, value);
       }
       
      Returns:
      shape flags
      Since:
      20.2.0
      See Also:
    • setShapeFlags

      public abstract boolean setShapeFlags(DynamicObject object, int flags)
      Sets language-specific object shape flags, changing the object's shape if need be. These flags may be used to tag objects that possess characteristics that need to be queried efficiently on fast and slow paths. For example, they can be used to mark objects as frozen. Only the lowest 8 bits (i.e. values in the range 0 to 255) are allowed, the remaining bits are currently reserved.

      Usage example:

       @Specialization(limit = "3")
       static void preventExtensions(DynamicObject receiver,
                       @CachedLibrary("receiver") DynamicObjectLibrary objLib) {
           objLib.setShapeFlags(receiver, objLib.getShapeFlags(receiver) | FROZEN);
       }
       
      Parameters:
      flags - the flags to set; must be in the range from 0 to 255 (inclusive).
      Returns:
      true if the object's shape changed, false if no change was made.
      Throws:
      IllegalArgumentException - if the flags are not in the allowed range.
      Since:
      20.2.0
      See Also:
    • getProperty

      public abstract Property getProperty(DynamicObject object, Object key)
      Gets a property descriptor for the requested property key. Returns null if the object contains no such property.
      Returns:
      Property if the property exists, else null
      Since:
      20.2.0
    • getPropertyFlagsOrDefault

      public final int getPropertyFlagsOrDefault(DynamicObject object, Object key, int defaultValue)
      Gets the property flags associated with the requested property key. Returns the defaultValue if the object contains no such property. If the property exists but no flags were explicitly set, returns the default of 0.

      Convenience method equivalent to:

       Property property = getProperty(object, key);
       return property != null ? property.getFlags() : defaultValue;
       
      Parameters:
      key - the property key
      defaultValue - value to return if no such property exists
      Returns:
      the property flags if the property exists, else defaultValue
      Since:
      20.2.0
      See Also:
    • setPropertyFlags

      public abstract boolean setPropertyFlags(DynamicObject object, Object key, int propertyFlags)
      Sets the property flags associated with the requested property.
      Parameters:
      key - the property key
      Returns:
      true if the property was found and its flags were changed, else false
      Since:
      20.2.0
    • markShared

      public abstract void markShared(DynamicObject object)
      Marks this object as shared. Makes the object use a shared variant of the Shape, to allow safe usage of this object between threads. Objects with a shared Shape will not reuse storage locations for other fields. In combination with careful synchronization on writes, this can prevent reading out-of-thin-air values.
      Throws:
      UnsupportedOperationException - if the object is already shared.
      Since:
      20.2.0
      See Also:
    • isShared

      public abstract boolean isShared(DynamicObject object)
      Checks whether this object is marked as shared.
      Returns:
      true if the object is shared
      Since:
      20.2.0
      See Also:
    • updateShape

      public abstract boolean updateShape(DynamicObject object)
      Ensures the object's shape is up-to-date. If the object's shape has been marked as invalid, this method will attempt to bring the object into a valid shape again. If the object's shape is already valid, this method will have no effect. This method does not need to be called normally; all the messages in this library will work on invalid shapes as well, but it can be useful in some cases to avoid such shapes being cached which can cause unnecessary cache polymorphism and invalidations.
      Returns:
      true if the object's shape was changed, otherwise false.
      Since:
      20.2.0
    • resetShape

      public abstract boolean resetShape(DynamicObject object, Shape otherShape)
      Empties and resets the object to the given root shape, which must not contain any instance properties (but may contain properties with a constant value).
      Parameters:
      otherShape - the desired shape
      Returns:
      true if the object's shape was changed
      Throws:
      IllegalArgumentException - if the shape contains instance properties
      Since:
      20.2.0
    • getKeyArray

      public abstract Object[] getKeyArray(DynamicObject object)
      Gets a snapshot of the object's property keys, in insertion order. The returned array may have been cached and must not be mutated. Properties with a HiddenKey are not included.

      Usage example:

      The example below shows how the returned keys array could be translated to an interop array for use with InteropLibrary.
       @ExportMessage
       Object getMembers(
                       @CachedLibrary("this") DynamicObjectLibrary objLib) {
           return new Keys(objLib.getKeyArray(this));
       }
      
       @ExportLibrary(InteropLibrary.class)
       static final class Keys implements TruffleObject {
      
           @CompilationFinal(dimensions = 1) final Object[] keys;
      
           Keys(Object[] keys) {
               this.keys = keys;
           }
      
           @ExportMessage
           boolean hasArrayElements() {
               return true;
           }
      
           @ExportMessage
           Object readArrayElement(long index) throws InvalidArrayIndexException {
               if (!isArrayElementReadable(index)) {
                   throw InvalidArrayIndexException.create(index);
               }
               return keys[(int) index];
           }
      
           @ExportMessage
           long getArraySize() {
               return keys.length;
           }
      
           @ExportMessage
           boolean isArrayElementReadable(long index) {
               return index >= 0 && index < keys.length;
           }
       }
       
      Returns:
      a read-only array of the object's property keys.
      Since:
      20.2.0
    • getPropertyArray

      public abstract Property[] getPropertyArray(DynamicObject object)
      Gets an array snapshot of the object's properties, in insertion order. The returned array may have been cached and must not be mutated. Properties with a HiddenKey are not included. Similar to getKeyArray(com.oracle.truffle.api.object.DynamicObject) but allows the properties' flags to be queried simultaneously which may be relevant for quick filtering.
      Returns:
      a read-only array of the object's properties.
      Since:
      20.2.0
      See Also: