30
30
*/
31
31
public class ASMAPI {
32
32
public static MethodNode getMethodNode () {
33
- return new MethodNode (Opcodes .ASM6 );
33
+ return new MethodNode (Opcodes .ASM9 );
34
34
}
35
35
36
36
// Terribly named method. Should be called "prependMethodCall" or something similar.
@@ -48,10 +48,20 @@ public static void appendMethodCall(MethodNode node, MethodInsnNode methodCall)
48
48
* Signifies the method invocation type. Mirrors "INVOKE-" opcodes from ASM.
49
49
*/
50
50
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
+ }
52
62
53
63
public int toOpcode () {
54
- return Opcodes . INVOKEVIRTUAL + this .ordinal () ;
64
+ return this .opcode ;
55
65
}
56
66
}
57
67
@@ -66,7 +76,7 @@ public int toOpcode() {
66
76
* @return The built method call node
67
77
*/
68
78
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 );
70
80
}
71
81
72
82
/**
@@ -129,9 +139,9 @@ public static String mapField(String name) {
129
139
@ Deprecated (forRemoval = true , since = "5.1" )
130
140
private static String map (String name , INameMappingService .Domain domain ) {
131
141
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 );
135
145
}
136
146
137
147
/**
@@ -151,6 +161,41 @@ public enum InsertMode {
151
161
REMOVE_ORIGINAL , INSERT_BEFORE , INSERT_AFTER
152
162
}
153
163
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
+
154
199
/**
155
200
* Finds the first instruction with matching opcode.
156
201
*
@@ -159,7 +204,19 @@ public enum InsertMode {
159
204
* @return the found instruction node or null if none matched
160
205
*/
161
206
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 );
163
220
}
164
221
165
222
/**
@@ -171,10 +228,26 @@ public static AbstractInsnNode findFirstInstruction(MethodNode method, int opCod
171
228
* @return the found instruction node or null if none matched after the given index
172
229
*/
173
230
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 ;
174
245
for (int i = Math .max (0 , startIndex ); i < method .instructions .size (); i ++) {
175
246
AbstractInsnNode ain = method .instructions .get (i );
176
247
if (ain .getOpcode () == opCode ) {
177
- return ain ;
248
+ if (!checkType || type .get () == ain .getType ()) {
249
+ return ain ;
250
+ }
178
251
}
179
252
}
180
253
return null ;
@@ -189,10 +262,25 @@ public static AbstractInsnNode findFirstInstructionAfter(MethodNode method, int
189
262
* @return the found instruction node or null if none matched before the given startIndex
190
263
*/
191
264
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 ;
192
278
for (int i = Math .min (method .instructions .size () - 1 , startIndex ); i >= 0 ; i --) {
193
279
AbstractInsnNode ain = method .instructions .get (i );
194
280
if (ain .getOpcode () == opCode ) {
195
- return ain ;
281
+ if (!checkType || type .get () == ain .getType ()) {
282
+ return ain ;
283
+ }
196
284
}
197
285
}
198
286
return null ;
@@ -337,19 +425,19 @@ public static void redirectFieldToMethod(final ClassNode classNode, final String
337
425
if (foundField == null ) {
338
426
foundField = fieldNode ;
339
427
} else {
340
- throw new IllegalStateException ("Found multiple fields with name " + fieldName );
428
+ throw new IllegalStateException ("Found multiple fields with name " + fieldName );
341
429
}
342
430
}
343
431
}
344
432
345
433
if (foundField == null ) {
346
- throw new IllegalStateException ("No field with name " + fieldName + " found" );
434
+ throw new IllegalStateException ("No field with name " + fieldName + " found" );
347
435
}
348
436
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" );
350
438
}
351
439
352
- final String methodSignature = "()" + foundField .desc ;
440
+ final String methodSignature = "()" + foundField .desc ;
353
441
354
442
for (MethodNode methodNode : classNode .methods ) {
355
443
if (Objects .equals (methodNode .desc , methodSignature )) {
@@ -358,13 +446,13 @@ public static void redirectFieldToMethod(final ClassNode classNode, final String
358
446
} else if (foundMethod == null && methodName == null ) {
359
447
foundMethod = methodNode ;
360
448
} 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 );
362
450
}
363
451
}
364
452
}
365
453
366
454
if (foundMethod == null ) {
367
- throw new IllegalStateException ("Unable to find method " + methodSignature );
455
+ throw new IllegalStateException ("Unable to find method " + methodSignature );
368
456
}
369
457
370
458
for (MethodNode methodNode : classNode .methods ) {
@@ -462,6 +550,31 @@ public static String methodNodeToString(MethodNode node) {
462
550
return toString (text );
463
551
}
464
552
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
+
465
578
/**
466
579
* Gets the LDC constant's class name as a string. Useful for debugging existing LDC instructions.
467
580
*
0 commit comments