Skip to content

CTarget

Lenni0451 edited this page Mar 17, 2024 · 1 revision

The CTarget annotation specifies the position in the method to inject into.
The value field of the annotation specifies the type of the target. Depending on the type, the target field has to be used to provide more information on the target.
By default, the shift field is set to AFTER which means the injection will be performed after the target, but it can be changed to BEFORE which is the default behavior of Mixins.
The ordinal allows choosing the target if multiple targets are available. If the ordinal is not set, all targets will be used.
A target can be made optional by setting the optional field to true. If the target is not found, the injection will be skipped instead of throwing an exception.

Built-in types

The following types are built-in:

Type Target Supported shifts Target information
HEAD The first instruction in the method BEFORE
RETURN All return statements BEFORE
TAIL The last return statement BEFORE
THROW All throw statements BEFORE
INVOKE All method invocations BEFORE, AFTER Method Target
FIELD All field accesses BEFORE, AFTER Field Target
GETFIELD All field reads BEFORE, AFTER Field Target
PUTFIELD All field writes BEFORE, AFTER Field Target
NEW All object instantiations BEFORE, AFTER Method Target/Descriptor/Class name
OPCODE All instructions BEFORE, AFTER Opcode name or value
CONSTANT null/int/long/float/double/String/Type constant BEFORE, AFTER Constant value

Additional information about the built-in types:

New type

The NEW type allows matching all object instantiations.
Examples:

//Class name with slashes
@CTarget(value = "NEW", target = "java/lang/String")
@CTarget(value = "NEW", target = "Ljava/lang/String;")
//Method target
@CTarget(value = "NEW", target = "java/lang/String.<init>(Ljava/lang/String;)V")
@CTarget(value = "NEW", target = "Ljava/lang/String;<init>(Ljava/lang/String;)V")
//Descriptor
@CTarget(value = "NEW", target = "(Ljava/lang/String;)V") //Be aware that this does not match the owner!
//Specific constructor
//The return type is the class name
@CTarget(value = "NEW", target = "(Ljava/lang/String;)Ljava/lang/String;")
Opcodes type

The OPCODE type allows matching all instructions with a specific opcode.
Examples:

@CTarget(value = "OPCODE", target = "INVOKESTATIC")
@CTarget(value = "OPCODE", target = "184")

All Opcodes can be found in the Opcodes class in the ASM library.

Constant type

The CONSTANT type allows matching all ldc instructions with a specific constant value.
Syntax for the target field:

Type Syntax Example
null null null
int int <constant> int 1
long long <constant> long 2
float float <constant> float 3
double double <constant> double 4
String string <text> string Hello World
Type type <descriptor> type Ljava/lang/String;

Custom types

Custom types can be created by implementing the IInjectionTarget interface.
The List<AbstractInsnNode> getTargets(final Map<String, IInjectionTarget> injectionTargets, final MethodNode method, final CTarget target, final CSlice slice) method needs to be implemented and should return a list of instructions that match the target.

The injectionTargets parameter contains all available injection targets.
The method parameter is the method that should be targeted.
The target parameter is the target annotation which contains the target information.
The slice parameter is the slice annotation which contains the slice information (optional).

Example

//Target all INVOKESTATIC instructions
@CTarget(value = "OPCODE", target = "INVOKESTATIC")

//Target the second null constant
@CTarget(value = "CONSTANT", target = "null", ordinal = 1)

//Target the first String.equals() call
@CTarget(value = "INVOKE", target = "java/lang/String.equals(Ljava/lang/Object;)Z", ordinal = 0)

//Target the last return statement
@CTarget(value = "TAIL")

//Target the first field access
@CTarget(value = "FIELD", target = "java/lang/String.length:I", ordinal = 0)