Skip to content

Expression Language

LlamaLad7 edited this page Aug 18, 2024 · 3 revisions

Full documentation for the language used inside @Expressions.

This feature is in beta. It may be changed in a future release and may cause unforeseen issues in its current state. Make sure you are using the latest beta.

Literals

'hello' // String
'x' // String or char
23 // int or long
0xFF // int or long
1.5 // float or double
true // boolean (or int, be careful)
null

Unary expressions

-x
~x
  • Note: a bitwise not is indistinguishable from x ^ -1, so it will match both

Binary expressions

a * b
a / b
a % b
a + b
a - b
a << b
a >> b
a >>> b
a & b
a ^ b
a | b
  • Precedences match java where applicable
  • & and | are bitwise, there is no way to match logical && or || (or !)

Comparisons

a == b
a != b
a < b
a <= b
a > b
a >= b
  • Comparisons must be top-level
  • You can @ModifyExpressionValue them and you can @WrapOperation them
  • For all types except doubles and floats, comparisons in bytecode cannot be distinguished from their inverted counterparts, so you must be careful that your expression is specific enough to avoid this issue

Identifier expressions

someLocal // load
someLocal = someValue // store
SOME_STATIC_FIELD // get
SOME_STATIC_FIELD = someValue // put
  • The identifier must be defined in a @Definition

Member expressions

x.someField // get
x.someField = someValue // put
  • The field's identifier must be defined in a @Definition
  • .length on arrays is built-in

Method calls

x.someMethod() // no arguments
x.someMethod(x) // 1 argument
x.someMethod(x, y) // 2 arguments
...
staticMethod() // no arguments
staticMethod(x) // 1 argument
  • The method's identifier must be defined in a @Definition

Wildcards

?
someObject.?
someObject.?(someArg)
  • Can either replace an entire expression or an identifier
  • Used for brevity or if it would be brittle to add a @Definition for the thing, e.g. with some locals
  • Can also match expressions which there is no way to explicitly specify, e.g. those involving jumps

Array operations

someArray[someIndex] // load
someArray[someIndex] = someValue // store
  • These have special support in @WrapOperation

Array creations

new SomeType[]{someValue, someOtherValue, aThirdValue} // filled array creation
new SomeType[someLength] // empty array creation
new SomeType[3][4][5] // multi-dimensional array creation
  • The type's identifier must be defined in a @Definition

Casts

(SomeType) someExpression
  • The type's identifier must be defined in a @Definition
  • Primitive casts are built-in, e.g. (float) x + y

Instanceof

x instanceof SomeType
  • The type's identifier must be defined in a @Definition

Instantiations

new SomeType() // no arguments
new SomeType(x, y) // 2 arguments
...
  • The type's identifier must be defined in a @Definition

Method references, constructor references and lambdas

::someMethod // unbound reference
someReceiver::someMethod // bound reference
SomeType::new // constructor reference

::someLambda // the lambda is considered unbound if it doesn't access `this`, i.e. its implementation is static
this::someLambda // if the lambda does access `this`, it is considered bound
  • Method references and lambdas are treated the same way, and must be appropriately defined in a @Definition
  • Lambdas that capture other local variables are treated as normal, it is only the capture of this that is important
  • Unbound references can match both non-static and static methods
  • Bound references obviously can only match non-static methods, since a receiver is required

Returns and throws

return someExpression
throw someException

Targets

@(someExpression)
// E.g.:
someMethod(@(new SomeType()))
this.something + @(this.somethingElse)
this.someMethod(@(value1), @(value2))
  • Each thing you target will be modified by the injector
  • If you don't target anything explicitly then the entire expression is implicitly targeted