module lang::flybytes::Syntax
Usage
import lang::flybytes::Syntax;
Source code
http://github.com/usethesource/flybytes/blob/main/src/lang/flybytes/Syntax.rsc
Dependencies
import List;
data Class
The top-level compilation unit for flybytes is the class or the interface.
data Class (list[Annotation] annotations = [], loc src = |unknown:///|)
= class(Type \type /* object(str name) */,
set[Modifier] modifiers = {\public()},
Type super = object(),
list[Type] interfaces = [],
list[Field] fields = [],
list[Method] methods = []
//list[Class] children = [],
)
| interface(Type \type /* object(str name) */,
list[Type] interfaces = [],
list[Field] fields = [],
list[Method] methods = []
)
;
data Modifier
data Modifier
= \public()
| \private()
| \protected()
| \friendly()
| \static()
| \final()
| \synchronized()
| \abstract()
;
data Field
Class fields members have optional initialization expressions.
data Field (list[Annotation] annotations = [], set[Modifier] modifiers = {\private()}, loc src=|unknown:///|)
= field(Type \type, str name, Exp init = defVal(\type))
;
data Method
Four kinds of methods can be generated.
data Method (list[Annotation] annotations = [], loc src=|unknown:///|)
= method(Signature desc, list[Formal] formals, list[Stat] block, set[Modifier] modifiers = {\public()})
| procedure(Signature desc, list[Formal] formals, list[Instruction] instructions, set[Modifier] modifiers = {\public()})
| method(Signature desc, set[Modifier] modifiers={\abstract(), \public()})
| static(list[Stat] block)
;
procedure
is a special extension since it contains only JVM bytecode instructions as a body, instead of high-level programming constructs like expressions and statements.- one alternative of
method
is abstract methods, which do not have a body, the other is methods - static blocks are also modeled as methods but without a signature.
- normal static methods are methods with a static Modifier.
The src
field is important for stack traces and other debugging features.
function method
Shorthand for directly producing a method with its signature and code block.
Method method(Modifier access, Type ret, str name, list[Formal] formals, list[Stat] block)
data Signature
Method and constructor signatures.
data Signature
= methodDesc(Type \return, str name, list[Type] formals)
| constructorDesc(list[Type] formals)
;
data Type
JVM type symbols.
data Type
= byte()
| boolean()
| short()
| character()
| integer()
| float()
| double()
| long()
| object(str name)
| array(Type arg)
| \void()
| string()
;
data Annotation
data Annotation (RetentionPolicy retention=runtime())
= \anno(str annoClass, Type \type, value val, str name = "value")
| \tag(str annoClass)
;
data RetentionPolicy
data RetentionPolicy
= class()
| runtime()
| source()
;
data Formal
Optional init expressions will be used at run-time if null
is passed as actual parameter.
data Formal
= var(Type \type, str name, Exp init = defVal(\type))
;
data Stat
Structured programming with statements, OO primitives like new
, and JVM monitor blocks and breakpoints.
data Stat (loc src = |unknown:///|)
= \store(str name, Exp \value)
| \decl(Type \type, str name, Exp init = defVal(\type))
| \astore(Exp array, Exp index, Exp arg)
| \do(Exp exp)
| \return()
| \return(Exp arg)
| \putField(Type class, Exp receiver, Type \type, str name, Exp arg)
| \putStatic(Type class, str name, Type \type, Exp arg)
| \if(Exp condition, list[Stat] thenBlock)
| \if(Exp condition, list[Stat] thenBlock, list[Stat] elseBlock)
| \for(list[Stat] init,
Exp condition,
list[Stat] next,
list[Stat] statements, str label = "")
| \block(list[Stat] block, str label = "")
| \break(str label = "")
| \continue(str label = "")
| \while(Exp condition, list[Stat] block, str label = "")
| \doWhile(list[Stat] block, Exp condition, str label = "")
| \throw(Exp arg)
| \monitor(Exp arg, list[Stat] block)
| \acquire(Exp arg)
| \release(Exp arg)
| \try(list[Stat] block, list[Handler] \catch)
| \switch(Exp arg, list[Case] cases, SwitchOption option = lookup(/*for best performance on current JVMs*/))
| invokeSuper(Signature desc, list[Exp] args)
| \asm(list[Instruction] instructions)
;
invokeSuper
is typically only used as the first statement of a constructormonitor
guarantees release of the lock in case of exceptions, break and continue that break out of the block, but only ifrelease
andacquire
are not used on the same lock object anywhere.asm
allows for inling raw bytecode instructions, but please understand that the monitor blocks' semantics can be broken by jumping out of the block unseen.
data SwitchOption
data SwitchOption
= table()
| lookup()
| auto()
;
data Case
data Case
= \case(int key, list[Stat] block)
| \default(list[Stat] block)
;
data Handler
data Handler
= \catch(Type \type, str name, list[Stat] block)
| \finally(list[Stat] block)
;
data Exp
JVM bytecode instructions lifted to the expression level.
data Exp (loc src = |unknown:///|)
= null()
| \true()
| \false()
| load(str name)
| aload(Exp array, Exp index)
| \const(Type \type, value constant)
| sblock(list[Stat] statements, Exp arg)
| invokeStatic(Type class, Signature desc, list[Exp] args)
| invokeSpecial(Type class, Exp receiver, Signature desc, list[Exp] args)
| invokeVirtual(Type class, Exp receiver, Signature desc, list[Exp] args)
| invokeInterface(Type class, Exp receiver, Signature desc, list[Exp] args)
| invokeDynamic(BootstrapCall handle, Signature desc, list[Exp] args)
| newInstance(Type class, Signature desc, list[Exp] args)
| getField(Type class, Exp receiver, Type \type, str name)
| getStatic(Type class, Type \type, str name)
| instanceof(Exp arg, Type class)
| eq(Exp lhs, Exp rhs)
| ne(Exp lhs, Exp rhs)
| le(Exp lhs, Exp rhs)
| gt(Exp lhs, Exp rhs)
| ge(Exp lhs, Exp rhs)
| lt(Exp lhs, Exp rhs)
| newArray(Type \type, Exp size)
| newInitArray(Type \type, list[Exp] args)
| alength(Exp arg)
| checkcast(Exp arg, Type \type)
| coerce(Type from, Type to, Exp arg)
| shr(Exp lhs, Exp shift)
| shl(Exp lhs, Exp shift)
| ushr(Exp lhs, Exp shift)
| and(Exp lhs, Exp rhs)
| sand(Exp lhs, Exp rhs)
| or(Exp lhs, Exp rhs)
| sor(Exp lhs, Exp rhs)
| xor(Exp lhs, Exp rhs)
| add(Exp lhs, Exp rhs)
| sub(Exp lhs, Exp rhs)
| div(Exp lhs, Exp rhs)
| rem(Exp lhs, Exp rhs)
| mul(Exp lhs, Exp rhs)
| neg(Exp arg)
| inc(str name, int inc)
| cond(Exp condition, Exp thenExp, Exp elseExp)
;
The Exp
syntax allows for expression all JVM bytecode expression stackmachine operations but with a nested recursive
syntax that is closer to programming languages.
- with
load
, name resolution is limited to local variables and parameter names. - method invocation always requires the full method signature.
- field access and update also require the full class name and field name.
- about invokving methods:
invokeStatic
must be used when invoking methods with thestatic
modifier.invokeSpecial
can be used when no dynamic dispatch is needed, or searching superclasses is required, and you know which class implements the method. Use this to invoke a method for efficiency's sake. The invocation is type-checked at class load time.invokeVirtual
should be used if you do need dynamic dispatch, or the method is implemented in a superclass, and this is not a default method of an interface, use this invocation method. You need to be sure the method exists somewhere reachable from the \class reference type. The invocation is type-checked at class load time.invokeInterface
is for invoking methods you know only from interfaces, such as default methods. The method can even be absent at runtime in which case this throws a RuntimeException. The type-check occurs at the first invocation at run-time.invokeDynamic
is very special. It generates a new call site at run-time using a static "bootstrap" method, then caches the site and invokes it. Depending on the semantics of the bootstrap method the call site has the samen semantics or a different semantics the next time it is invoked.- The first type in
desc
of `invokeDynamic must be the receiver type if the method is not static. - The first argument in
args
is then also the receiver itself and not one of the method's arguments.
- The first type in
data Instruction
The JVM low-level instruction set.
data Instruction
= LABEL(str label)
| LINENUMBER(int line, str label)
| LOCALVARIABLE(str name, Type \type, str \start, str end, int var)
| TRYCATCH(Type \type, str \start, str end, str handler)
| NOP()
| ACONST_NULL()
| ICONST_M1()
| ICONST_0()
| ICONST_1()
| ICONST_2()
| ICONST_3()
| ICONST_4()
| ICONST_5()
| LCONST_0()
| LCONST_1()
| FCONST_0()
| FCONST_1()
| FCONST_2()
| DCONST_0()
| DCONST_1()
| IALOAD()
| LALOAD()
| FALOAD()
| DALOAD()
| AALOAD()
| BALOAD()
| CALOAD()
| SALOAD()
| IASTORE()
| LASTORE()
| FASTORE()
| DASTORE()
| AASTORE()
| BASTORE()
| CASTORE()
| SASTORE()
| POP()
| POP2()
| DUP()
| DUP_X1()
| DUP_X2()
| DUP2()
| DUP2_X1()
| DUP2_X2()
| SWAP()
| IADD()
| LADD()
| FADD()
| DADD()
| ISUB()
| LSUB()
| FSUB()
| DSUB()
| IMUL()
| LMUL()
| FMUL()
| DMUL()
| IDIV()
| LDIV()
| FDIV()
| DDIV()
| IREM()
| LREM()
| FREM()
| DREM()
| INEG()
| LNEG()
| FNEG()
| DNEG()
| ISHL()
| LSHL()
| ISHR()
| LSHR()
| IUSHR()
| LUSHR()
| IAND()
| LAND()
| IOR()
| LOR()
| IXOR()
| LXOR()
| I2L()
| I2F()
| I2D()
| L2I()
| L2F()
| L2D()
| F2I()
| F2L()
| F2D()
| D2I()
| D2L()
| D2F()
| I2B()
| I2C()
| I2S()
| LCMP()
| FCMPL()
| FCMPG()
| DCMPL()
| DCMPG()
| IRETURN()
| LRETURN()
| FRETURN()
| DRETURN()
| ARETURN()
| RETURN()
| ARRAYLENGTH()
| ATHROW()
| MONITORENTER()
| MONITOREXIT()
| ILOAD(int var)
| LLOAD(int var)
| FLOAD(int var)
| DLOAD(int var)
| ALOAD(int var)
| ISTORE(int var)
| LSTORE(int var)
| FSTORE(int var)
| DSTORE(int var)
| ASTORE(int var)
| RET(int var)
| BIPUSH(int val)
| SIPUSH(int val)
| NEWARRAY(Type element)
| LDC(Type \type, value constant)
| IINC(int var, int inc)
| IFEQ(str label)
| IFNE(str label)
| IFLT(str label)
| IFGE(str label)
| IFGT(str label)
| IFLE(str label)
| IF_ICMPEQ(str label)
| IF_ICMPNE(str label)
| IF_ICMPLT(str label)
| IF_ICMPGE(str label)
| IF_ICMPGT(str label)
| IF_ICMPLE(str label)
| IF_ACMPEQ(str label)
| IF_ACMPNE(str label)
| GOTO(str label)
| JSR(str label)
| IFNULL(str label)
| IFNONNULL(str label)
| TABLESWITCH(int min, int max, str defaultLabel, list[str] labels)
| LOOKUPSWITCH(str defaultLabel, list[int] keys, list[str] labels)
| GETSTATIC(Type class, str name, Type \type)
| PUTSTATIC(Type class, str name, Type \type)
| GETFIELD(Type class, str name, Type \type)
| PUTFIELD(Type class, str name, Type \type)
| INVOKEVIRTUAL(Type class, Signature desc, bool isInterface)
| INVOKESPECIAL(Type class, Signature desc, bool isInterface)
| INVOKESTATIC(Type class, Signature desc, bool isInterface)
| INVOKEINTERFACE(Type class, Signature desc, bool isInterface)
| INVOKEDYNAMIC(Signature desc, BootstrapCall handle)
| NEW(Type \type)
| ANEWARRAY(Type \type)
| CHECKCAST(Type \type)
| INSTANCEOF(Type \type)
| MULTIANEWARRAY(Type \type, int numDimensions)
| exp(Exp expression, str label="")
| stat(Stat statement, str label="")
;
function defVal
Exp defVal(boolean())
Exp defVal(integer())
Exp defVal(long())
Exp defVal(byte())
Exp defVal(character())
Exp defVal(short())
Exp defVal(float())
Exp defVal(double())
Exp defVal(object(str _))
Exp defVal(array(Type _))
Exp defVal(string())
function object
Object is the top of the JVMs type system.
Type object()
function incr
Generates name+=i;
.
Stat incr(str name, int i)
function invokeSuper
Generates super(f1, f2); for a given anonymous constructor of type (F1 f1, F2 f2)
.
Stat invokeSuper(list[Type] formals, list[Exp] args)
function invokeSuper
Generates super();
.
Stat invokeSuper()
function main
Generates a main method public static final void main(String[] args) { block }
.
Method main(str args, list[Stat] block)
function method
Short-hand for generating a normal method.
Method method(Modifier access, Type ret, str name, list[Formal] args, list[Stat] block)
function method
Short-hand for generating a normal public method.
Method method(Type ret, str name, list[Formal] args, list[Stat] block)
function staticMethod
Short-hand for generating a static method.
Method staticMethod(Modifier access, Type ret, str name, list[Formal] args, list[Stat] block)
function staticMethod
Short-hand for generating a public static method.
Method staticMethod(Type ret, str name, list[Formal] args, list[Stat] block)
function constructor
Short-hand for generating a constructor.
Method constructor(Modifier access, list[Formal] formals, list[Stat] block)
Pitfalls
Don't forgot to generate a super call.
function new
"new" short-hand with parameters.
Exp new(Type class, list[Type] argTypes, list[Exp] args)
function new
"new" short-hand, without parameters.
Exp new(Type class)
function this
Load the standard "this" reference for every object.
Exp this()
Pitfalls
This works only inside non-static methods and inside constructors
function current
The <current>
class refers to the class currently being compiled, for convenience's sake.
Type current()
function getField
Load a field from the currently compiled class.
Exp getField(Type \type, str name)
function getStatic
Load a static field from the currently compiled class.
Exp getStatic(Type \type, str name)
function putField
Store a field in the currently compiled class.
Stat putField(Type \type, str name, Exp arg)
function putStatic
Store a static field in the currently defined class.
Stat putStatic(Type \type, str name, Exp arg)
function invokeStatic
Invoke a static method on the currently defined class.
Exp invokeStatic(Signature desc, list[Exp] args)
function invokeSpecial
Invoke a method on the currently defined class using invokeSpecial.
Exp invokeSpecial(Exp receiver, Signature desc, list[Exp] args)
function invokeVirtual
Invoke a method on the currently defined class using invokeVirtual.
Exp invokeVirtual(Exp receiver, Signature desc, list[Exp] args)
function invokeInterface
Invoke a method on the currently defined interface using invokeInterface.
Exp invokeInterface(Exp receiver, Signature desc, list[Exp] args)
function iconst
Exp iconst(int i)
function sconst
Exp sconst(int i)
function bconst
Exp bconst(int i)
function cconst
Exp cconst(int i)
function zconst
Exp zconst(bool i)
function jconst
Exp jconst(int i)
function sconst
Exp sconst(str i)
function dconst
Exp dconst(real i)
function fconst
Exp fconst(real i)
data BootstrapCall
A bootstrap handle is a name of a static method (as defined by its host class, its name and its type signature), and a list of "constant" arguments.
data BootstrapCall
= bootstrap(Type class, Signature desc, list[CallSiteInfo] args)
;
These constant arguments can be used to declare properties of the call site which can then be used by the bootstrap method to define in which way the dynamic call must be resolved. So these argument help to avoid having to define a combinatorially large number of bootstrap methods (one for each call site situation).
It's advisable to use the convenience function below to create a BootstrapCall
instance:
bootstrap(Type class, str name, list[BootstrapInfo] args)
That function makes sure to line up the additional information in the extra arguments about the call site with the static type of the static bootstrap method.
function bootstrap
Generate a bootstrap call with all the required standard parameters, and optionally more.
BootstrapCall bootstrap(Type class, str name, list[CallSiteInfo] args)
Benefits
- A raw BootstrapCall must return a CallSite and take a MethodHandle.Lookup, a string and a MethodType as the first three parameters.
This convenience function guarantees that this the true, but allows for adding additional static information about the call site. - The types of the additional parameters are inferred automatically from the CallSiteInfo structures, so you do not have to distribute this information during code generation.
Pitfalls
- the signature of the bootstrap method
name
inclass
(which you might have written in Java, or generated in a different part of your compiler) must be the exactly the same as generated here.
function bootstrap
Generate a basic bootstrap caller with only the minimally required information for a dynamic invoke.
BootstrapCall bootstrap(str name, list[CallSiteInfo] args)
Benefits
This is the starting point for any use of invokeDynamic. Get this working first and add additional information later
Pitfalls
Writing bootstrap method implementations is hard.
function bootstrapMethod
Convenience function to use existing BootstrapCall information to generate a fitting bootstrap method to call.
Method bootstrapMethod(BootstrapCall b, list[Stat] body)
This function mirrors the bootstrap
function; it generates the method referenced by the output of that function.
Benefits
- the bootstrap method and the bootstrapCall information have to be aligned perfectly. If you use the pair
of functions
bootstrapMethod
andbootstrap
together then this alignment is guaranteed.
Pitfalls
- generating the body of a bootstrap method can be challenging. It is recommended to first write the example
in Java and decompile the resulting method, and copy the result into the
body
parameter of this function. - another solution is to keep the source of the bootstrap method completely in Java, but making sure the signatures keep matching inside the BootstrapCall data. This is usually possible; when the bootstrap method is generic enough.
data CallSiteInfo
data CallSiteInfo
= stringInfo(str s)
| classInfo(str name)
| integerInfo(int i)
| longInfo(int l)
| floatInfo(real f)
| doubleInfo(real d)
| methodTypeInfo(Signature desc)
| virtualHandle(Type class, str name, Signature desc)
| specialHandle(Type class, str name, Signature desc, Type caller)
| getterHandle(Type class, str name, Type \type)
| setterHandle(Type class, str name, Type \type)
| staticGetterHandle(Type class, str name, Type \type)
| staticSetterHandle(Type class, str name, Type \type)
| constructorHandle(Type class, Signature desc)
;
function callsiteInfoType
Type inference for callsiteInfo parameters to bootstrap methods.
Type callsiteInfoType(stringInfo(_))
Type callsiteInfoType(classInfo(_))
Type callsiteInfoType(integerInfo(_))
Type callsiteInfoType(longInfo(_))
Type callsiteInfoType(floatInfo(_))
Type callsiteInfoType(doubleInfo(_))
Type callsiteInfoType(virtualHandle(_,_,_))
Type callsiteInfoType(specialHandle(_,_,_,_))
Type callsiteInfoType(getterHandle(_,_,_))
Type callsiteInfoType(setterHandle(_,_,_))
Type callsiteInfoType(staticGetterHandle(_,_,_))
Type callsiteInfoType(staticSetterHandle(_,_,_))
Type callsiteInfoType(constructorHandle(_,_))
Type callsiteInfoType(methodTypeInfo(_))