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