Full documentation for the language used inside @Expression
s.
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
double
s and float
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
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