diff --git a/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java b/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java index 1ae3424..f400f7d 100644 --- a/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java +++ b/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java @@ -20,6 +20,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.ListIterator; import java.util.Objects; import java.util.Optional; @@ -29,8 +30,210 @@ * Helper methods for working with ASM. */ public class ASMAPI { + /** + * Creates a new empty {@link MethodNode}. + * + * @return The created method node + * + * @see MethodNode#MethodNode(int) + */ public static MethodNode getMethodNode() { - return new MethodNode(Opcodes.ASM9); + var method = new MethodNode(Opcodes.ASM9); + + // ASM usually creates an empty list for null exceptions on the other constructors + // let's do this as well, just to make sure we don't run into problems later. + method.exceptions = new ArrayList<>(); + + return method; + } + + /** + * Creates a new empty {@link MethodNode} with the given access codes, name and descriptor. + * + * @param access The access codes + * @param name The method name + * @param descriptor The method descriptor + * @return The created method node + * + * @see MethodNode#MethodNode(int, int, String, String, String, String[]) + */ + public static MethodNode getMethodNode(int access, String name, String descriptor) { + return new MethodNode(Opcodes.ASM9, access, name, descriptor, null, null); + } + + /** + * Creates a new empty {@link MethodNode} with the given access codes, name, descriptor, and signature. + * + * @param access The access codes + * @param name The method name + * @param descriptor The method descriptor + * @param signature The method signature + * @return The created method node + * + * @see MethodNode#MethodNode(int, int, String, String, String, String[]) + */ + public static MethodNode getMethodNode(int access, String name, String descriptor, @Nullable String signature) { + return new MethodNode(Opcodes.ASM9, access, name, descriptor, signature, null); + } + + /** + * Creates a new empty {@link MethodNode} with the given access codes, name, descriptor, signature, and exceptions. + * + * @param access The access codes + * @param name The method name + * @param descriptor The method descriptor + * @param signature The method signature + * @param exceptions The internal names of the method's exceptions + * @return The created method node + * + * @see MethodNode#MethodNode(int, int, String, String, String, String[]) + */ + public static MethodNode getMethodNode(int access, String name, String descriptor, @Nullable String signature, @Nullable String[] exceptions) { + return new MethodNode(Opcodes.ASM9, access, name, descriptor, signature, exceptions); + } + + /** + * Creates a new empty {@link FieldNode} with the given access codes, name, and descriptor. + * + * @param access The access codes + * @param name The field name + * @param descriptor The field descriptor + * @return The created field node + * + * @see FieldNode#FieldNode(int, int, String, String, String, Object) + */ + public static FieldNode getFieldNode(int access, String name, String descriptor) { + return new FieldNode(Opcodes.ASM9, access, name, descriptor, null, null); + } + + /** + * Creates a new empty {@link FieldNode} with the given access codes, name, descriptor, and signature. + * + * @param access The access codes + * @param name The field name + * @param descriptor The field descriptor + * @param signature The field signature + * @return The created field node + * + * @see FieldNode#FieldNode(int, int, String, String, String, Object) + */ + public static FieldNode getFieldNode(int access, String name, String descriptor, @Nullable String signature) { + return new FieldNode(Opcodes.ASM9, access, name, descriptor, signature, null); + } + + /** + * Creates a new empty {@link FieldNode} with the given access codes, name, descriptor, signature, and initial + * object value. + * + * @param access The access codes + * @param name The field name + * @param descriptor The field descriptor + * @param signature The field signature + * @param value The initial value of the field + * @return The created field node + * + * @see FieldNode#FieldNode(int, int, String, String, String, Object) + */ + public static FieldNode getFieldNode(int access, String name, String descriptor, @Nullable String signature, String value) { + return new FieldNode(Opcodes.ASM9, access, name, descriptor, signature, value); + } + + /** + * Creates a new empty {@link FieldNode} with the given access codes, name, descriptor, signature, and initial + * number value. + * + * @param access The access codes + * @param name The field name + * @param descriptor The field descriptor + * @param signature The field signature + * @param value The initial value of the field + * @param valueType The number type of the initial value + * @return The created field node + * + * @see FieldNode#FieldNode(int, int, String, String, String, Object) + */ + public static FieldNode getFieldNode(int access, String name, String descriptor, @Nullable String signature, Number value, NumberType valueType) { + return new FieldNode(Opcodes.ASM9, access, name, descriptor, signature, castNumber(value, valueType)); + } + + /** + * Finds the first method node from the given class node that matches the given name and descriptor. + * + * @param clazz The class node to search + * @param name The name of the desired method + * @param desc The descriptor of the desired method + * @return The found method node or null if none matched + */ + public static @Nullable MethodNode findMethodNode(ClassNode clazz, String name, String desc) { + return findMethodNode(clazz, name, desc, null, false); + } + + /** + * Finds the first method node from the given class node that matches the given name, descriptor, and signature. + * + * @param clazz The class node to search + * @param name The name of the desired method + * @param desc The descriptor of the desired method + * @param signature The signature of the desired method + * @return The found method node or null if none matched + * + * @apiNote This method will attempt to match the signature of the method, even if it is {@code null}. It may be + * useful for that use case in particular. If you have no need to match the signature, consider using + * {@link #findMethodNode(ClassNode, String, String)} + */ + public static @Nullable MethodNode findMethodNode(ClassNode clazz, String name, String desc, @Nullable String signature) { + return findMethodNode(clazz, name, desc, signature, true); + } + + private static @Nullable MethodNode findMethodNode(ClassNode clazz, String name, String desc, @Nullable String signature, boolean checkSignature) { + for (MethodNode method : clazz.methods) { + // we have to use Objects.equals here in case the found method has null attributes + if (Objects.equals(method.name, name) && Objects.equals(method.desc, desc) && (!checkSignature || Objects.equals(method.signature, signature))) { + return method; + } + } + + return null; + } + + /** + * Finds the first field node from the given class node that matches the given name and descriptor. + * + * @param clazz The class node to search + * @param name The name of the desired field + * @param desc The descriptor of the desired field + * @return The found field node or null if none matched + */ + public static @Nullable FieldNode findFieldNode(ClassNode clazz, String name, String desc) { + return findFieldNode(clazz, name, desc, null, false); + } + + /** + * Finds the first field node from the given class node that matches the given name, descriptor, and signature. + * + * @param clazz The class node to search + * @param name The name of the desired field + * @param desc The descriptor of the desired field + * @param signature The signature of the desired field + * @return The found field node or null if none matched + * + * @apiNote This method will attempt to match the signature of the field, even if it is {@code null}. It may be + * useful for that use case in particular. If you have no need to match the signature, consider using + * {@link #findFieldNode(ClassNode, String, String)} + */ + public static @Nullable FieldNode findFieldNode(ClassNode clazz, String name, String desc, @Nullable String signature) { + return findFieldNode(clazz, name, desc, signature, true); + } + + private static @Nullable FieldNode findFieldNode(ClassNode clazz, String name, String desc, @Nullable String signature, boolean checkSignature) { + for (FieldNode field : clazz.fields) { + // we have to use Objects.equals here in case the found field has null attributes + if (Objects.equals(field.name, name) && Objects.equals(field.desc, desc) && (!checkSignature || Objects.equals(field.signature, signature))) { + return field; + } + } + + return null; } /**