Commit e1d71003fe25283187a18c7ec9deba2a8e67fdc7
1 parent
4b564b9b
adding more javadoc to ByteCodeUtilities
Showing
2 changed files
with
77 additions
and
10 deletions
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<Event> | @@ -340,7 +340,7 @@ public class Event implements Comparable<Event> | ||
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 |