Skip to content

@Inject without object allocation #716

Description

@mega12345mega

When injecting into methods that get called a lot (in my case hundreds of thousands of times per second), a large number of CallbackInfo or CallbackInfoReturnable instances get created. Here is an example of this issue getting reported to a different project I found when researching this: FTBTeam/FTB-Mods-Issues#2012

Ideally, it would be possible to inject code that can return without allocating objects in the process. I have a couple of ideas on how this could be implemented:

Cache CallbackInfo[Returnable] objects for each thread

Mixin:

@Inject(method = "targetMethod", at = @At("HEAD"), cancellable = true, highTraffic = true) // highTraffic indicates caching should be used
private void injected(int arg, CallbackInfoReturnable<String> info) {
    if (Math.random() < 0.5)
        info.setReturnValue("foo");
}

Target:

+ private static final ThreadLocal cache_targetMethod_injected = ThreadLocal.withInitial(() -> new CallbackInfoReturnable<>("targetMethod", true));
  public String targetMethod(int arg) {
+     CallbackInfoReturnable<String> info = cache_targetMethod_injected.get();
+     info.reset(); // Sets cancelled to false, return value to null
+     injected(arg, info);
+     if (info.isCancelled())
+         return info.getReturnValue();
      ...
  }

Actually return from mixin method

Mixin:

@InjectInline(method = "targetMethod", at = @At("HEAD"))
private String injected(int arg) {
    if (Math.random() < 0.5)
        return "foo";

    // This should be stripped out of the bytecode before being injected
    // Can only show up at the end of the mixin method
    throw InjectInline.DONT_RETURN;

    // Another option
    return InjectInline.DontReturn.forReference(); // non-void methods
    InjectInline.DontReturn.forVoid(); // void methods
}

Target:

  public String targetMethod(int arg) {
+     if (Math.random() < 0.5) // Bytecode is injected into the target method directly
+         return "foo";
+     // throw is removed
      ...
  }

InjectInline:

public @interface InjectInline {
    public static final RuntimeException DONT_RETURN = null;

    // Alternatively
    public static class DontReturn {
        public static <T> T forReference() {
            return null;
        }
        public static void forVoid() {}
    }

    ...
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions