Callback Injection: @Inject
This annotation lets you place custom code at a specific point inside an existing method.
Annotation Source
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
public String id() default "";
public String[] method() default {};
public Desc[] target() default {};
public Slice[] slice() default {};
public At[] at();
public boolean cancellable() default false;
public LocalCapture locals() default LocalCapture.NO_CAPTURE;
public boolean remap() default true;
public int require() default -1;
public int expect() default 1;
public int allow() default -1;
public String constraints() default "";
}
Usage
The basic injection template is as follows:
@Inject(method = "methodToInject", at = @At("injectionPointReference"))
private void injectMethod(METHOD ARGS, CallbackInfo info) {
//your code
}
To cancel or return early inside the method, use CallbackInfo.cancel(); or CallbackInfoReturnable<T>.setReturnValue(T);. Note that cancel() does not need to be called after setReturnValue(). In both cases, cancellable must be set to true in the injection annotation:
@Inject(method = "...", at = @At("..."), cancellable = true)
To inject into a constructor, use <init>()V as the method target, where () contains the constructor parameter descriptors. When injecting into a constructor, @At must be TAIL or RETURN; other injection points are not supported. Note that some classes have a method named init, which is different from <init> — don't confuse them!
To inject into a static initializer, use <clinit> as the method name.
Note: the method must be private
This annotation can also be used to modify return values, as shown below:
@Inject(method = "foo()I;", at = @At("RETURN"), cancellable = true)
private void injected(CallbackInfoReturnable<Integer> cir) {
cir.setReturnValue(cir.getReturnValue() * 3);
}