diff --git a/src/main/java/ftbsc/lll/proxies/AbstractProxy.java b/src/main/java/ftbsc/lll/proxies/AbstractProxy.java index 4c57e20..ff320f0 100644 --- a/src/main/java/ftbsc/lll/proxies/AbstractProxy.java +++ b/src/main/java/ftbsc/lll/proxies/AbstractProxy.java @@ -1,68 +1,71 @@ package ftbsc.lll.proxies; import java.lang.reflect.Modifier; +import org.objectweb.asm.Type; /** - * Abstract proxy class, implementing common aspects - * of {@link MethodProxy} and {@link FieldProxy}. + * Abstract proxy class, implementing common aspects. * @since 0.3.0 */ public abstract class AbstractProxy { /** - * The name of the corresponding class member. + * Which type of proxy this is. */ - private final String name; + public final ProxyType proxyType; + + /** + * The name of the corresponding element. + */ + public final String name; + + /** + * The descriptor for this element. + */ + public final String descriptor; /** * The fully qualified name (i.e. java.lang.String) of * the parent class. */ - private final String parent; + public final QualifiableProxy parent; /** - * The modifiers of the member, as a packed int. - * @see java.lang.reflect.Modifier - */ - private final int modifiers; - - /** - * @return the name of the item - */ - public String getName() { - return this.name; - } - - /** - * @return the modifiers of the member, as a packed int + * The modifiers of the member, as a packed int * @see Modifier */ - public int getModifiers() { - return this.modifiers; - } - - /** - * @return the fully qualified name of the parent class - */ - public String getParent() { - return this.parent; - } - - /** - * @return the descriptor of the member - */ - public abstract String getDescriptor(); + public final int modifiers; /** * The private constructor, should be called by all classes extending this in theirs. - * @param name the name of the member + * @param name the name of the element + * @param descriptor the descriptor for the element * @param modifiers the modifiers, as a packed int * @param parent the FQN of the parent class + * @param proxyType the {@link ProxyType} being represented here */ - protected AbstractProxy(String name, int modifiers, String parent) { + protected AbstractProxy(String name, String descriptor, int modifiers, QualifiableProxy parent, ProxyType proxyType) { this.name = name; + this.descriptor = descriptor; this.modifiers = modifiers; this.parent = parent; + this.proxyType = proxyType; + } + + /** + * 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.descriptor.equals(this.descriptor); + } else return false; } /** @@ -72,38 +75,34 @@ public abstract class AbstractProxy { public abstract static class Builder { /** - * The name of the member. + * The name of the element. */ - protected final String name; + protected String name; /** - * The modifiers of the member, as a packed int. + * The modifiers of the element, as a packed int. */ protected int modifiers; /** * The fully qualified name of the parent. */ - protected String parent; + protected QualifiableProxy parent; + + /** + * The descriptor of the element. + */ + protected String descriptor; /** * The constructor. - * @param name the name of the member + * @param name the name of the element */ protected Builder(String name) { this.name = name; this.modifiers = 0; } - /** - * @param parentFQN the fully qualified name of the parent - * @return the current state of the builder - */ - public Builder setParent(String parentFQN) { - this.parent = parentFQN; - return this; - } - /** * @param newModifier the modifier to add * @return the current state of the builder @@ -117,11 +116,38 @@ public abstract class AbstractProxy { * @param newModifier the new modifier value * @return the current state of the builder */ - public Builder setModifier(int newModifier) { + public Builder setModifiers(int newModifier) { this.modifiers = newModifier; return this; } + /** + * @param parent the {@link QualifiableProxy} representing the parent + * @return the current state of the builder + */ + public Builder setParent(QualifiableProxy parent) { + this.parent = parent; + 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} + * @return the builder's state after the change + */ + public Builder setDescriptor(String descriptor) { + this.descriptor = descriptor; + return this; + } + + /** + * @param type the {@link Type} corresponding to the element + * @return the current state of the builder + */ + public Builder setType(Type type) { + return this.setDescriptor(type.getDescriptor()); + } + /** * @return the built proxy object */ diff --git a/src/main/java/ftbsc/lll/proxies/FieldProxy.java b/src/main/java/ftbsc/lll/proxies/FieldProxy.java deleted file mode 100644 index 0edd735..0000000 --- a/src/main/java/ftbsc/lll/proxies/FieldProxy.java +++ /dev/null @@ -1,116 +0,0 @@ -package ftbsc.lll.proxies; - -import ftbsc.lll.tools.DescriptorBuilder; -import org.objectweb.asm.Type; - -import java.lang.reflect.Field; - -/** - * A container for information about class fields to be used - * in ASM patching. - * @since 0.3.0 - */ -public class FieldProxy extends AbstractProxy { - - /** - * The descriptor of the field's type. - */ - private final String typeDescriptor; - - /** - * A public constructor, builds a proxy from a {@link Field} - * obtained from reflection. - * @param f the {@link Field} object corresponding to this. - */ - public FieldProxy(Field f) { - super(f.getName(), f.getModifiers(), Type.getInternalName(f.getDeclaringClass())); - this.typeDescriptor = Type.getDescriptor(f.getType()); - } - - /** - * A protected constructor, called only from the builder. - * @param name the name of the field - * @param modifiers the modifiers of the field - * @param parent the FQN of the parent class of the field - * @param typeDescriptor the type descriptor of the field - */ - FieldProxy(String name, int modifiers, String parent, String typeDescriptor) { - super(name, modifiers, parent); - this.typeDescriptor = typeDescriptor; - } - - /** - * @return the field's type descriptor - */ - @Override - public String getDescriptor() { - return typeDescriptor; - } - - /** - * Returns a new instance of {@link FieldProxy.Builder}. - * @param name the name of the field - * @return the builder object for field proxies - */ - public static Builder builder(String name) { - return new Builder(name); - } - - /** - * A builder object for {@link FieldProxy}. - */ - public static class Builder extends AbstractProxy.Builder { - /** - * The descriptor of the field's type. - */ - private String typeDescriptor; - - /** - * The constructor of the builder, used only internally. - * @param name the name of the field - */ - Builder(String name) { - super(name); - } - - /** - * Sets the descriptor of the field type to the given {@link String}. - * @param typeDescriptor the descriptor of the field type - * @return the builder's state after the change - */ - public Builder setDescriptor(String typeDescriptor) { - this.typeDescriptor = typeDescriptor; - return this; - } - - /** - * Sets the descriptor of the field type to match the give {@link Class}. - * @param fqn the fully qualified name of the field type - * @param arrayLevel the array level of the field type - * @return the builder's state after the change - */ - public Builder setType(String fqn, int arrayLevel) { - this.typeDescriptor = DescriptorBuilder.nameToDescriptor(fqn, arrayLevel); - return this; - } - - /** - * Sets the descriptor of the field type to match the give {@link Class}. - * @param type a {@link Class} object representing the field type - * @return the builder's state after the change - */ - public Builder setType(Class type) { - this.typeDescriptor = Type.getDescriptor(type); - return this; - } - - /** - * Builds a {@link FieldProxy} of the given kind. - * @return the built {@link FieldProxy} - */ - @Override - public FieldProxy build() { - return new FieldProxy(this.name, this.modifiers, this.parent, this.typeDescriptor); - } - } -} diff --git a/src/main/java/ftbsc/lll/proxies/MethodProxy.java b/src/main/java/ftbsc/lll/proxies/MethodProxy.java deleted file mode 100644 index b9abe84..0000000 --- a/src/main/java/ftbsc/lll/proxies/MethodProxy.java +++ /dev/null @@ -1,230 +0,0 @@ -package ftbsc.lll.proxies; - -import ftbsc.lll.tools.DescriptorBuilder; -import org.objectweb.asm.Type; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -/** - * A container for information about class methods to be used - * in ASM patching. - * @since 0.3.0 - */ -public class MethodProxy extends AbstractProxy { - - /** - * The parameters of the method. - * It holds fully qualified names for objects, and {@link Class} - * objects for primitives. - */ - private final Object[] parameters; - - /** - * The return type of the method. - * It contains if it's an object, or a {@link Class} - * object for primitives. - */ - private final Object returnType; - - /** - * Caches the the descriptor after generating it once for - * performance. - */ - private String descriptorCache; - - /** - * A public constructor, builds a proxy from a {@link Method} - * obtained from reflection. - * @param m the {@link Method} object corresponding to this. - */ - public MethodProxy(Method m) { - super(m.getName(), m.getModifiers(), Type.getInternalName(m.getDeclaringClass())); - List parameters = new ArrayList<>(); - for(Class p : m.getParameterTypes()) - parameters.add(p.isPrimitive() ? p : new TypeContainer(p)); - this.parameters = parameters.toArray(); - Class returnType = m.getReturnType(); - this.returnType = returnType.isPrimitive() ? returnType : new TypeContainer(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 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, Object[] parameters, Object returnType) { - super(name, modifiers, parent); - this.parameters = parameters; - this.returnType = returnType; - this.descriptorCache = null; - } - - /** - * Builds (or returns from cache if present) - * the method's descriptor. - * @return the method's descriptor - */ - @Override - public String getDescriptor() { - if(this.descriptorCache != null) - return this.descriptorCache; - DescriptorBuilder b = new DescriptorBuilder(); - for(Object p : this.parameters) - addTypeToDescriptorBuilder(b, p, false); - addTypeToDescriptorBuilder(b, this.returnType, true); - this.descriptorCache = b.build(); - return this.descriptorCache; - } - - /** - * A static method used internally to detect and correctly insert a - * {@link TypeContainer} into a {@link DescriptorBuilder}. - * @param b the {@link DescriptorBuilder} - * @param p the {@link TypeContainer} - * @param isReturnType whether it should be inserted as a return type - */ - private static void addTypeToDescriptorBuilder(DescriptorBuilder b, Object p, boolean isReturnType) { - if(p instanceof TypeContainer) { - TypeContainer param = (TypeContainer) p; - if(isReturnType) - b.setReturnType(param.fqn, param.arrayLevel); - else b.addParameter(param.fqn, param.arrayLevel); - } else { - if(isReturnType) - b.setReturnType((Class) p); - else b.addParameter((Class) p); - } - } - - /** - * Returns a new instance of {@link MethodProxy.Builder}. - * @param name the name of the method - * @return the builder object for method proxies - */ - public static Builder builder(String name) { - return new Builder(name); - } - - /** - * A builder object for {@link MethodProxy}. - */ - public static class Builder extends AbstractProxy.Builder { - /** - * The parameters of the method. - */ - private final List parameters; - - /** - * The return type of the method. Defaults to void. - */ - private Object returnType; - - /** - * The constructor of the builder, used only internally. - * @param name the name of the method - */ - Builder(String name) { - super(name); - this.parameters = new ArrayList<>(); - this.returnType = void.class; - } - - /** - * Adds a parameter of a given type. - * @param fqn the fully qualified name of the parameter type - * @param arrayLevel the array level of the parameter type - * @return the builder's state after the change - */ - public Builder addParameter(String fqn, int arrayLevel) { - this.parameters.add(new TypeContainer(fqn, arrayLevel)); - return this; - } - - /** - * Adds a parameter of a given type. - * @param paramType the {@link Class} object corresponding to - * the parameter type. - * @return the builder's state after the change - */ - public Builder addParameter(Class paramType) { - this.parameters.add(paramType); - return this; - } - - /** - * Sets the return type to the given type. - * @param fqn the fully qualified name of the return type - * @param arrayLevel the array level of the return type - * @return the builder's state after the change - */ - public Builder setReturnType(String fqn, int arrayLevel) { - this.returnType = new TypeContainer(fqn, arrayLevel); - return this; - } - - /** - * Sets the return type to the given type. - * @param returnType the {@link Class} object corresponding to - * the return type - * @return the builder's state after the change - */ - public Builder setReturnType(Class returnType) { - this.returnType = returnType; - return this; - } - - /** - * Builds a {@link MethodProxy} of the given kind. - * @return the built {@link MethodProxy} - */ - @Override - public MethodProxy build() { - return new MethodProxy(name, modifiers, parent, parameters.toArray(), returnType); - } - } - - /** - * A container class, holding information about a given type. - */ - protected static class TypeContainer { - /** - * The fully qualified name of the type. - */ - public final String fqn; - - /** - * The array level of the type. - */ - public final int arrayLevel; - - /** - * Public constructor for the class. - * @param fqn the fully qualified name of the type - * @param arrayLevel the array level of the type - */ - public TypeContainer(String fqn, int arrayLevel) { - this.fqn = fqn; - this.arrayLevel = arrayLevel; - } - - /** - * Public constructor for the class, extracting the - * necessary information from a {@link Class} object. - * @param clazz the class object - */ - public TypeContainer(Class clazz) { - int arrayLevel = 0; - while(clazz.isArray()) { - arrayLevel++; - clazz = clazz.getComponentType(); - } - this.arrayLevel = arrayLevel; - this.fqn = clazz.getCanonicalName(); - } - } -} diff --git a/src/main/java/ftbsc/lll/proxies/ProxyType.java b/src/main/java/ftbsc/lll/proxies/ProxyType.java new file mode 100644 index 0000000..1fd8169 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/ProxyType.java @@ -0,0 +1,11 @@ +package ftbsc.lll.proxies; + +/** + * An enum listing the various proxies. + */ +public enum ProxyType { + FIELD, + METHOD, + TYPE, + PACKAGE +} 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..d245124 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/QualifiableProxy.java @@ -0,0 +1,67 @@ +package ftbsc.lll.proxies; + +/** + * A proxy for elements who have a fully-qualified name. + * @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 descriptor the descriptor 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 + * @param proxyType the {@link ProxyType} being represented here + */ + protected QualifiableProxy(String descriptor, int modifiers, QualifiableProxy parent, String fullyQualifiedName, ProxyType proxyType) { + super(extractSimpleNameFromFQN(fullyQualifiedName), descriptor, modifiers, parent, proxyType); + 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. + * @param fqn the fully qualified name of the element + * @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. + * @param fqn the fully qualified 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/proxies/impl/FieldProxy.java b/src/main/java/ftbsc/lll/proxies/impl/FieldProxy.java new file mode 100644 index 0000000..ceb8277 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/impl/FieldProxy.java @@ -0,0 +1,98 @@ +package ftbsc.lll.proxies.impl; + +import ftbsc.lll.proxies.AbstractProxy; +import ftbsc.lll.proxies.ProxyType; +import ftbsc.lll.proxies.QualifiableProxy; +import org.objectweb.asm.Type; + +import java.lang.reflect.Field; + +/** + * A container for information about class fields to be used + * in ASM patching. + * @since 0.3.0 + */ +public class FieldProxy extends AbstractProxy { + /** + * Protected constructor, called only from the builder. + * @param name the name of the field + * @param descriptor the descriptor of the field + * @param modifiers the modifiers of the field + * @param parent the {@link QualifiableProxy} for the parent + */ + protected FieldProxy(String name, String descriptor, int modifiers, QualifiableProxy parent) { + super(name, descriptor, modifiers, parent, ProxyType.FIELD); + } + + /** + * A public constructor, builds a proxy from a {@link Field} + * obtained from reflection. + * @param f the {@link Field} object corresponding to this. + */ + public FieldProxy(Field f) { + this(f.getName(), Type.getDescriptor(f.getType()), f.getModifiers(), TypeProxy.from(f.getDeclaringClass())); + } + + /** + * Returns a new instance of {@link FieldProxy.Builder}. + * @param name the name of the field + * @return the builder object for field proxies + */ + public static Builder builder(String name) { + 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}. + */ + public static class Builder extends AbstractProxy.Builder { + /** + * The constructor of the builder, used only internally. + * @param name the name of the field + */ + Builder(String name) { + 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 + * @param modifiers the modifiers of the parent + * @return the builder's state after the change + */ + public Builder setParent(String parentFQN, int modifiers) { + super.setParent(TypeProxy.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} + */ + @Override + public FieldProxy build() { + return new FieldProxy(this.name, this.descriptor, this.modifiers, this.parent); + } + } +} diff --git a/src/main/java/ftbsc/lll/proxies/impl/MethodProxy.java b/src/main/java/ftbsc/lll/proxies/impl/MethodProxy.java new file mode 100644 index 0000000..3710d36 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/impl/MethodProxy.java @@ -0,0 +1,188 @@ +package ftbsc.lll.proxies.impl; + +import ftbsc.lll.proxies.AbstractProxy; +import ftbsc.lll.proxies.ProxyType; +import ftbsc.lll.proxies.QualifiableProxy; +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; + +/** + * A container for information about class methods to be used + * in ASM patching. + * @since 0.3.0 + */ +public class MethodProxy extends AbstractProxy { + + /** + * An array of {@link TypeProxy} each representing the parameters of the method. + */ + public final TypeProxy[] parameters; + + /** + * The {@link TypeProxy} for the return type of the method. + */ + public final TypeProxy 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.getMethodDescriptor(returnType, parameters), modifiers, parent, ProxyType.METHOD); + this.parameters = Arrays.stream(parameters) + .map(t -> TypeProxy.from(t, 0)) + .toArray(TypeProxy[]::new); + this.returnType = TypeProxy.from(returnType, 0); + } + + /** + * A public constructor, builds a proxy from a {@link Method} + * obtained from reflection. + * @param m the {@link Method} object corresponding to this. + */ + public MethodProxy(Method m) { + this(m.getName(), + m.getModifiers(), + TypeProxy.from(m.getDeclaringClass()), + Type.getArgumentTypes(m), + Type.getReturnType(m) + ); + } + + /** + * Returns a new instance of {@link MethodProxy.Builder}. + * @param name the name of the method + * @return the builder object for method proxies + */ + public static Builder builder(String name) { + 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}. + */ + public static class Builder extends AbstractProxy.Builder { + /** + * The parameters of the method. + */ + private final List parameters; + + /** + * The return type of the method. Defaults to void. + */ + private Type returnType; + + /** + * The constructor of the builder, used only internally. + * @param name the name of the method + */ + Builder(String name) { + super(name); + this.parameters = new ArrayList<>(); + this.returnType = Type.getType(void.class); + } + + /** + * Adds a parameter of a given type. + * @param fqn the fully qualified name of the parameter type + * @param arrayLevel the array level of the parameter type + * @return the builder's state after the change + */ + public Builder addParameter(String fqn, int arrayLevel) { + this.parameters.add(Type.getType(nameToDescriptor(fqn, arrayLevel))); + return this; + } + + /** + * Adds a parameter of a given type. + * @param paramType the {@link Class} object corresponding to + * the parameter type. + * @return the builder's state after the change + */ + public Builder addParameter(Class paramType) { + this.parameters.add(Type.getType(paramType)); + return this; + } + + /** + * Sets the return type to the given type. + * @param fqn the fully qualified name of the return type + * @param arrayLevel the array level of the return type + * @return the builder's state after the change + */ + public Builder setReturnType(String fqn, int arrayLevel) { + this.returnType = Type.getType(nameToDescriptor(fqn, arrayLevel)); + 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 + * @param modifiers the modifiers of the parent + * @return the builder's state after the change + */ + public Builder setParent(String parentFQN, int modifiers) { + super.setParent(TypeProxy.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 + * the return type + * @return the builder's state after the change + */ + public Builder setReturnType(Class returnType) { + this.returnType = Type.getType(returnType); + return this; + } + + /** + * Builds a {@link MethodProxy} of the given kind. + * @return the built {@link MethodProxy} + */ + @Override + public MethodProxy build() { + 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/impl/PackageProxy.java b/src/main/java/ftbsc/lll/proxies/impl/PackageProxy.java new file mode 100644 index 0000000..5989da3 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/impl/PackageProxy.java @@ -0,0 +1,54 @@ +package ftbsc.lll.proxies.impl; + +import ftbsc.lll.proxies.ProxyType; +import ftbsc.lll.proxies.QualifiableProxy; + +/** + * 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, ProxyType.PACKAGE); + } + + /** + * 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/impl/TypeProxy.java b/src/main/java/ftbsc/lll/proxies/impl/TypeProxy.java new file mode 100644 index 0000000..9f613b0 --- /dev/null +++ b/src/main/java/ftbsc/lll/proxies/impl/TypeProxy.java @@ -0,0 +1,199 @@ +package ftbsc.lll.proxies.impl; + +import ftbsc.lll.proxies.AbstractProxy; +import ftbsc.lll.proxies.ProxyType; +import ftbsc.lll.proxies.QualifiableProxy; +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 TypeProxy extends QualifiableProxy { + /** + * Whether this proxy represents a primitive. + */ + public final boolean primitive; + + /** + * Protected constructor, called only from the builder. + * @param name the name of the class + * @param descriptor the descriptor of the class + * @param modifiers the modifiers of the class + * @param parent the package containing this class + * @param primitive whether the proxy is a primitive + */ + protected TypeProxy(String name, String descriptor, int modifiers, String parent, boolean primitive) { + super(descriptor, modifiers, PackageProxy.from(parent), String.format("%s.%s", name, parent), ProxyType.TYPE); + this.primitive = primitive; + } + + /** + * Protected constructor, called only from the builder. + * @param name the name of the class + * @param descriptor the descriptor of the element + * @param modifiers the modifiers of the class + * @param primitive whether the proxy is a primitive + * @param containerClass the FQN of the parent class of the class + */ + protected TypeProxy(String name, String descriptor, int modifiers, QualifiableProxy containerClass, boolean primitive) { + super(descriptor, modifiers, containerClass, String.format("%s$%s", name, containerClass.fullyQualifiedName), ProxyType.TYPE); + this.primitive = primitive; + } + + /** + * Builds a {@link TypeProxy} from a {@link Type} and modifiers. + * @param type the {@link Type} representing this Class + * @param modifiers the modifiers of the class + * @return the builty {@link TypeProxy} + */ + public static TypeProxy from(Type type, int modifiers) { + while(type.getSort() == Type.ARRAY) + type = type.getElementType(); + String fqn = type.getInternalName().replace('/', '.'); + String simpleName = extractSimpleNameFromFQN(fqn); + String parent = extractParentFromFQN(fqn); + boolean primitive = type.getSort() < Type.ARRAY; + if(fqn.contains("$")) + return new TypeProxy(simpleName, type.getDescriptor(), modifiers, from(type, Modifier.PUBLIC), primitive); + else return new TypeProxy(simpleName, type.getDescriptor(), modifiers, parent, primitive); + } + + /** + * Builds a {@link TypeProxy} given only the fully-qualified name and modifiers. + * If present, parent classes will be assumed to have {@code public} as their + * only modifier. + * @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 + * @return the built {@link TypeProxy} + */ + protected static TypeProxy from(String fqn, int arrayLevel, int modifiers) { + return from(Type.getObjectType(nameToDescriptor(fqn, arrayLevel)), modifiers); + } + + /** + * Builds a {@link TypeProxy} from a {@link Class} object. + * @param clazz the {@link Class} object representing the target class + * @return the built {@link TypeProxy} + */ + public static TypeProxy from(Class clazz) { + Class parentClass = clazz.getEnclosingClass(); + if(parentClass == null) + return new TypeProxy( + clazz.getSimpleName(), + Type.getDescriptor(clazz), + clazz.getModifiers(), + clazz.getPackage().getName(), + clazz.isPrimitive() + ); + else + return new TypeProxy( + clazz.getSimpleName(), + Type.getDescriptor(clazz), + clazz.getModifiers(), + from(parentClass), + clazz.isPrimitive() + ); + } + + /** + * Returns a new instance of {@link TypeProxy.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); + } + + /** + * 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 TypeProxy && super.equals(obj); + } + + /** + * A builder object for {@link TypeProxy}. + */ + public static class Builder extends AbstractProxy.Builder { + + /** + * Whether the proxy represents a primitive. + */ + private boolean primitive; + + /** + * The constructor of the builder, used only internally. + * @param name the "simple name" of the class + */ + Builder(String name) { + super(name); + this.primitive = false; + } + + /** + * 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 setParent(Class containerClass) { + super.setParent(TypeProxy.from(containerClass)); + return this; + } + + /** + * Sets this class as an inner class and builds a {@link TypeProxy} + * from the given parent and modifiers. + * @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, boolean isParentPackage) { + super.setParent(isParentPackage ? PackageProxy.from(parentFQN) : TypeProxy.from(parentFQN, 0, modifiers)); + return this; + } + + /** + * Sets this class as an inner class and builds a {@link TypeProxy} + * from the given 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 + */ + public Builder setParent(String parentFQN, boolean isParentPackage) { + return this.setParent(parentFQN, 0, isParentPackage); + } + + /** + * Sets the primitive flag to true or false, to signal that the type here specified + * is a primitive. + * @param primitive the new state of the primitive flag + * @return the builder's state after the change + */ + public Builder setPrimitive(boolean primitive) { + this.primitive = primitive; + return this; + } + + /** + * Builds a {@link TypeProxy} of the given kind. + * @return the built {@link TypeProxy} + */ + @Override + public TypeProxy build() { + return new TypeProxy(this.name, this.descriptor, this.modifiers, this.parent, this.primitive); + } + } +} \ 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..1b33790 100644 --- a/src/main/java/ftbsc/lll/tools/DescriptorBuilder.java +++ b/src/main/java/ftbsc/lll/tools/DescriptorBuilder.java @@ -26,16 +26,15 @@ 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). + * 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. * @param returnType the Class object corresponding to the return type * @return the builder's state after the change */ @@ -72,9 +71,9 @@ 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). + * 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. * @param param the Class object corresponding to the parameter * @return the builder's state after the change */ @@ -113,7 +112,7 @@ public class DescriptorBuilder { /** * Builds the descriptor into a string. - * Example result: int m(Object[] o) -> ([Ljava/lang/Object;)I + * Example result: {@code int m(Object[] o)} becomes {@code ([Ljava/lang/Object;)I} * @return the resulting descriptor */ public String build() { diff --git a/src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java b/src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java index 896dc42..d1ac595 100644 --- a/src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java +++ b/src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java @@ -1,6 +1,6 @@ package ftbsc.lll.tools.nodes; -import ftbsc.lll.proxies.FieldProxy; +import ftbsc.lll.proxies.impl.FieldProxy; import org.objectweb.asm.tree.FieldInsnNode; /** @@ -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.getParent().replace('.', '/'), - f.getName(), - f.getDescriptor() - ); + super(opcode, f.parent.internalName, f.name, f.descriptor); } } diff --git a/src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java b/src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java index 63196fd..73a26d7 100644 --- a/src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java +++ b/src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java @@ -1,6 +1,6 @@ package ftbsc.lll.tools.nodes; -import ftbsc.lll.proxies.MethodProxy; +import ftbsc.lll.proxies.impl.MethodProxy; import org.objectweb.asm.tree.MethodInsnNode; /** @@ -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.getParent().replace('.', '/'), - m.getName(), - m.getDescriptor() - ); + super(opcode, m.parent.internalName, m.name, m.descriptor); } } diff --git a/src/main/java/ftbsc/lll/tools/nodes/TypeProxyInsnNode.java b/src/main/java/ftbsc/lll/tools/nodes/TypeProxyInsnNode.java new file mode 100644 index 0000000..9e78dc9 --- /dev/null +++ b/src/main/java/ftbsc/lll/tools/nodes/TypeProxyInsnNode.java @@ -0,0 +1,23 @@ +package ftbsc.lll.tools.nodes; + +import ftbsc.lll.proxies.impl.TypeProxy; +import org.objectweb.asm.tree.TypeInsnNode; + +/** + * Overrides the {@link TypeInsnNode} to add a constructor + * taking in a {@link TypeProxy}. + * @since 0.4.0 + */ +public class TypeProxyInsnNode extends TypeInsnNode { + /** + * Constructs a new {@link TypeInsnNode} starting from a + * {@link TypeProxy}. The user should ensure that the TypeInsnNode + * represents a declared type before calling this. + * @param opcode the opcode, must be one of NEW, ANEWARRAY, + * CHECKCAST or INSTANCEOF + * @param t a {@link TypeProxy} representing the type to call + */ + public TypeProxyInsnNode(int opcode, TypeProxy t) { + super(opcode, t.internalName); + } +}