Skip to content
Aki edited this page Apr 10, 2019 · 5 revisions

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

Maven-plugin

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.

The Runtime

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.

Getters and Setters

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.

Boxing

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.