Commit 755c90be849b4453c30c6c819ef4036c9a5ef6fd
1 parent
7b700b7b
generate local variable table for methods when not available in the bytecode
Showing
3 changed files
with
151 additions
and
6 deletions
ant/build_liteloader.xml
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | 5 | ||
6 | <!-- Versions !!IMPORTANT --> | 6 | <!-- Versions !!IMPORTANT --> |
7 | <property name="version" value="1.8.0_00" /> | 7 | <property name="version" value="1.8.0_00" /> |
8 | - <property name="mcversion" value="1.8.00" /> | 8 | + <property name="mcversion" value="1.8" /> |
9 | <property name="author" value="Mumfrey" /> | 9 | <property name="author" value="Mumfrey" /> |
10 | 10 | ||
11 | <!-- Project definitions and dependencies --> | 11 | <!-- Project definitions and dependencies --> |
@@ -288,6 +288,6 @@ | @@ -288,6 +288,6 @@ | ||
288 | <target name="deploy" depends="production" description="Deploy artifact to local minecraft installation in APPDATA"> | 288 | <target name="deploy" depends="production" description="Deploy artifact to local minecraft installation in APPDATA"> |
289 | <mkdir dir="${env.APPDATA}/.minecraft/libraries/com/mumfrey/liteloader/${mcversion}"/> | 289 | <mkdir dir="${env.APPDATA}/.minecraft/libraries/com/mumfrey/liteloader/${mcversion}"/> |
290 | <copy todir="${env.APPDATA}/.minecraft/libraries/com/mumfrey/liteloader/${mcversion}" file="${dist.dir}/${ant.project.name}-${mcversion}.${filetype}" failonerror="false" overwrite="true" /> | 290 | <copy todir="${env.APPDATA}/.minecraft/libraries/com/mumfrey/liteloader/${mcversion}" file="${dist.dir}/${ant.project.name}-${mcversion}.${filetype}" failonerror="false" overwrite="true" /> |
291 | - <copy todir="${eclipse}/Installer/src/main/resources" file="${dist.dir}/${ant.project.name}-${mcversion}.${filetype}" failonerror="false" overwrite="true" /> | 291 | + <copy todir="${eclipse}/LiteLoaderInstaller/src/main/resources" file="${dist.dir}/${ant.project.name}-${mcversion}.${filetype}" failonerror="false" overwrite="true" /> |
292 | </target> | 292 | </target> |
293 | </project> | 293 | </project> |
294 | \ No newline at end of file | 294 | \ No newline at end of file |
java/common/com/mumfrey/liteloader/transformers/ByteCodeUtilities.java
1 | package com.mumfrey.liteloader.transformers; | 1 | package com.mumfrey.liteloader.transformers; |
2 | 2 | ||
3 | import java.lang.annotation.Annotation; | 3 | import java.lang.annotation.Annotation; |
4 | +import java.util.ArrayList; | ||
5 | +import java.util.HashMap; | ||
4 | import java.util.Iterator; | 6 | import java.util.Iterator; |
5 | import java.util.List; | 7 | import java.util.List; |
8 | +import java.util.Map; | ||
6 | 9 | ||
7 | import org.objectweb.asm.Opcodes; | 10 | import org.objectweb.asm.Opcodes; |
8 | import org.objectweb.asm.Type; | 11 | import org.objectweb.asm.Type; |
@@ -12,9 +15,15 @@ import org.objectweb.asm.tree.ClassNode; | @@ -12,9 +15,15 @@ import org.objectweb.asm.tree.ClassNode; | ||
12 | import org.objectweb.asm.tree.FieldNode; | 15 | import org.objectweb.asm.tree.FieldNode; |
13 | import org.objectweb.asm.tree.FrameNode; | 16 | import org.objectweb.asm.tree.FrameNode; |
14 | import org.objectweb.asm.tree.InsnList; | 17 | import org.objectweb.asm.tree.InsnList; |
18 | +import org.objectweb.asm.tree.LabelNode; | ||
15 | import org.objectweb.asm.tree.LocalVariableNode; | 19 | import org.objectweb.asm.tree.LocalVariableNode; |
16 | import org.objectweb.asm.tree.MethodNode; | 20 | import org.objectweb.asm.tree.MethodNode; |
17 | import org.objectweb.asm.tree.VarInsnNode; | 21 | import org.objectweb.asm.tree.VarInsnNode; |
22 | +import org.objectweb.asm.tree.analysis.Analyzer; | ||
23 | +import org.objectweb.asm.tree.analysis.AnalyzerException; | ||
24 | +import org.objectweb.asm.tree.analysis.BasicValue; | ||
25 | +import org.objectweb.asm.tree.analysis.Frame; | ||
26 | +import org.objectweb.asm.tree.analysis.SimpleVerifier; | ||
18 | 27 | ||
19 | /** | 28 | /** |
20 | * Utility methods for working with bytecode using ASM | 29 | * Utility methods for working with bytecode using ASM |
@@ -23,6 +32,8 @@ import org.objectweb.asm.tree.VarInsnNode; | @@ -23,6 +32,8 @@ import org.objectweb.asm.tree.VarInsnNode; | ||
23 | */ | 32 | */ |
24 | public abstract class ByteCodeUtilities | 33 | public abstract class ByteCodeUtilities |
25 | { | 34 | { |
35 | + private static Map<String, List<LocalVariableNode>> calculatedLocalVariables = new HashMap<String, List<LocalVariableNode>>(); | ||
36 | + | ||
26 | private ByteCodeUtilities() {} | 37 | private ByteCodeUtilities() {} |
27 | 38 | ||
28 | /** | 39 | /** |
@@ -236,8 +247,9 @@ public abstract class ByteCodeUtilities | @@ -236,8 +247,9 @@ public abstract class ByteCodeUtilities | ||
236 | LocalVariableNode localVariableNode = null; | 247 | LocalVariableNode localVariableNode = null; |
237 | 248 | ||
238 | int pos = method.instructions.indexOf(node); | 249 | int pos = method.instructions.indexOf(node); |
239 | - | ||
240 | - for (LocalVariableNode local : method.localVariables) | 250 | + |
251 | + List<LocalVariableNode> localVariables = ByteCodeUtilities.getLocalVariableTable(classNode, method); | ||
252 | + for (LocalVariableNode local : localVariables) | ||
241 | { | 253 | { |
242 | if (local.index != var) continue; | 254 | if (local.index != var) continue; |
243 | int start = method.instructions.indexOf(local.start); | 255 | int start = method.instructions.indexOf(local.start); |
@@ -250,8 +262,141 @@ public abstract class ByteCodeUtilities | @@ -250,8 +262,141 @@ public abstract class ByteCodeUtilities | ||
250 | 262 | ||
251 | return localVariableNode; | 263 | return localVariableNode; |
252 | } | 264 | } |
265 | + | ||
266 | + /** | ||
267 | + * Fetches or generates the local variable table for the specified method. Since Mojang strip the local variable table | ||
268 | + * as part of the obfuscation process, we need to generate the local variable table when running obfuscated. We cache | ||
269 | + * the generated tables so that we only need to do the relatively expensive calculation once per method we encounter. | ||
270 | + * | ||
271 | + * @param classNode | ||
272 | + * @param method | ||
273 | + * @return | ||
274 | + */ | ||
275 | + public static List<LocalVariableNode> getLocalVariableTable(ClassNode classNode, MethodNode method) | ||
276 | + { | ||
277 | + if (method.localVariables.isEmpty()) | ||
278 | + { | ||
279 | + String signature = String.format("%s.%s%s", classNode.name, method.name, method.desc); | ||
280 | + | ||
281 | + List<LocalVariableNode> localVars = ByteCodeUtilities.calculatedLocalVariables.get(signature); | ||
282 | + if (localVars != null) | ||
283 | + { | ||
284 | + return localVars; | ||
285 | + } | ||
286 | + | ||
287 | + localVars = ByteCodeUtilities.generateLocalVariableTable(classNode, method); | ||
288 | + ByteCodeUtilities.calculatedLocalVariables.put(signature, localVars); | ||
289 | + return localVars; | ||
290 | + } | ||
291 | + | ||
292 | + return method.localVariables; | ||
293 | + } | ||
253 | 294 | ||
254 | /** | 295 | /** |
296 | + * Use ASM Analyzer to generate the local variable table for the specified method | ||
297 | + * | ||
298 | + * @param classNode | ||
299 | + * @param method | ||
300 | + * @return | ||
301 | + */ | ||
302 | + public static List<LocalVariableNode> generateLocalVariableTable(ClassNode classNode, MethodNode method) | ||
303 | + { | ||
304 | + // Use Analyzer to generate the bytecode frames | ||
305 | + Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(new SimpleVerifier(Type.getObjectType(classNode.name), null, null, false)); | ||
306 | + try | ||
307 | + { | ||
308 | + analyzer.analyze(classNode.name, method); | ||
309 | + } | ||
310 | + catch (AnalyzerException ex) | ||
311 | + { | ||
312 | + ex.printStackTrace(); | ||
313 | + } | ||
314 | + | ||
315 | + // Get frames from the Analyzer | ||
316 | + Frame<BasicValue>[] frames = analyzer.getFrames(); | ||
317 | + | ||
318 | + // Record the original size of hte method | ||
319 | + int methodSize = method.instructions.size(); | ||
320 | + | ||
321 | + // List of LocalVariableNodes to return | ||
322 | + List<LocalVariableNode> localVariables = new ArrayList<LocalVariableNode>(); | ||
323 | + | ||
324 | + LocalVariableNode[] localNodes = new LocalVariableNode[method.maxLocals]; // LocalVariableNodes for current frame | ||
325 | + BasicValue[] locals = new BasicValue[method.maxLocals]; // locals in previous frame, used to work out what changes between frames | ||
326 | + LabelNode[] labels = new LabelNode[methodSize]; // Labels to add to the method, for the markers | ||
327 | + | ||
328 | + // Traverse the frames and work out when locals begin and end | ||
329 | + for (int i = 0; i < methodSize; i++) | ||
330 | + { | ||
331 | + Frame<BasicValue> f = frames[i]; | ||
332 | + if (f == null) continue; | ||
333 | + LabelNode label = null; | ||
334 | + | ||
335 | + for (int j = 0; j < f.getLocals(); j++) | ||
336 | + { | ||
337 | + BasicValue local = f.getLocal(j); | ||
338 | + if (local == null && locals[j] == null) continue; | ||
339 | + if (local != null && local.equals(locals[j])) continue; | ||
340 | + | ||
341 | + if (label == null) | ||
342 | + { | ||
343 | + label = new LabelNode(); | ||
344 | + labels[i] = label; | ||
345 | + } | ||
346 | + | ||
347 | + if (local == null && locals[j] != null) | ||
348 | + { | ||
349 | + localVariables.add(localNodes[j]); | ||
350 | + localNodes[j].end = label; | ||
351 | + localNodes[j] = null; | ||
352 | + } | ||
353 | + else if (local != null) | ||
354 | + { | ||
355 | + if (locals[j] != null) | ||
356 | + { | ||
357 | + localVariables.add(localNodes[j]); | ||
358 | + localNodes[j].end = label; | ||
359 | + localNodes[j] = null; | ||
360 | + } | ||
361 | + | ||
362 | + String desc = (local.getType() != null) ? local.getType().getDescriptor() : null; | ||
363 | + localNodes[j] = new LocalVariableNode("var" + j, desc, null, label, null, j); | ||
364 | + } | ||
365 | + | ||
366 | + locals[j] = local; | ||
367 | + } | ||
368 | + } | ||
369 | + | ||
370 | + // Reached the end of the method so flush all current locals and mark the end | ||
371 | + LabelNode label = null; | ||
372 | + for (int k = 0; k < localNodes.length; k++) | ||
373 | + { | ||
374 | + if (localNodes[k] != null) | ||
375 | + { | ||
376 | + if (label == null) | ||
377 | + { | ||
378 | + label = new LabelNode(); | ||
379 | + method.instructions.add(label); | ||
380 | + } | ||
381 | + | ||
382 | + localNodes[k].end = label; | ||
383 | + localVariables.add(localNodes[k]); | ||
384 | + } | ||
385 | + } | ||
386 | + | ||
387 | + // Insert generated labels into the method body | ||
388 | + for (int n = methodSize - 1; n >= 0; n--) | ||
389 | + { | ||
390 | + if (labels[n] != null) | ||
391 | + { | ||
392 | + method.instructions.insert(method.instructions.get(n), labels[n]); | ||
393 | + } | ||
394 | + } | ||
395 | + | ||
396 | + return localVariables; | ||
397 | + } | ||
398 | + | ||
399 | + /** | ||
255 | * Get the source code name for the specified type | 400 | * Get the source code name for the specified type |
256 | * | 401 | * |
257 | * @param type | 402 | * @param type |
java/common/com/mumfrey/liteloader/transformers/event/EventTransformer.java
@@ -131,7 +131,7 @@ public final class EventTransformer extends ClassTransformer | @@ -131,7 +131,7 @@ public final class EventTransformer extends ClassTransformer | ||
131 | { | 131 | { |
132 | if (injectionPoint.captureLocals != this.captureLocals) | 132 | if (injectionPoint.captureLocals != this.captureLocals) |
133 | { | 133 | { |
134 | - throw new RuntimeException("Overlapping injection points defined with incompatible settings. Attempting to handle " + injectionPoint.getClass().getSimpleName() + " with capture locals [" + injectionPoint.captureLocals + "] but already defined injection point with [" + this.captureLocals + "]"); | 134 | + throw new RuntimeException("Overlapping injection points defined with incompatible settings. Attempting to handle " + injectionPoint + " with capture locals [" + injectionPoint.captureLocals + "] but already defined injection point with [" + this.captureLocals + "]"); |
135 | } | 135 | } |
136 | } | 136 | } |
137 | 137 | ||
@@ -285,7 +285,7 @@ public final class EventTransformer extends ClassTransformer | @@ -285,7 +285,7 @@ public final class EventTransformer extends ClassTransformer | ||
285 | int startPos = ByteCodeUtilities.getFirstNonArgLocalIndex(method); | 285 | int startPos = ByteCodeUtilities.getFirstNonArgLocalIndex(method); |
286 | 286 | ||
287 | LiteLoaderLogger.debug(ClassTransformer.HORIZONTAL_RULE); | 287 | LiteLoaderLogger.debug(ClassTransformer.HORIZONTAL_RULE); |
288 | - LiteLoaderLogger.debug("Logging local variables for " + injectionPoint.getClass().getSimpleName()); | 288 | + LiteLoaderLogger.debug("Logging local variables for " + injectionPoint); |
289 | for (int i = startPos; i < locals.length; i++) | 289 | for (int i = startPos; i < locals.length; i++) |
290 | { | 290 | { |
291 | LocalVariableNode local = locals[i]; | 291 | LocalVariableNode local = locals[i]; |