> On Apr 10, 2019, at 5:22 PM, Brian Goetz <brian.go...@oracle.com> wrote:
> 
> OK, so in the old world, D has m(Date).  
> 
> 
> 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 
> 
>> 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)
(LDT is LocalDateTime)
> 
> 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 {
>         @Override 
>         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).  
From a source perspective, E has the same names of members, although it has 
overridden the contents of m(Date).

> 
> Here's how I would imagine this turns into in the VM: 
not important, but this was m(LDT) not m(LTD)
> 
>     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 }
I would expect that the existing m(Date) with the real method would stay 
unchanged - including
the name and the access controls - since there may be clients of subclass E 
still trying to invoke it.

>         void m(LTD ltd) { m$synthetic(adapt(ltd)); }  // generated reverser
I think we are in agreement that there is a reverser:
          void m(LDT) generated receiver:
              1) adapt LDT -> Date
              2) invoke local m(Date)
              3) if return had changed, adapt back
// adaptations for reverser are the inverse as for the forwarder
>     }
> 
> 
> resolves
> selects
> invokevirtual D::m(LTD)
> D::m(LTD)
> E::m(LTD)
> invokevirtual D::m(Date)
> D::m(Date)
> D::m(Date), forwards to invvir D::m(LTD)
> In turn, selects E::m(LTD)
> invokevirtual E::m(LTD)
> E::m(LTD)
> E::m(LTD)
> invokevirtual E::m(Date)
> D::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.  
A VM perspective:

invocation
dynamic receiver
resolution
NOT invoked
selection:
actual execution
invokevirtual D::m(LDT)
D
D.m(LDT)
D.m(LDT)
invokevirtual D::m(LDT)
E
D.m(LDT)
E.m(LDT) 
reverser: adapt LDT->Date
               invoke local E.m(Date)
               if return had changed, adapt return back
invokevirtual D::m(Date)
D
D.m(Date)
D.m(Date)
forwarder: adapt Date->LDT
                 invoke local m(LDT)
                 if return had changed, adapt
invokevirtual D.m(Date)
E
D.m(Date)
E.m(Date)
invokevirtual E.m(LDT)
E
E.m(LDT)
reverser)
E.m(LDT): 
reverser: adapt LDT->Date
               invoke local E.m(Date)
               if return had changed, adapt return back
invokevirtual E.m(Date)
E
E.m(Date)
E.m(Date) // original - unchanged behavior

Point 1: The resolved method is NOT invoked, it is only the selected method 
that is invoked.
We do NOT follow forwarding for the resolved method.
If the resolved method happens to also be the selected method, we will now 
execute it and will follow the forwarding.

Note, the same applies to fields - we will not get/set the resolved field. We 
will get/set the
selected field, and follow the forwarding at that point.

Point 2: Hotspot’s vtable implementation is set up so that for
class E - a vtable (or itable) is a selection cache. It allows for fast virtual 
dispatch.
  For Hotspot, for class E, the vtable starts with the inherited vtable from 
superclass D.
  Any entries in the table are replaced when a method overrides an inherited 
method.
  Additional methods are appended.

So resolution gives you the offset in the vtable. Selection tells you which 
vtable owner
to index based on that offset. 

We KEEP the existing methods in the subclass so that they are executed exactly 
the same
with no change in behavior (no exceptions due to narrowing etc.)

Agree that so far the only reverser need I have identified is when a class 
overrides 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?
I think so on this sentence - we already have E::m(Date) overriding forwarder 
D::m(Date) so we need
a reverser E::m(LDT) to override the forwardee and reverse to call E::m(Date) 
with the reverse adaptations.

thanks,
Karen
> 

Reply via email to