com.oracle.truffle.api.object.dsl.Layout Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of truffle-api Show documentation
Show all versions of truffle-api Show documentation
Truffle is a multi-language framework for executing dynamic languages
that achieves high performance when combined with Graal.
/*
* Copyright (c) 2015, 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.dsl;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.DynamicObjectFactory;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.api.object.ObjectType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.atomic.AtomicReference;
/**
* Annotate an interface with {@link Layout} to generate an implementation of the interface which
* uses object-model properties. {@link Layout} allows you to use the object-model in a similar way
* to writing a normal Java class for statically declared and implementation-specific fields. Most
* methods generated from an {@link Layout}-annotated interface are suitable for use on the
* fast-path.
*
* The name of the interface should end with 'Layout'.
*
* {@codesnippet rectlayout}
*
* The generated class is named with the name of the interface and then {@code -Impl}. A singleton
* instance of the interface, {@code -Impl.INSTANCE} is available as a static field in the class.
*
* {@codesnippet rectlayoutimpl}
*
* Factory method
*
* A factory method named {@code create-} and then the name of the layout creates instances of the
* layout. It returns a {@link DynamicObject}, not an instance of the interface.
*
* {@codesnippet rectlayoutcreate}
*
* Alternative constructor method
*
* As an alternative to the {@code create-} factory, you can declare a method named {@code build},
* which returns the arguments packed for {@link DynamicObjectFactory#newInstance(Object...)}.
*
* {@codesnippet rectlayoutbuild}
*
* This is particularly useful when the {@code DynamicObjectFactory} is not statically known or some
* generic allocation node taking {@code Object[]} arguments is used to create objects.
*
* {@codesnippet rectlayoutbuildinstance}
*
* Guards
*
* Guards can tell you if an object is using layout. Guards are defined for {@link DynamicObject},
* the more general {@link Object} which first checks if the arguments is a {@link DynamicObject},
* and {@link ObjectType}, which you can get through the shape of a {@link DynamicObject}. To add a
* guard, define the method in your interface.
*
*
* {@codesnippet rectlayoutguards}
*
* Properties
*
* To add properties, define a getter and setter, and add a parameter to the factory method.
*
* {@codesnippet rectlayoutprops}
*
* If you don't define a setter, the property will be final. This may improve the performance of the
* property.
*
* Nullable Properties
*
* By default, properties are non-nullable, which means that they always need an instance of an
* object and they cannot be assigned the value {@code null}. This has performance benefits in the
* implementation of the object-model.
*
* To make a property nullable so that you can assign {@code null} to it, annotate the constructor
* parameter with {@link Nullable}.
*
* {@codesnippet nullable}
*
* Volatile Properties
*
* To define a property with volatile semantics, in the sense of the Java Language Specification
* section 8.3.1.4, annotate the constructor parameter with {@link Volatile}. A property annotated
* as volatile also allows you to define atomic operation methods in your layout interface for that
* property. Methods available are {@code compareAndSet}, in the sense of
* {@link AtomicReference#compareAndSet}, and {@code getAndSet}, in the sense of
* {@link AtomicReference#getAndSet}.
*
* {@codesnippet volatile}
*
* Volatile properties generally have lower performance than the default non-volatile properties.
*
* Semi-Final Properties
*
* It is possible to define a 'back-door' and unsafe setter for otherwise-final properties by
* appending {@code -Unsafe} to the setter name.
*
* {@codesnippet semifinal}
*
* Final and semi-final properties may be assumed by a dynamic compiler to not change for a given
* instance of an object after it is constructed. Unsafe setters are therefore unsafe as a
* modification to the property could be ignored by the dynamic compiler. You should only use unsafe
* setters if you have reasoned that it is not possible for the dynamic compiler to compile a
* reference to the object and the property before the unsafe setter is used. One use-case is
* closing cycles in class graphs, such as the classic class-of-class-is-class problem, where you
* normally want the class property to be final for performance but just as the graph is created
* this one cycle needs to be closed.
*
* Errors due to the incorrect use of unsafe getters are likely to be non-deterministic and
* difficult to isolate. Consider making properties temporarily non-final with a conventional getter
* if stale value are experienced in dynamically compiled code.
*
* Shape Properties
*
* A shape property is a property that is shared between many objects and does not frequently
* change. One intended use-case is a property to store the class of an object, which is likely
* shared between many objects and likely does not change after the object is created.
*
* Shape properties should be cached against an object's shape as there is an extra level of
* indirection used to look up their value for an object. They may save space as they are not stored
* for all instances.
*
* It is important to note that changing a shape-property for an existing object is both not a
* fast-path operation, and depending on the design of your interpreter is likely to invalidate
* caches.
*
* When shape properties are used there is an extra level of indirection, in that a
* {@link DynamicObjectFactory} (referred to as the shape, because it is the shape that the factory
* object contains that is used to look up shape properties) is created by the layout and then used
* when creating new instances. As shape properties are set and changed, multiple factories will be
* created and it is up to the user to store and supply these as needed.
*
* Consider the example of a Java-style object, with a class and a hash code. The class would be a
* shape property, as many objects will share the same class, and the hash code will be a normal
* property.
*
* Shape properties are created by parameters in the method that creates the shape. The factory
* method then accepts an instance of a factory when creating the object, which is how the instance
* knows the value of the class property to use. A getter for a shape property can be defined as
* normal.
*
* {@codesnippet javaobject}
*
* When we load our Java interpreter we need to set the class property of the {@code Class} object
* to be itself. This means in this one isolated, slow-path, case we need to change a shape property
* for an object that is already allocated. Getters for shape properties can be defined for the
* {@link DynamicObjectFactory}, and for the {@link ObjectType}.
*
* Setters for shape properties are more complex, and they are not intended to be used in the fast
* path. Setters can be defined on a {@link DynamicObjectFactory}, in which case they return a new
* factory, or on a {@link DynamicObject}, in which they they change the shape of the object. This
* is a slow-path operation and is likely to invalidate caches in your interpreter.
*
* {@codesnippet shapesetters}
*
* Apply this to our example with Java classes:
*
* {@codesnippet closecycle}
*
* Layout Inheritance
*
* Inheritance of layout interfaces allows you to model classical class inheritance, such as in a
* language like Java. Use normal interface inheritance to make one layout inherit from another. You
* then need to add the parameters for super-layouts at the beginning of sub-layout constructor
* methods.
*
* Inherited shape properties work in a similar way.
*
* {@codesnippet inheritanceinterfaces}
*
* {@codesnippet inheritanceuse}
*
* Custom Object-Type Superclass
*
* Generated layouts use custom {@link ObjectType} subclasses internally. The default base class
* that is inherited from is simply {@link ObjectType}. You can change this with the
* {@link #objectTypeSuperclass} property on the {@link Layout} annotation.
*
* Implicit Casts
*
* {@code IntToLong} and {@code IntToDouble} implicit cast flags can be set in the generated layout
* by setting {@link #implicitCastIntToLong} or {@link #implicitCastIntToDouble}. This can only be
* done in base layouts, not subclassed layouts.
*
* Custom Identifiers
*
* By default, internal {@link HiddenKey} identifiers with descriptive names will be created for
* your properties automatically. You can also specify the identifiers to use by defining them as
* public static final fields in the interface.
*
* {@codesnippet customid}
*
* @since 0.12
* @deprecated with no replacement.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@Deprecated
public @interface Layout {
/** @since 0.12 */
@Deprecated
Class extends ObjectType> objectTypeSuperclass() default ObjectType.class;
/** @since 0.12 */
@Deprecated
boolean implicitCastIntToLong() default false;
/** @since 0.12 */
@Deprecated
boolean implicitCastIntToDouble() default false;
/**
* Non-instantiable class representing default value for {@link #dispatch()}.
*
* @since 20.0
*/
@Deprecated
final class DispatchDefaultValue {
private DispatchDefaultValue() {
}
}
/**
* Generate override of {@link ObjectType#dispatch()} method in the generated inner *Type class.
*
* @since 20.0
*/
@Deprecated
Class> dispatch() default DispatchDefaultValue.class;
}
@SuppressWarnings("deprecation")
class Snippets {
// BEGIN: rectlayout
@Layout
private interface RectLayout {
}
// END: rectlayout
private static class RectLayoutImpl {
public static final RectLayoutImpl INSTANCE = new RectLayoutImpl();
}
static Object rectLayoutImpl() {
return
// BEGIN: rectlayoutimpl
RectLayoutImpl.INSTANCE;
// END: rectlayoutimpl
}
interface CreateSnippets {
// BEGIN: rectlayoutcreate
DynamicObject createRect(int x, int y, int width, int height);
// END: rectlayoutcreate
// BEGIN: rectlayoutbuild
Object[] build(int x, int y, int width, int height);
// END: rectlayoutbuild
}
static class BuildSnippets {
static class RectLayoutImpl implements CreateSnippets {
public DynamicObject createRect(int x, int y, int width, int height) {
return null;
}
public Object[] build(int x, int y, int width, int height) {
return null;
}
public static final CreateSnippets INSTANCE = new RectLayoutImpl();
}
DynamicObject createRect(Object type, int x, int y, int width, int height) {
// BEGIN: rectlayoutbuildinstance
DynamicObjectFactory factory = getCachedFactory(type);
return factory.newInstance(RectLayoutImpl.INSTANCE.build(x, y, width, height));
// END: rectlayoutbuildinstance
}
private static DynamicObjectFactory getCachedFactory(Object type) {
return (DynamicObjectFactory) type;
}
}
interface InterfaceSnippets {
// BEGIN: rectlayoutguards
boolean isRect(DynamicObject object);
boolean isRect(Object object);
boolean isRect(ObjectType objectType);
// END: rectlayoutguards
// BEGIN: rectlayoutprops
DynamicObject createRect(int x, int y, int width, int height);
int getX(DynamicObject object);
void setX(DynamicObject object, int value);
int getWidth(DynamicObject object);
void setWidth(DynamicObject object, int value);
// END: rectlayoutprops
// BEGIN: nullable
DynamicObject createObject(@Nullable Object nullableProperty);
// END: nullable
// BEGIN: volatile
boolean compareAndSetWidth(DynamicObject object,
int expectedValue, int newValue);
int getAndSet(DynamicObject object, int value);
// END: volatile
// BEGIN: semifinal
void setValueUnsafe(DynamicObject object, Object value);
// END: semifinal
class JavaClass {
}
// BEGIN: javaobject
@Layout
interface JavaObjectLayout {
DynamicObjectFactory createJavaObjectShape(JavaClass klass);
DynamicObject createJavaObject(DynamicObjectFactory factory, int hashCode);
JavaClass getKlass(DynamicObjectFactory factory);
JavaClass getKlass(ObjectType objectType);
JavaClass getKlass(DynamicObject object);
int getHashCode(DynamicObject object);
}
// END: javaobject
// BEGIN: shapesetters
DynamicObjectFactory setKlass(DynamicObjectFactory factory, JavaClass value);
void setKlass(DynamicObject object, JavaClass value);
// END: shapesetters
}
static class JavaObjectImpl {
static final JavaObjectImpl INSTANCE = new JavaObjectImpl();
@SuppressWarnings("unused")
Object createJavaObject(Object x, Object y) {
return null;
}
@SuppressWarnings("unused")
Object createJavaObjectShape(Object x) {
return null;
}
@SuppressWarnings("unused")
void setKlass(Object x, Object y) {
}
}
Object defaultHashCode() {
return null;
}
void closeCycle() {
Object
// BEGIN: closecycle
javaClassObject = JavaObjectImpl.INSTANCE.createJavaObject(
JavaObjectImpl.INSTANCE.createJavaObjectShape(null),
defaultHashCode());
JavaObjectImpl.INSTANCE.setKlass(javaClassObject, javaClassObject);
// END: closecycle
}
// BEGIN: inheritanceinterfaces
@Layout
interface BaseLayout {
DynamicObject createBase(int a);
boolean isBase(DynamicObject object);
int getA(DynamicObject object);
void setA(DynamicObject object, int value);
}
@Layout
interface SuperLayout extends BaseLayout {
DynamicObject createSuper(int a, int b);
int getB(DynamicObject object);
void setB(DynamicObject object, int value);
}
// END: inheritanceinterfaces
static class BaseImpl {
static final BaseImpl INSTANCE = new BaseImpl();
@SuppressWarnings("unused")
boolean isBase(Object x) {
return false;
}
@SuppressWarnings("unused")
Object getA(Object x) {
return null;
}
}
static class SuperImpl {
static final SuperImpl INSTANCE = new SuperImpl();
@SuppressWarnings("unused")
DynamicObject createSuper(int x, int y) {
return null;
}
}
void inheritanceUse() {
// BEGIN: inheritanceuse
DynamicObject object = SuperImpl.INSTANCE.createSuper(14, 2);
BaseImpl.INSTANCE.isBase(object);
BaseImpl.INSTANCE.getA(object);
// END: inheritanceuse
}
// Checkstyle: stop
// BEGIN: customid
@Layout
interface CustomIdentifierLayout {
public static final String A_IDENTIFIER = "A";
DynamicObject createCustomIdentifier(int a);
}
// END: customid
// Checkstyle: resume
}