@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.
|
Class<?> |
receiverType
The explicit receiver type to use if specified.
|
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(ReflectionLibrary.class)
final class ArrayFullWrapper {
final Object delegate;
ArrayFullWrapper(Object delegate) {
this.delegate = delegate;
}
@ExportMessage
final Object send(Message message, Object[] args,
@CachedLibrary("this.delegate") ReflectionLibrary lib)
throws Exception {
return lib.send(delegate, message, args);
}
@ExportMessage
final String toDisplayString() {
return "Wrapped";
}
}