[SPIGOT-893] JDK behavior change breaks Listeners that override a generic method Created: 18/May/15  Updated: 18/May/15  Resolved: 18/May/15

Status: Resolved
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major
Reporter: Andrew Shulman Assignee: Unassigned
Resolution: Fixed Votes: 0
Labels: bukkit
Environment:

Java 7u80 (or above)
Java 8 (any version)



 Description   

Consider the following example:

Base.java
public abstract class Base<T extends Event> implements Listener {
    abstract void method(T event);
}
Derived.java
public class Derived extends Base<BlockPlaceEvent> {
    @EventHandler
    void onEvent(BlockPlaceEvent event) {}
}

If you examine Derived with javap, you discover that it actually has two methods.

Compiled from "Derived.java"
public class Derived extends Base<org.bukkit.event.block.BlockPlaceEvent> {
  public Derived();
  void onEvent(org.bukkit.event.block.BlockPlaceEvent);
  void onEvent(org.bukkit.event.Event);
}

In Java 7u79 and earlier, the @EventHandler annotation only exists on the void onEvent(org.bukkit.event.block.BlockPlaceEvent) method. However, a change was made to the JDK (see https://bugs.openjdk.java.net/browse/JDK-6695379) to copy the annotation to the overridden method, that being void onEvent(org.bukkit.event.Event).

Bukkit reflectively looks for the getHandlerList method for any class in the parameter list of a function annotated with @EventHandler. In earlier versions of Java 7, it would only notice the void onEvent(BlockPlaceEvent) version and successfully register the class. In code compiled with the above mentioned versions, @EventHandler is copied to the void onEvent(Event) method, and then an exception is thrown when Bukkit can't find the getHandlerList method.



 Comments   
Comment by Jonas Konrad [ 18/May/15 ]

Yes, you're right, I forgot that the bukkit event system doesn't invoke super handlers with the event.

However, the test still works - it fails on the old version with the exception mentioned in the report, and passes with the fix applied. I suppose you can see it as a test that only bridge and synthetic methods get filtered

Comment by Andrew Shulman [ 18/May/15 ]

Thanks for the quick fix. It's worth noting, though, that your test won't do exactly what you think it will. If you decompile the bridge method

javap -v Derived
  void method(org.bukkit.event.Event);
    descriptor: (Lorg/bukkit/event/Event;)V
    flags: ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #2                  // class org/bukkit/event/block/BlockPlaceEvent
         5: invokevirtual #3                  // Method method:(Lorg/bukkit/event/block/BlockPlaceEvent;)V
         8: return
      LineNumberTable:
        line 2: 0
    RuntimeVisibleAnnotations:
      0: #13()

All it does is call the overriding method, so even without your fix callCount will never be anything other than 1.

Comment by Jonas Konrad [ 18/May/15 ]

Fixed in bukkit PR #65.

Comment by SpigotMC [ 18/May/15 ]

Your build is not the latest and therefore may be the reason you are having this issue. Spigot is 1 version(s) behind. CraftBukkit is 1 version(s) behind. This message was automatically generated and is not guaranteed to be a solution to your issue.

Generated at Sat Mar 29 11:39:55 UTC 2025 using Jira 10.3.3#10030003-sha1:d220e3fefc8dfc6d47f522d3b9a20c1455e12b7b.