Skip to content

Injecting

Reflxction edited this page Jul 16, 2021 · 4 revisions

Injecting is a powerful feature that is intended to solve the drawbacks of overwriting methods.

Sometimes, overwriting a method is an overkill, when we would like to only add a small line of code, or add an additional constraint to the method, without having to overwrite everything.

Another important use case is having multiple injects on the same method, without one of them dismissing the other.

Injection can happen at 5 places:

  • Beginning of a method: Just before anything happens in the method
  • Very end of the method: At the very end of the method (note: the end! not before each return statement)
  • Before each return statement: Before the method returns any result. This also includes any stop-all-the-execution point in the method.
  • Before a certain line number: In case of knowing the line number from the source code, we can inject code before that line
  • After a certain line number: In case of knowing the line number from the source code, we can inject code right after that line

In the last 2 cases, where a line number is to be specified, the lineNumber property has to be set in the @Inject annotation.

Let's say we have a fluffy, simple Animal class:

public class Animal {

    public String getSound(String animal) {
        switch (animal) {
            case "Wolf":
                return "Bark";
            case "Cat":
                return "Meow";
            case "Cow":
                return "MOO";
            default:
                return "*makes human sound*";
        }
    }
}

Injection is done using the @Inject annotation:

import io.tunabytes.Mirror;
import io.tunabytes.Inject;
import io.tunabytes.Inject.At;
import io.tunabytes.Mixin;

@Mixin(Animal.class)
public class AnimalMixin {

    @Inject(method = "getSound", at = At.BEFORE_EACH_RETURN)
    public void hearSomething() {
        System.out.println("I think I heard something");
    }
}
import io.tunabytes.bytecode.MixinsBootstrap;

public final class AnimalTest {

    public static void main(String[] args) {
        MixinsBootstrap.init();
        Animal animal = new Animal();
        System.out.println(animal.getSound("Cat"));
        System.out.println(animal.getSound("human thing"));
    }
}

Output:

I think I heard something
Meow
I think I heard something
*makes human noise*

Important note: Injected code will be directly translated into the targeted method, and not wrapped into an external method invocation. For example, any return in the injected code will translate into an actual return in the target method.

Another note: When injecting, it is quite possible that you may need to reference to the class fields or other methods. In that case, take a look at mirroring.

Clone this wiki locally