-
Notifications
You must be signed in to change notification settings - Fork 7
The Magic Behind
Linkstone consists of a maven plugin that processes your annotations and a runtime build into Glowstone that patches installed Bukkit plugins.
Annotation | Compile time | Runtime |
---|---|---|
@LPackage | X | |
@LClassfile | X | |
@LMethod | X | |
@LField | X | X |
@LBox | X | X |
@LDelegate | X |
The maven plugin lints wrong annotation usages and modifies the bytecode of your compiled classfiles.
The plugin targets one CraftBukkit version. Classes, Methods and Fields are filtered. Only those containing the targeted version in their annotation are left in the classfile. If their annotation contains a name attribute, they will be renamed in that step.
Everything that is not annotated is considered to be a utility for the CraftBukkit implementations and will not get filtered.
The Field getter and setter that match the targeted version are selected and renamed to $linkstone$getter$<fieldname>
or $linkstone$setter$<fieldname>
respectively. If an annotated field has no @LRedirect
annotation, a getter and setter that accesses/sets the field value will be generated. Therefore any annotated field will have getters and setters.
The Maven plugin also generates code for the @LDelegate
annotation.
Before Glowstone starts to load plugins, all plugin jars will get scanned. The runtime searches for @LField
annotated fields and classes with @LBox
annotations and provides a custom plugin loader that transform every plugin class before it gets loaded.
Reads and writes of fields with a @LField
annotation will get replaces against invokes of the corresponding getter and setter. Since getters and setters have been renamed by the maven-plugin, this is an easy task.
The runtime reflects on the java.lang.reflect.Field
instances of annotated fields and replaces the internal FieldAccessor
against an implementation that invokes the getter and setter instead.
This might not work on all JVM implementations. Especially since Java 9 cut the power of reflections this hack only works if the java.base
module was opened so there is a Fallback.
Uses of Field.get
, Field.set
and their specialized forms will get replaced against calls to the linkstone runtime. A runtime check determines whether the field has a @LField
annotation and invokes the getter and setter instead.
Another task of the runtime is the support of @LBox
annotations. Casts to classes with a @LBox
annotation will get replaced against a call to the runtime that initializes the box.
The runtime caches all box instances to make sure identity equal references are also identity equal in their boxed form. It's also ensured that objects will always get boxed in the optimal box. Let's say you've got an instance of a Pig and the runtime should box it in a AnimalBox, then runtime will decide to use a PigBox (a subclass of AnimalBox) instead.
Reflections are supported by reflecting on the java.lang.reflect.Method
and java.lang.reflect.Field
instances of @LBox
annotated classes. The internal MethodAccessors and FieldAccessors are replaced against implementations that box the instance before the method is invoked / the field is accessed.
Since this hack might not work on all JVMs there is a fallback. As already mentioned above, all Field.get
and Field.set
, but also all Method.invoke
invokes get replaced against calls to the linkstone runtime. The runtime checks whether the field/method is declared within a @LBox
annotated class. If so, the instance gets boxed before the actual reflective operation is executed.