-
Notifications
You must be signed in to change notification settings - Fork 18
Expression Language
LlamaLad7 edited this page Aug 18, 2024
·
3 revisions
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.
'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
-x
~x
- Note: a bitwise not is indistinguishable from
x ^ -1
, so it will match both
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!
)
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
double
s andfloat
s, 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
someLocal // load
someLocal = someValue // store
SOME_STATIC_FIELD // get
SOME_STATIC_FIELD = someValue // put
- The identifier must be defined in a
@Definition
x.someField // get
x.someField = someValue // put
- The field's identifier must be defined in a
@Definition
-
.length
on arrays is built-in
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
?
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
someArray[someIndex] // load
someArray[someIndex] = someValue // store
- These have special support in
@WrapOperation
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
(SomeType) someExpression
- The type's identifier must be defined in a
@Definition
- Primitive casts are built-in, e.g.
(float) x + y
x instanceof SomeType
- The type's identifier must be defined in a
@Definition
new SomeType() // no arguments
new SomeType(x, y) // 2 arguments
...
- The type's identifier must be defined in a
@Definition
::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
return someExpression
throw someException
@(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