@Retention(value=RUNTIME) @Target(value=TYPE) @Repeatable(value=ExportLibrary.Repeat.class) public @interface ExportLibrary
value
specifies the library class that is exported. If there are abstract methods specified by a
library then those messages need to be implemented. A receiver may export multiple libraries at
the same time, by specifying multiple export annotations. Subclasses of the receiver type inherit
all exported messages and may also be exported again. In this case the subclass overrides the
base class export.
@ExportLibrary(ArrayLibrary.class) static final class BufferArray { private int length; private int[] buffer; BufferArray(int length) { this.length = length; this.buffer = new int[length]; } @ExportMessage boolean isArray() { return true; } @ExportMessage int read(int index) { return buffer[index]; } }
specialization
then the export
must be specified as class. In this case the simple name of the message is resolved by using the
class name and turning the first character lower-case. So for an exported class named
Read
the message read
would be used. It is not allowed to use a method
export and a class export for the same message at the same time. Multiple ExportMessage
annotations may be used for the same method or class to export them for multiple messages. In
this case the message name
needs to be specified explicitly and the
target signatures need to match for all exported messages.
@ExportLibrary(value = ArrayLibrary.class) static final class SequenceArray { final int start; final int stride; final int length; SequenceArray(int start, int stride, int length) { this.start = start; this.stride = stride; this.length = length; } @ExportMessage boolean isArray() { return true; } @ExportMessage static class Read { @Specialization(guards = {"seq.stride == cachedStride", "seq.start == cachedStart"}, limit = "1") static int doSequenceCached(SequenceArray seq, int index, @Cached("seq.start") int cachedStart, @Cached("seq.stride") int cachedStride) { return cachedStart + cachedStride * index; } @Specialization(replaces = "doSequenceCached") static int doSequence(SequenceArray seq, int index) { return doSequenceCached(seq, index, seq.start, seq.stride); } } }
default exports
or types that support dynamic dispatch
the export may declare an explicit receiver type
.GenerateLibrary
,
CachedLibrary
Modifier and Type | Required Element and Description |
---|---|
Class<? extends Library> |
value
The library class that specifies the messages that are exported.
|
Modifier and Type | Optional Element and Description |
---|---|
String |
delegateTo
Automatically forwards all messages of the library which are not exported to the value of a
delegate field.
|
int |
priority
Specifies the priority for service provider lookup based default exports.
|
Class<?> |
receiverType
The explicit receiver type to use if specified.
|
String |
transitionLimit
By default export libraries don't allow changes in the behavior of accepts for a receiver
instance.
|
boolean |
useForAOT
Enables
AOT
preparation for this export. |
int |
useForAOTPriority
Specifies the priority for the export when used for AOT preparation.
|
public abstract Class<?> receiverType
default exports
or types that are
dynamically dispatched
. If specified, all exported methods
need to be declared statically with the receiver type argument as first parameter.
@ExportLibrary(value = ArrayLibrary.class, receiverType = Integer.class) static final class ScalarIntegerArray { @ExportMessage static boolean isArray(Integer receiver) { return true; } @ExportMessage int read(Integer receiver, int index) { if (index == 0) { return receiver; } else { throw new ArrayIndexOutOfBoundsException(index); } } }
public abstract String delegateTo
reflection proxies
.
The specified field name must link to a field in the specified receiver type
, or in the annotated type if no receiver type is specified. The field must
have the modifier final
. The specified field must be visible to the generated
code and therefore not private. The referenced field must not be static.
@GenerateLibrary public abstract class ArrayLibrary extends Library { public String toDisplayString(Object receiver) { return receiver.toString(); } public String otherMessage(Object receiver) { return "otherResult"; } }In the following wrapper all messages of ArrayLibrary will be forwarded to the value of the delegate field.
@ExportLibrary(value = ArrayLibrary.class, delegateTo = "delegate") final class ArrayDelegateWrapper { final Object delegate; ArrayDelegateWrapper(Object delegate) { this.delegate = delegate; } }In the following wrapper the toDisplayString will be re-exported and not delegated to the delegate field. All other messages of the ArrayLibrary are implicitly delegated to the value of the delegate field.
@ExportLibrary(value = ArrayLibrary.class, delegateTo = "delegate") final class ArrayOverrideWrapper { final Object delegate; ArrayOverrideWrapper(Object delegate) { this.delegate = delegate; } @ExportMessage final String toDisplayString() { return "Wrapped"; } }In the following wrapper the toDisplayString will be exported but forwards to the delegate manually adding brackets around the delegate value.
@ExportLibrary(value = ArrayLibrary.class, delegateTo = "delegate") final class ArrayManualDelegateWrapper { final Object delegate; ArrayManualDelegateWrapper(Object delegate) { this.delegate = delegate; } @ExportMessage final String toDisplayString( @CachedLibrary("this.delegate") ArrayLibrary arrayLibrary) { return "Wrapped[" + arrayLibrary.toDisplayString(delegate) + "]"; } }In the following wrapper the toDisplayString message is re-exported. Other messages of the ArrayLibrary are delegated as well as all messages of any other library that supports reflection.
@ExportLibrary(value = ArrayLibrary.class, delegateTo = "delegate") @ExportLibrary(value = ReflectionLibrary.class, delegateTo = "delegate") final class ArrayFullWrapper { final Object delegate; ArrayFullWrapper(Object delegate) { this.delegate = delegate; } @ExportMessage final String toDisplayString() { return "Wrapped"; } }
public abstract int priority
default
exports
, negative values lower than default exports. A priority equal to 0 is invalid.public abstract String transitionLimit
AssertionError
is thrown. If the
transition limit is set then the accepts condition is allowed to transition from
true
to false
for a library created for a receiver instance. The
limit expression specifies how many fallback library instances should be created until the
library is dispatching to uncached cases of the library. By default accepts transitions are
not allowed. Note this option is only relevant if you use a custom accepts implementation in
the export. If the receiver transitions in parallel then there are no guarantees provided.
The library caller is responsible to provide proper synchronization.
This feature is useful to implement runtime value representations that dynamically transition from one state to the next. With arrays, a common example is the access strategy that changes from sparse or dense arrays. Another use-case is the Truffle object model, where the shape should be used in the accepts condition, to common out the shape check, but at the same time the shape should be able to transition due to a property write.
The transition limit expression is allowed to access visible static fields or methods of the enclosing class. If the limit needs to be looked up from an option it is recommended to extract the option lookup in a static Java method.
Performance note: If any number of transitions is enabled, the accepts guard of this library effectively needs to be repeated on every message invocation of this export. It is therefore recommended to not set this property for performance reasons, if possible. It is also recommended to double check that the duplicated accepts guard for every message is eliminated in the compiler graphs after Partial evaluation.
Library.accepts(Object)
public abstract boolean useForAOT
AOT
preparation
for this export. An exported library must use the GenerateAOT
annotation
on the library class to allow exports to enable AOT. Multiple AOT exports may be specified
for each library. The library may be used for implicit and explicit receivers, dynamic
dispatch and default exports. Merged libraries are currently not supported for AOT enabled
exports. A priority
must be set in order to resolve a
deterministic order for AOT initialized exports.
All exported libraries with AOT enabled are loaded and initialized eagerly when a library
that supports AOT is used for the first time. In order for this to work the exported library
automatically generates subclass of EagerExportProvider
, that must be registered in
the module descriptor as a provided service. When the language is compiled using native-image
then this is not necessary as native-image compilation discovers all AOT exports for a
library during native image generation.
public abstract int useForAOTPriority