Commit 5225abe9413b4aa801488bc01834bc3aedf386e7

Authored by Mumfrey
1 parent 38127d17

some tweaks to HandlerList and added more javadoc

java/common/com/mumfrey/liteloader/core/event/HandlerList.java
@@ -82,9 +82,8 @@ public class HandlerList<T> extends LinkedList<T> @@ -82,9 +82,8 @@ public class HandlerList<T> extends LinkedList<T>
82 */ 82 */
83 protected void bake() 83 protected void bake()
84 { 84 {
85 - HandlerListClassLoader<T> classLoader = new HandlerListClassLoader<T>(this.type, this.size());  
86 - this.bakedHandler = classLoader.newHandler();  
87 - this.bakedHandler.populate(this); 85 + HandlerListClassLoader<T> classLoader = new HandlerListClassLoader<T>(this.type);
  86 + this.bakedHandler = classLoader.newHandler(this);
88 } 87 }
89 88
90 /** 89 /**
@@ -370,7 +369,22 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -370,7 +369,22 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
370 { 369 {
371 public abstract T get(); 370 public abstract T get();
372 371
373 - public abstract void populate(List<T> listeners); 372 + public abstract BakedHandlerList<T> populate(List<T> listeners);
  373 + }
  374 +
  375 + /**
  376 + * Exception to throw when failing to bake a handler list
  377 + *
  378 + * @author Adam Mummery-Smith
  379 + */
  380 + static class BakingFailedException extends RuntimeException
  381 + {
  382 + private static final long serialVersionUID = 1L;
  383 +
  384 + public BakingFailedException(Throwable cause)
  385 + {
  386 + super("An unexpected error occurred while baking the handler list", cause);
  387 + }
374 } 388 }
375 389
376 /** 390 /**
@@ -381,66 +395,118 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -381,66 +395,118 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
381 */ 395 */
382 static class HandlerListClassLoader<T> extends URLClassLoader 396 static class HandlerListClassLoader<T> extends URLClassLoader
383 { 397 {
  398 + private static final String HANDLER_VAR_PREFIX = "handler$";
  399 +
384 /** 400 /**
385 * Unique index number, just to ensure no name clashes 401 * Unique index number, just to ensure no name clashes
386 */ 402 */
387 private static int handlerIndex; 403 private static int handlerIndex;
388 404
389 - private int lineNumber = 1;  
390 - 405 + /**
  406 + * Interface type which this classloader is generating handler for
  407 + */
391 private final Class<T> type; 408 private final Class<T> type;
392 409
  410 + /**
  411 + * Calculated class ref for the class type so that we don't have to keep calling getName().replace('.', '/')
  412 + */
393 private final String typeRef; 413 private final String typeRef;
394 414
  415 + /**
  416 + * Size of the handler list
  417 + */
395 private int size; 418 private int size;
396 - 419 +
397 /** 420 /**
398 * @param type 421 * @param type
399 * @param size 422 * @param size
400 */ 423 */
401 - HandlerListClassLoader(Class<T> type, int size) 424 + HandlerListClassLoader(Class<T> type)
402 { 425 {
403 super(new URL[0], Launch.classLoader); 426 super(new URL[0], Launch.classLoader);
404 this.type = type; 427 this.type = type;
405 this.typeRef = type.getName().replace('.', '/'); 428 this.typeRef = type.getName().replace('.', '/');
406 - this.size = size;  
407 } 429 }
408 430
409 /** 431 /**
410 * Create and return a new baked handler list 432 * Create and return a new baked handler list
411 */ 433 */
412 @SuppressWarnings("unchecked") 434 @SuppressWarnings("unchecked")
413 - public BakedHandlerList<T> newHandler() 435 + public BakedHandlerList<T> newHandler(HandlerList<T> list)
  436 + {
  437 + this.size = list.size();
  438 +
  439 + Class<BakedHandlerList<T>> handlerClass = null;
  440 +
  441 + try
  442 + {
  443 + // Inflect the class name and attempt to generate the class
  444 + String className = HandlerListClassLoader.getNextClassName(Obf.HandlerList.name, this.type.getSimpleName());
  445 + handlerClass = (Class<BakedHandlerList<T>>)this.loadClass(className);
  446 + }
  447 + catch (ClassNotFoundException ex)
  448 + {
  449 + throw new BakingFailedException(ex);
  450 + }
  451 +
  452 + try
  453 + {
  454 + // Create an instance of the class, populate the entries from the supplied list and return it
  455 + BakedHandlerList<T> handlerList = this.createInstance(handlerClass);
  456 + return handlerList.populate(list);
  457 + }
  458 + catch (InstantiationException ex)
  459 + {
  460 + throw new BakingFailedException(ex);
  461 + }
  462 + }
  463 +
  464 + /**
  465 + * Create an instance of the baked class
  466 + *
  467 + * @param handlerClass Baked HandlerList class
  468 + * @return new instance of the Baked HandlerList class
  469 + * @throws InstantiationException if the handler can't be created for some reason
  470 + */
  471 + private BakedHandlerList<T> createInstance(Class<BakedHandlerList<T>> handlerClass) throws InstantiationException
414 { 472 {
415 try 473 try
416 { 474 {
417 - String className = this.getNextClassName();  
418 - Class<BakedHandlerList<T>> handlerClass = (Class<BakedHandlerList<T>>)this.loadClass(className);  
419 Constructor<BakedHandlerList<T>> ctor = handlerClass.getDeclaredConstructor(); 475 Constructor<BakedHandlerList<T>> ctor = handlerClass.getDeclaredConstructor();
420 ctor.setAccessible(true); 476 ctor.setAccessible(true);
421 return ctor.newInstance(); 477 return ctor.newInstance();
422 } 478 }
423 catch (Exception ex) 479 catch (Exception ex)
424 { 480 {
425 - throw new RuntimeException(ex); 481 + InstantiationException ie = new InstantiationException("Error instantiating class " + handlerClass);
  482 + ie.setStackTrace(ex.getStackTrace());
  483 + throw ie;
426 } 484 }
427 } 485 }
428 486
  487 + /* (non-Javadoc)
  488 + * @see java.net.URLClassLoader#findClass(java.lang.String)
  489 + */
429 @Override 490 @Override
430 protected Class<?> findClass(String name) throws ClassNotFoundException 491 protected Class<?> findClass(String name) throws ClassNotFoundException
431 { 492 {
432 try 493 try
433 { 494 {
  495 + // Read the basic class template
434 byte[] bytes = Launch.classLoader.getClassBytes(Obf.BakedHandlerList.name); 496 byte[] bytes = Launch.classLoader.getClassBytes(Obf.BakedHandlerList.name);
435 ClassReader classReader = new ClassReader(bytes); 497 ClassReader classReader = new ClassReader(bytes);
436 ClassNode classNode = new ClassNode(); 498 ClassNode classNode = new ClassNode();
437 classReader.accept(classNode, ClassReader.EXPAND_FRAMES); 499 classReader.accept(classNode, ClassReader.EXPAND_FRAMES);
438 500
  501 + // Apply all transformations to the class, injects our custom code
439 this.transform(name, classNode); 502 this.transform(name, classNode);
440 503
  504 + // Write the class
441 ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); 505 ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
442 classNode.accept(classWriter); 506 classNode.accept(classWriter);
443 bytes = classWriter.toByteArray(); 507 bytes = classWriter.toByteArray();
  508 +
  509 + // Delegate to ClassLoader's usual behaviour to load the class we just generated
444 return this.defineClass(name, bytes, 0, bytes.length); 510 return this.defineClass(name, bytes, 0, bytes.length);
445 } 511 }
446 catch (Throwable th) 512 catch (Throwable th)
@@ -450,30 +516,49 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -450,30 +516,49 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
450 } 516 }
451 } 517 }
452 518
453 - private void transform(String name, ClassNode classNode) 519 + /**
  520 + * Perform all class bytecode transformations
  521 + *
  522 + * @param name
  523 + * @param classNode
  524 + * @throws IOException
  525 + */
  526 + private void transform(String name, ClassNode classNode) throws IOException
454 { 527 {
455 LiteLoaderLogger.info("Baking listener list for %s with %d listeners", this.type.getSimpleName(), this.size); 528 LiteLoaderLogger.info("Baking listener list for %s with %d listeners", this.type.getSimpleName(), this.size);
456 LiteLoaderLogger.debug("Generating: %s", name); 529 LiteLoaderLogger.debug("Generating: %s", name);
457 530
458 this.populateClass(name, classNode); 531 this.populateClass(name, classNode);
459 this.transformMethods(name, classNode); 532 this.transformMethods(name, classNode);
460 - this.injectInterfaceMethods(name, classNode); 533 + this.injectInterfaceMethods(classNode, this.type.getName());
461 } 534 }
462 535
  536 + /**
  537 + * Populate the class node itself
  538 + *
  539 + * @param name
  540 + * @param classNode
  541 + */
463 private void populateClass(String name, ClassNode classNode) 542 private void populateClass(String name, ClassNode classNode)
464 { 543 {
465 classNode.access = classNode.access & ~Opcodes.ACC_ABSTRACT; 544 classNode.access = classNode.access & ~Opcodes.ACC_ABSTRACT;
466 classNode.name = name.replace('.', '/'); 545 classNode.name = name.replace('.', '/');
467 classNode.superName = Obf.BakedHandlerList.ref; 546 classNode.superName = Obf.BakedHandlerList.ref;
468 - classNode.interfaces.add(this.type.getName().replace('.', '/'));  
469 - classNode.sourceFile = "Dynamic"; 547 + classNode.interfaces.add(this.typeRef);
  548 + classNode.sourceFile = name.substring(name.lastIndexOf('.') + 1) + ".java";
470 549
471 - for (int i = 0; i < this.size; i++) 550 + for (int handlerIndex = 0; handlerIndex < this.size; handlerIndex++)
472 { 551 {
473 - classNode.fields.add(new FieldNode(Opcodes.ACC_PRIVATE, "handler$" + i, "L" + this.typeRef + ";", null, null)); 552 + classNode.fields.add(new FieldNode(Opcodes.ACC_PRIVATE, HandlerListClassLoader.HANDLER_VAR_PREFIX + handlerIndex, "L" + this.typeRef + ";", null, null));
474 } 553 }
475 } 554 }
476 555
  556 + /**
  557 + * Transform existing methods in the template class
  558 + *
  559 + * @param name
  560 + * @param classNode
  561 + */
477 private void transformMethods(String name, ClassNode classNode) 562 private void transformMethods(String name, ClassNode classNode)
478 { 563 {
479 for (Iterator<MethodNode> methodIterator = classNode.methods.iterator(); methodIterator.hasNext();) 564 for (Iterator<MethodNode> methodIterator = classNode.methods.iterator(); methodIterator.hasNext();)
@@ -494,6 +579,12 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -494,6 +579,12 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
494 } 579 }
495 } 580 }
496 581
  582 + /**
  583 + * Transform the constructor
  584 + *
  585 + * @param classNode
  586 + * @param method
  587 + */
497 private void processCtor(ClassNode classNode, MethodNode method) 588 private void processCtor(ClassNode classNode, MethodNode method)
498 { 589 {
499 for (Iterator<AbstractInsnNode> iter = method.instructions.iterator(); iter.hasNext();) 590 for (Iterator<AbstractInsnNode> iter = method.instructions.iterator(); iter.hasNext();)
@@ -510,6 +601,12 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -510,6 +601,12 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
510 } 601 }
511 } 602 }
512 603
  604 + /**
  605 + * Transform .get()
  606 + *
  607 + * @param classNode
  608 + * @param method
  609 + */
513 private void processGet(ClassNode classNode, MethodNode method) 610 private void processGet(ClassNode classNode, MethodNode method)
514 { 611 {
515 method.access = method.access & ~Opcodes.ACC_ABSTRACT; 612 method.access = method.access & ~Opcodes.ACC_ABSTRACT;
@@ -519,40 +616,41 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -519,40 +616,41 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
519 method.instructions.add(new InsnNode(Opcodes.ARETURN)); 616 method.instructions.add(new InsnNode(Opcodes.ARETURN));
520 } 617 }
521 618
  619 + /**
  620 + * Transform .processPopulate()
  621 + *
  622 + * @param classNode
  623 + * @param method
  624 + */
522 private void processPopulate(ClassNode classNode, MethodNode method) 625 private void processPopulate(ClassNode classNode, MethodNode method)
523 { 626 {
524 method.access = method.access & ~Opcodes.ACC_ABSTRACT; 627 method.access = method.access & ~Opcodes.ACC_ABSTRACT;
525 method.instructions.clear(); 628 method.instructions.clear();
526 629
527 - for (int i = 0; i < this.size; i++) 630 + for (int handlerIndex = 0; handlerIndex < this.size; handlerIndex++)
528 { 631 {
529 method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); 632 method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
530 method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); 633 method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
531 - method.instructions.add(new IntInsnNode(Opcodes.BIPUSH, i)); 634 + method.instructions.add(new IntInsnNode(Opcodes.BIPUSH, handlerIndex));
532 method.instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true)); 635 method.instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true));
533 method.instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, this.typeRef)); 636 method.instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, this.typeRef));
534 - method.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, "handler$" + i, "L" + this.typeRef + ";")); 637 + method.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, HandlerListClassLoader.HANDLER_VAR_PREFIX + handlerIndex, "L" + this.typeRef + ";"));
535 } 638 }
536 639
537 - method.instructions.add(new InsnNode(Opcodes.RETURN));  
538 - }  
539 -  
540 - private void injectInterfaceMethods(String name, ClassNode classNode)  
541 - {  
542 - try  
543 - {  
544 - String interfaceName = this.type.getName();  
545 - this.injectInterfaceMethods(classNode, interfaceName);  
546 - }  
547 - catch (IOException ex)  
548 - {  
549 - ex.printStackTrace();  
550 - } 640 + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
  641 + method.instructions.add(new InsnNode(Opcodes.ARETURN));
551 } 642 }
552 643
  644 + /**
  645 + * Recurse down the interface inheritance hierarchy and inject methods to handle each interface
  646 + *
  647 + * @param classNode
  648 + * @param interfaceName
  649 + * @throws IOException
  650 + */
553 private void injectInterfaceMethods(ClassNode classNode, String interfaceName) throws IOException 651 private void injectInterfaceMethods(ClassNode classNode, String interfaceName) throws IOException
554 { 652 {
555 - ClassReader interfaceReader = new ClassReader(this.getInterfaceBytes(interfaceName)); 653 + ClassReader interfaceReader = new ClassReader(HandlerListClassLoader.getInterfaceBytes(interfaceName));
556 ClassNode interfaceNode = new ClassNode(); 654 ClassNode interfaceNode = new ClassNode();
557 interfaceReader.accept(interfaceNode, 0); 655 interfaceReader.accept(interfaceNode, 0);
558 656
@@ -568,6 +666,12 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -568,6 +666,12 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
568 } 666 }
569 } 667 }
570 668
  669 + /**
  670 + * Inject the supplied interface method into the target class an populate it with method calls to the list members
  671 + *
  672 + * @param classNode
  673 + * @param method
  674 + */
571 private void populateInterfaceMethod(ClassNode classNode, MethodNode method) 675 private void populateInterfaceMethod(ClassNode classNode, MethodNode method)
572 { 676 {
573 Type returnType = Type.getReturnType(method.desc); 677 Type returnType = Type.getReturnType(method.desc);
@@ -577,13 +681,13 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -577,13 +681,13 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
577 Type[] args = Type.getArgumentTypes(method.desc); 681 Type[] args = Type.getArgumentTypes(method.desc);
578 method.access = Opcodes.ACC_PUBLIC; 682 method.access = Opcodes.ACC_PUBLIC;
579 683
580 - for (int i = 0; i < this.size; i++) 684 + for (int handlerIndex = 0; handlerIndex < this.size; handlerIndex++)
581 { 685 {
582 LabelNode lineNumberLabel = new LabelNode(new Label()); 686 LabelNode lineNumberLabel = new LabelNode(new Label());
583 method.instructions.add(lineNumberLabel); 687 method.instructions.add(lineNumberLabel);
584 - method.instructions.add(new LineNumberNode(++this.lineNumber, lineNumberLabel)); 688 + method.instructions.add(new LineNumberNode(100 + handlerIndex, lineNumberLabel));
585 method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); 689 method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
586 - method.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, "handler$" + i, "L" + this.typeRef + ";")); 690 + method.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, HandlerListClassLoader.HANDLER_VAR_PREFIX + handlerIndex, "L" + this.typeRef + ";"));
587 this.invokeInterfaceMethod(method, args); 691 this.invokeInterfaceMethod(method, args);
588 } 692 }
589 693
@@ -591,6 +695,12 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -591,6 +695,12 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
591 } 695 }
592 } 696 }
593 697
  698 + /**
  699 + * Inject instructions into the supplied method to invoke the same method on the supplied interface
  700 + *
  701 + * @param method
  702 + * @param args
  703 + */
594 private void invokeInterfaceMethod(MethodNode method, Type[] args) 704 private void invokeInterfaceMethod(MethodNode method, Type[] args)
595 { 705 {
596 int argNumber = 1; 706 int argNumber = 1;
@@ -603,27 +713,28 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt; @@ -603,27 +713,28 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
603 method.instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, this.typeRef, method.name, method.desc, true)); 713 method.instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, this.typeRef, method.name, method.desc, true));
604 } 714 }
605 715
606 - private String getNextClassName() 716 + /**
  717 + * @param baseName
  718 + * @param typeName
  719 + * @return
  720 + */
  721 + private static String getNextClassName(String baseName, String typeName)
607 { 722 {
608 - return String.format("%s$%s$%s", Obf.HandlerList.name, this.type.getSimpleName(), HandlerListClassLoader.handlerIndex++); 723 + return String.format("%s$%s%d", baseName, typeName, HandlerListClassLoader.handlerIndex++);
609 } 724 }
610 725
611 - private byte[] getInterfaceBytes(String name) throws IOException 726 + /**
  727 + * @param name
  728 + * @return
  729 + * @throws IOException
  730 + */
  731 + private static byte[] getInterfaceBytes(String name) throws IOException
612 { 732 {
613 byte[] bytes = Launch.classLoader.getClassBytes(name); 733 byte[] bytes = Launch.classLoader.getClassBytes(name);
614 734
615 - final List<IClassTransformer> transformers = Launch.classLoader.getTransformers();  
616 -  
617 - for (final IClassTransformer transformer : transformers) 735 + for (final IClassTransformer transformer : Launch.classLoader.getTransformers())
618 { 736 {
619 - try  
620 - {  
621 - bytes = transformer.transform(name, name, bytes);  
622 - }  
623 - catch (Exception ex)  
624 - {  
625 - ex.printStackTrace();  
626 - } 737 + bytes = transformer.transform(name, name, bytes);
627 } 738 }
628 739
629 return bytes; 740 return bytes;