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 | 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<Event> |
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 | ... | ... |