Redundant bridge methods in nested classes

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Jul 31 10:18:52 UTC 2015



On 31/07/15 00:55, Liam Miller-Cushon wrote:
> Interesting, thanks Maurizio.
>
> On Wed, Jul 29, 2015 at 4:31 PM, Maurizio Cimadamore 
> <maurizio.cimadamore at oracle.com 
> <mailto:maurizio.cimadamore at oracle.com>> wrote:
>
>     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.
>
>
> As long as the hierarchy is well formed, is it possible to enforce 
> that superclasses and interfaces (possibly including enclosing 
> classes) are always translated before the current class? I suppose 
> there are some cycles that would be tricky to get right: `class C<T 
> extends C.N> { static class N extends C {} }`. If that was possible, 
> though, the implementation could assert that classes are never 
> translated recursively, and catch issues like the one I noticed.
There are many kinds of dependencies - the one in your example involves 
tvar bounds, and I believe these are harmless when it comes to bridging; 
the bridge generation process depends on the members of 
superclasses/superinterfaces, not on the members of their parameter 
bounds. That said, I believe it's hard to linearize a hierarchy; in your 
original example we have that ('->' means 'should be translated after'):

A -> I
A.Nested -> B
B -> I
B.Nested -> A

If you treat this as a graph, and you do a topological sort of the 
nodes, you end up with the following possible translation order:

I, B, A.Nested, A, B.Nested

As you noticed, this order doesn't sit well with a tree visitor, as 
there's no way i.e. to visit B without also doing B.Nested - which will 
mess up the order completely (as is the case now). What this means is 
that TransTypes should probably NOT translate class recursively. I think 
that such an approach would have more chances to succeed than the 
existing one.
>
>     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???).
>
>
> That does sound way nicer. Did you consider switching the regular 
> bridge implementation over to use the new algorithm?
It's on our radar - but it's also a very very risky change, as you can 
appreciate. Realistically, we've been generating code this way for so 
long, that I wouldn't be surprised if there were clients out there 
relying on current bridge generation bugs - which makes everything a lot 
harder (reflection depends on that too - as some bridges are generated 
just for that use case :-( ). It's a big knot.
>
> I'm trying to grok functionalInterfaceBridges. Is the presence of 
> bridges only simulated for classes that will be translated in the 
> current compilation? The implementation appears to deal with classes 
> completed from class files that are missing bridges (maybe because 
> they were compiled by another compiler, or javac <= 7?).
The idea behind this logic is to compute the set of bridges associated 
with a given functional type A. As a functional interface might depend 
on superinterfaces that could be coming from both sourcefile and 
classfile, the logic must be able to compute the union of the bridges 
available in such dependecies; for classfiles we can expect bridges to 
be already there (the check is done with binaryImplementation [*]) - for 
sourcefile we just can't rely on membership (because of the problem you 
mentioned), so we simulate bridge presence by exploiting overriding 
properties.

[*] - for this very reason, bridge methods are among the (very) few 
synthetic symbols that are preserved at class reading time.
>
> Liam

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20150731/3f77f8e0/attachment.html>


More information about the compiler-dev mailing list