Commit 0e3dd41eb133563ef7cc5c1d1a2fedde47eb4142
1 parent
d28d5dde
adding javadoc to AccessorTransformer, throw an exception from un-annotated methods
Showing
1 changed file
with
195 additions
and
30 deletions
java/common/com/mumfrey/liteloader/transformers/access/AccessorTransformer.java
| @@ -7,7 +7,6 @@ import java.util.List; | @@ -7,7 +7,6 @@ import java.util.List; | ||
| 7 | 7 | ||
| 8 | import net.minecraft.launchwrapper.Launch; | 8 | import net.minecraft.launchwrapper.Launch; |
| 9 | 9 | ||
| 10 | -import org.objectweb.asm.ClassReader; | ||
| 11 | import org.objectweb.asm.Opcodes; | 10 | import org.objectweb.asm.Opcodes; |
| 12 | import org.objectweb.asm.Type; | 11 | import org.objectweb.asm.Type; |
| 13 | import org.objectweb.asm.tree.AnnotationNode; | 12 | import org.objectweb.asm.tree.AnnotationNode; |
| @@ -15,8 +14,10 @@ import org.objectweb.asm.tree.ClassNode; | @@ -15,8 +14,10 @@ import org.objectweb.asm.tree.ClassNode; | ||
| 15 | import org.objectweb.asm.tree.FieldInsnNode; | 14 | import org.objectweb.asm.tree.FieldInsnNode; |
| 16 | import org.objectweb.asm.tree.FieldNode; | 15 | import org.objectweb.asm.tree.FieldNode; |
| 17 | import org.objectweb.asm.tree.InsnNode; | 16 | import org.objectweb.asm.tree.InsnNode; |
| 17 | +import org.objectweb.asm.tree.LdcInsnNode; | ||
| 18 | import org.objectweb.asm.tree.MethodInsnNode; | 18 | import org.objectweb.asm.tree.MethodInsnNode; |
| 19 | import org.objectweb.asm.tree.MethodNode; | 19 | import org.objectweb.asm.tree.MethodNode; |
| 20 | +import org.objectweb.asm.tree.TypeInsnNode; | ||
| 20 | import org.objectweb.asm.tree.VarInsnNode; | 21 | import org.objectweb.asm.tree.VarInsnNode; |
| 21 | 22 | ||
| 22 | import com.mumfrey.liteloader.core.runtime.Obf; | 23 | import com.mumfrey.liteloader.core.runtime.Obf; |
| @@ -26,7 +27,7 @@ import com.mumfrey.liteloader.transformers.ObfProvider; | @@ -26,7 +27,7 @@ import com.mumfrey.liteloader.transformers.ObfProvider; | ||
| 26 | import com.mumfrey.liteloader.util.log.LiteLoaderLogger; | 27 | import com.mumfrey.liteloader.util.log.LiteLoaderLogger; |
| 27 | 28 | ||
| 28 | /** | 29 | /** |
| 29 | - * Transformer which can inject accessor methods into a target class | 30 | + * Transformer which can inject accessor methods defined by an annotated interface into a target class |
| 30 | * | 31 | * |
| 31 | * @author Adam Mummery-Smith | 32 | * @author Adam Mummery-Smith |
| 32 | */ | 33 | */ |
| @@ -59,34 +60,47 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -59,34 +60,47 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 59 | */ | 60 | */ |
| 60 | private final Obf target; | 61 | private final Obf target; |
| 61 | 62 | ||
| 63 | + /** | ||
| 64 | + * Create a new new accessor using the specified template interface | ||
| 65 | + * | ||
| 66 | + * @param iface Template interface | ||
| 67 | + * @throws IOException Thrown if an problem occurs when loading the interface bytecode | ||
| 68 | + */ | ||
| 62 | protected AccessorInjection(String iface) throws IOException | 69 | protected AccessorInjection(String iface) throws IOException |
| 63 | { | 70 | { |
| 64 | this(iface, null); | 71 | this(iface, null); |
| 65 | } | 72 | } |
| 66 | 73 | ||
| 74 | + /** | ||
| 75 | + * Create a new new accessor using the specified template interface | ||
| 76 | + * | ||
| 77 | + * @param iface Template interface | ||
| 78 | + * @param obfProvider Obfuscation provider for this context | ||
| 79 | + * @throws IOException Thrown if an problem occurs when loading the interface bytecode | ||
| 80 | + */ | ||
| 67 | protected AccessorInjection(String iface, ObfProvider obfProvider) throws IOException | 81 | protected AccessorInjection(String iface, ObfProvider obfProvider) throws IOException |
| 68 | { | 82 | { |
| 69 | - ClassNode ifaceNode = this.loadClass(iface); | 83 | + ClassNode ifaceNode = ByteCodeUtilities.loadClass(iface, false); |
| 84 | + | ||
| 85 | + if (ifaceNode.interfaces.size() > 0) | ||
| 86 | + { | ||
| 87 | + String interfaceList = ifaceNode.interfaces.toString().replace('/', '.'); | ||
| 88 | + throw new RuntimeException("Accessor interface must not extend other interfaces. Found " + interfaceList + " in " + iface); | ||
| 89 | + } | ||
| 90 | + | ||
| 70 | this.table = this.setupTable(ifaceNode); | 91 | this.table = this.setupTable(ifaceNode); |
| 71 | this.target = this.setupTarget(ifaceNode); | 92 | this.target = this.setupTarget(ifaceNode); |
| 72 | this.iface = iface; | 93 | this.iface = iface; |
| 73 | this.obfProvider = obfProvider; | 94 | this.obfProvider = obfProvider; |
| 74 | } | 95 | } |
| 75 | - | ||
| 76 | - private ClassNode loadClass(String iface) throws IOException | ||
| 77 | - { | ||
| 78 | - byte[] bytes = this.getClassBytes(iface); | ||
| 79 | - ClassReader classReader = new ClassReader(bytes); | ||
| 80 | - ClassNode classNode = new ClassNode(); | ||
| 81 | - classReader.accept(classNode, 0); | ||
| 82 | - return classNode; | ||
| 83 | - } | ||
| 84 | - | ||
| 85 | - private byte[] getClassBytes(String iface) throws IOException | ||
| 86 | - { | ||
| 87 | - return Launch.classLoader.getClassBytes(iface); | ||
| 88 | - } | ||
| 89 | 96 | ||
| 97 | + /** | ||
| 98 | + * Get an obfuscation table mapping by name, first uses any supplied context provider, then any obfuscation table | ||
| 99 | + * class specified by an {@link ObfTableClass} annotation on the interface itself, and fails over onto the LiteLoader | ||
| 100 | + * obfuscation table. If the entry is not matched in any of the above locations then an exception is thrown | ||
| 101 | + * | ||
| 102 | + * @param name Obfuscation table entry to fetch | ||
| 103 | + */ | ||
| 90 | private Obf getObf(String name) | 104 | private Obf getObf(String name) |
| 91 | { | 105 | { |
| 92 | if (this.obfProvider != null) | 106 | if (this.obfProvider != null) |
| @@ -107,11 +121,18 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -107,11 +121,18 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 107 | throw new RuntimeException("No obfuscation table entry could be found for '" + name + "'"); | 121 | throw new RuntimeException("No obfuscation table entry could be found for '" + name + "'"); |
| 108 | } | 122 | } |
| 109 | 123 | ||
| 124 | + /** | ||
| 125 | + * Get the target class of this injection | ||
| 126 | + */ | ||
| 110 | protected Obf getTarget() | 127 | protected Obf getTarget() |
| 111 | { | 128 | { |
| 112 | return this.target; | 129 | return this.target; |
| 113 | } | 130 | } |
| 114 | 131 | ||
| 132 | + /** | ||
| 133 | + * Inspects the target class for an {@link ObfTableClass} annotation and attempts to get a handle for the class | ||
| 134 | + * specified. On failure, the LiteLoader {@link Obf} is returned. | ||
| 135 | + */ | ||
| 115 | @SuppressWarnings("unchecked") | 136 | @SuppressWarnings("unchecked") |
| 116 | private Class<? extends Obf> setupTable(ClassNode ifaceNode) | 137 | private Class<? extends Obf> setupTable(ClassNode ifaceNode) |
| 117 | { | 138 | { |
| @@ -132,12 +153,31 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -132,12 +153,31 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 132 | return Obf.class; | 153 | return Obf.class; |
| 133 | } | 154 | } |
| 134 | 155 | ||
| 156 | + /** | ||
| 157 | + * Locates the {@link Accessor} annotation on the interface in order to determine the target class | ||
| 158 | + */ | ||
| 135 | private Obf setupTarget(ClassNode ifaceNode) | 159 | private Obf setupTarget(ClassNode ifaceNode) |
| 136 | { | 160 | { |
| 137 | AnnotationNode annotation = ByteCodeUtilities.getInvisibleAnnotation(ifaceNode, Accessor.class); | 161 | AnnotationNode annotation = ByteCodeUtilities.getInvisibleAnnotation(ifaceNode, Accessor.class); |
| 138 | - return this.getObf(ByteCodeUtilities.<String>getAnnotationValue(annotation)); | 162 | + if (annotation == null) |
| 163 | + { | ||
| 164 | + throw new RuntimeException("Accessor interfaces must be annotated with an @Accessor annotation specifying the target class"); | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + String targetClass = ByteCodeUtilities.<String>getAnnotationValue(annotation); | ||
| 168 | + if (targetClass == null || targetClass.isEmpty()) | ||
| 169 | + { | ||
| 170 | + throw new RuntimeException("Invalid @Accessor annotation, the annotation must specify a target class"); | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + return this.getObf(targetClass); | ||
| 139 | } | 174 | } |
| 140 | 175 | ||
| 176 | + /** | ||
| 177 | + * Apply this injection to the specified target ClassNode | ||
| 178 | + * | ||
| 179 | + * @param classNode Class tree to apply to | ||
| 180 | + */ | ||
| 141 | protected void apply(ClassNode classNode) | 181 | protected void apply(ClassNode classNode) |
| 142 | { | 182 | { |
| 143 | String ifaceRef = this.iface.replace('.', '/'); | 183 | String ifaceRef = this.iface.replace('.', '/'); |
| @@ -166,6 +206,12 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -166,6 +206,12 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 166 | } | 206 | } |
| 167 | } | 207 | } |
| 168 | 208 | ||
| 209 | + /** | ||
| 210 | + * Add a method from the interface to the target class | ||
| 211 | + * | ||
| 212 | + * @param classNode Target class | ||
| 213 | + * @param method Method to add | ||
| 214 | + */ | ||
| 169 | private void addMethod(ClassNode classNode, MethodNode method) | 215 | private void addMethod(ClassNode classNode, MethodNode method) |
| 170 | { | 216 | { |
| 171 | if (!this.addMethodToClass(classNode, method)) | 217 | if (!this.addMethodToClass(classNode, method)) |
| @@ -176,26 +222,39 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -176,26 +222,39 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 176 | 222 | ||
| 177 | LiteLoaderLogger.debug("[AccessorTransformer] Attempting to add %s to %s", method.name, classNode.name); | 223 | LiteLoaderLogger.debug("[AccessorTransformer] Attempting to add %s to %s", method.name, classNode.name); |
| 178 | 224 | ||
| 225 | + String targetId = null; | ||
| 179 | AnnotationNode accessor = ByteCodeUtilities.getInvisibleAnnotation(method, Accessor.class); | 226 | AnnotationNode accessor = ByteCodeUtilities.getInvisibleAnnotation(method, Accessor.class); |
| 180 | AnnotationNode invoker = ByteCodeUtilities.getInvisibleAnnotation(method, Invoker.class); | 227 | AnnotationNode invoker = ByteCodeUtilities.getInvisibleAnnotation(method, Invoker.class); |
| 181 | if (accessor != null) | 228 | if (accessor != null) |
| 182 | { | 229 | { |
| 183 | - Obf targetName = this.getObf(ByteCodeUtilities.<String>getAnnotationValue(accessor)); | 230 | + targetId = ByteCodeUtilities.<String>getAnnotationValue(accessor); |
| 231 | + Obf targetName = this.getObf(targetId); | ||
| 184 | if (this.injectAccessor(classNode, method, targetName)) return; | 232 | if (this.injectAccessor(classNode, method, targetName)) return; |
| 185 | } | 233 | } |
| 186 | else if (invoker != null) | 234 | else if (invoker != null) |
| 187 | { | 235 | { |
| 188 | - Obf targetName = this.getObf(ByteCodeUtilities.<String>getAnnotationValue(invoker)); | 236 | + targetId = ByteCodeUtilities.<String>getAnnotationValue(invoker); |
| 237 | + Obf targetName = this.getObf(targetId); | ||
| 189 | if (this.injectInvoker(classNode, method, targetName)) return; | 238 | if (this.injectInvoker(classNode, method, targetName)) return; |
| 190 | } | 239 | } |
| 191 | else | 240 | else |
| 192 | { | 241 | { |
| 193 | LiteLoaderLogger.severe("[AccessorTransformer] Method %s for %s has no @Accessor or @Invoker annotation, the method will be ABSTRACT!", method.name, this.iface); | 242 | LiteLoaderLogger.severe("[AccessorTransformer] Method %s for %s has no @Accessor or @Invoker annotation, the method will be ABSTRACT!", method.name, this.iface); |
| 243 | + this.injectException(classNode, method, "No @Accessor or @Invoker annotation on method"); | ||
| 244 | + return; | ||
| 194 | } | 245 | } |
| 195 | 246 | ||
| 196 | LiteLoaderLogger.severe("[AccessorTransformer] Method %s for %s could not locate target member, the method will be ABSTRACT!", method.name, this.iface); | 247 | LiteLoaderLogger.severe("[AccessorTransformer] Method %s for %s could not locate target member, the method will be ABSTRACT!", method.name, this.iface); |
| 248 | + this.injectException(classNode, method, "Could not locate target class member '" + targetId + "'"); | ||
| 197 | } | 249 | } |
| 198 | 250 | ||
| 251 | + /** | ||
| 252 | + * Inject an accessor method into the target class | ||
| 253 | + * | ||
| 254 | + * @param classNode | ||
| 255 | + * @param method | ||
| 256 | + * @param targetName | ||
| 257 | + */ | ||
| 199 | private boolean injectAccessor(ClassNode classNode, MethodNode method, Obf targetName) | 258 | private boolean injectAccessor(ClassNode classNode, MethodNode method, Obf targetName) |
| 200 | { | 259 | { |
| 201 | FieldNode targetField = this.findField(classNode, targetName); | 260 | FieldNode targetField = this.findField(classNode, targetName); |
| @@ -217,6 +276,13 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -217,6 +276,13 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 217 | return false; | 276 | return false; |
| 218 | } | 277 | } |
| 219 | 278 | ||
| 279 | + /** | ||
| 280 | + * Inject an invoke (proxy) method into the target class | ||
| 281 | + * | ||
| 282 | + * @param classNode | ||
| 283 | + * @param method | ||
| 284 | + * @param targetName | ||
| 285 | + */ | ||
| 220 | private boolean injectInvoker(ClassNode classNode, MethodNode method, Obf targetName) | 286 | private boolean injectInvoker(ClassNode classNode, MethodNode method, Obf targetName) |
| 221 | { | 287 | { |
| 222 | MethodNode targetMethod = this.findMethod(classNode, targetName, method.desc); | 288 | MethodNode targetMethod = this.findMethod(classNode, targetName, method.desc); |
| @@ -230,6 +296,13 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -230,6 +296,13 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 230 | return false; | 296 | return false; |
| 231 | } | 297 | } |
| 232 | 298 | ||
| 299 | + /** | ||
| 300 | + * Populate the bytecode instructions for a getter accessor | ||
| 301 | + * | ||
| 302 | + * @param classNode | ||
| 303 | + * @param method | ||
| 304 | + * @param field | ||
| 305 | + */ | ||
| 233 | private void populateGetter(ClassNode classNode, MethodNode method, FieldNode field) | 306 | private void populateGetter(ClassNode classNode, MethodNode method, FieldNode field) |
| 234 | { | 307 | { |
| 235 | Type returnType = Type.getReturnType(method.desc); | 308 | Type returnType = Type.getReturnType(method.desc); |
| @@ -238,24 +311,32 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -238,24 +311,32 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 238 | { | 311 | { |
| 239 | throw new RuntimeException("Incompatible types! Field type: " + fieldType + " Method type: " + returnType); | 312 | throw new RuntimeException("Incompatible types! Field type: " + fieldType + " Method type: " + returnType); |
| 240 | } | 313 | } |
| 314 | + boolean isStatic = (field.access & Opcodes.ACC_STATIC) != 0; | ||
| 241 | 315 | ||
| 242 | method.instructions.clear(); | 316 | method.instructions.clear(); |
| 243 | method.maxLocals = ByteCodeUtilities.getFirstNonArgLocalIndex(method); | 317 | method.maxLocals = ByteCodeUtilities.getFirstNonArgLocalIndex(method); |
| 244 | method.maxStack = fieldType.getSize(); | 318 | method.maxStack = fieldType.getSize(); |
| 245 | 319 | ||
| 246 | - if ((field.access & Opcodes.ACC_STATIC) == 0) | 320 | + if (isStatic) |
| 247 | { | 321 | { |
| 248 | - method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); | ||
| 249 | - method.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, field.name, field.desc)); | 322 | + method.instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, field.name, field.desc)); |
| 250 | } | 323 | } |
| 251 | else | 324 | else |
| 252 | { | 325 | { |
| 253 | - method.instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, field.name, field.desc)); | 326 | + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); |
| 327 | + method.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, field.name, field.desc)); | ||
| 254 | } | 328 | } |
| 255 | 329 | ||
| 256 | method.instructions.add(new InsnNode(returnType.getOpcode(Opcodes.IRETURN))); | 330 | method.instructions.add(new InsnNode(returnType.getOpcode(Opcodes.IRETURN))); |
| 257 | } | 331 | } |
| 258 | 332 | ||
| 333 | + /** | ||
| 334 | + * Populate the bytecode instructions for a setter | ||
| 335 | + * | ||
| 336 | + * @param classNode | ||
| 337 | + * @param method | ||
| 338 | + * @param field | ||
| 339 | + */ | ||
| 259 | private void populateSetter(ClassNode classNode, MethodNode method, FieldNode field) | 340 | private void populateSetter(ClassNode classNode, MethodNode method, FieldNode field) |
| 260 | { | 341 | { |
| 261 | Type[] argTypes = Type.getArgumentTypes(method.desc); | 342 | Type[] argTypes = Type.getArgumentTypes(method.desc); |
| @@ -269,26 +350,34 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -269,26 +350,34 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 269 | { | 350 | { |
| 270 | throw new RuntimeException("Incompatible types! Field type: " + fieldType + " Method type: " + argType); | 351 | throw new RuntimeException("Incompatible types! Field type: " + fieldType + " Method type: " + argType); |
| 271 | } | 352 | } |
| 353 | + boolean isStatic = (field.access & Opcodes.ACC_STATIC) != 0; | ||
| 272 | 354 | ||
| 273 | method.instructions.clear(); | 355 | method.instructions.clear(); |
| 274 | method.maxLocals = ByteCodeUtilities.getFirstNonArgLocalIndex(method); | 356 | method.maxLocals = ByteCodeUtilities.getFirstNonArgLocalIndex(method); |
| 275 | method.maxStack = fieldType.getSize(); | 357 | method.maxStack = fieldType.getSize(); |
| 276 | 358 | ||
| 277 | - if ((field.access & Opcodes.ACC_STATIC) == 0) | 359 | + if (isStatic) |
| 278 | { | 360 | { |
| 279 | - method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); | ||
| 280 | - method.instructions.add(new VarInsnNode(argType.getOpcode(Opcodes.ILOAD), 1)); | ||
| 281 | - method.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc)); | 361 | + method.instructions.add(new VarInsnNode(argType.getOpcode(Opcodes.ILOAD), 0)); |
| 362 | + method.instructions.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc)); | ||
| 282 | } | 363 | } |
| 283 | else | 364 | else |
| 284 | { | 365 | { |
| 285 | - method.instructions.add(new VarInsnNode(argType.getOpcode(Opcodes.ILOAD), 0)); | ||
| 286 | - method.instructions.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc)); | 366 | + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); |
| 367 | + method.instructions.add(new VarInsnNode(argType.getOpcode(Opcodes.ILOAD), 1)); | ||
| 368 | + method.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc)); | ||
| 287 | } | 369 | } |
| 288 | 370 | ||
| 289 | method.instructions.add(new InsnNode(Opcodes.RETURN)); | 371 | method.instructions.add(new InsnNode(Opcodes.RETURN)); |
| 290 | } | 372 | } |
| 291 | 373 | ||
| 374 | + /** | ||
| 375 | + * Populate the bytecode instructions for an invoker (proxy) method | ||
| 376 | + * | ||
| 377 | + * @param classNode | ||
| 378 | + * @param method | ||
| 379 | + * @param targetMethod | ||
| 380 | + */ | ||
| 292 | private void populateInvoker(ClassNode classNode, MethodNode method, MethodNode targetMethod) | 381 | private void populateInvoker(ClassNode classNode, MethodNode method, MethodNode targetMethod) |
| 293 | { | 382 | { |
| 294 | Type[] args = Type.getArgumentTypes(targetMethod.desc); | 383 | Type[] args = Type.getArgumentTypes(targetMethod.desc); |
| @@ -313,6 +402,31 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -313,6 +402,31 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 313 | method.instructions.add(new InsnNode(returnType.getOpcode(Opcodes.IRETURN))); | 402 | method.instructions.add(new InsnNode(returnType.getOpcode(Opcodes.IRETURN))); |
| 314 | } | 403 | } |
| 315 | 404 | ||
| 405 | + /** | ||
| 406 | + * Populate bytecode instructions for a method which throws an exception | ||
| 407 | + * | ||
| 408 | + * @param classNode | ||
| 409 | + * @param method | ||
| 410 | + * @param message | ||
| 411 | + */ | ||
| 412 | + private void injectException(ClassNode classNode, MethodNode method, String message) | ||
| 413 | + { | ||
| 414 | + method.instructions.clear(); | ||
| 415 | + method.maxStack = 2; | ||
| 416 | + | ||
| 417 | + method.instructions.add(new TypeInsnNode(Opcodes.NEW, "java/lang/RuntimeException")); | ||
| 418 | + method.instructions.add(new InsnNode(Opcodes.DUP)); | ||
| 419 | + method.instructions.add(new LdcInsnNode(message)); | ||
| 420 | + method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V", false)); | ||
| 421 | + method.instructions.add(new InsnNode(Opcodes.ATHROW)); | ||
| 422 | + } | ||
| 423 | + | ||
| 424 | + /** | ||
| 425 | + * Find a field in the target class which matches the specified field name | ||
| 426 | + * | ||
| 427 | + * @param classNode | ||
| 428 | + * @param fieldName | ||
| 429 | + */ | ||
| 316 | private FieldNode findField(ClassNode classNode, Obf fieldName) | 430 | private FieldNode findField(ClassNode classNode, Obf fieldName) |
| 317 | { | 431 | { |
| 318 | for (FieldNode field : classNode.fields) | 432 | for (FieldNode field : classNode.fields) |
| @@ -324,6 +438,13 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -324,6 +438,13 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 324 | return null; | 438 | return null; |
| 325 | } | 439 | } |
| 326 | 440 | ||
| 441 | + /** | ||
| 442 | + * Find a method in the target class which matches the specified method name and descriptor | ||
| 443 | + * | ||
| 444 | + * @param classNode | ||
| 445 | + * @param methodName | ||
| 446 | + * @param desc | ||
| 447 | + */ | ||
| 327 | private MethodNode findMethod(ClassNode classNode, Obf methodName, String desc) | 448 | private MethodNode findMethod(ClassNode classNode, Obf methodName, String desc) |
| 328 | { | 449 | { |
| 329 | for (MethodNode method : classNode.methods) | 450 | for (MethodNode method : classNode.methods) |
| @@ -335,6 +456,12 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -335,6 +456,12 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 335 | return null; | 456 | return null; |
| 336 | } | 457 | } |
| 337 | 458 | ||
| 459 | + /** | ||
| 460 | + * Add a method from the template interface to the target class | ||
| 461 | + * | ||
| 462 | + * @param classNode | ||
| 463 | + * @param method | ||
| 464 | + */ | ||
| 338 | private boolean addMethodToClass(ClassNode classNode, MethodNode method) | 465 | private boolean addMethodToClass(ClassNode classNode, MethodNode method) |
| 339 | { | 466 | { |
| 340 | MethodNode existingMethod = ByteCodeUtilities.findTargetMethod(classNode, method); | 467 | MethodNode existingMethod = ByteCodeUtilities.findTargetMethod(classNode, method); |
| @@ -345,18 +472,33 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -345,18 +472,33 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 345 | } | 472 | } |
| 346 | } | 473 | } |
| 347 | 474 | ||
| 475 | + /** | ||
| 476 | + * List of accessors to inject | ||
| 477 | + */ | ||
| 348 | private final List<AccessorInjection> accessors = new ArrayList<AccessorInjection>(); | 478 | private final List<AccessorInjection> accessors = new ArrayList<AccessorInjection>(); |
| 349 | 479 | ||
| 480 | + /** | ||
| 481 | + * ctor | ||
| 482 | + */ | ||
| 350 | public AccessorTransformer() | 483 | public AccessorTransformer() |
| 351 | { | 484 | { |
| 352 | this.addAccessors(); | 485 | this.addAccessors(); |
| 353 | } | 486 | } |
| 354 | 487 | ||
| 488 | + /** | ||
| 489 | + * @param interfaceName | ||
| 490 | + */ | ||
| 355 | public void addAccessor(String interfaceName) | 491 | public void addAccessor(String interfaceName) |
| 356 | { | 492 | { |
| 357 | this.addAccessor(interfaceName, null); | 493 | this.addAccessor(interfaceName, null); |
| 358 | } | 494 | } |
| 359 | 495 | ||
| 496 | + /** | ||
| 497 | + * Add an accessor to the accessors list | ||
| 498 | + * | ||
| 499 | + * @param interfaceName | ||
| 500 | + * @param obfProvider | ||
| 501 | + */ | ||
| 360 | public void addAccessor(String interfaceName, ObfProvider obfProvider) | 502 | public void addAccessor(String interfaceName, ObfProvider obfProvider) |
| 361 | { | 503 | { |
| 362 | try | 504 | try |
| @@ -369,6 +511,9 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -369,6 +511,9 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 369 | } | 511 | } |
| 370 | } | 512 | } |
| 371 | 513 | ||
| 514 | + /* (non-Javadoc) | ||
| 515 | + * @see net.minecraft.launchwrapper.IClassTransformer#transform(java.lang.String, java.lang.String, byte[]) | ||
| 516 | + */ | ||
| 372 | @Override | 517 | @Override |
| 373 | public byte[] transform(String name, String transformedName, byte[] basicClass) | 518 | public byte[] transform(String name, String transformedName, byte[] basicClass) |
| 374 | { | 519 | { |
| @@ -385,6 +530,16 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -385,6 +530,16 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 385 | return basicClass; | 530 | return basicClass; |
| 386 | } | 531 | } |
| 387 | 532 | ||
| 533 | + /** | ||
| 534 | + * Apply this transformer, used when this transformer is acting as a delegate via another transformer | ||
| 535 | + * (eg. an EventTransformer) and the parent transformer already has a ClassNode for the target class. | ||
| 536 | + * | ||
| 537 | + * @param name | ||
| 538 | + * @param transformedName | ||
| 539 | + * @param basicClass | ||
| 540 | + * @param classNode | ||
| 541 | + * @return | ||
| 542 | + */ | ||
| 388 | public ClassNode apply(String name, String transformedName, byte[] basicClass, ClassNode classNode) | 543 | public ClassNode apply(String name, String transformedName, byte[] basicClass, ClassNode classNode) |
| 389 | { | 544 | { |
| 390 | for (Iterator<AccessorInjection> iter = this.accessors.iterator(); iter.hasNext(); ) | 545 | for (Iterator<AccessorInjection> iter = this.accessors.iterator(); iter.hasNext(); ) |
| @@ -403,10 +558,20 @@ public abstract class AccessorTransformer extends ClassTransformer | @@ -403,10 +558,20 @@ public abstract class AccessorTransformer extends ClassTransformer | ||
| 403 | return classNode; | 558 | return classNode; |
| 404 | } | 559 | } |
| 405 | 560 | ||
| 561 | + /** | ||
| 562 | + * Subclasses should add their accessors here | ||
| 563 | + */ | ||
| 406 | protected void addAccessors() | 564 | protected void addAccessors() |
| 407 | { | 565 | { |
| 408 | } | 566 | } |
| 409 | 567 | ||
| 568 | + /** | ||
| 569 | + * Called after transformation is applied, allows custom transforms to be performed by subclasses | ||
| 570 | + * | ||
| 571 | + * @param name | ||
| 572 | + * @param transformedName | ||
| 573 | + * @param classNode | ||
| 574 | + */ | ||
| 410 | protected void postTransform(String name, String transformedName, ClassNode classNode) | 575 | protected void postTransform(String name, String transformedName, ClassNode classNode) |
| 411 | { | 576 | { |
| 412 | } | 577 | } |