From 84519bceca8d574038c3e07d27913b45dfb94f4d Mon Sep 17 00:00:00 2001
From: Runemoro
Date: Fri, 13 Jul 2018 14:36:29 -0400
Subject: [PATCH] Support injecting into and overwriting constructors
---
.../MixinObfuscationProcessorTargets.java | 8 +--
.../org/spongepowered/asm/mixin/Copy.java | 45 +++++++++++++
.../spongepowered/asm/mixin/Overwrite.java | 2 +-
.../org/spongepowered/asm/mixin/Shadow.java | 2 +-
.../injection/callback/CallbackInjector.java | 20 ++++--
.../injection/invoke/InvokeInjector.java | 3 +-
.../injection/invoke/ModifyArgInjector.java | 6 --
.../injection/invoke/ModifyArgsInjector.java | 6 --
.../asm/mixin/injection/struct/Target.java | 14 ++--
.../asm/mixin/transformer/ClassContext.java | 3 +-
.../asm/mixin/transformer/ClassInfo.java | 4 +-
.../transformer/MixinApplicatorStandard.java | 64 +++++++++++++++++--
.../MixinPreProcessorStandard.java | 4 +-
.../mixin/transformer/MixinTargetContext.java | 10 ++-
.../org/spongepowered/asm/util/Bytecode.java | 12 ++--
15 files changed, 154 insertions(+), 49 deletions(-)
create mode 100644 src/main/java/org/spongepowered/asm/mixin/Copy.java
diff --git a/src/ap/java/org/spongepowered/tools/obfuscation/MixinObfuscationProcessorTargets.java b/src/ap/java/org/spongepowered/tools/obfuscation/MixinObfuscationProcessorTargets.java
index 6d1a78465..a712d0ff2 100644
--- a/src/ap/java/org/spongepowered/tools/obfuscation/MixinObfuscationProcessorTargets.java
+++ b/src/ap/java/org/spongepowered/tools/obfuscation/MixinObfuscationProcessorTargets.java
@@ -107,10 +107,10 @@ private void processShadows(RoundEnvironment roundEnv) {
if (elem.getKind() == ElementKind.FIELD) {
this.mixins.registerShadow((TypeElement)parent, (VariableElement)elem, shadow);
- } else if (elem.getKind() == ElementKind.METHOD) {
+ } else if (elem.getKind() == ElementKind.METHOD || elem.getKind() == ElementKind.CONSTRUCTOR) {
this.mixins.registerShadow((TypeElement)parent, (ExecutableElement)elem, shadow);
} else {
- this.mixins.printMessage(Kind.ERROR, "Element is not a method or field", elem);
+ this.mixins.printMessage(Kind.ERROR, "Element is not a method, constructor or field", elem);
}
}
}
@@ -127,10 +127,10 @@ private void processOverwrites(RoundEnvironment roundEnv) {
continue;
}
- if (elem.getKind() == ElementKind.METHOD) {
+ if (elem.getKind() == ElementKind.METHOD || elem.getKind() == ElementKind.CONSTRUCTOR) {
this.mixins.registerOverwrite((TypeElement)parent, (ExecutableElement)elem);
} else {
- this.mixins.printMessage(Kind.ERROR, "Element is not a method", elem);
+ this.mixins.printMessage(Kind.ERROR, "Element is not a method or constructor", elem);
}
}
}
diff --git a/src/main/java/org/spongepowered/asm/mixin/Copy.java b/src/main/java/org/spongepowered/asm/mixin/Copy.java
new file mode 100644
index 000000000..bbd1e6046
--- /dev/null
+++ b/src/main/java/org/spongepowered/asm/mixin/Copy.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used to indicate that a constructor should be copied to the
+ * target class.
+ *
+ * The default behaviour of mixin classes when merging mixin methods is to
+ * not copy any constructors that are not annotated with {@link Overwrite}. This
+ * annotation can be used to override that behaviour and copy a constructor to
+ * the target class.
+ */
+@Target({ ElementType.CONSTRUCTOR })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Copy {
+
+}
diff --git a/src/main/java/org/spongepowered/asm/mixin/Overwrite.java b/src/main/java/org/spongepowered/asm/mixin/Overwrite.java
index 4894c1fd2..b9f29289b 100644
--- a/src/main/java/org/spongepowered/asm/mixin/Overwrite.java
+++ b/src/main/java/org/spongepowered/asm/mixin/Overwrite.java
@@ -49,7 +49,7 @@
* overwrite a member in the target class, and should be added to the
* obfuscation table if {@link #remap} is true.
*/
-@Target({ ElementType.METHOD })
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
@Retention(RetentionPolicy.RUNTIME)
public @interface Overwrite {
diff --git a/src/main/java/org/spongepowered/asm/mixin/Shadow.java b/src/main/java/org/spongepowered/asm/mixin/Shadow.java
index 79aadd1ff..4d0d3aa9d 100644
--- a/src/main/java/org/spongepowered/asm/mixin/Shadow.java
+++ b/src/main/java/org/spongepowered/asm/mixin/Shadow.java
@@ -35,7 +35,7 @@
* Used to indicate a Mixin class member which is acting as a placeholder for a
* method or field in the target class
*/
-@Target({ ElementType.METHOD, ElementType.FIELD })
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Shadow {
diff --git a/src/main/java/org/spongepowered/asm/mixin/injection/callback/CallbackInjector.java b/src/main/java/org/spongepowered/asm/mixin/injection/callback/CallbackInjector.java
index f7ca1016a..30b66bfcd 100644
--- a/src/main/java/org/spongepowered/asm/mixin/injection/callback/CallbackInjector.java
+++ b/src/main/java/org/spongepowered/asm/mixin/injection/callback/CallbackInjector.java
@@ -37,7 +37,6 @@
import org.spongepowered.asm.mixin.injection.InjectionPoint;
import org.spongepowered.asm.mixin.injection.Surrogate;
import org.spongepowered.asm.mixin.injection.code.Injector;
-import org.spongepowered.asm.mixin.injection.points.BeforeReturn;
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
import org.spongepowered.asm.mixin.injection.struct.Target;
import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode;
@@ -369,12 +368,20 @@ protected void sanityCheck(Target target, List injectionPoints)
if (target.isStatic != this.isStatic) {
throw new InvalidInjectionException(this.info, "'static' modifier of callback method does not match target in " + this);
}
+ }
+
+ protected void checkTargetForNode(Target target, InjectionNode node) {
+ if (target.isCtor) {
+ MethodInsnNode superCall = target.findSuperOrThisInitNode();
+ int superCallIndex = target.indexOf(superCall);
+ int targetIndex = target.indexOf(node.getCurrentTarget());
+ if (targetIndex <= superCallIndex) {
+ if (!this.isStatic) {
+ throw new InvalidInjectionException(this.info, "Pre-super " + this.info + " injection must be static in " + this);
+ }
- if (Constants.CTOR.equals(target.method.name)) {
- for (InjectionPoint injectionPoint : injectionPoints) {
- if (!injectionPoint.getClass().equals(BeforeReturn.class)) {
- throw new InvalidInjectionException(this.info, "Found injection point type " + injectionPoint.getClass().getSimpleName()
- + " targetting a ctor in " + this + ". Only RETURN allowed for a ctor target");
+ if (this.cancellable) {
+ throw new InvalidInjectionException(this.info, "Pre-super " + this.info + " injection must not be cancellable in " + this);
}
}
}
@@ -422,6 +429,7 @@ protected void inject(Target target, InjectionNode node) {
locals = Locals.getLocalsAt(this.classNode, target.method, node.getCurrentTarget());
}
+ this.checkTargetForNode(target, node);
this.inject(new Callback(this.methodNode, target, node, locals, this.localCapture.isCaptureLocals()));
}
diff --git a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/InvokeInjector.java b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/InvokeInjector.java
index e5995065d..6b444aa46 100644
--- a/src/main/java/org/spongepowered/asm/mixin/injection/invoke/InvokeInjector.java
+++ b/src/main/java/org/spongepowered/asm/mixin/injection/invoke/InvokeInjector.java
@@ -104,7 +104,7 @@ protected final void checkTargetModifiers(Target target, boolean exactMatch) {
*/
protected void checkTargetForNode(Target target, InjectionNode node) {
if (target.isCtor) {
- MethodInsnNode superCall = target.findSuperInitNode();
+ MethodInsnNode superCall = target.findSuperOrThisInitNode();
int superCallIndex = target.indexOf(superCall);
int targetIndex = target.indexOf(node.getCurrentTarget());
if (targetIndex <= superCallIndex) {
@@ -129,6 +129,7 @@ protected void inject(Target target, InjectionNode node) {
+ " in " + this);
}
+ this.checkTargetForNode(target, node);
this.injectAtInvoke(target, node);
}
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 a908983c5..2dc44f458 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
@@ -94,12 +94,6 @@ protected void checkTarget(Target target) {
}
}
- @Override
- protected void inject(Target target, InjectionNode node) {
- this.checkTargetForNode(target, node);
- super.inject(target, node);
- }
-
/**
* Do the injection
*/
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 46b2023b7..1640fd53f 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
@@ -62,12 +62,6 @@ public ModifyArgsInjector(InjectionInfo info) {
protected void checkTarget(Target target) {
this.checkTargetModifiers(target, false);
}
-
- @Override
- protected void inject(Target target, InjectionNode node) {
- this.checkTargetForNode(target, node);
- super.inject(target, node);
- }
/**
* Do the injection
diff --git a/src/main/java/org/spongepowered/asm/mixin/injection/struct/Target.java b/src/main/java/org/spongepowered/asm/mixin/injection/struct/Target.java
index 72b4f0ba6..7ec2fd97b 100644
--- a/src/main/java/org/spongepowered/asm/mixin/injection/struct/Target.java
+++ b/src/main/java/org/spongepowered/asm/mixin/injection/struct/Target.java
@@ -490,18 +490,20 @@ public MethodInsnNode findInitNodeFor(TypeInsnNode newNode) {
}
/**
- * Find the call to super() in a constructor. This attempts to
- * locate the first call to <init> which isn't an inline call
- * to another object ctor being passed into the super invocation.
+ * Find the call to super() or this() in a constructor.
+ * This attempts to locate the first call to <init> which
+ * isn't an inline call to another object ctor being passed into the
+ * super invocation.
*
- * @return Call to super() or null if not found
+ * @return Call to super() or this(), or null
+ * if not found.
*/
- public MethodInsnNode findSuperInitNode() {
+ public MethodInsnNode findSuperOrThisInitNode() {
if (!this.isCtor) {
return null;
}
- return Bytecode.findSuperInit(this.method, ClassInfo.forName(this.classNode.name).getSuperName());
+ return Bytecode.findSuperOrThisInit(this.method, this.classNode.name, ClassInfo.forName(this.classNode.name).getSuperName());
}
/**
diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/ClassContext.java b/src/main/java/org/spongepowered/asm/mixin/transformer/ClassContext.java
index 3845ef9ed..7f9f05136 100644
--- a/src/main/java/org/spongepowered/asm/mixin/transformer/ClassContext.java
+++ b/src/main/java/org/spongepowered/asm/mixin/transformer/ClassContext.java
@@ -35,6 +35,7 @@
import org.spongepowered.asm.lib.tree.MethodNode;
import org.spongepowered.asm.mixin.struct.MemberRef;
import org.spongepowered.asm.mixin.transformer.ClassInfo.Method;
+import org.spongepowered.asm.util.Constants;
/**
* Base for class context objects
@@ -107,7 +108,7 @@ private void upgradeMethod(MethodNode method) {
}
protected void upgradeMethodRef(MethodNode containingMethod, MemberRef methodRef, Method method) {
- if (methodRef.getOpcode() != Opcodes.INVOKESPECIAL) {
+ if (methodRef.getOpcode() != Opcodes.INVOKESPECIAL || Constants.CTOR.equals(methodRef.getName())) {
return;
}
diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/ClassInfo.java b/src/main/java/org/spongepowered/asm/mixin/transformer/ClassInfo.java
index 342360b54..5e3035bc0 100644
--- a/src/main/java/org/spongepowered/asm/mixin/transformer/ClassInfo.java
+++ b/src/main/java/org/spongepowered/asm/mixin/transformer/ClassInfo.java
@@ -772,9 +772,7 @@ void addMethod(MethodNode method) {
}
private void addMethod(MethodNode method, boolean injected) {
- if (!method.name.startsWith("<")) {
- this.methods.add(new Method(method, injected));
- }
+ this.methods.add(new Method(method, injected));
}
/**
diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinApplicatorStandard.java b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinApplicatorStandard.java
index aeeed5292..507238a2f 100644
--- a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinApplicatorStandard.java
+++ b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinApplicatorStandard.java
@@ -44,6 +44,7 @@
import org.spongepowered.asm.lib.signature.SignatureReader;
import org.spongepowered.asm.lib.signature.SignatureVisitor;
import org.spongepowered.asm.lib.tree.*;
+import org.spongepowered.asm.mixin.Copy;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Intrinsic;
import org.spongepowered.asm.mixin.MixinEnvironment;
@@ -441,6 +442,14 @@ protected void applyShadowMethod(MixinTargetContext mixin, MethodNode shadow) {
}
protected void applyNormalMethod(MixinTargetContext mixin, MethodNode mixinMethod) {
+ boolean isMeargableConstructor = Constants.CTOR.equals(mixinMethod.name)
+ && (Annotations.getVisible(mixinMethod, Overwrite.class) != null
+ || Annotations.getVisible(mixinMethod, Copy.class) != null);
+
+ if (isMeargableConstructor) {
+ this.checkConstructorCall(mixin, mixinMethod);
+ }
+
// Reparent all mixin methods into the target class
mixin.transformMethod(mixinMethod);
@@ -448,6 +457,11 @@ protected void applyNormalMethod(MixinTargetContext mixin, MethodNode mixinMetho
this.checkMethodVisibility(mixin, mixinMethod);
this.checkMethodConstraints(mixin, mixinMethod);
this.mergeMethod(mixin, mixinMethod);
+ } else if (isMeargableConstructor) {
+ mixin.transformMethod(mixinMethod);
+ this.checkMethodVisibility(mixin, mixinMethod);
+ this.checkMethodConstraints(mixin, mixinMethod);
+ this.mergeMethod(mixin, mixinMethod);
} else if (Constants.CLINIT.equals(mixinMethod.name)) {
// Class initialiser insns get appended
this.appendInsns(mixin, mixinMethod);
@@ -677,11 +691,14 @@ protected void applyInitialisers(MixinTargetContext mixin) {
return;
}
- // Patch the initialiser into the target class ctors
+ // Patch the initialiser into the target class ctors and ctors from other mixins
for (MethodNode method : this.targetClass.methods) {
if (Constants.CTOR.equals(method.name)) {
- method.maxStack = Math.max(method.maxStack, ctor.maxStack);
- this.injectInitialiser(mixin, method, initialiser);
+ AnnotationNode merged = Annotations.getVisible(method, MixinMerged.class);
+ if (merged == null || !mixin.getClassName().equals(Annotations.getValue(merged, "mixin"))) {
+ method.maxStack = Math.max(method.maxStack, ctor.maxStack);
+ this.injectInitialiser(mixin, method, initialiser);
+ }
}
}
}
@@ -843,7 +860,6 @@ protected final void injectInitialiser(MixinTargetContext mixin, MethodNode ctor
AbstractInsnNode insn = this.findInitialiserInjectionPoint(mixin, ctor, initialiser);
if (insn == null) {
- this.logger.warn("Failed to locate initialiser injection point in {}, initialiser was not mixed in.", ctor.desc);
return;
}
@@ -886,7 +902,12 @@ protected AbstractInsnNode findInitialiserInjectionPoint(MixinTargetContext mixi
AbstractInsnNode insn = iter.next();
if (insn.getOpcode() == Opcodes.INVOKESPECIAL && Constants.CTOR.equals(((MethodInsnNode)insn).name)) {
String owner = ((MethodInsnNode)insn).owner;
- if (owner.equals(targetName) || owner.equals(targetSuperName)) {
+
+ if (owner.equals(targetName)) {
+ return null;
+ }
+
+ if (owner.equals(targetSuperName)) {
targetInsn = insn;
if (mode == InitialiserInjectionMode.SAFE) {
break;
@@ -900,6 +921,10 @@ protected AbstractInsnNode findInitialiserInjectionPoint(MixinTargetContext mixi
}
}
+ if (targetInsn == null) {
+ this.logger.warn("Failed to locate initialiser injection point in {}, initialiser was not mixed in.", ctor.desc);
+ }
+
return targetInsn;
}
@@ -1012,6 +1037,35 @@ protected final void checkConstraints(MixinTargetContext mixin, MethodNode metho
}
}
+ /**
+ * Check that a method calls a either another constructor in the mixin or a constructor in the
+ * superclass of the target or a mixin targetting that superclass.
+ *
+ * @param mixin Mixin being applied
+ * @param method annotated method
+ */
+ protected void checkConstructorCall(MixinTargetContext mixin, MethodNode method) {
+ String targetName = mixin.getTarget().getClassName();
+ String superName = mixin.getTarget().getClassInfo().getSuperName();
+
+ MethodInsnNode constructor = Bytecode.findSuperOrThisInit(method, targetName, superName);
+ if (constructor == null) {
+ String message = String.format("Constructor %s%s in %s does call a this or super constructor copied to the target class", method.name, method.desc, mixin);
+ throw new InvalidMixinException(mixin, message);
+ } else if (constructor.name.equals(superName)) {
+ ClassInfo targetSuperClass = mixin.getTarget().getClassInfo().getSuperClass();
+ ClassInfo mixinSuperClass = mixin.getClassInfo().getSuperClass();
+
+ if (!mixinSuperClass.equals(targetSuperClass)) {
+ // Necessary because updateBinding may have updated a call to super in a class other than the target's super or mixin targetting it
+ String message = String.format("Constructor %s%s in %s contains call to super constructor, but mixin does " +
+ "not extend target's superclass or mixin targetting it",
+ method.name, method.desc, mixin);
+ throw new InvalidMixinException(mixin, message);
+ }
+ }
+ }
+
/**
* Finds a method in the target class
* @param searchFor
diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinPreProcessorStandard.java b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinPreProcessorStandard.java
index 3e47d35a5..7915dff2c 100644
--- a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinPreProcessorStandard.java
+++ b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinPreProcessorStandard.java
@@ -391,8 +391,8 @@ protected boolean attachSpecialMethod(MixinTargetContext context, MixinMethodNod
mixinMethod.name = method.renameTo(target.name);
}
- if (Constants.CTOR.equals(target.name)) {
- throw new InvalidMixinException(this.mixin, String.format("Nice try! %s in %s cannot alias a constructor", mixinMethod.name, this.mixin));
+ if (Constants.CTOR.equals(target.name) && !Constants.CTOR.equals(mixinMethod.name)) {
+ throw new InvalidMixinException(this.mixin, String.format("%s in %s cannot alias a constructor", mixinMethod.name, this.mixin));
}
if (!Bytecode.compareFlags(mixinMethod, target, Opcodes.ACC_STATIC)) {
diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinTargetContext.java b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinTargetContext.java
index 6812c9522..123b9da38 100644
--- a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinTargetContext.java
+++ b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinTargetContext.java
@@ -50,6 +50,7 @@
import org.spongepowered.asm.mixin.SoftOverride;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.mixin.gen.AccessorInfo;
+import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
import org.spongepowered.asm.mixin.injection.struct.InjectorGroupInfo;
@@ -539,7 +540,14 @@ private void transformMethodRef(MethodNode method, Iterator it
methodRef.setDesc(this.transformMethodDescriptor(methodRef.getDesc()));
} else if (this.detachedSuper || this.inheritsFromMixin) {
if (methodRef.getOpcode() == Opcodes.INVOKESPECIAL) {
- this.updateStaticBinding(method, methodRef);
+ if (Constants.CTOR.equals(methodRef.getName())) {
+ if (methodRef.getOwner().equals(mixin.getClassInfo().getSuperName()) &&
+ this.mixin.getClassInfo().getSuperClass().isMixin()) {
+ this.updateStaticBinding(method, methodRef);
+ }
+ } else {
+ this.updateStaticBinding(method, methodRef);
+ }
} else if (methodRef.getOpcode() == Opcodes.INVOKEVIRTUAL && ClassInfo.forName(methodRef.getOwner()).isMixin()) {
this.updateDynamicBinding(method, methodRef);
}
diff --git a/src/main/java/org/spongepowered/asm/util/Bytecode.java b/src/main/java/org/spongepowered/asm/util/Bytecode.java
index 794162cb5..c80e7bbb2 100644
--- a/src/main/java/org/spongepowered/asm/util/Bytecode.java
+++ b/src/main/java/org/spongepowered/asm/util/Bytecode.java
@@ -253,15 +253,15 @@ public static AbstractInsnNode findInsn(MethodNode method, int opcode) {
}
/**
- * Find the call to super() in a constructor. This attempts to
- * locate the first call to <init> which isn't an inline call
- * to another object ctor being passed into the super invocation.
+ * Find the call to super() or this() in a constructor. This
+ * attempts to locate the first call to <init> which isn't an
+ * inline call to another object ctor being passed into the super invocation.
*
* @param method ctor to scan
* @param superName name of superclass
- * @return Call to super() or null if not found
+ * @return Call to super() or this(), or null if not found
*/
- public static MethodInsnNode findSuperInit(MethodNode method, String superName) {
+ public static MethodInsnNode findSuperOrThisInit(MethodNode method, String className, String superName) {
if (!Constants.CTOR.equals(method.name)) {
return null;
}
@@ -276,7 +276,7 @@ public static MethodInsnNode findSuperInit(MethodNode method, String superName)
if (Constants.CTOR.equals(methodNode.name)) {
if (news > 0) {
news--;
- } else if (methodNode.owner.equals(superName)) {
+ } else if (methodNode.owner.equals(className) || methodNode.owner.equals(superName)) {
return methodNode;
}
}