summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
author zaaarf <zaaarf@proton.me>2023-03-25 20:53:09 +0100
committer zaaarf <zaaarf@proton.me>2023-03-25 20:53:09 +0100
commitb8cd398fd5be79bbe5add01b6b6b998881a1bb46 (patch)
tree237a6bd5fb75c75bc636c666eeeaf102c893d4f8 /src
parentc1cb2184aa1b86dd1624a3b7c98b51f89af587eb (diff)
parent17a0b4d0172c0b3399fb5ed0ef1f89b41a1fc67f (diff)
Merge branch 'version4' into dev
Diffstat (limited to 'src')
-rw-r--r--src/main/java/ftbsc/lll/proxies/AbstractProxy.java118
-rw-r--r--src/main/java/ftbsc/lll/proxies/FieldProxy.java116
-rw-r--r--src/main/java/ftbsc/lll/proxies/MethodProxy.java230
-rw-r--r--src/main/java/ftbsc/lll/proxies/ProxyType.java11
-rw-r--r--src/main/java/ftbsc/lll/proxies/QualifiableProxy.java67
-rw-r--r--src/main/java/ftbsc/lll/proxies/impl/FieldProxy.java98
-rw-r--r--src/main/java/ftbsc/lll/proxies/impl/MethodProxy.java188
-rw-r--r--src/main/java/ftbsc/lll/proxies/impl/PackageProxy.java54
-rw-r--r--src/main/java/ftbsc/lll/proxies/impl/TypeProxy.java199
-rw-r--r--src/main/java/ftbsc/lll/tools/DescriptorBuilder.java15
-rw-r--r--src/main/java/ftbsc/lll/tools/nodes/FieldProxyInsnNode.java9
-rw-r--r--src/main/java/ftbsc/lll/tools/nodes/MethodProxyInsnNode.java9
-rw-r--r--src/main/java/ftbsc/lll/tools/nodes/TypeProxyInsnNode.java23
13 files changed, 723 insertions, 414 deletions
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 fully qualified name (i.e. java.lang.String) of
- * the parent class.
+ * The name of the corresponding element.
*/
- private final String parent;
+ public final String name;
/**
- * The modifiers of the member, as a packed int.
- * @see java.lang.reflect.Modifier
+ * The descriptor for this element.
*/
- private final int modifiers;
+ public final String descriptor;
/**
- * @return the name of the item
+ * The fully qualified name (i.e. java.lang.String) of
+ * the parent class.
*/
- public String getName() {
- return this.name;
- }
+ public final QualifiableProxy parent;
/**
- * @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,23 +75,28 @@ public abstract class AbstractProxy {
public abstract static class Builder<T extends AbstractProxy> {
/**
- * 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;
@@ -96,33 +104,51 @@ public abstract class AbstractProxy {
}
/**
- * @param parentFQN the fully qualified name of the parent
+ * @param newModifier the modifier to add
* @return the current state of the builder
*/
- public Builder<T> setParent(String parentFQN) {
- this.parent = parentFQN;
+ public Builder<T> addModifier(int newModifier) {
+ this.modifiers |= newModifier;
return this;
}
/**
- * @param newModifier the modifier to add
+ * @param newModifier the new modifier value
* @return the current state of the builder
*/
- public Builder<T> addModifier(int newModifier) {
- this.modifiers |= newModifier;
+ public Builder<T> setModifiers(int newModifier) {
+ this.modifiers = newModifier;
return this;
}
/**
- * @param newModifier the new modifier value
+ * @param parent the {@link QualifiableProxy} representing the parent
* @return the current state of the builder
*/
- public Builder<T> setModifier(int newModifier) {
- this.modifiers = newModifier;
+ public Builder<T> 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<T> 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<T> setType(Type type) {
+ return this.setDescriptor(type.getDescriptor());
+ }
+
+ /**
* @return the built proxy object
*/
public abstract T build();
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<FieldProxy> {
- /**
- * 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<Object> 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<MethodProxy> {
- /**
- * The parameters of the method.
- */
- private final List<Object> 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<FieldProxy> {
+ /**
+ * 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<MethodProxy> {
+ /**
+ * The parameters of the method.
+ */
+ private final List<Type> 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<TypeProxy> {
+
+ /**
+ * 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);
+ }
+}