Skip to content

Commit 612a709

Browse files
NotStirredCursedFlames
authored andcommitted
Add ConstructorSuper custom injection point
1 parent 936b1f8 commit 612a709

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package io.github.opencubicchunks.cubicchunks;
2+
3+
import org.apache.commons.lang3.NotImplementedException;
4+
import org.jetbrains.annotations.Nullable;
5+
import org.objectweb.asm.Opcodes;
6+
import org.objectweb.asm.tree.*;
7+
import org.spongepowered.asm.mixin.injection.IInjectionPointContext;
8+
import org.spongepowered.asm.mixin.injection.InjectionPoint;
9+
import org.spongepowered.asm.mixin.injection.InjectionPoint.AtCode;
10+
import org.spongepowered.asm.mixin.injection.selectors.ISelectorContext;
11+
import org.spongepowered.asm.mixin.injection.struct.InjectionPointData;
12+
import org.spongepowered.asm.mixin.transformer.MixinTargetContext;
13+
import org.spongepowered.asm.util.Constants;
14+
15+
import java.lang.reflect.Field;
16+
import java.util.ArrayList;
17+
import java.util.Collection;
18+
import java.util.List;
19+
20+
/**
21+
* This is a temporary hack until we update to a mixin version that allows you to target CTOR_SUPER.
22+
* <p>
23+
* To use this injection point specify its fully qualified name in the @At annotation.
24+
* <h1>Example</h1>
25+
* <pre>{@code @Inject(method = "<init>", target = @At("io.github.opencubicchunks.cubicchunks.ConstructorSuper"))}</pre>
26+
*/
27+
@AtCode("CC:SUPER")
28+
public class ConstructorSuper extends InjectionPoint {
29+
protected final String superName;
30+
protected final String ownerName;
31+
32+
protected final List<MethodNode> methodNodes;
33+
34+
public ConstructorSuper(InjectionPointData data) {
35+
super(data);
36+
if (!data.getMixin().getMixin().getConfig().getEnvironment().getVersion().equals("0.8.5"))
37+
throw new NotImplementedException("We have updated mixin, please use CTOR_HEAD instead of this disaster.");
38+
39+
ISelectorContext parent = data.getContext().getParent();
40+
ClassNode classNode = ((MixinTargetContext) parent.getMixin()).getTargetClassNode();
41+
this.superName = classNode.superName;
42+
this.ownerName = classNode.name;
43+
44+
this.methodNodes = new ArrayList<>();
45+
46+
try {
47+
Field targetsField = parent.getClass().getSuperclass().getDeclaredField("targets");
48+
targetsField.setAccessible(true);
49+
List<?> targets = (List<?>) targetsField.get(parent);
50+
51+
if (targets.isEmpty()) {
52+
return;
53+
}
54+
55+
Field methodField = targets.get(0).getClass().getDeclaredField("method");
56+
methodField.setAccessible(true);
57+
58+
for (Object target : targets) {
59+
MethodNode method = (MethodNode) methodField.get(target);
60+
if (!method.name.equals("<init>")) {
61+
throw new IllegalArgumentException("ConstructorSuper inject must target a constructor");
62+
}
63+
this.methodNodes.add(method);
64+
}
65+
} catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
66+
throw new RuntimeException(String.format("Failed to create super injection point for method %s %s", data.getMethod().name, data.getMixin().getClassName()), e);
67+
}
68+
}
69+
70+
@Override
71+
public boolean checkPriority(int targetPriority, int ownerPriority) {
72+
return true;
73+
}
74+
75+
@Override
76+
public boolean find(String desc, InsnList insns, Collection<AbstractInsnNode> nodes) {
77+
for (MethodNode node : this.methodNodes) {
78+
if (instructionsMatch(node.instructions, insns)) {
79+
MethodInsnNode delegateInit = findDelegateInit(insns, this.superName, this.ownerName);
80+
if (delegateInit == null) {
81+
continue;
82+
}
83+
nodes.add(delegateInit.getNext()); // Assume there is always an instruction node after the super call
84+
return true;
85+
}
86+
}
87+
return false;
88+
}
89+
90+
@Nullable
91+
public static MethodInsnNode findDelegateInit(InsnList insns, String superName, String ownerName) {
92+
// Looking for the invocation of the super class constructor without a new before it
93+
int news = 0;
94+
for (AbstractInsnNode insn : insns) {
95+
if (insn instanceof TypeInsnNode && insn.getOpcode() == Opcodes.NEW) {
96+
news++;
97+
} else if (insn instanceof MethodInsnNode methodInsn && insn.getOpcode() == Opcodes.INVOKESPECIAL) {
98+
if (Constants.CTOR.equals(methodInsn.name)) {
99+
if (news > 0) {
100+
news--;
101+
} else {
102+
boolean isSuper = methodInsn.owner.equals(superName);
103+
if (isSuper || methodInsn.owner.equals(ownerName)) {
104+
return methodInsn;
105+
}
106+
}
107+
}
108+
}
109+
}
110+
return null;
111+
}
112+
113+
private boolean instructionsMatch(InsnList a, InsnList b) {
114+
if (a.size() != b.size()) {
115+
return false;
116+
}
117+
118+
for (int i = 0; i < a.size(); i++) {
119+
if (a.get(i) != b.get(i)) {
120+
return false;
121+
}
122+
}
123+
return true;
124+
}
125+
}

0 commit comments

Comments
 (0)