Skip to content

Fix override annotation adder bugs - #66

Open
jpenilla wants to merge 1 commit into
mainfrom
fix/override-annotation-adder
Open

jpenilla wants to merge 1 commit into
mainfrom
fix/override-annotation-adder

Conversation

@jpenilla

@jpenilla jpenilla commented Nov 2, 2026

Copy link
Copy Markdown
Member

Fixes three bugs in OverrideAnnotationAdder, with regression tests in FixJarPageTest.

Fixes

Early return skipped the rest of the class. If a method's synthetic sources were all in other classes, the adder exited the whole class. This happens when a subclass inherits the method to implement a generic interface, which makes javac generate a bridge in the subclass:

interface J { void m(String s); }
class Base implements J { public void m(String s) {} } // overrides J#m
class Sub extends Base implements A<String> {}         // bridge in Sub targets Base#m

continue wouldn't be enough, because Base#m still has its own super method. The adder now checks superMethod() first, then any bridges in the same class.

@Override on interface clone() and finalize(). Hypo treats Object as the superclass of interfaces, so these methods got a super method. Interfaces don't inherit Object's non-public methods, and javac rejects the annotation (JLS 9.2, 9.6.4.4). It's still added when a superinterface declares the method.

@Override on synthetic methods. Bridges were annotated even though they aren't declared in source. Synthetic methods are now skipped.

Output impact

Server jar @Override before After Removed from synthetic methods Non-synthetic methods changed
26.1.2 20,335 18,850 1,485 0
26.4-snapshot-6 22,110 20,592 1,518 0

The return and interface fixes don't change the output of these two jars, as neither pattern occurs in them.

Notes

  • Overrides with a different descriptor: superMethod() only matches exact descriptors, but no signature matching was added. javac 8, 9, 11, 16, 17, 21, 25 and 27 all emit bridges for abstract and default interface redeclarations, abstract class redeclarations, covariant returns and bounded generics, so the bridge lookup already finds them. The tests cover these cases.
  • Not changed: BridgeMethodHydrator treats any synthetic forwarding method as a bridge, even without ACC_BRIDGE. This affects 950 methods on the obfuscated 1.21.11-pre1 server jar and none on the two jars above, so it's left alone here.

Testing

  • ./gradlew check passes.
  • The annotation counts come from running the page on the server jars with a scratch harness, comparing against the old logic.
  • Not tested: client jars, a full codebook run, or decompiled output.

- Stop skipping the rest of a class when a method's only bridges are in
  a subclass. The method itself is now still checked for a super method.
- Don't add @OverRide to clone() or finalize() in interfaces unless a
  superinterface declares them. Interfaces don't inherit Object's
  non-public methods, so javac rejects the annotation (JLS 9.6.4.4).
- Don't add @OverRide to synthetic methods, as they aren't declared in
  source.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant