summaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll
diff options
context:
space:
mode:
author zaaarf <zaaarf@proton.me>2023-03-01 18:05:02 +0100
committer zaaarf <zaaarf@proton.me>2023-03-01 18:05:02 +0100
commitbc649ed0f10a15585e6379551da0b169de1e0c64 (patch)
treef45aac119e946d99b298b729b1db37595b2d7d72 /src/main/java/ftbsc/lll
parente8b85872f9fb6d8e5719c6224e36d412c35aba7d (diff)
feat: implemented field and method proxies
Diffstat (limited to 'src/main/java/ftbsc/lll')
-rw-r--r--src/main/java/ftbsc/lll/proxies/AbstractProxy.java119
-rw-r--r--src/main/java/ftbsc/lll/proxies/FieldProxy.java113
-rw-r--r--src/main/java/ftbsc/lll/proxies/MethodProxy.java223
-rw-r--r--src/main/java/ftbsc/lll/tools/StackTools.java18
4 files changed, 472 insertions, 1 deletions
diff --git a/src/main/java/ftbsc/lll/proxies/AbstractProxy.java b/src/main/java/ftbsc/lll/proxies/AbstractProxy.java
new file mode 100644
index 0000000..8baf112
--- /dev/null
+++ b/src/main/java/ftbsc/lll/proxies/AbstractProxy.java
@@ -0,0 +1,119 @@
+package ftbsc.lll.proxies;
+
+/**
+ * Abstract proxy class, implementing common aspects
+ * of {@link MethodProxy} and {@link FieldProxy}.
+ * @since 0.3.0
+ */
+public abstract class AbstractProxy {
+
+ /**
+ * The SRG name of the corresponding class member.
+ */
+ private final String srgName;
+
+ /**
+ * The fully qualified name (i.e. java.lang.String) of
+ * the parent class.
+ */
+ private final String parent;
+
+ /**
+ * The modifiers of the member, as a packed int.
+ * @see java.lang.reflect.Modifier
+ */
+ private final int modifiers;
+
+ /**
+ * @return the SRG name of the item
+ */
+ public String getSrgName() {
+ return this.srgName;
+ }
+
+ /**
+ * @return the modifiers of the member, as a packed int
+ * @see java.lang.reflect.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();
+
+ /**
+ * The private constructor, should be called by all classes extending this in theirs.
+ * @param srgName the SRG name of the member
+ * @param modifiers the modifiers, as a packed int
+ * @param parent the FQN of the parent class
+ */
+ protected AbstractProxy(String srgName, int modifiers, String parent) {
+ this.srgName = srgName;
+ this.modifiers = modifiers;
+ this.parent = parent;
+ }
+
+ /**
+ * A Builder for the generic proxy.
+ * @param <T> the type of proxy
+ */
+ public abstract static class Builder<T extends AbstractProxy> {
+
+ /**
+ * The SRG name of the member.
+ */
+ protected final String srgName;
+
+ /**
+ * The modifiers of the member, as a packed int.
+ */
+ protected int modifiers;
+
+ /**
+ * The fully qualified name of the parent.
+ */
+ protected String parent;
+
+ /**
+ * The constructor.
+ * @param srgName the SRG name of the member
+ */
+ protected Builder(String srgName) {
+ this.srgName = srgName;
+ this.modifiers = 0;
+ }
+
+ /**
+ * @param parentFQN the fully qualified name of the parent
+ * @return the current state of the builder
+ */
+ public Builder<T> setParent(String parentFQN) {
+ this.parent = parentFQN;
+ return this;
+ }
+
+ /**
+ * @param newModifier the modifier to add
+ * @return the current state of the builder
+ */
+ public Builder<T> addModifier(int newModifier) {
+ this.modifiers |= newModifier;
+ return this;
+ }
+
+ /**
+ * @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
new file mode 100644
index 0000000..2bd0115
--- /dev/null
+++ b/src/main/java/ftbsc/lll/proxies/FieldProxy.java
@@ -0,0 +1,113 @@
+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 srgName the SRG 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 srgName, int modifiers, String parent, String typeDescriptor) {
+ super(srgName, 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 srgName the SRG name of the field
+ * @return the builder object for field proxies
+ */
+ public static Builder builder(String srgName) {
+ return new Builder(srgName);
+ }
+
+ 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 srgName the SRG name of the field
+ */
+ Builder(String srgName) {
+ super(srgName);
+ }
+
+ /**
+ * 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.srgName, 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
new file mode 100644
index 0000000..1df4678
--- /dev/null
+++ b/src/main/java/ftbsc/lll/proxies/MethodProxy.java
@@ -0,0 +1,223 @@
+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 srgName the SRG 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 srgName, int modifiers, String parent, Object[] parameters, Object returnType) {
+ super(srgName, 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)
+ addParameterToBuilder(b, p);
+ addParameterToBuilder(b, this.returnType);
+ this.descriptorCache = b.build();
+ return this.descriptorCache;
+ }
+
+ /**
+ * A static method used internally to correctly insert a
+ * {@link TypeContainer} into a {@link DescriptorBuilder}.
+ * @param b the {@link DescriptorBuilder}
+ * @param p the {@link TypeContainer}
+ */
+ private static void addParameterToBuilder(DescriptorBuilder b, Object p) {
+ if(p instanceof TypeContainer) {
+ TypeContainer param = (TypeContainer) p;
+ b.addParameter(param.fqn, param.arrayLevel);
+ } else b.addParameter((Class<?>) p);
+ }
+
+ /**
+ * Returns a new instance of {@link MethodProxy.Builder}.
+ * @param srgName the SRG name of the method
+ * @return the builder object for method proxies
+ */
+ public static Builder builder(String srgName) {
+ return new Builder(srgName);
+ }
+
+ /**
+ * 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 srgName the SRG name of the method
+ */
+ Builder(String srgName) {
+ super(srgName);
+ 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(srgName, 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/tools/StackTools.java b/src/main/java/ftbsc/lll/tools/StackTools.java
index 8cb4504..dbf1858 100644
--- a/src/main/java/ftbsc/lll/tools/StackTools.java
+++ b/src/main/java/ftbsc/lll/tools/StackTools.java
@@ -1,5 +1,6 @@
package ftbsc.lll.tools;
+import ftbsc.lll.proxies.MethodProxy;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
@@ -62,7 +63,7 @@ public class StackTools implements Opcodes {
}
/**
- * Creates a new local variable, lasting in scope between two given LabelNodes.
+ * Creates a new local variable, lasting in scope between two given {@link LabelNode}s.
* @param method the method for which to declare the local variable
* @param name the variable's name
* @param desc the type descriptor for the new variable
@@ -83,4 +84,19 @@ public class StackTools implements Opcodes {
method.localVariables.add(variable);
return targetIndex;
}
+
+ /**
+ * Calls the given {@link MethodProxy} with the given opcode.
+ * @param m a {@link MethodProxy} representing the method to call.
+ * @return a {@link MethodInsnNode} representing the build node.
+ * @since 0.3.0
+ */
+ public static MethodInsnNode call(MethodProxy m, int opcode) {
+ return new MethodInsnNode(
+ opcode,
+ m.getParent().replace('.', '/'),
+ m.getSrgName(),
+ m.getDescriptor()
+ );
+ }
}