Commit e1d71003fe25283187a18c7ec9deba2a8e67fdc7

Authored by Mumfrey
1 parent 4b564b9b

adding more javadoc to ByteCodeUtilities

java/common/com/mumfrey/liteloader/transformers/ByteCodeUtilities.java
... ... @@ -16,26 +16,55 @@ import org.objectweb.asm.tree.LocalVariableNode;
16 16 import org.objectweb.asm.tree.MethodNode;
17 17 import org.objectweb.asm.tree.VarInsnNode;
18 18  
  19 +/**
  20 + * Utility methods for working with bytecode using ASM
  21 + *
  22 + * @author Adam Mummery-Smith
  23 + */
19 24 public abstract class ByteCodeUtilities
20 25 {
21 26 private ByteCodeUtilities() {}
22 27  
  28 + /**
  29 + * Injects appropriate LOAD opcodes into the supplied InsnList appropriate for each entry in the args array starting at pos
  30 + *
  31 + * @param args Argument types
  32 + * @param insns Instruction List to inject into
  33 + * @param pos Start position
  34 + */
23 35 public static void loadArgs(Type[] args, InsnList insns, int pos)
24 36 {
25 37 ByteCodeUtilities.loadArgs(args, insns, pos, -1);
26 38 }
27 39  
  40 + /**
  41 + * Injects appropriate LOAD opcodes into the supplied InsnList appropriate for each entry in the args array starting at start and ending at end
  42 + *
  43 + * @param args Argument types
  44 + * @param insns Instruction List to inject into
  45 + * @param start Start position
  46 + * @param end End position
  47 + */
28 48 public static void loadArgs(Type[] args, InsnList insns, int start, int end)
29 49 {
  50 + int pos = start;
  51 +
30 52 for (Type type : args)
31 53 {
32   - insns.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), start));
33   - start += type.getSize();
34   - if (end >= start && start >= end) return;
  54 + insns.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), pos));
  55 + pos += type.getSize();
  56 + if (end >= start && pos >= end) return;
35 57 }
36 58 }
37 59  
38   - public static void pushLocals(Type[] locals, InsnList insns, int pos)
  60 + /**
  61 + * Injects appropriate LOAD opcodes into the supplied InsnList for each entry in the supplied locals array starting at pos
  62 + *
  63 + * @param locals Local types (can contain nulls for uninitialised, TOP, or RETURN values in locals)
  64 + * @param insns Instruction List to inject into
  65 + * @param pos Start position
  66 + */
  67 + public static void loadLocals(Type[] locals, InsnList insns, int pos)
39 68 {
40 69 for (; pos < locals.length; pos++)
41 70 {
... ... @@ -47,14 +76,38 @@ public abstract class ByteCodeUtilities
47 76 }
48 77  
49 78 /**
50   - * @param method
51   - * @param node
52   - * @return
  79 + * Attempts to identify available locals at an arbitrary point in the bytecode specified by node.
  80 + *
  81 + * This method builds an approximate view of the locals available at an arbitrary point in the bytecode by examining the following
  82 + * features in the bytecode:
  83 + *
  84 + * * Any available stack map frames
  85 + * * STORE opcodes
  86 + * * The local variable table
  87 + *
  88 + * Inference proceeds by walking the bytecode from the start of the method looking for stack frames and STORE opcodes. When either
  89 + * of these is encountered, an attempt is made to cross-reference the values in the stack map or STORE opcode with the value in the
  90 + * local variable table which covers the code range. Stack map frames overwrite the entire simulated local variable table with their
  91 + * own value types, STORE opcodes overwrite only the local slot to which they pertain. Values in the simulated locals array are spaced
  92 + * according to their size (unlike the representation in FrameNode) and this TOP, NULL and UNINTITIALIZED_THIS opcodes will be
  93 + * represented as null values in the simulated frame.
  94 + *
  95 + * This code does not currently simulate the prescribed JVM behaviour where overwriting the second slot of a DOUBLE or LONG actually
  96 + * invalidates the DOUBLE or LONG stored in the previous location, so we have to hope (for now) that this behaviour isn't emitted by
  97 + * the compiler or any upstream transformers. I may have to re-think this strategy if this situation is encountered in the wild.
  98 + *
  99 + * @param classNode ClassNode containing the method, used to initialise the implicit "this" reference in simple methods with no stack frames
  100 + * @param method MethodNode to explore
  101 + * @param node Node indicating the position at which to determine the locals state. The locals will be enumerated UP TO the specified
  102 + * node, so bear in mind that if the specified node is itself a STORE opcode, then we will be looking at the state of the locals
  103 + * PRIOR to its invokation
  104 + * @return A sparse array containing a view (hopefully) of the locals at the specified location
53 105 */
54 106 public static LocalVariableNode[] getLocalsAt(ClassNode classNode, MethodNode method, AbstractInsnNode node)
55 107 {
56 108 LocalVariableNode[] frame = new LocalVariableNode[method.maxLocals];
57 109  
  110 + // Initialise implicit "this" reference in non-static methods
58 111 if ((method.access & Opcodes.ACC_STATIC) == 0)
59 112 {
60 113 frame[0] = new LocalVariableNode("this", classNode.name, null, null, null, 0);
... ... @@ -66,15 +119,19 @@ public abstract class ByteCodeUtilities
66 119 if (insn instanceof FrameNode)
67 120 {
68 121 FrameNode frameNode = (FrameNode)insn;
  122 +
  123 + // localPos tracks the location in the frame node's locals list, which doesn't leave space for TOP entries
69 124 int localPos = 0;
70 125 for (int framePos = 0; framePos < frame.length; framePos++)
71 126 {
  127 + // Get the local at the current position in the FrameNode's locals list
72 128 final Object localType = (localPos < frameNode.local.size()) ? frameNode.local.get(localPos) : null;
73   - if (localType instanceof String)
  129 +
  130 + if (localType instanceof String) // String refers to a reference type
74 131 {
75 132 frame[framePos] = ByteCodeUtilities.getLocalVariableAt(classNode, method, node, framePos);
76 133 }
77   - else if (localType instanceof Integer)
  134 + else if (localType instanceof Integer) // Integer refers to a primitive type or other marker
78 135 {
79 136 boolean isMarkerType = localType == Opcodes.UNINITIALIZED_THIS || localType == Opcodes.TOP || localType == Opcodes.NULL;
80 137 boolean is32bitValue = localType == Opcodes.INTEGER || localType == Opcodes.FLOAT;
... ... @@ -90,8 +147,13 @@ public abstract class ByteCodeUtilities
90 147 if (is64bitValue)
91 148 {
92 149 framePos++;
  150 + frame[framePos] = null; // TOP
93 151 }
94 152 }
  153 + else
  154 + {
  155 + throw new RuntimeException("Unrecognised locals opcode " + localType + " in locals array at position " + localPos + " in " + classNode.name + "." + method.name + method.desc);
  156 + }
95 157 }
96 158 else if (localType == null)
97 159 {
... ... @@ -120,6 +182,9 @@ public abstract class ByteCodeUtilities
120 182 }
121 183  
122 184 /**
  185 + * Attempts to locate the appropriate entry in the local variable table for the specified local variable index at the location
  186 + * specified by node.
  187 + *
123 188 * @param classNode
124 189 * @param method
125 190 * @param node
... ... @@ -147,6 +212,8 @@ public abstract class ByteCodeUtilities
147 212 }
148 213  
149 214 /**
  215 + * Get the source code name for the specified type
  216 + *
150 217 * @param type
151 218 * @return
152 219 */
... ...
java/common/com/mumfrey/liteloader/transformers/event/Event.java
... ... @@ -340,7 +340,7 @@ public class Event implements Comparable&lt;Event&gt;
340 340 ByteCodeUtilities.loadArgs(argumentTypes, insns, this.methodIsStatic ? 0 : 1);
341 341 if (doCaptureLocals)
342 342 {
343   - ByteCodeUtilities.pushLocals(locals, insns, initialFrameSize);
  343 + ByteCodeUtilities.loadLocals(locals, insns, initialFrameSize);
344 344 }
345 345 insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Event.getActiveProxyRef(), handler.name, handler.desc, false));
346 346  
... ...