-
Notifications
You must be signed in to change notification settings - Fork 24
Expression Language
LlamaLad7 edited this page Jul 28, 2025
·
5 revisions
'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
@ModifyExpressionValuethem and you can@WrapOperationthem - For all types except
doubles andfloats, 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 -
.lengthon 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
@Definitionfor 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
thisthat 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