com.oracle.truffle.api.object.DynamicObjectLibrary Maven / Gradle / Ivy
Show all versions of truffle-api Show documentation
/*
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.api.object;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.GenerateLibrary;
import com.oracle.truffle.api.library.Library;
import com.oracle.truffle.api.library.LibraryFactory;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
/**
* {@link DynamicObject} access library.
*
* This is the central interface for accessing and mutating properties and other state (flags,
* dynamic type) of {@link DynamicObject}s.
*
**
* It is recommended that you use the {@link CachedLibrary} annotation in Truffle DSL nodes. You can
* also use the library either {@linkplain #getUncached() without caching} or create a
* {@linkplain LibraryFactory#create(Object) manually} or
* {@linkplain LibraryFactory#createDispatched(int) automatically} dispatched cached library. Cached
* libraries must be adopted before use.
*
* The cached library instances dispatch by object {@linkplain Shape shape} and, if applicable,
* automatically by property key.
*
*
* Property keys are compared using object identity ({@code ==}) first and then
* {@link 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 {@code equals}. Keys must not be
* {@code 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
*/
@GenerateLibrary(defaultExportLookupEnabled = true, dynamicDispatchEnabled = false, pushEncapsulatingNode = false)
public abstract class DynamicObjectLibrary extends Library {
private static final LibraryFactory FACTORY = LibraryFactory.resolve(DynamicObjectLibrary.class);
private static final DynamicObjectLibrary UNCACHED = FACTORY.getUncached();
/**
* @since 20.2.0
*/
protected DynamicObjectLibrary() {
}
/**
* Returns the library factory for {@link DynamicObjectLibrary}.
*
* @since 20.2.0
*/
public static LibraryFactory getFactory() {
return FACTORY;
}
/**
* Gets the shared {@link DynamicObjectLibrary} instance for uncached accesses. Equivalent to
* {@code DynamicObjectLibrary.getFactory().getUncached()}.
*
* @return an uncached automatically dispatched version of the library
* @since 20.2.0
*/
public static DynamicObjectLibrary getUncached() {
return UNCACHED;
}
/**
* Gets the {@link Shape shape} of the object. Returns the cached shape if the library is a
* cached library instance.
*
* @return the object's current {@link Shape}
* @see DynamicObject#getShape()
* @since 20.2.0
*/
public abstract Shape getShape(DynamicObject object);
/**
* 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);
* }
*
*
* @param key the property key
* @param defaultValue value to be returned if the property does not exist
* @return the property's value if it exists, else {@code defaultValue}.
* @since 20.2.0
*/
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.
*
* @param key the property key
* @param defaultValue the value to be returned if the property does not exist
* @return the property's value if it exists, else {@code defaultValue}.
* @throws UnexpectedResultException if the (default) value is not an {@code int}
* @see #getOrDefault(DynamicObject, Object, Object)
* @since 20.2.0
*/
public int getIntOrDefault(DynamicObject object, Object key, Object defaultValue) throws UnexpectedResultException {
Object value = getOrDefault(object, key, defaultValue);
if (value instanceof Integer) {
return (int) value;
} else {
throw new UnexpectedResultException(value);
}
}
/**
* Gets the value of an existing property or returns the provided default value if no such
* property exists.
*
* @param key the property key
* @param defaultValue the value to be returned if the property does not exist
* @return the property's value if it exists, else {@code defaultValue}.
* @throws UnexpectedResultException if the (default) value is not a {@code double}
* @see #getOrDefault(DynamicObject, Object, Object)
* @since 20.2.0
*/
public double getDoubleOrDefault(DynamicObject object, Object key, Object defaultValue) throws UnexpectedResultException {
Object value = getOrDefault(object, key, defaultValue);
if (value instanceof Double) {
return (double) value;
} else {
throw new UnexpectedResultException(value);
}
}
/**
* Gets the value of an existing property or returns the provided default value if no such
* property exists.
*
* @param key the property key
* @param defaultValue the value to be returned if the property does not exist
* @return the property's value if it exists, else {@code defaultValue}.
* @throws UnexpectedResultException if the (default) value is not a {@code long}
* @see #getOrDefault(DynamicObject, Object, Object)
* @since 20.2.0
*/
public long getLongOrDefault(DynamicObject object, Object key, Object defaultValue) throws UnexpectedResultException {
Object value = getOrDefault(object, key, defaultValue);
if (value instanceof Long) {
return (long) value;
} else {
throw new UnexpectedResultException(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 {@link #putWithFlags} to set property flags as well.
*
* Usage example:
*
*
* @ExportMessage
* Object writeMember(String member, Object value,
* @CachedLibrary("this") DynamicObjectLibrary objLib) {
* objLib.put(this, member, value);
* }
*
*
* @param key the property key
* @param value the value to be set
* @see #putInt(DynamicObject, Object, int)
* @see #putDouble(DynamicObject, Object, double)
* @see #putLong(DynamicObject, Object, long)
* @see #putIfPresent(DynamicObject, Object, Object)
* @see #putWithFlags(DynamicObject, Object, Object, int)
* @since 20.2.0
*/
public abstract void put(DynamicObject object, Object key, Object value);
/**
* Int-typed variant of {@link #put}.
*
* @see #put(DynamicObject, Object, Object)
* @since 20.2.0
*/
public void putInt(DynamicObject object, Object key, int value) {
put(object, key, value);
}
/**
* Double-typed variant of {@link #put}.
*
* @see #put(DynamicObject, Object, Object)
* @since 20.2.0
*/
public void putDouble(DynamicObject object, Object key, double value) {
put(object, key, value);
}
/**
* Long-typed variant of {@link #put}.
*
* @see #put(DynamicObject, Object, Object)
* @since 20.2.0
*/
public void putLong(DynamicObject object, Object key, long value) {
put(object, key, value);
}
/**
* Sets the value of the property if present, otherwise returns {@code false}.
*
* @param key property identifier
* @param value value to be set
* @return {@code true} if the property was present and the value set, otherwise {@code false}
* @see #put(DynamicObject, Object, Object)
* @since 20.2.0
*/
public abstract boolean putIfPresent(DynamicObject object, Object key, Object value);
/**
* Like {@link #put}, but additionally assigns flags to the property. If the property already
* exists, its flags will be updated before the value is set.
*
* @param key property identifier
* @param value value to be set
* @param flags flags to be set
* @see #put(DynamicObject, Object, Object)
* @see #setPropertyFlags(DynamicObject, Object, int)
* @since 20.2.0
*/
public abstract void putWithFlags(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);
*
*
* @param key property identifier
* @param value the constant value to be set
* @param flags property flags or 0
* @see #put(DynamicObject, Object, Object)
* @since 20.2.0
*/
public abstract void putConstant(DynamicObject object, Object key, Object value, int flags);
/**
* Removes the property with the given key from the object.
*
* @param key the property key
* @return {@code true} if the property was removed or {@code false} if property was not found
* @since 20.2.0
*/
public abstract boolean removeKey(DynamicObject object, Object key);
/**
* 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 {@code equals}.
*
* @param type a non-null type identifier defined by the guest language.
* @return {@code true} if the type (and the object's shape) changed
* @throws IllegalArgumentException if the type is not an instance of {@link ObjectType} and the
* object has been created with the legacy layout.
* @since 20.2.0
* @see #getDynamicType(DynamicObject)
*/
public abstract boolean setDynamicType(DynamicObject object, Object type);
/**
* 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.
*
* @return the object type
* @since 20.2.0
* @see #setDynamicType(DynamicObject, Object)
* @see Shape#getDynamicType()
*/
public abstract Object getDynamicType(DynamicObject object);
/**
* Returns {@code 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);
* }
*
*
* @param key the property key
* @return {@code true} if the object contains a property with this key, else {@code false}
* @since 20.2.0
*/
public abstract boolean containsKey(DynamicObject object, Object key);
/**
* Gets the language-specific object shape flags previously set using
* {@link DynamicObjectLibrary#setShapeFlags(DynamicObject, int)} or
* {@link 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);
* }
*
*
* @return shape flags
* @see #setShapeFlags(DynamicObject, int)
* @see Shape.Builder#shapeFlags(int)
* @see Shape#getFlags()
* @since 20.2.0
*/
public abstract int getShapeFlags(DynamicObject object);
/**
* 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);
* }
*
*
* @param flags the flags to set; must be in the range from 0 to 255 (inclusive).
* @return {@code true} if the object's shape changed, {@code false} if no change was made.
* @throws IllegalArgumentException if the flags are not in the allowed range.
* @see #getShapeFlags(DynamicObject)
* @see Shape.Builder#shapeFlags(int)
* @since 20.2.0
*/
public abstract boolean setShapeFlags(DynamicObject object, int flags);
/**
* Gets a {@linkplain Property property descriptor} for the requested property key. Returns
* {@code null} if the object contains no such property.
*
* @return {@link Property} if the property exists, else {@code null}
* @since 20.2.0
*/
public abstract Property getProperty(DynamicObject object, Object key);
/**
* Gets the property flags associated with the requested property key. Returns the
* {@code 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;
*
*
* @param key the property key
* @param defaultValue value to return if no such property exists
* @return the property flags if the property exists, else {@code defaultValue}
* @see #getProperty(DynamicObject, Object)
* @since 20.2.0
*/
public final int getPropertyFlagsOrDefault(DynamicObject object, Object key, int defaultValue) {
Property property = getProperty(object, key);
return property != null ? property.getFlags() : defaultValue;
}
/**
* Sets the property flags associated with the requested property.
*
* @param key the property key
* @return {@code true} if the property was found and its flags were changed, else {@code false}
* @since 20.2.0
*/
public abstract boolean setPropertyFlags(DynamicObject object, Object key, int propertyFlags);
/**
* Marks this object as shared.
*
* Makes the object use a shared variant of the {@link Shape}, to allow safe usage of this
* object between threads. Objects with a shared {@link 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 {@linkplain #isShared shared}.
* @see #isShared(DynamicObject)
* @since 20.2.0
*/
public abstract void markShared(DynamicObject object);
/**
* Checks whether this object is marked as shared.
*
* @return {@code true} if the object is shared
* @see #markShared(DynamicObject)
* @see Shape#isShared()
* @since 20.2.0
*/
public abstract boolean isShared(DynamicObject object);
/**
* Ensures the object's shape is up-to-date. If the object's shape has been marked as
* {@linkplain Shape#isValid() invalid}, this method will attempt to bring the object into a
* valid shape again. If the object's shape is already {@linkplain Shape#isValid() 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.
*
* @return {@code true} if the object's shape was changed, otherwise {@code false}.
* @since 20.2.0
*/
public abstract boolean updateShape(DynamicObject object);
/**
* 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).
*
* @param otherShape the desired shape
* @return {@code true} if the object's shape was changed
* @throws IllegalArgumentException if the shape contains instance properties
* @since 20.2.0
*/
public abstract boolean resetShape(DynamicObject object, Shape otherShape);
/**
* 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 {@link 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;
* }
* }
*
*
* @return a read-only array of the object's property keys.
* @since 20.2.0
*/
public abstract Object[] getKeyArray(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 {@link HiddenKey} are not included.
*
* Similar to {@link #getKeyArray} but allows the properties' flags to be queried simultaneously
* which may be relevant for quick filtering.
*
* @return a read-only array of the object's properties.
* @see #getKeyArray(DynamicObject)
* @since 20.2.0
*/
public abstract Property[] getPropertyArray(DynamicObject object);
}