From 40686b8c929279bd486529fceae1d8dd7fa2735f Mon Sep 17 00:00:00 2001 From: zaaarf Date: Tue, 21 Mar 2023 14:05:14 +0100 Subject: [PATCH] feat: added ClassProxy --- .../java/ftbsc/lll/proxies/ClassProxy.java | 174 ++++++++++++++++++ .../ftbsc/lll/tools/DescriptorBuilder.java | 15 +- 2 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 src/main/java/ftbsc/lll/proxies/ClassProxy.java diff --git a/src/main/java/ftbsc/lll/proxies/ClassProxy.java b/src/main/java/ftbsc/lll/proxies/ClassProxy.java new file mode 100644 index 0000000..c369231 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/ClassProxy.java @@ -0,0 +1,174 @@ +package ftbsc.lll.proxies; + +import org.objectweb.asm.Type; + +/** + * A container for information about classes to be used + * in ASM patching. + * @since 0.4.0 + */ +public class ClassProxy extends AbstractProxy { + + /** + * The fully-qualified name of the class represented by this proxy. + */ + public final String fqn; + + /** + * The {@link ClassProxy} representing the class which contains the + * class represented by this proxy. May be null if the class represented + * by this proxy is not an inner class. + */ + public final ClassProxy containerClass; + + /** + * Protected constructor, called only from the builder. + * @param name the name of the class + * @param type the {@link Type} of the class + * @param modifiers the modifiers of the class + * @param parent the FQN of the parent class of the class + */ + protected ClassProxy(String name, Type type, int modifiers, String parent) { + super(name, type, modifiers, parent); + this.fqn = String.format("%s.%s", name, parent); + this.containerClass = null; + } + + /** + * Protected constructor, called only from the builder. + * @param name the name of the class + * @param type the {@link Type} of the class + * @param modifiers the modifiers of the class + * @param containerClass the FQN of the parent class of the class + */ + protected ClassProxy(String name, Type type, int modifiers, ClassProxy containerClass) { + super(name, type, modifiers, containerClass.fqn); + this.fqn = String.format("%s$%s", name, parent); + this.containerClass = containerClass; + } + + /** + * Builds a {@link ClassProxy} given only the fully-qualified name and modifiers. + * @param fqn the fully qualified name of the desired class + * @param modifiers the access modifiers of the desired class + * @return the built {@link ClassProxy} + */ + protected static ClassProxy from(String fqn, int modifiers) { + Type type = Type.getObjectType(fqn.replace('.', '/')); + if(fqn.contains("$")) { + String[] split = fqn.split("\\$"); + String simpleName = split[split.length - 1]; + ClassProxy parentClass = from(fqn.replace("$" + simpleName, ""), 0); + return new ClassProxy(simpleName, type, modifiers, parentClass); + } else { + String[] split = fqn.split("\\."); + String simpleName = split[split.length - 1]; + String parent = fqn.replace("." + simpleName, ""); + return new ClassProxy(simpleName, type, modifiers, parent); + } + } + + /** + * Builds a {@link ClassProxy} from a {@link Class} object. + * @param clazz the {@link Class} object representing the target class + * @return the built {@link ClassProxy} + */ + protected static ClassProxy from(Class clazz) { + if(clazz.getEnclosingClass() == null) + return new ClassProxy( + clazz.getSimpleName(), + Type.getType(clazz), + clazz.getModifiers(), + clazz.getPackage().getName() + ); + else + return new ClassProxy( + clazz.getSimpleName(), + Type.getType(clazz), + clazz.getModifiers(), + from(clazz.getEnclosingClass()) + ); + } + + /** + * Returns a new instance of {@link ClassProxy.Builder}. + * @param name the name of the class + * @return the builder object for class proxies + */ + public static Builder builder(String name) { + return new Builder(name); + } + + /** + * A builder object for {@link ClassProxy}. + */ + public static class Builder extends AbstractProxy.Builder { + + private ClassProxy containerClass; + + /** + * The constructor of the builder, used only internally. + * @param name the "simple name" of the class + */ + Builder(String name) { + super(name); + this.containerClass = null; + } + + /** + * Sets this class as an inner class and sets the containing + * class to the given class object. + * @param containerClass the {@link Class} representing the + * container class + * @return the builder's state after the change + */ + public Builder setContainerClass(Class containerClass) { + this.containerClass = ClassProxy.from(containerClass); + return this; + } + + /** + * Sets this class as an inner class and sets the containing + * class to the given proxy. + * @param containerClass the {@link ClassProxy} representing + * the container class + * @return the builder's state after the change + */ + public Builder setContainerClass(ClassProxy containerClass) { + this.containerClass = containerClass; + return this; + } + + /** + * Sets this class as an inner class and builds a {@link ClassProxy} + * from the given parent and modifiers. + * @param parentFQN the fully qualified name of the parent + * @return the builder's state after the change + */ + public Builder setParent(String parentFQN, int modifiers) { + return this.setContainerClass(ClassProxy.from(parentFQN, modifiers)); + } + + /** + * Sets this class as an inner class and builds a {@link ClassProxy} + * from the given parent. + * @param parentFQN the fully qualified name of the parent + * @return the builder's state after the change + */ + @Override + public Builder setParent(String parentFQN) { + return this.setParent(parentFQN, 0); + } + + /** + * Builds a {@link ClassProxy} of the given kind. + * @return the built {@link ClassProxy} + */ + @Override + public ClassProxy build() { + if(this.containerClass == null) + return new ClassProxy(this.name, this.type, this.modifiers, this.parent); + else return new ClassProxy(this.name, this.type, this.modifiers, this.containerClass); + } + } +} \ No newline at end of file diff --git a/src/main/java/ftbsc/lll/tools/DescriptorBuilder.java b/src/main/java/ftbsc/lll/tools/DescriptorBuilder.java index d59f97d..252c2a8 100644 --- a/src/main/java/ftbsc/lll/tools/DescriptorBuilder.java +++ b/src/main/java/ftbsc/lll/tools/DescriptorBuilder.java @@ -26,16 +26,16 @@ public class DescriptorBuilder { * Initialises default values. */ public DescriptorBuilder() { - this.returnType = Type.getDescriptor(void.class); this.params = new ArrayList<>(); } /** * Sets the return type to the given type. - * WARNING: will most likely cause problems if used with objects outside the - * Java SDK. Pass the fully qualified name as a String rather than the Class - * object for non-standard types (such as Minecraft classes). + * @implNote Passing a {@link Class} may cause problems if used with objects outside + * the Java SDK. Pass the fully qualified name as a {@link String} rather + * than the {@link Class} object for non-standard types (such as Minecraft + * classes). * @param returnType the Class object corresponding to the return type * @return the builder's state after the change */ @@ -72,9 +72,10 @@ public class DescriptorBuilder { /** * Adds a parameter of the given class type to the method. * Parameter order matters. - * WARNING: will most likely cause problems if used with objects outside the - * Java SDK. Pass the fully qualified name as a String rather than the Class - * object for non-standard types (such as Minecraft classes). + * @implNote Passing a {@link Class} may cause problems if used with objects outside + * the Java SDK. Pass the fully qualified name as a {@link String} rather + * than the {@link Class} object for non-standard types (such as Minecraft + * classes). * @param param the Class object corresponding to the parameter * @return the builder's state after the change */