From de8da3abda70c216e4c282a0977364169ab6878d Mon Sep 17 00:00:00 2001 From: zaaarf Date: Tue, 7 Feb 2023 17:42:53 +0100 Subject: [PATCH] feat: written stacktools --- .../java/ftbsc/lll/tools/InsnSequence.java | 12 ++- src/main/java/ftbsc/lll/tools/StackTools.java | 88 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ftbsc/lll/tools/StackTools.java diff --git a/src/main/java/ftbsc/lll/tools/InsnSequence.java b/src/main/java/ftbsc/lll/tools/InsnSequence.java index 4afd626..1e667f5 100644 --- a/src/main/java/ftbsc/lll/tools/InsnSequence.java +++ b/src/main/java/ftbsc/lll/tools/InsnSequence.java @@ -8,7 +8,7 @@ import java.util.Objects; /** * Represents a sequence of instructions contained within two given nodes. - * Extends InsnList, but provides additional flexibility. + * Extends InsnList, but provides additional flexibility and features. */ public class InsnSequence extends InsnList { /** @@ -55,6 +55,16 @@ public class InsnSequence extends InsnList { */ public void add(AbstractInsnNode... nodes) { for(AbstractInsnNode node : nodes) + this.add(node); + } + + /** + * Wraps InsnList's add() to ignore null values. + * @param node to add + */ + @Override + public void add(AbstractInsnNode node) { + if(node != null) super.add(node); } diff --git a/src/main/java/ftbsc/lll/tools/StackTools.java b/src/main/java/ftbsc/lll/tools/StackTools.java new file mode 100644 index 0000000..bbb663b --- /dev/null +++ b/src/main/java/ftbsc/lll/tools/StackTools.java @@ -0,0 +1,88 @@ +package ftbsc.lll.tools; + +import com.sun.istack.internal.Nullable; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; + +import java.util.Comparator; +import java.util.Objects; + +/** + * Various methods for manipulating the stack. + * Includes anything from instantiation to variable manipulation - just about + * anything that loads stuff on or from the stack. + */ +public class StackTools implements Opcodes { + /** + * Creates a new instance of an object, given its internal name, constructor descriptor and instructions to load + * the parameters. + * The created object is loaded on the stack. + * @param name the internal name of the object to initialize, where + * the internal name of a class is its fully qualified name, where '.' are replaced by '/' + * @param desc the descriptor of the constructor to call + * @param args nodes containing instructions to load the constructor arguments, in the right order + * @return an instruction list containing the opcodes needed to create the new object and load it on the stack. + */ + public static InsnList instantiate(String name, String desc, @Nullable AbstractInsnNode... args) { + InsnSequence is = new InsnSequence(); + is.add(args); + return instantiate(name, desc, is); + } + + /** + * Creates a new instance of an object, given its internal name, constructor descriptor and instructions to load + * the parameters. + * The created object is loaded on the stack. + * @param name the internal name of the object to initialize, where + * the internal name of a class is its fully qualified name, where '.' are replaced by '/' + * @param desc the descriptor of the constructor to call + * @param args a list of instructions loading the constructor arguments onto the stack in the correct order + * @return an instruction list containing the opcodes needed to create the new object and load it on the stack. + */ + public static InsnList instantiate(String name, String desc, @Nullable InsnList args) { + InsnSequence list = new InsnSequence(); + list.add(new TypeInsnNode(NEW, name), new InsnNode(DUP)); + if (args != null) list.add(args); + list.add(new MethodInsnNode(INVOKESPECIAL, name, "", desc, false)); + return list; + } + + + /** + * Creates a new local variable, lasting in scope from the first to the last label of the given method. + * @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 + * @return the index value of the new local variable + */ + public static int addLocalVariable(MethodNode method, String name, String desc) { + return addLocalVariable( + method, name, desc, + (LabelNode) PatternMatcher.builder().label().build().find(method).getFirst(), + (LabelNode) PatternMatcher.builder().label().reverse().build().find(method).getFirst() + ); + } + + /** + * Creates a new local variable, lasting in scope between two given LabelNodes. + * @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 + * @param start the label at which the variable should enter scope + * @param end the label at which the variable should go out of scope + * @return the index value of the new local variable + */ + public static int addLocalVariable(MethodNode method, String name, String desc, LabelNode start, LabelNode end) { + final int targetIndex = + method.localVariables + .stream() + .max(Comparator.comparingInt(v -> v.index)) + .map(var -> var.desc.equals("J") || var.desc.equals("D") + ? var.index + 2 //skip two if long or double - major java moment + : var.index + 1) + .orElse(0); + LocalVariableNode variable = new LocalVariableNode(name, desc, null, start, end, targetIndex); + method.localVariables.add(variable); + return targetIndex; + } +}