Updated VM-bridges document

Brian Goetz brian.goetz at oracle.com
Wed Apr 10 21:22:46 UTC 2019

OK, so in the old world, D has m(Date).

> Migration step 1: author: Date -> LocalDateTime
> old class D { m(Date); }
> Migration step 2: change method declarer: new class D {m(LDT);}
>            and javac creates a forwarder m(Date); -> Date->LDT/m(LDT);

Now, D has m(LDT), with a forwarder from m(Date) -> m(LDT), with some 
sort of metadata stapled somewhere to effect the Date <--> LDT conversions.

> class E extends D { m(Date); } which now overrides the forwarder.
> We do not change class E. We do not recompile it (I don’t know what recompilation would do here?)

On recompilation, we could do one of three things:

1.  Error: you're overriding a bridge, fix your program!
2.  Warning: you're overriding a bridge, I'll fix it for you (compiler 
adapts m(Date) to m(LDT).
3.  Warning: you're overriding a bridge, I'll believe you, and the VM 
will fix it for you (bringing us back to where you started: "we do not 
change E."

Which we choose at compilation time doesn't really affect what the VM 
has to do (you still have to deal with the unrecompiled E), so we can 
make this decision later.

> old class ClientD invokevirtual D.m(Date) receiver:E
> Migration step 3: new class ClientD invokevirtual D.m(LDT) receiver:E
>     resolution: finds D.m(LDT)
>     selection: starts with E, there is no E.m(LDT) so call D.m(LDT)

OK, so at this point, the classfiles that have been loaded look like:

     class D {
         void m(LDT) { real method }
         @Forwarding(m(LDT)) abstract void m(Date);

     class E extends D {
         m(Date) { impl }

So D has members m(LTD) and m(Date), the latter is a forwarder. 
Therefore E has the same members (instance methods are inherited).

Here's how I would imagine this turns into in the VM:

     class D {
         void m(LTD) { real method }
         void m(Date d) { m(adapt(d)); }  // generated forwarder

     class E extends D {
         private void m$synthetic(Date d) { real method, body as present 
in classfile }
         void m(LTD ltd) { m$synthetic(adapt(ltd)); }  // generated reverser

invokevirtual D::m(LTD)
invokevirtual D::m(Date)
	D::m(Date), forwards to invvir D::m(LTD)
In turn, selects E::m(LTD)
invokevirtual E::m(LTD)
invokevirtual E::m(Date)
	D::m(Date), forwards to invvir D::m(LTD)
In turn, selects E::m(LTD)

In other words, we arrange that once the vtable is laid out, it is as if 
no one ever overrides the forwarders -- they only override the real 
method.  Hence the reverser is needed only where a class (like E) 
actually overrides a descriptor that corresponds to a forwarder.

> It is my belief that the expected behavior is that we want to invoke E.m(Date) with asType signature matching.
> To do that, I propose that if the vm detects overriding of a forwarder, that we need to generate a reverser:
>     E.m(Date) overrides D.m(Date)// forwarder: Date->LDT/invoke D.m(LDT)/return conversion
> The reverser that we want would be
>     E.m(LDT) overrides D.m(LDT) // reverser: LDT->Date/invoke E.m(Date)/return reverse conversion

I think we want: a reverser for E::m(LTD), but not for E::m(Date). Are 
we saying the same thing?

More information about the valhalla-spec-observers mailing list