Redundant bridge methods in nested classes
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Jul 29 23:31:46 UTC 2015
Nicely spotted - maybe in this case it's not an issue, but I'd like to
file a bug and keep track of this, to see if there's something nastier
that could happen as an outcome. The reality is that the bridge code is
pretty ancient (dates back to 2001-2002) - and the spec wasn't settled
back then; the biggest problem of the current code is that the bridge
analysis depends on the members that were physically present on the
superclasses; meaning that your analysis is chronically dependent on
compilation order issues.
In JDK 8 we had a similar issue when we implemented the logic for
generating the set of bridges required by a lambda/method reference; as
I was very aware of the compilation order issues, we designed an
algorithm that was capable of 'simulating' the bridge presence in a
given class, simply by looking at overriding properties between methods.
This solution is, I believe, far superior, and should be preferred to
the current approach which, in fact, adds a lot of convolutedness to the
entire compiler pipeline, as it forces superclasses to be translated
before subclasses (hint hint - what about superinterfaces???).
Maurizio
On 29/07/15 18:30, Liam Miller-Cushon wrote:
> I've been trying to get a better understanding of bridge methods, and
> noticed that javac seems to emit different bridge methods depending on
> the order of sources passed to the compilation.
>
> As far as I can tell this occurs because TransTypes.translateClass
> calls super.visitClassDef before addBridges. The super implementation
> of visitClassDef processes all declarations (including nested
> classes), so nested classes get translated before their enclosing
> classes. If a nested class extends its enclosing class, the enclosing
> class' bridges won't have been generated, so the nested class ends up
> with redundant bridge methods.
>
> I don't think it's a correctness issue, but I found it surprising. Do
> you think this is worth fixing?
>
> Here's the repro:
>
> A and B are symmetric: A's nested class extends B, B's nested class
> extends A. Depending on the order of compilation a bridge method is
> generated in A$Nested or B$Nested, but not both.
>
> $ cat I.java
> interface I<T> {
> void f(T t);
> }
> $ cat A.java
> class A implements I<Number> {
> public void f(Number x) {}
> static class Nested extends B {
> }
> }
> $ cat B.java
> class B implements I<Number> {
> public void f(Number x) {}
> static class Nested extends A {
> }
> }
>
> # pass A.java to javac first, A$Nested has a bridge method
>
> $ rm -f *.class
> $ javac I.java A.java B.java
> $ javap -private 'A$Nested'
> Compiled from "A.java"
> class A$Nested extends B {
> A$Nested();
> public void f(java.lang.Object);
> }
> $ javap -private 'B$Nested'
> Compiled from "B.java"
> class B$Nested extends A {
> B$Nested();
> }
>
> # pass B.java to javac first, B$Nested has a bridge method
>
> $ rm -f *.class
> $ javac I.java B.java A.java
> $ javap -private 'A$Nested'
> Compiled from "A.java"
> class A$Nested extends B {
> A$Nested();
> }
> $ javap -private 'B$Nested'
> Compiled from "B.java"
> class B$Nested extends A {
> B$Nested();
> public void f(java.lang.Object);
> }
More information about the compiler-dev
mailing list