@Target(value=TYPE) @Retention(value=SOURCE) public @interface Layout
Layout to generate an implementation of the interface which
uses object-model properties. 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 Layout-annotated interface are suitable for use on the
fast-path.
The name of the interface should end with 'Layout'.
@Layout
private interface RectLayout {
}
The generated class is named with the name of the interface and then -Impl. A singleton
instance of the interface, -Impl.INSTANCE is available as a static field in the class.
RectLayoutImpl.INSTANCE;
create- and then the name of the layout creates instances of the
layout. It returns a DynamicObject, not an instance of the interface.
DynamicObject createRect(int x, int y, int width, int height);
create- factory, you can declare a method named build,
which returns the arguments packed for DynamicObjectFactory.newInstance(Object...).
Object[] build(int x, int y, int width, int height);
This is particularly useful when the DynamicObjectFactory is not statically known or some
generic allocation node taking Object[] arguments is used to create objects.
DynamicObjectFactory factory = getCachedFactory(type);
return factory.newInstance(RectLayoutImpl.INSTANCE.build(x, y, width, height));
DynamicObject,
the more general Object which first checks if the arguments is a DynamicObject,
and ObjectType, which you can get through the shape of a DynamicObject. To add a
guard, define the method in your interface.
boolean isRect(DynamicObjectobject); boolean isRect(Objectobject); boolean isRect(ObjectTypeobjectType);
If you don't define a setter, the property will be final. This may improve the performance of the property.DynamicObjectcreateRect(int x, int y, int width, int height); int getX(DynamicObjectobject); void setX(DynamicObjectobject, int value); int getWidth(DynamicObjectobject); void setWidth(DynamicObjectobject, int value);
null. This has performance benefits in the
implementation of the object-model.
To make a property nullable so that you can assign null to it, annotate the constructor
parameter with Nullable.
DynamicObjectcreateObject(@NullableObjectnullableProperty);
Volatile. A property annotated
as volatile also allows you to define atomic operation methods in your layout interface for that
property. Methods available are compareAndSet, in the sense of
AtomicReference.compareAndSet(V, V), and getAndSet, in the sense of
AtomicReference.getAndSet(V).
boolean compareAndSetWidth(Volatile properties generally have lower performance than the default non-volatile properties.DynamicObjectobject, int expectedValue, int newValue); int getAndSet(DynamicObjectobject, int value);
-Unsafe to the setter name.
void setValueUnsafe(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.DynamicObjectobject,Objectvalue);
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.
@When we load our Java interpreter we need to set the class property of theLayoutinterface JavaObjectLayout {DynamicObjectFactorycreateJavaObjectShape(JavaClass klass);DynamicObjectcreateJavaObject(DynamicObjectFactoryfactory, int hashCode); JavaClass getKlass(DynamicObjectFactoryfactory); JavaClass getKlass(ObjectTypeobjectType); JavaClass getKlass(DynamicObjectobject); int getHashCode(DynamicObjectobject); }
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
DynamicObjectFactory, and for the 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 DynamicObjectFactory, in which case they return a new
factory, or on a 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.
Apply this to our example with Java classes:DynamicObjectFactorysetKlass(DynamicObjectFactoryfactory, JavaClass value); void setKlass(DynamicObjectobject, JavaClass value);
javaClassObject = JavaObjectImpl.INSTANCE.createJavaObject(
JavaObjectImpl.INSTANCE.createJavaObjectShape(null),
defaultHashCode());
JavaObjectImpl.INSTANCE.setKlass(javaClassObject, javaClassObject);
@Layoutinterface BaseLayout {DynamicObjectcreateBase(int a); boolean isBase(DynamicObjectobject); int getA(DynamicObjectobject); void setA(DynamicObjectobject, int value); } @Layoutinterface SuperLayout extends BaseLayout {DynamicObjectcreateSuper(int a, int b); int getB(DynamicObjectobject); void setB(DynamicObjectobject, int value); }
DynamicObject object = SuperImpl.INSTANCE.createSuper(14, 2);
BaseImpl.INSTANCE.isBase(object);
BaseImpl.INSTANCE.getA(object);
ObjectType subclasses internally. The default base class
that is inherited from is simply ObjectType. You can change this with the
Layout.objectTypeSuperclass() property on the Layout annotation.
IntToLong and IntToDouble implicit cast flags can be set in the generated layout
by setting Layout.implicitCastIntToLong() or Layout.implicitCastIntToDouble(). This can only be
done in base layouts, not subclassed layouts.
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.
@Layoutinterface CustomIdentifierLayout { public static finalStringA_IDENTIFIER = "A";DynamicObjectcreateCustomIdentifier(int a); }
| Modifier and Type | Optional Element and Description |
|---|---|
Class<?> |
dispatch
Generate override of
ObjectType.dispatch() method in the generated inner *Type class. |
boolean |
implicitCastIntToDouble |
boolean |
implicitCastIntToLong |
Class<? extends ObjectType> |
objectTypeSuperclass |
public abstract Class<? extends ObjectType> objectTypeSuperclass
public abstract Class<?> dispatch
ObjectType.dispatch() method in the generated inner *Type class.