Advanced Syntax (Bytecode)
A brief introduction to writing advanced syntax using Java Virtual Machine bytecode, for maximum efficiency and greater control.
Advantages
Syntax written using the bytecode assember directly can be more efficient than using a handler method.
For small or commonly-repeated tasks, a significant amount of overhead can be reduced by doing the operation directly.
Library developers have much greater control over how tasks are run.
Write Operation Structure
If more complex behaviour is required than a method invocation, syntax can interact directly with the assembler, which uses Foundation for accessibility.
There are two entry-points for accessing this, during the first and second pass.
ByteSkript's compiler runs in two orders.
The first pass works outer -> inner, left-to-right.
The line print 1 + 2
would be run in:
print %Object%
%Object% + %Object%
1 (literal)
2 (literal)
The second pass works inner -> outer, left-to-right.
The line print 1 + 1
would be run in:
1 (literal)
2 (literal)
%Object% + %Object%
print %Object%
Almost all instruction assembly will be done on the second pass: this follows the natural order of bytecode instructions. The first pass is typically used for providing lookahead information to inputs, when special behaviour is required.
E.g. the set ...
effect uses the first pass to tell the first input expression to use the SET
handler.
The first pass uses the preCompile
method, and the second pass uses the compile
method.
If we wanted to implement special behaviour, we can override this.
In this example we are pushing a null
value onto the stack ('returning' it from our expression.)
The Context
provides a lot of information about what's going on at this exact point in the script, giving us access to variables available, the programmatic flow tree (if/elses, loops, etc.) and a lot more.
This is also how we access the method assembler where the code is being written, using getMethod
.
The writeCode
instruction puts a Foundation WriteInstruction
into the assembler. During the final compilation (done after the entire script has been parsed), this will write the bytecode.
The pushNull
write instruction pushes an aconst_null
(null) value onto the stack, making it available for whatever is using this expression.
At the end of our expression we need to tell the compiler we're in a CompileState.STATEMENT
state, so it knows what to look for next. While this is default, it is important in case an inner expression has changed this for some reason.
For very advanced users, bytecode can be written directly with ASM in this compile method.
This is not recommended unless you are experienced with the bytecode layout and the Virtual Machine instruction set.
This is provided for developers who wish to extend or modify the Skript language at a fundamental level, such as by adding entirely new constructs or features.
Last updated