Commit 3340467d961b639381483255b3d01009a21aee66
1 parent
00d2ffc1
separating event proxy generation to improve resilience against bad transformers
Showing
4 changed files
with
95 additions
and
67 deletions
java/client/com/mumfrey/liteloader/client/api/LiteLoaderCoreAPIClient.java
| @@ -22,9 +22,10 @@ import com.mumfrey.liteloader.interfaces.ObjectFactory; | @@ -22,9 +22,10 @@ import com.mumfrey.liteloader.interfaces.ObjectFactory; | ||
| 22 | */ | 22 | */ |
| 23 | public class LiteLoaderCoreAPIClient extends LiteLoaderCoreAPI | 23 | public class LiteLoaderCoreAPIClient extends LiteLoaderCoreAPI |
| 24 | { | 24 | { |
| 25 | - private static final String PKG_LITELOADER_CLIENT = LiteLoaderCoreAPI.PKG_LITELOADER + ".client"; | 25 | + private static final String PKG_LITELOADER_CLIENT = LiteLoaderCoreAPI.PKG_LITELOADER + ".client"; |
| 26 | 26 | ||
| 27 | private static final String[] requiredTransformers = { | 27 | private static final String[] requiredTransformers = { |
| 28 | + LiteLoaderCoreAPI.PKG_LITELOADER + ".transformers.event.EventProxyTransformer", | ||
| 28 | LiteLoaderCoreAPI.PKG_LITELOADER + ".launch.LiteLoaderTransformer", | 29 | LiteLoaderCoreAPI.PKG_LITELOADER + ".launch.LiteLoaderTransformer", |
| 29 | LiteLoaderCoreAPIClient.PKG_LITELOADER_CLIENT + ".transformers.CrashReportTransformer" | 30 | LiteLoaderCoreAPIClient.PKG_LITELOADER_CLIENT + ".transformers.CrashReportTransformer" |
| 30 | }; | 31 | }; |
java/common/com/mumfrey/liteloader/launch/RealmsJsonUpdateThread.java
| @@ -106,11 +106,7 @@ public class RealmsJsonUpdateThread extends Thread | @@ -106,11 +106,7 @@ public class RealmsJsonUpdateThread extends Thread | ||
| 106 | 106 | ||
| 107 | private boolean writeLocalVersionJson(Map<String, ?> localJson) | 107 | private boolean writeLocalVersionJson(Map<String, ?> localJson) |
| 108 | { | 108 | { |
| 109 | - File versionDir = new File(this.versionsDir, this.version); | ||
| 110 | - if (!versionDir.exists() || !versionDir.isDirectory()) return false; | ||
| 111 | - | ||
| 112 | - File versionJsonFile = new File(versionDir, this.version + ".json"); | ||
| 113 | - | 109 | + File versionJsonFile = this.getLocalVersionJsonFile(); |
| 114 | FileWriter writer = null; | 110 | FileWriter writer = null; |
| 115 | 111 | ||
| 116 | try | 112 | try |
| @@ -227,11 +223,8 @@ public class RealmsJsonUpdateThread extends Thread | @@ -227,11 +223,8 @@ public class RealmsJsonUpdateThread extends Thread | ||
| 227 | @SuppressWarnings("unchecked") | 223 | @SuppressWarnings("unchecked") |
| 228 | private Map<String, ?> getLocalVersionJson() | 224 | private Map<String, ?> getLocalVersionJson() |
| 229 | { | 225 | { |
| 230 | - File versionDir = new File(this.versionsDir, this.version); | ||
| 231 | - if (!versionDir.exists() || !versionDir.isDirectory()) return null; | ||
| 232 | - | ||
| 233 | - File versionJsonFile = new File(versionDir, this.version + ".json"); | ||
| 234 | - if (!versionJsonFile.exists()) return null; | 226 | + File versionJsonFile = this.getLocalVersionJsonFile(); |
| 227 | + if (versionJsonFile == null || !versionJsonFile.exists()) return null; | ||
| 235 | 228 | ||
| 236 | FileReader reader = null; | 229 | FileReader reader = null; |
| 237 | 230 | ||
| @@ -254,13 +247,22 @@ public class RealmsJsonUpdateThread extends Thread | @@ -254,13 +247,22 @@ public class RealmsJsonUpdateThread extends Thread | ||
| 254 | 247 | ||
| 255 | private boolean isLocalJsonUpToDate() | 248 | private boolean isLocalJsonUpToDate() |
| 256 | { | 249 | { |
| 257 | - File versionDir = new File(this.versionsDir, this.version); | ||
| 258 | - if (!versionDir.exists() || !versionDir.isDirectory()) return false; | ||
| 259 | - | ||
| 260 | - File versionJsonFile = new File(versionDir, this.version + ".json"); | ||
| 261 | - if (!versionJsonFile.exists()) return false; | 250 | + File versionJsonFile = this.getLocalVersionJsonFile(); |
| 251 | + if (versionJsonFile == null || !versionJsonFile.exists()) return false; | ||
| 262 | 252 | ||
| 263 | long deltaTime = System.currentTimeMillis() - versionJsonFile.lastModified(); | 253 | long deltaTime = System.currentTimeMillis() - versionJsonFile.lastModified(); |
| 264 | return deltaTime < 86400000; | 254 | return deltaTime < 86400000; |
| 265 | } | 255 | } |
| 256 | + | ||
| 257 | + /** | ||
| 258 | + * @return | ||
| 259 | + */ | ||
| 260 | + private File getLocalVersionJsonFile() | ||
| 261 | + { | ||
| 262 | + File versionDir = new File(this.versionsDir, this.version); | ||
| 263 | + if (!versionDir.exists() || !versionDir.isDirectory()) return null; | ||
| 264 | + | ||
| 265 | + File versionJsonFile = new File(versionDir, this.version + ".json"); | ||
| 266 | + return versionJsonFile; | ||
| 267 | + } | ||
| 266 | } | 268 | } |
java/common/com/mumfrey/liteloader/transformers/event/EventInjectionTransformer.java
| @@ -10,14 +10,11 @@ import java.util.Set; | @@ -10,14 +10,11 @@ import java.util.Set; | ||
| 10 | import java.util.TreeSet; | 10 | import java.util.TreeSet; |
| 11 | 11 | ||
| 12 | import org.objectweb.asm.ClassWriter; | 12 | import org.objectweb.asm.ClassWriter; |
| 13 | -import org.objectweb.asm.Opcodes; | ||
| 14 | import org.objectweb.asm.tree.AbstractInsnNode; | 13 | import org.objectweb.asm.tree.AbstractInsnNode; |
| 15 | import org.objectweb.asm.tree.ClassNode; | 14 | import org.objectweb.asm.tree.ClassNode; |
| 16 | -import org.objectweb.asm.tree.InsnNode; | ||
| 17 | import org.objectweb.asm.tree.MethodNode; | 15 | import org.objectweb.asm.tree.MethodNode; |
| 18 | import org.objectweb.asm.util.CheckClassAdapter; | 16 | import org.objectweb.asm.util.CheckClassAdapter; |
| 19 | 17 | ||
| 20 | -import com.mumfrey.liteloader.core.runtime.Obf; | ||
| 21 | import com.mumfrey.liteloader.transformers.ClassTransformer; | 18 | import com.mumfrey.liteloader.transformers.ClassTransformer; |
| 22 | import com.mumfrey.liteloader.util.log.LiteLoaderLogger; | 19 | import com.mumfrey.liteloader.util.log.LiteLoaderLogger; |
| 23 | 20 | ||
| @@ -59,7 +56,7 @@ public abstract class EventInjectionTransformer extends ClassTransformer | @@ -59,7 +56,7 @@ public abstract class EventInjectionTransformer extends ClassTransformer | ||
| 59 | /** | 56 | /** |
| 60 | * Multiple event injection transformers may exist but to allow co-operation the events themselves are registered | 57 | * Multiple event injection transformers may exist but to allow co-operation the events themselves are registered |
| 61 | * statically. The first EventInjectionTransformer instance to be created becomes the "master" and is actually responsible | 58 | * statically. The first EventInjectionTransformer instance to be created becomes the "master" and is actually responsible |
| 62 | - * for injecting the events and transforming the EventProxy class. | 59 | + * for injecting the events |
| 63 | */ | 60 | */ |
| 64 | private static EventInjectionTransformer master; | 61 | private static EventInjectionTransformer master; |
| 65 | 62 | ||
| @@ -156,23 +153,6 @@ public abstract class EventInjectionTransformer extends ClassTransformer | @@ -156,23 +153,6 @@ public abstract class EventInjectionTransformer extends ClassTransformer | ||
| 156 | { | 153 | { |
| 157 | if (EventInjectionTransformer.master == this) | 154 | if (EventInjectionTransformer.master == this) |
| 158 | { | 155 | { |
| 159 | - if (transformedName != null && transformedName.startsWith(Obf.EventProxy.name)) | ||
| 160 | - { | ||
| 161 | - int dollarPos = transformedName.indexOf('$'); | ||
| 162 | - int proxyIndex = (dollarPos > -1) ? Integer.parseInt(transformedName.substring(dollarPos + 1)) : 0; | ||
| 163 | - if (proxyIndex != 1) | ||
| 164 | - { | ||
| 165 | - try | ||
| 166 | - { | ||
| 167 | - return this.transformEventProxy(transformedName, basicClass, proxyIndex); | ||
| 168 | - } | ||
| 169 | - catch (Throwable th) | ||
| 170 | - { | ||
| 171 | - th.printStackTrace(); | ||
| 172 | - } | ||
| 173 | - } | ||
| 174 | - } | ||
| 175 | - | ||
| 176 | if (basicClass != null && EventInjectionTransformer.eventMappings.containsKey(transformedName)) | 156 | if (basicClass != null && EventInjectionTransformer.eventMappings.containsKey(transformedName)) |
| 177 | { | 157 | { |
| 178 | return this.injectEvents(basicClass, EventInjectionTransformer.eventMappings.get(transformedName)); | 158 | return this.injectEvents(basicClass, EventInjectionTransformer.eventMappings.get(transformedName)); |
| @@ -182,36 +162,6 @@ public abstract class EventInjectionTransformer extends ClassTransformer | @@ -182,36 +162,6 @@ public abstract class EventInjectionTransformer extends ClassTransformer | ||
| 182 | return basicClass; | 162 | return basicClass; |
| 183 | } | 163 | } |
| 184 | 164 | ||
| 185 | - private byte[] transformEventProxy(String transformedName, byte[] basicClass, int proxyIndex) | ||
| 186 | - { | ||
| 187 | - ClassNode classNode = this.getProxyByteCode(transformedName, basicClass, proxyIndex); | ||
| 188 | - return this.writeClass(Event.populateProxy(classNode, proxyIndex == 0 ? 1 : proxyIndex)); | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - private ClassNode getProxyByteCode(String transformedName, byte[] basicClass, int proxyIndex) | ||
| 192 | - { | ||
| 193 | - if (proxyIndex == 0 || basicClass != null) | ||
| 194 | - { | ||
| 195 | - ClassNode classNode = this.readClass(basicClass, true); | ||
| 196 | - | ||
| 197 | - for (MethodNode method : classNode.methods) | ||
| 198 | - { | ||
| 199 | - // Strip the sanity code out of the EventProxy class initialiser | ||
| 200 | - if ("<clinit>".equals(method.name)) | ||
| 201 | - { | ||
| 202 | - method.instructions.clear(); | ||
| 203 | - method.instructions.add(new InsnNode(Opcodes.RETURN)); | ||
| 204 | - } | ||
| 205 | - } | ||
| 206 | - | ||
| 207 | - return classNode; | ||
| 208 | - } | ||
| 209 | - | ||
| 210 | - ClassNode classNode = new ClassNode(); | ||
| 211 | - classNode.visit(50, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, transformedName.replace('.', '/'), null, "java/lang/Object", null); | ||
| 212 | - return classNode; | ||
| 213 | - } | ||
| 214 | - | ||
| 215 | private byte[] injectEvents(byte[] basicClass, Map<String, Map<Event, InjectionPoint>> mappings) | 165 | private byte[] injectEvents(byte[] basicClass, Map<String, Map<Event, InjectionPoint>> mappings) |
| 216 | { | 166 | { |
| 217 | if (mappings == null) return basicClass; | 167 | if (mappings == null) return basicClass; |
java/common/com/mumfrey/liteloader/transformers/event/EventProxyTransformer.java
0 → 100644
| 1 | +package com.mumfrey.liteloader.transformers.event; | ||
| 2 | + | ||
| 3 | +import org.objectweb.asm.Opcodes; | ||
| 4 | +import org.objectweb.asm.tree.ClassNode; | ||
| 5 | +import org.objectweb.asm.tree.InsnNode; | ||
| 6 | +import org.objectweb.asm.tree.MethodNode; | ||
| 7 | + | ||
| 8 | +import com.mumfrey.liteloader.core.runtime.Obf; | ||
| 9 | +import com.mumfrey.liteloader.transformers.ClassTransformer; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * Transformer responsible for transforming/generating the EventProxy inner classes, separated from the Event Transformer | ||
| 13 | + * itself so that we can place it higher up the tranformer chain to avoid broken mod transformers screwing things up. | ||
| 14 | + * | ||
| 15 | + * @author Adam Mummery-Smith | ||
| 16 | + */ | ||
| 17 | +public class EventProxyTransformer extends ClassTransformer | ||
| 18 | +{ | ||
| 19 | + public EventProxyTransformer() | ||
| 20 | + { | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public byte[] transform(String name, String transformedName, byte[] basicClass) | ||
| 25 | + { | ||
| 26 | + if (transformedName != null && transformedName.startsWith(Obf.EventProxy.name)) | ||
| 27 | + { | ||
| 28 | + int dollarPos = transformedName.indexOf('$'); | ||
| 29 | + int proxyIndex = (dollarPos > -1) ? Integer.parseInt(transformedName.substring(dollarPos + 1)) : 0; | ||
| 30 | + if (proxyIndex != 1) | ||
| 31 | + { | ||
| 32 | + try | ||
| 33 | + { | ||
| 34 | + return this.transformEventProxy(transformedName, basicClass, proxyIndex); | ||
| 35 | + } | ||
| 36 | + catch (Throwable th) | ||
| 37 | + { | ||
| 38 | + th.printStackTrace(); | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + return basicClass; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + private byte[] transformEventProxy(String transformedName, byte[] basicClass, int proxyIndex) | ||
| 47 | + { | ||
| 48 | + ClassNode classNode = this.getProxyByteCode(transformedName, basicClass, proxyIndex); | ||
| 49 | + return this.writeClass(Event.populateProxy(classNode, proxyIndex == 0 ? 1 : proxyIndex)); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + private ClassNode getProxyByteCode(String transformedName, byte[] basicClass, int proxyIndex) | ||
| 53 | + { | ||
| 54 | + if (proxyIndex == 0 || basicClass != null) | ||
| 55 | + { | ||
| 56 | + ClassNode classNode = this.readClass(basicClass, true); | ||
| 57 | + | ||
| 58 | + for (MethodNode method : classNode.methods) | ||
| 59 | + { | ||
| 60 | + // Strip the sanity code out of the EventProxy class initialiser | ||
| 61 | + if ("<clinit>".equals(method.name)) | ||
| 62 | + { | ||
| 63 | + method.instructions.clear(); | ||
| 64 | + method.instructions.add(new InsnNode(Opcodes.RETURN)); | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + return classNode; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + ClassNode classNode = new ClassNode(); | ||
| 72 | + classNode.visit(50, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, transformedName.replace('.', '/'), null, "java/lang/Object", null); | ||
| 73 | + return classNode; | ||
| 74 | + } | ||
| 75 | +} |