Mixin
Mixin copied to clipboard
Add InjectionPoint for after object initialization
It would be nice to have an injection point for after an object has been created, initialized, and stored.
Given Object object = new Object();
roughly compiles to:
NEW Ljava/lang/Object;
INVOKESPECIAL Ljava/lang/Object;<init>()V
ASTORE 1
this injection point would inject after the ASTORE
.
Potential Names:
-
INVOKE_INIT
-
NEW_INIT
-
NEW_ASSIGN
Doesn't an inject to an INVOKE_ASSIGN
for an <init>
method call suffice?
No, per mumfrey in discord:
INVOKE_ASSIGN is looking for a method call where the return value is assigned to a local
constructors don't return a value, the "value" is created by the NEW and just initialised by the ctor call
so INVOKE_ASSIGN isn't going to match that invocation in the first place since it returns void```
ASTORE 1
One problem I see here is that it's not always stored, it's possible the new object is not stored to the local variable table, and simply just provided on the stack, and immediately passed through another method invocation.
One way that I've already mentioned to work around this is with:
@Inject(method = "derp(I)V",
at = @At(value = "NEW", target = "java/lang/StringBuilder", shift = Shift.AFTER),
locals = LocalCapture.FAIL_HARD
)
private void derpStringBuilder(int arg, CallbackInfo ci, StringBuilder builder) {
}
I found an instance of the case @gabizou described:
(new Minecraft(var45)).run();
(This appears in net.minecraft.client.main.Main
in 1.8.9)
This example doesn't store it in a local variable:
L70 {
new net/minecraft/client/Minecraft
dup
aload45
invokespecial net/minecraft/client/Minecraft.<init>(Lnet/minecraft/client/main/GameConfiguration;)V
invokevirtual net/minecraft/client/Minecraft.run()V
}
I was trying to inject something between the <init>
and run
calls. (I ended up moving my code to inject into run
itself instead.) The workaround posted isn't working because I believe it is using the instance of Minecraft
before it can be passed to run
, and then there is no instance for run
to use.
The Mixin I was trying
@Inject(method = "main([Ljava/lang/String;)V",
at = @At(value = "NEW", target = "net/minecraft/client/Minecraft", shift = At.Shift.BY, by = 3),
locals = LocalCapture.CAPTURE_FAILHARD)
private static void impl$mainMc(String[] args, CallbackInfo ci, Minecraft instance) {
System.out.println("Got Minecraft instance!!!\n!!!!!\n!!!\n" + instance.mcDataDir.getAbsolutePath());
}
Critical injection failure: LVT in net/minecraft/client/main/Main::main([Ljava/lang/String;)V has incompatible changes at opcode 600 in callback net/minecraft/client/main/Main::impl$mainMc. Expected: [Lave;] Found: [Ljoptsimple/OptionParser;]
So I think if you want to capture the new instance as a local variable in your Mixin, then Mixin would need to insert a dup
instruction so the reference can stay on the stack.
@phase why are you shifting by 3? You would want to shift after the init to get the minecraft instance, or just inject at run with no shift and your injection would have the LVT along with the Minecraft instance.