diff --git a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/ModifyArgInjector.java b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/ModifyArgInjector.java index d4805a90f..dd1639e1f 100644 --- a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/ModifyArgInjector.java +++ b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/ModifyArgInjector.java @@ -27,13 +27,13 @@ import java.util.Arrays; import java.util.List; -import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodInsnNode; import org.spongepowered.asm.mixin.injection.InjectionPoint; import org.spongepowered.asm.mixin.injection.InjectionPoint.RestrictTargetLevel; import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.invoke.util.InvokeUtil; import org.spongepowered.asm.mixin.injection.struct.InjectionInfo; import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode; import org.spongepowered.asm.mixin.injection.struct.Target; @@ -110,12 +110,8 @@ protected void inject(Target target, InjectionNode node) { @Override protected void injectAtInvoke(Target target, InjectionNode node) { MethodInsnNode methodNode = (MethodInsnNode)node.getCurrentTarget(); - Type[] originalArgs = Type.getArgumentTypes(((MethodInsnNode) node.getOriginalTarget()).desc); - Type[] currentArgs = Type.getArgumentTypes(methodNode.desc); - if (node.isReplaced() && node.hasDecoration(RedirectInjector.Meta.KEY) && methodNode.getOpcode() != Opcodes.INVOKESTATIC) { - // A redirect handler method for a virtual target will have an extra arg at the start that we don't care about. - currentArgs = Arrays.copyOfRange(currentArgs, 1, currentArgs.length); - } + Type[] originalArgs = InvokeUtil.getOriginalArgs(node); + Type[] currentArgs = InvokeUtil.getCurrentArgs(node); int argIndex = this.findArgIndex(target, originalArgs); InsnList insns = new InsnList(); Extension extraLocals = target.extendLocals(); diff --git a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/ModifyArgsInjector.java b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/ModifyArgsInjector.java index 051831a0d..f67aad0f1 100644 --- a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/ModifyArgsInjector.java +++ b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/ModifyArgsInjector.java @@ -33,6 +33,7 @@ import org.spongepowered.asm.mixin.injection.InjectionPoint.RestrictTargetLevel; import org.spongepowered.asm.mixin.injection.ModifyArgs; import org.spongepowered.asm.mixin.injection.invoke.arg.ArgsClassGenerator; +import org.spongepowered.asm.mixin.injection.invoke.util.InvokeUtil; import org.spongepowered.asm.mixin.injection.struct.InjectionInfo; import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode; import org.spongepowered.asm.mixin.injection.struct.Target; @@ -40,6 +41,8 @@ import org.spongepowered.asm.mixin.injection.throwables.InvalidInjectionException; import org.spongepowered.asm.util.Bytecode; +import java.util.Arrays; + /** * A bytecode injector which allows a single argument of a chosen method call to * be altered. For details see javadoc for {@link ModifyArgs @ModifyArgs}. @@ -78,28 +81,33 @@ protected void inject(Target target, InjectionNode node) { @Override protected void injectAtInvoke(Target target, InjectionNode node) { MethodInsnNode targetMethod = (MethodInsnNode)node.getCurrentTarget(); - - Type[] args = Type.getArgumentTypes(targetMethod.desc); - if (args.length == 0) { + + Type[] originalArgs = InvokeUtil.getOriginalArgs(node); + Type[] currentArgs = InvokeUtil.getCurrentArgs(node); + if (originalArgs.length == 0) { throw new InvalidInjectionException(this.info, "@ModifyArgs injector " + this + " targets a method invocation " + targetMethod.name + targetMethod.desc + " with no arguments!"); } - - String clArgs = this.argsClassGenerator.getArgsClass(targetMethod.desc, this.info.getMixin().getMixin()).getName(); + + String originalDesc = Type.getMethodDescriptor(Type.VOID_TYPE, originalArgs); + String clArgs = this.argsClassGenerator.getArgsClass(originalDesc, this.info.getMixin().getMixin()).getName(); boolean withArgs = this.verifyTarget(target); InsnList insns = new InsnList(); Extension extraStack = target.extendStack().add(1); - - this.packArgs(insns, clArgs, targetMethod); - + + Type[] extraArgs = Arrays.copyOfRange(currentArgs, originalArgs.length, currentArgs.length); + int[] extraArgMap = this.storeArgs(target, extraArgs, insns, 0); + this.packArgs(insns, clArgs, originalDesc); + if (withArgs) { extraStack.add(target.arguments); Bytecode.loadArgs(target.arguments, insns, target.isStatic ? 0 : 1); } this.invokeHandler(insns); - this.unpackArgs(insns, clArgs, args); + this.unpackArgs(insns, clArgs, originalArgs); + this.pushArgs(extraArgs, insns, extraArgMap, 0, extraArgs.length); extraStack.apply(); target.insns.insertBefore(targetMethod, insns); @@ -121,8 +129,8 @@ private boolean verifyTarget(Target target) { return false; } - private void packArgs(InsnList insns, String clArgs, MethodInsnNode targetMethod) { - String factoryDesc = Bytecode.changeDescriptorReturnType(targetMethod.desc, "L" + clArgs + ";"); + private void packArgs(InsnList insns, String clArgs, String targetDesc) { + String factoryDesc = Bytecode.changeDescriptorReturnType(targetDesc, "L" + clArgs + ";"); insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, clArgs, "of", factoryDesc, false)); insns.add(new InsnNode(Opcodes.DUP)); diff --git a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/RedirectInjector.java b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/RedirectInjector.java index 3a5b5fc7f..ee57296ba 100644 --- a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/RedirectInjector.java +++ b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/RedirectInjector.java @@ -103,7 +103,7 @@ public class RedirectInjector extends InvokeInjector { /** * Meta decoration object for redirector target nodes */ - class Meta { + public class Meta { public static final String KEY = "redirector"; diff --git a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/util/InvokeUtil.java b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/util/InvokeUtil.java new file mode 100644 index 000000000..9069d1f24 --- /dev/null +++ b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/util/InvokeUtil.java @@ -0,0 +1,49 @@ +/* + * This file is part of Mixin, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.asm.mixin.injection.invoke.util; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.MethodInsnNode; +import org.spongepowered.asm.mixin.injection.invoke.RedirectInjector; +import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode; + +import java.util.Arrays; + +public class InvokeUtil { + public static Type[] getOriginalArgs(InjectionNode node) { + return Type.getArgumentTypes(((MethodInsnNode) node.getOriginalTarget()).desc); + } + + public static Type[] getCurrentArgs(InjectionNode node) { + MethodInsnNode methodNode = (MethodInsnNode) node.getCurrentTarget(); + Type[] currentArgs = Type.getArgumentTypes(methodNode.desc); + if (node.isReplaced() && node.hasDecoration(RedirectInjector.Meta.KEY) && methodNode.getOpcode() != Opcodes.INVOKESTATIC) { + // A redirect handler method for a virtual target will have an extra arg at the start that we don't care about. + return Arrays.copyOfRange(currentArgs, 1, currentArgs.length); + } + return currentArgs; + } +}