From 884cefead9e4fede7243650afc4f22f08f8e5090 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Tue, 21 Mar 2023 16:32:19 +0100 Subject: [PATCH] feat: expanded ClassProxies, now all fields and methods include classproxies to represent their parents (as well as parameters and return type for methods) --- .../java/ftbsc/lll/proxies/AbstractProxy.java | 33 +++-- .../java/ftbsc/lll/proxies/ClassProxy.java | 122 ++++++++---------- .../java/ftbsc/lll/proxies/FieldProxy.java | 37 +++++- .../java/ftbsc/lll/proxies/MethodProxy.java | 90 +++++++++---- .../java/ftbsc/lll/proxies/PackageProxy.java | 51 ++++++++ .../ftbsc/lll/proxies/QualifiableProxy.java | 68 ++++++++++ .../lll/tools/nodes/FieldProxyInsnNode.java | 7 +- .../lll/tools/nodes/MethodProxyInsnNode.java | 7 +- 8 files changed, 300 insertions(+), 115 deletions(-) create mode 100644 src/main/java/ftbsc/lll/proxies/PackageProxy.java create mode 100644 src/main/java/ftbsc/lll/proxies/QualifiableProxy.java diff --git a/src/main/java/ftbsc/lll/proxies/AbstractProxy.java b/src/main/java/ftbsc/lll/proxies/AbstractProxy.java index 4dd2a72..93eaec8 100644 --- a/src/main/java/ftbsc/lll/proxies/AbstractProxy.java +++ b/src/main/java/ftbsc/lll/proxies/AbstractProxy.java @@ -14,7 +14,6 @@ public abstract class AbstractProxy { */ public final String name; - /** * The {@link Type} corresponding to this element. */ @@ -24,7 +23,7 @@ public abstract class AbstractProxy { * The fully qualified name (i.e. java.lang.String) of * the parent class. */ - public final String parent; + public final QualifiableProxy parent; /** * The modifiers of the element, as a packed int. @@ -39,13 +38,29 @@ public abstract class AbstractProxy { * @param modifiers the modifiers, as a packed int * @param parent the FQN of the parent class */ - protected AbstractProxy(String name, Type type, int modifiers, String parent) { + protected AbstractProxy(String name, Type type, int modifiers, QualifiableProxy parent) { this.name = name; this.type = type; this.modifiers = modifiers; this.parent = parent; } + /** + * Indicates whether the given object is a proxy for the same element as this. + * @param obj the object to perform + * @return true if it's equal + */ + @Override + public boolean equals(Object obj) { + if(obj instanceof AbstractProxy) { + AbstractProxy p = (AbstractProxy) obj; + return p.parent.equals(this.parent) + && p.name.equals(this.name) + && p.modifiers == this.modifiers + && p.type.equals(this.type); + } else return false; + } + /** * A Builder for the generic proxy. * @param the type of proxy @@ -65,7 +80,7 @@ public abstract class AbstractProxy { /** * The fully qualified name of the parent. */ - protected String parent; + protected QualifiableProxy parent; /** * The {@link Type} corresponding to the element. @@ -81,7 +96,6 @@ public abstract class AbstractProxy { this.modifiers = 0; } - /** * @param newModifier the modifier to add * @return the current state of the builder @@ -101,11 +115,11 @@ public abstract class AbstractProxy { } /** - * @param parentFQN the fully qualified name of the parent + * @param parent the {@link QualifiableProxy} representing the parent * @return the current state of the builder */ - public Builder setParent(String parentFQN) { - this.parent = parentFQN; + public Builder setParent(QualifiableProxy parent) { + this.parent = parent; return this; } @@ -118,7 +132,6 @@ public abstract class AbstractProxy { return this; } - /** * Sets {@link Type} for this element from the descriptor, passed as a {@link String}. * @param descriptor the descriptor passed as a {@link String} @@ -128,8 +141,6 @@ public abstract class AbstractProxy { return this.setType(Type.getType(descriptor)); } - - /** * @return the built proxy object */ diff --git a/src/main/java/ftbsc/lll/proxies/ClassProxy.java b/src/main/java/ftbsc/lll/proxies/ClassProxy.java index c369231..41e9bb8 100644 --- a/src/main/java/ftbsc/lll/proxies/ClassProxy.java +++ b/src/main/java/ftbsc/lll/proxies/ClassProxy.java @@ -2,36 +2,25 @@ package ftbsc.lll.proxies; import org.objectweb.asm.Type; +import java.lang.reflect.Modifier; + +import static ftbsc.lll.tools.DescriptorBuilder.nameToDescriptor; + /** * 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; - +public class ClassProxy extends QualifiableProxy { /** * 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 + * @param parent the package containing this 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; + super(type, modifiers, PackageProxy.from(parent), String.format("%s.%s", name, parent)); } /** @@ -41,31 +30,35 @@ public class ClassProxy extends AbstractProxy { * @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; + protected ClassProxy(String name, Type type, int modifiers, QualifiableProxy containerClass) { + super(type, modifiers, containerClass, String.format("%s$%s", name, containerClass.fullyQualifiedName)); + } + + /** + * Builds a {@link ClassProxy} from a {@link Type} and modifiers. + * @param type the {@link Type} representing this Class + * @param modifiers the modifiers of the class + */ + public static ClassProxy from(Type type, int modifiers) { + String fqn = type.getInternalName().replace('/', '.'); + String simpleName = extractSimpleNameFromFQN(fqn); + String parent = extractParentFromFQN(fqn); + if(fqn.contains("$")) + return new ClassProxy(simpleName, type, modifiers, from(parent, 0, Modifier.PUBLIC)); + else return new ClassProxy(simpleName, type, modifiers, parent); } /** * Builds a {@link ClassProxy} given only the fully-qualified name and modifiers. * @param fqn the fully qualified name of the desired class + * @param arrayLevel the array level for this type * @param modifiers the access modifiers of the desired class + * @implNote If present, parent classes will be assumed to have {@code public} as + * their only modifier. * @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); - } + protected static ClassProxy from(String fqn, int arrayLevel, int modifiers) { + return from(Type.getObjectType(nameToDescriptor(fqn, arrayLevel)), modifiers); } /** @@ -73,8 +66,9 @@ public class ClassProxy extends AbstractProxy { * @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) + public static ClassProxy from(Class clazz) { + Class parentClass = clazz.getEnclosingClass(); + if(parentClass == null) return new ClassProxy( clazz.getSimpleName(), Type.getType(clazz), @@ -86,7 +80,7 @@ public class ClassProxy extends AbstractProxy { clazz.getSimpleName(), Type.getType(clazz), clazz.getModifiers(), - from(clazz.getEnclosingClass()) + from(parentClass) ); } @@ -99,20 +93,27 @@ public class ClassProxy extends AbstractProxy { return new Builder(name); } + /** + * Indicates whether the given object is a proxy for the same element as this. + * @param obj the object to perform + * @return true if it's equal + */ + @Override + public boolean equals(Object obj) { + return obj instanceof ClassProxy && super.equals(obj); + } + /** * 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; } /** @@ -122,42 +123,33 @@ public class ClassProxy extends AbstractProxy { * 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; + public Builder setParent(Class containerClass) { + super.setParent(ClassProxy.from(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 + * @param parentFQN the fully qualified name of the parent + * @param modifiers the modifiers of the parent (if it's a class) + * @param isParentPackage whether this parent should be interpreted as a package or class * @return the builder's state after the change */ - public Builder setParent(String parentFQN, int modifiers) { - return this.setContainerClass(ClassProxy.from(parentFQN, modifiers)); + public Builder setParent(String parentFQN, int modifiers, boolean isParentPackage) { + super.setParent(isParentPackage ? PackageProxy.from(parentFQN) : ClassProxy.from(parentFQN, 0, modifiers)); + return this; } /** * 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 + * @param parentFQN the fully qualified name of the parent + * @param isParentPackage whether this parent should be interpreted as a package or class * @return the builder's state after the change */ - @Override - public Builder setParent(String parentFQN) { - return this.setParent(parentFQN, 0); + public Builder setParent(String parentFQN, boolean isParentPackage) { + return this.setParent(parentFQN, 0, isParentPackage); } /** @@ -166,9 +158,7 @@ public class ClassProxy extends AbstractProxy { */ @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); + return new ClassProxy(this.name, this.type, this.modifiers, this.parent); } } } \ No newline at end of file diff --git a/src/main/java/ftbsc/lll/proxies/FieldProxy.java b/src/main/java/ftbsc/lll/proxies/FieldProxy.java index 72a4e40..8855cd8 100644 --- a/src/main/java/ftbsc/lll/proxies/FieldProxy.java +++ b/src/main/java/ftbsc/lll/proxies/FieldProxy.java @@ -16,7 +16,7 @@ public class FieldProxy extends AbstractProxy { * @param f the {@link Field} object corresponding to this. */ public FieldProxy(Field f) { - super(f.getName(), Type.getType(f.getType()), f.getModifiers(), Type.getInternalName(f.getDeclaringClass())); + super(f.getName(), Type.getType(f.getType()), f.getModifiers(), ClassProxy.from(f.getDeclaringClass())); } /** @@ -24,9 +24,9 @@ public class FieldProxy extends AbstractProxy { * @param name the name of the field * @param type the {@link Type} of the field * @param modifiers the modifiers of the field - * @param parent the FQN of the parent class of the field + * @param parent the {@link QualifiableProxy} for the parent */ - protected FieldProxy(String name, Type type, int modifiers, String parent) { + protected FieldProxy(String name, Type type, int modifiers, QualifiableProxy parent) { super(name, type, modifiers, parent); } @@ -39,6 +39,16 @@ public class FieldProxy extends AbstractProxy { return new Builder(name); } + /** + * Indicates whether the given object is a proxy for the same element as this. + * @param obj the object to perform + * @return true if it's equal + */ + @Override + public boolean equals(Object obj) { + return obj instanceof FieldProxy && super.equals(obj); + } + /** * A builder object for {@link FieldProxy}. */ @@ -51,6 +61,27 @@ public class FieldProxy extends AbstractProxy { super(name); } + /** + * Sets the parent class of this field to the one described by the + * fully qualified name and with the given 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) { + super.setParent(ClassProxy.from(parentFQN, 0, modifiers)); + return this; + } + + /** + * Sets the parent class of this field to the one described by the + * fully qualified name. + * @param parentFQN the fully qualified name of the parent + * @return the builder's state after the change + */ + public Builder setParent(String parentFQN) { + return this.setParent(parentFQN, 0); + } + /** * Builds a {@link FieldProxy} of the given kind. * @return the built {@link FieldProxy} diff --git a/src/main/java/ftbsc/lll/proxies/MethodProxy.java b/src/main/java/ftbsc/lll/proxies/MethodProxy.java index 5f80c08..5d014e9 100644 --- a/src/main/java/ftbsc/lll/proxies/MethodProxy.java +++ b/src/main/java/ftbsc/lll/proxies/MethodProxy.java @@ -4,6 +4,7 @@ import org.objectweb.asm.Type; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static ftbsc.lll.tools.DescriptorBuilder.nameToDescriptor; @@ -16,14 +17,30 @@ import static ftbsc.lll.tools.DescriptorBuilder.nameToDescriptor; public class MethodProxy extends AbstractProxy { /** - * The parameters of the method. + * An array of {@link ClassProxy} each representing the parameters of the method. */ - public final Type[] parameters; + public final ClassProxy[] parameters; /** - * The return type of the method. + * The {@link ClassProxy} for the return type of the method. */ - public final Type returnType; + public final ClassProxy returnType; + + /** + * A protected constructor, called only from the builder. + * @param name the name of the method + * @param modifiers the modifiers of the method + * @param parent the {@link QualifiableProxy} for the parent + * @param parameters the parameters of the method + * @param returnType the return type of the method + */ + protected MethodProxy(String name, int modifiers, QualifiableProxy parent, Type[] parameters, Type returnType) { + super(name, Type.getMethodType(returnType, parameters), modifiers, parent); + this.parameters = Arrays.stream(parameters) + .map(t -> ClassProxy.from(t, 0)) + .toArray(ClassProxy[]::new); + this.returnType = ClassProxy.from(returnType, 0); + } /** * A public constructor, builds a proxy from a {@link Method} @@ -31,24 +48,12 @@ public class MethodProxy extends AbstractProxy { * @param m the {@link Method} object corresponding to this. */ public MethodProxy(Method m) { - super(m.getName(), Type.getType(m), m.getModifiers(), Type.getInternalName(m.getDeclaringClass())); - Type mt = Type.getType(m); - this.parameters = mt.getArgumentTypes(); - this.returnType = mt.getReturnType(); - } - - /** - * A protected constructor, called only from the builder. - * @param name the name of the method - * @param modifiers the modifiers of the method - * @param parent the FQN of the parent class of the method - * @param parameters the parameters of the method - * @param returnType the return type of the method - */ - protected MethodProxy(String name, int modifiers, String parent, Type[] parameters, Type returnType) { - super(name, Type.getMethodType(returnType, parameters), modifiers, parent); - this.parameters = parameters; - this.returnType = returnType; + this(m.getName(), + m.getModifiers(), + ClassProxy.from(m.getDeclaringClass()), + Type.getArgumentTypes(m), + Type.getReturnType(m) + ); } /** @@ -60,6 +65,19 @@ public class MethodProxy extends AbstractProxy { return new Builder(name); } + /** + * Indicates whether the given object is a proxy for the same element as this. + * @param obj the object to perform + * @return true if it's equal + */ + @Override + public boolean equals(Object obj) { + if(obj instanceof MethodProxy) { + MethodProxy m = (MethodProxy) obj; + return super.equals(obj) && m.returnType.equals(this.returnType) && Arrays.equals(m.parameters, this.parameters); + } else return false; + } + /** * A builder object for {@link MethodProxy}. */ @@ -117,6 +135,27 @@ public class MethodProxy extends AbstractProxy { return this; } + /** + * Sets the parent class of this method to the one described by the + * fully qualified name and with the given 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) { + super.setParent(ClassProxy.from(parentFQN, 0, modifiers)); + return this; + } + + /** + * Sets the parent class of this method to the one described by the + * fully qualified name. + * @param parentFQN the fully qualified name of the parent + * @return the builder's state after the change + */ + public Builder setParent(String parentFQN) { + return this.setParent(parentFQN, 0); + } + /** * Sets the return type to the given type. * @param returnType the {@link Class} object corresponding to @@ -134,7 +173,12 @@ public class MethodProxy extends AbstractProxy { */ @Override public MethodProxy build() { - return new MethodProxy(name, modifiers, parent, parameters.toArray(new Type[0]), returnType); + return new MethodProxy( + this.name, + this.modifiers, + this.parent, + this.parameters.toArray(new Type[0]), + this.returnType); } } } diff --git a/src/main/java/ftbsc/lll/proxies/PackageProxy.java b/src/main/java/ftbsc/lll/proxies/PackageProxy.java new file mode 100644 index 0000000..fbfe6c0 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/PackageProxy.java @@ -0,0 +1,51 @@ +package ftbsc.lll.proxies; + +/** + * A container for information about a package. + * @since 0.4.0 + */ +public class PackageProxy extends QualifiableProxy { + + /** + * The {@link PackageProxy} representing the root package. + */ + public static final PackageProxy ROOT = new PackageProxy(null, ""); + + /** + * The protected constructor, called only from {@link PackageProxy#from(String)}. + * @param parent the {@link PackageProxy} representing the parent + * @param fqn the fully-qualified name of this package + */ + protected PackageProxy(PackageProxy parent, String fqn) { + super(null, 0, parent, fqn); + } + + /** + * Builds a {@link PackageProxy} from its fully-qualified name. + * @param fqn the fully-qualified name of the package + * @return the built {@link PackageProxy} + */ + protected static PackageProxy from(String fqn) { + if(fqn == null || fqn.equals("")) return ROOT; + return new PackageProxy(from(extractParentFromFQN(fqn)), fqn); + } + + /** + * Builds a {@link PackageProxy} from a reflective {@link Package} object. + * @param p the {@link Package} object + * @return the built {@link PackageProxy} + */ + protected static PackageProxy from(Package p) { + return from(extractParentFromFQN(p.getName())); + } + + /** + * Indicates whether the given object is a proxy for the same element as this. + * @param obj the object to perform + * @return true if it's equal + */ + @Override + public boolean equals(Object obj) { + return obj instanceof PackageProxy && super.equals(obj); + } +} diff --git a/src/main/java/ftbsc/lll/proxies/QualifiableProxy.java b/src/main/java/ftbsc/lll/proxies/QualifiableProxy.java new file mode 100644 index 0000000..2efada8 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/QualifiableProxy.java @@ -0,0 +1,68 @@ +package ftbsc.lll.proxies; + +import org.objectweb.asm.Type; + +/** + * A container for information about an element which has a fully-qualified name. + * @see ClassProxy + * @see PackageProxy + * @since 0.4.0 + */ +public abstract class QualifiableProxy extends AbstractProxy { + /** + * The fully-qualified name of the element represented by this proxy. + */ + public final String fullyQualifiedName; + + /** + * The "internal name" (fully-qualified with slashes) of the element + * represented by this proxy. + */ + public final String internalName; + + /** + * The protected constructor, should be called by all classes extending this in theirs. + * @param type the {@link Type} for the element + * @param modifiers the modifiers, as a packed int + * @param parent the {@link QualifiableProxy} representing the parent of this element + * @param fullyQualifiedName the FQN of the element + */ + protected QualifiableProxy(Type type, int modifiers, QualifiableProxy parent, String fullyQualifiedName) { + super(extractSimpleNameFromFQN(fullyQualifiedName), type, modifiers, parent); + this.fullyQualifiedName = fullyQualifiedName; + this.internalName = this.fullyQualifiedName.replace('.', '/'); + } + + /** + * Returns a {@link String} containing the FQN of the parent element + * to this, which may represent a package or class. + * @return the parent, or null if the parent was the root element + */ + protected static String extractParentFromFQN(String fqn) { + String lastSeparator = fqn.contains("$") ? "\\$" : "\\."; + String[] split = fqn.split(lastSeparator); + if(split.length == 1) return null; + return fqn.substring(0, split[split.length - 1].length() - 1); + } + + /** + * Returns a {@link String} containing the simple name of the element + * @return the simple name + */ + protected static String extractSimpleNameFromFQN(String fqn) { + String lastSeparator = fqn.contains("$") ? "\\$" : "\\."; + String[] split = fqn.split(lastSeparator); + if(split.length == 1) return fqn; + else return split[split.length - 1]; + } + + /** + * Indicates whether the given object is a proxy for the same element as this. + * @param obj the object to perform + * @return true if it's equal + */ + @Override + public boolean equals(Object obj) { + return obj instanceof QualifiableProxy && super.equals(obj) && ((QualifiableProxy) obj).fullyQualifiedName.equals(fullyQualifiedName); + } +} diff --git a/src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java b/src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java index d23686d..479e651 100644 --- a/src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java +++ b/src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java @@ -17,11 +17,6 @@ public class FieldProxyInsnNode extends FieldInsnNode { * @param f a {@link FieldProxy} representing the field to call */ public FieldProxyInsnNode(int opcode, FieldProxy f) { - super( - opcode, - f.parent.replace('.', '/'), - f.name, - f.type.getDescriptor() - ); + super(opcode, f.parent.internalName, f.name, f.type.getDescriptor()); } } diff --git a/src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java b/src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java index 904615d..4fb3b89 100644 --- a/src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java +++ b/src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java @@ -18,11 +18,6 @@ public class MethodProxyInsnNode extends MethodInsnNode { * @param m a {@link MethodProxy} representing the method to call */ public MethodProxyInsnNode(int opcode, MethodProxy m) { - super( - opcode, - m.parent.replace('.', '/'), - m.name, - m.type.getDescriptor() - ); + super(opcode, m.parent.internalName, m.name, m.type.getDescriptor()); } }