Skip to content

Commit c3dac8c

Browse files
authored
Add InsnType for precise instruction searching, additional cleanup (#49)
Update getMethodNode() to use Opcodes.ASM9 Add insnToString to help debug single instructions Add InsnType as an additional filter in instruction searching Add insnListToString to assist in debugging instruction lists
1 parent deac494 commit c3dac8c

File tree

1 file changed

+129
-16
lines changed
  • src/main/java/net/minecraftforge/coremod/api

1 file changed

+129
-16
lines changed

src/main/java/net/minecraftforge/coremod/api/ASMAPI.java

+129-16
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
*/
3131
public class ASMAPI {
3232
public static MethodNode getMethodNode() {
33-
return new MethodNode(Opcodes.ASM6);
33+
return new MethodNode(Opcodes.ASM9);
3434
}
3535

3636
// Terribly named method. Should be called "prependMethodCall" or something similar.
@@ -48,10 +48,20 @@ public static void appendMethodCall(MethodNode node, MethodInsnNode methodCall)
4848
* Signifies the method invocation type. Mirrors "INVOKE-" opcodes from ASM.
4949
*/
5050
public enum MethodType {
51-
VIRTUAL, SPECIAL, STATIC, INTERFACE, DYNAMIC;
51+
VIRTUAL(Opcodes.INVOKEVIRTUAL),
52+
SPECIAL(Opcodes.INVOKESPECIAL),
53+
STATIC(Opcodes.INVOKESTATIC),
54+
INTERFACE(Opcodes.INVOKEINTERFACE),
55+
DYNAMIC(Opcodes.INVOKEDYNAMIC);
56+
57+
private final int opcode;
58+
59+
MethodType(int opcode) {
60+
this.opcode = opcode;
61+
}
5262

5363
public int toOpcode() {
54-
return Opcodes.INVOKEVIRTUAL + this.ordinal();
64+
return this.opcode;
5565
}
5666
}
5767

@@ -66,7 +76,7 @@ public int toOpcode() {
6676
* @return The built method call node
6777
*/
6878
public static MethodInsnNode buildMethodCall(final String ownerName, final String methodName, final String methodDescriptor, final MethodType type) {
69-
return new MethodInsnNode(type.toOpcode(), ownerName, methodName, methodDescriptor, type==MethodType.INTERFACE);
79+
return new MethodInsnNode(type.toOpcode(), ownerName, methodName, methodDescriptor, type == MethodType.INTERFACE);
7080
}
7181

7282
/**
@@ -129,9 +139,9 @@ public static String mapField(String name) {
129139
@Deprecated(forRemoval = true, since = "5.1")
130140
private static String map(String name, INameMappingService.Domain domain) {
131141
return Optional.ofNullable(Launcher.INSTANCE).
132-
map(Launcher::environment).
133-
flatMap(env->env.findNameMapping("srg")).
134-
map(f -> f.apply(domain, name)).orElse(name);
142+
map(Launcher::environment).
143+
flatMap(env -> env.findNameMapping("srg")).
144+
map(f -> f.apply(domain, name)).orElse(name);
135145
}
136146

137147
/**
@@ -151,6 +161,41 @@ public enum InsertMode {
151161
REMOVE_ORIGINAL, INSERT_BEFORE, INSERT_AFTER
152162
}
153163

164+
/**
165+
* The type of instruction. Useful for searching for a specfic instruction, and is preferred over checking the
166+
* opcode or other equivalent.
167+
*
168+
* @see AbstractInsnNode
169+
*/
170+
public enum InsnType {
171+
INSN(AbstractInsnNode.INSN),
172+
INT_INSN(AbstractInsnNode.INT_INSN),
173+
VAR_INSN(AbstractInsnNode.VAR_INSN),
174+
TYPE_INSN(AbstractInsnNode.TYPE_INSN),
175+
FIELD_INSN(AbstractInsnNode.FIELD_INSN),
176+
METHOD_INSN(AbstractInsnNode.METHOD_INSN),
177+
INVOKE_DYNAMIC_INSN(AbstractInsnNode.INVOKE_DYNAMIC_INSN),
178+
JUMP_INSN(AbstractInsnNode.JUMP_INSN),
179+
LABEL(AbstractInsnNode.LABEL),
180+
LDC_INSN(AbstractInsnNode.LDC_INSN),
181+
IINC_INSN(AbstractInsnNode.IINC_INSN),
182+
TABLESWITCH_INSN(AbstractInsnNode.TABLESWITCH_INSN),
183+
LOOKUPSWITCH_INSN(AbstractInsnNode.LOOKUPSWITCH_INSN),
184+
MULTIANEWARRAY_INSN(AbstractInsnNode.MULTIANEWARRAY_INSN),
185+
FRAME(AbstractInsnNode.FRAME),
186+
LINE(AbstractInsnNode.LINE);
187+
188+
private final int type;
189+
190+
InsnType(int type) {
191+
this.type = type;
192+
}
193+
194+
public int get() {
195+
return type;
196+
}
197+
}
198+
154199
/**
155200
* Finds the first instruction with matching opcode.
156201
*
@@ -159,7 +204,19 @@ public enum InsertMode {
159204
* @return the found instruction node or null if none matched
160205
*/
161206
public static AbstractInsnNode findFirstInstruction(MethodNode method, int opCode) {
162-
return findFirstInstructionAfter(method, opCode, 0);
207+
return findFirstInstructionAfter(method, opCode, null, 0);
208+
}
209+
210+
/**
211+
* Finds the first instruction with matching opcode.
212+
*
213+
* @param method the method to search in
214+
* @param opCode the opcode to search for
215+
* @param type the instruction type to search for
216+
* @return the found instruction node or null if none matched
217+
*/
218+
public static AbstractInsnNode findFirstInstruction(MethodNode method, int opCode, InsnType type) {
219+
return findFirstInstructionAfter(method, opCode, type, 0);
163220
}
164221

165222
/**
@@ -171,10 +228,26 @@ public static AbstractInsnNode findFirstInstruction(MethodNode method, int opCod
171228
* @return the found instruction node or null if none matched after the given index
172229
*/
173230
public static AbstractInsnNode findFirstInstructionAfter(MethodNode method, int opCode, int startIndex) {
231+
return findFirstInstructionAfter(method, opCode, null, startIndex);
232+
}
233+
234+
/**
235+
* Finds the first instruction with matching opcode after the given start index
236+
*
237+
* @param method the method to search in
238+
* @param opCode the opcode to search for
239+
* @param type the instruction type to search for
240+
* @param startIndex the index to start search after (inclusive)
241+
* @return the found instruction node or null if none matched after the given index
242+
*/
243+
public static AbstractInsnNode findFirstInstructionAfter(MethodNode method, int opCode, @Nullable InsnType type, int startIndex) {
244+
boolean checkType = type != null;
174245
for (int i = Math.max(0, startIndex); i < method.instructions.size(); i++) {
175246
AbstractInsnNode ain = method.instructions.get(i);
176247
if (ain.getOpcode() == opCode) {
177-
return ain;
248+
if (!checkType || type.get() == ain.getType()) {
249+
return ain;
250+
}
178251
}
179252
}
180253
return null;
@@ -189,10 +262,25 @@ public static AbstractInsnNode findFirstInstructionAfter(MethodNode method, int
189262
* @return the found instruction node or null if none matched before the given startIndex
190263
*/
191264
public static AbstractInsnNode findFirstInstructionBefore(MethodNode method, int opCode, int startIndex) {
265+
return findFirstInstructionBefore(method, opCode, null, startIndex);
266+
}
267+
268+
/**
269+
* Finds the first instruction with matching opcode before the given index in reverse search
270+
*
271+
* @param method the method to search in
272+
* @param opCode the opcode to search for
273+
* @param startIndex the index at which to start searching (inclusive)
274+
* @return the found instruction node or null if none matched before the given startIndex
275+
*/
276+
public static AbstractInsnNode findFirstInstructionBefore(MethodNode method, int opCode, @Nullable InsnType type, int startIndex) {
277+
boolean checkType = type != null;
192278
for (int i = Math.min(method.instructions.size() - 1, startIndex); i >= 0; i--) {
193279
AbstractInsnNode ain = method.instructions.get(i);
194280
if (ain.getOpcode() == opCode) {
195-
return ain;
281+
if (!checkType || type.get() == ain.getType()) {
282+
return ain;
283+
}
196284
}
197285
}
198286
return null;
@@ -337,19 +425,19 @@ public static void redirectFieldToMethod(final ClassNode classNode, final String
337425
if (foundField == null) {
338426
foundField = fieldNode;
339427
} else {
340-
throw new IllegalStateException("Found multiple fields with name "+fieldName);
428+
throw new IllegalStateException("Found multiple fields with name " + fieldName);
341429
}
342430
}
343431
}
344432

345433
if (foundField == null) {
346-
throw new IllegalStateException("No field with name "+fieldName+" found");
434+
throw new IllegalStateException("No field with name " + fieldName + " found");
347435
}
348436
if (!Modifier.isPrivate(foundField.access) || Modifier.isStatic(foundField.access)) {
349-
throw new IllegalStateException("Field "+fieldName+" is not private and an instance field");
437+
throw new IllegalStateException("Field " + fieldName + " is not private and an instance field");
350438
}
351439

352-
final String methodSignature = "()"+foundField.desc;
440+
final String methodSignature = "()" + foundField.desc;
353441

354442
for (MethodNode methodNode : classNode.methods) {
355443
if (Objects.equals(methodNode.desc, methodSignature)) {
@@ -358,13 +446,13 @@ public static void redirectFieldToMethod(final ClassNode classNode, final String
358446
} else if (foundMethod == null && methodName == null) {
359447
foundMethod = methodNode;
360448
} else if (foundMethod != null && (methodName == null || Objects.equals(methodNode.name, methodName))) {
361-
throw new IllegalStateException("Found duplicate method with signature "+methodSignature);
449+
throw new IllegalStateException("Found duplicate method with signature " + methodSignature);
362450
}
363451
}
364452
}
365453

366454
if (foundMethod == null) {
367-
throw new IllegalStateException("Unable to find method "+methodSignature);
455+
throw new IllegalStateException("Unable to find method " + methodSignature);
368456
}
369457

370458
for (MethodNode methodNode : classNode.methods) {
@@ -462,6 +550,31 @@ public static String methodNodeToString(MethodNode node) {
462550
return toString(text);
463551
}
464552

553+
/**
554+
* Converts an {@link InsnNode} to a string representation.
555+
*
556+
* @param insn The instruction to convert.
557+
* @return The string representation of the instruction.
558+
*/
559+
public static String insnToString(InsnNode insn) {
560+
Textifier text = new Textifier();
561+
insn.accept(new TraceMethodVisitor(text));
562+
return toString(text);
563+
}
564+
565+
/**
566+
* Converts a {@link InsnList} to a string representation, displaying each instruction in the list similar to
567+
* {@link #insnToString(InsnNode)}.
568+
*
569+
* @param list The list to convert.
570+
* @return The string
571+
*/
572+
public static String insnListToString(InsnList list) {
573+
Textifier text = new Textifier();
574+
list.accept(new TraceMethodVisitor(text));
575+
return toString(text);
576+
}
577+
465578
/**
466579
* Gets the LDC constant's class name as a string. Useful for debugging existing LDC instructions.
467580
*

0 commit comments

Comments
 (0)