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,26 +16,55 @@ import org.objectweb.asm.tree.LocalVariableNode;
16 import org.objectweb.asm.tree.MethodNode; 16 import org.objectweb.asm.tree.MethodNode;
17 import org.objectweb.asm.tree.VarInsnNode; 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 public abstract class ByteCodeUtilities 24 public abstract class ByteCodeUtilities
20 { 25 {
21 private ByteCodeUtilities() {} 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 public static void loadArgs(Type[] args, InsnList insns, int pos) 35 public static void loadArgs(Type[] args, InsnList insns, int pos)
24 { 36 {
25 ByteCodeUtilities.loadArgs(args, insns, pos, -1); 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 public static void loadArgs(Type[] args, InsnList insns, int start, int end) 48 public static void loadArgs(Type[] args, InsnList insns, int start, int end)
29 { 49 {
  50 + int pos = start;
  51 +
30 for (Type type : args) 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 for (; pos < locals.length; pos++) 69 for (; pos < locals.length; pos++)
41 { 70 {
@@ -47,14 +76,38 @@ public abstract class ByteCodeUtilities @@ -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 public static LocalVariableNode[] getLocalsAt(ClassNode classNode, MethodNode method, AbstractInsnNode node) 106 public static LocalVariableNode[] getLocalsAt(ClassNode classNode, MethodNode method, AbstractInsnNode node)
55 { 107 {
56 LocalVariableNode[] frame = new LocalVariableNode[method.maxLocals]; 108 LocalVariableNode[] frame = new LocalVariableNode[method.maxLocals];
57 109
  110 + // Initialise implicit "this" reference in non-static methods
58 if ((method.access & Opcodes.ACC_STATIC) == 0) 111 if ((method.access & Opcodes.ACC_STATIC) == 0)
59 { 112 {
60 frame[0] = new LocalVariableNode("this", classNode.name, null, null, null, 0); 113 frame[0] = new LocalVariableNode("this", classNode.name, null, null, null, 0);
@@ -66,15 +119,19 @@ public abstract class ByteCodeUtilities @@ -66,15 +119,19 @@ public abstract class ByteCodeUtilities
66 if (insn instanceof FrameNode) 119 if (insn instanceof FrameNode)
67 { 120 {
68 FrameNode frameNode = (FrameNode)insn; 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 int localPos = 0; 124 int localPos = 0;
70 for (int framePos = 0; framePos < frame.length; framePos++) 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 final Object localType = (localPos < frameNode.local.size()) ? frameNode.local.get(localPos) : null; 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 frame[framePos] = ByteCodeUtilities.getLocalVariableAt(classNode, method, node, framePos); 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 boolean isMarkerType = localType == Opcodes.UNINITIALIZED_THIS || localType == Opcodes.TOP || localType == Opcodes.NULL; 136 boolean isMarkerType = localType == Opcodes.UNINITIALIZED_THIS || localType == Opcodes.TOP || localType == Opcodes.NULL;
80 boolean is32bitValue = localType == Opcodes.INTEGER || localType == Opcodes.FLOAT; 137 boolean is32bitValue = localType == Opcodes.INTEGER || localType == Opcodes.FLOAT;
@@ -90,8 +147,13 @@ public abstract class ByteCodeUtilities @@ -90,8 +147,13 @@ public abstract class ByteCodeUtilities
90 if (is64bitValue) 147 if (is64bitValue)
91 { 148 {
92 framePos++; 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 else if (localType == null) 158 else if (localType == null)
97 { 159 {
@@ -120,6 +182,9 @@ public abstract class ByteCodeUtilities @@ -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 * @param classNode 188 * @param classNode
124 * @param method 189 * @param method
125 * @param node 190 * @param node
@@ -147,6 +212,8 @@ public abstract class ByteCodeUtilities @@ -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 * @param type 217 * @param type
151 * @return 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,7 +340,7 @@ public class Event implements Comparable&lt;Event&gt;
340 ByteCodeUtilities.loadArgs(argumentTypes, insns, this.methodIsStatic ? 0 : 1); 340 ByteCodeUtilities.loadArgs(argumentTypes, insns, this.methodIsStatic ? 0 : 1);
341 if (doCaptureLocals) 341 if (doCaptureLocals)
342 { 342 {
343 - ByteCodeUtilities.pushLocals(locals, insns, initialFrameSize); 343 + ByteCodeUtilities.loadLocals(locals, insns, initialFrameSize);
344 } 344 }
345 insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Event.getActiveProxyRef(), handler.name, handler.desc, false)); 345 insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Event.getActiveProxyRef(), handler.name, handler.desc, false));
346 346