5 | Injections

How to use and apply the @Inject annotation to your code. Also tells you how to read and write all the parameters housed inside the annotation.

What is Inject?

The @Inject annotation injects code into a specified target method.

Parameters, in order:

  • All target method parameters

  • CallbackInfo (for void return type on target) or CallbackInfoReturnable<R>

Return type: void

Consider the following code:

 public void target() {
      Dummy.getInstance().dummy("foo");
 }

If we were to inject into it, we would use something like this:

@Inject(method = "target()V", at = @At(value = "INVOKE", target = "Lnet/example/Dummy;dummy(Ljava/lang/String;)V"))
private void mixin(CallbackInfo ci) {
    injectedCode();
}

It might be a little hard to understand at first, so lets break it down.

@Inject(
    method = "target()V",
    at = @At(value = "INVOKE", target = "Lnet/example/Dummy;dummy(Ljava/lang/String;)V")
)

The first parameter is method , it tells the mixin what method we're going to modify

Now for the @At statement:

  • value = "INVOKE"

    • This tells the mixin that we're looking for the invocation of the method.

  • target = "Lnet/example/Dummy;dummy()V")

    • This tells the mixin where the method we're modifying is located

Now, lets break down the syntax of "Lnet/example/Dummy;dummy()V"

The L represents a class name, so Lnet/example/Dummy; is the path to the class where the method dummy , is located.

The dummy represents the method name inside the Dummy class.

Inside the brackets, we see another class (Ljava/lang/String;), this is the path to the String class, as String is actually a class, just one in the standard library, which doesn't need to be imported, everything inside these brackets are the parameters for dummy

Just after the brackets there is a V, which simply means void specifying that the println function doesn't return any value

The table below shows the different characters with their different meanings.

FieldType term

Type

Interpretation

B

byte

signed byte

C

char

Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16

D

double

double-precision floating-point value

F

float

single-precision floating-point value

I

int

integer

J

long

long integer

L

ClassName;

reference an instance of class ClassName

S

short

signed short

Z

boolean

true or false

[

reference

one array dimension

Read more

Inject, cancelable

Consider the following code:

 public void target() {
      Dummy.getInstance().dummy("foo");
 }

Example mixin:

@Inject(
    method = "target()V", 
    at = @At(value = "INVOKE", 
    target = "Lnet/example/Dummy;dummy(Ljava/lang/String;)V"), 
    cancellable = true
)
private void mixin(CallbackInfo ci) {
    if (condition) {
        ci.cancel();
    }
}

Lets dissect this; First, we start off with defining our method, in this case, it's target() then we define the @At statement, which houses our value and our target.

Oh, but what's that? It's the cancellable statement!

The cancellable statement, can only be used at TAIL or RETURN . It's job is to inject code into a target method, and if ci.cancel is called, it returns after the mixin is done executing.

So, if we were to apply our mixin to our target() function, we would have a result similar, if not exactly like this:

 public void target() {
      Dummy.getInstance().dummy();
      {
          boolean canceled = false;
          if (condition) {
              canceled = true;
          }
          if (canceled) return;
     }
  }

Last updated

Was this helpful?