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 82 */
83 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 369 {
371 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 395 */
382 396 static class HandlerListClassLoader<T> extends URLClassLoader
383 397 {
  398 + private static final String HANDLER_VAR_PREFIX = "handler$";
  399 +
384 400 /**
385 401 * Unique index number, just to ensure no name clashes
386 402 */
387 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 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 413 private final String typeRef;
394 414  
  415 + /**
  416 + * Size of the handler list
  417 + */
395 418 private int size;
396   -
  419 +
397 420 /**
398 421 * @param type
399 422 * @param size
400 423 */
401   - HandlerListClassLoader(Class<T> type, int size)
  424 + HandlerListClassLoader(Class<T> type)
402 425 {
403 426 super(new URL[0], Launch.classLoader);
404 427 this.type = type;
405 428 this.typeRef = type.getName().replace('.', '/');
406   - this.size = size;
407 429 }
408 430  
409 431 /**
410 432 * Create and return a new baked handler list
411 433 */
412 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 473 try
416 474 {
417   - String className = this.getNextClassName();
418   - Class<BakedHandlerList<T>> handlerClass = (Class<BakedHandlerList<T>>)this.loadClass(className);
419 475 Constructor<BakedHandlerList<T>> ctor = handlerClass.getDeclaredConstructor();
420 476 ctor.setAccessible(true);
421 477 return ctor.newInstance();
422 478 }
423 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 490 @Override
430 491 protected Class<?> findClass(String name) throws ClassNotFoundException
431 492 {
432 493 try
433 494 {
  495 + // Read the basic class template
434 496 byte[] bytes = Launch.classLoader.getClassBytes(Obf.BakedHandlerList.name);
435 497 ClassReader classReader = new ClassReader(bytes);
436 498 ClassNode classNode = new ClassNode();
437 499 classReader.accept(classNode, ClassReader.EXPAND_FRAMES);
438 500  
  501 + // Apply all transformations to the class, injects our custom code
439 502 this.transform(name, classNode);
440 503  
  504 + // Write the class
441 505 ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
442 506 classNode.accept(classWriter);
443 507 bytes = classWriter.toByteArray();
  508 +
  509 + // Delegate to ClassLoader's usual behaviour to load the class we just generated
444 510 return this.defineClass(name, bytes, 0, bytes.length);
445 511 }
446 512 catch (Throwable th)
... ... @@ -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 528 LiteLoaderLogger.info("Baking listener list for %s with %d listeners", this.type.getSimpleName(), this.size);
456 529 LiteLoaderLogger.debug("Generating: %s", name);
457 530  
458 531 this.populateClass(name, classNode);
459 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 542 private void populateClass(String name, ClassNode classNode)
464 543 {
465 544 classNode.access = classNode.access & ~Opcodes.ACC_ABSTRACT;
466 545 classNode.name = name.replace('.', '/');
467 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 562 private void transformMethods(String name, ClassNode classNode)
478 563 {
479 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 579 }
495 580 }
496 581  
  582 + /**
  583 + * Transform the constructor
  584 + *
  585 + * @param classNode
  586 + * @param method
  587 + */
497 588 private void processCtor(ClassNode classNode, MethodNode method)
498 589 {
499 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 601 }
511 602 }
512 603  
  604 + /**
  605 + * Transform .get()
  606 + *
  607 + * @param classNode
  608 + * @param method
  609 + */
513 610 private void processGet(ClassNode classNode, MethodNode method)
514 611 {
515 612 method.access = method.access & ~Opcodes.ACC_ABSTRACT;
... ... @@ -519,40 +616,41 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
519 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 625 private void processPopulate(ClassNode classNode, MethodNode method)
523 626 {
524 627 method.access = method.access & ~Opcodes.ACC_ABSTRACT;
525 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 632 method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
530 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 635 method.instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true));
533 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 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 654 ClassNode interfaceNode = new ClassNode();
557 655 interfaceReader.accept(interfaceNode, 0);
558 656  
... ... @@ -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 675 private void populateInterfaceMethod(ClassNode classNode, MethodNode method)
572 676 {
573 677 Type returnType = Type.getReturnType(method.desc);
... ... @@ -577,13 +681,13 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
577 681 Type[] args = Type.getArgumentTypes(method.desc);
578 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 686 LabelNode lineNumberLabel = new LabelNode(new Label());
583 687 method.instructions.add(lineNumberLabel);
584   - method.instructions.add(new LineNumberNode(++this.lineNumber, lineNumberLabel));
  688 + method.instructions.add(new LineNumberNode(100 + handlerIndex, lineNumberLabel));
585 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 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 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 704 private void invokeInterfaceMethod(MethodNode method, Type[] args)
595 705 {
596 706 int argNumber = 1;
... ... @@ -603,27 +713,28 @@ public class HandlerList&lt;T&gt; extends LinkedList&lt;T&gt;
603 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 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 740 return bytes;
... ...