I need to do many more additional examples offline.
I appreciate your trying to make overriding of forwarders simpler for the jvm.
I would like to continue to explore the option having the jvm do the 
calculation of overriding
both direct and indirect forwarders until we’ve worked more examples.

If we can find a way to do it, it helps with backward compatibility
   - old clients with old receivers don’t go through adaptors - so they miss 
adaptations
that could either throw an exception or potentially lose data through narrowing.
   - same issue for reflection - Class.getDeclaredMethods() which just returns 
local methods
     - would be nice if we could not lose existing method names here

I am also exploring invoke local with explicit local name of method - so we can 
try
to reduce loops - I believe there will be steps at which we will need to 
identify loops and throw
exceptions or not create a reverser.

That said, it is getting more complex, so glad you are exploring alternatives.

Link below spells out a bit more the rule I am exploring for creating reversers,
with both the example below and another example (Example II)  which has three 
migration steps,
F <: E <: D
all start with m(Date, Time)
step 1: D m(Date, Time) -> D.m(LDT, Time)
step 2: E.m(Date, Time) -> E.m( Date, LDT)
step 3: D.m(LDT, Time) -> D.m(LDT, LDT)

http://cr.openjdk.java.net/~acorn/Forwarders.pdf

thanks,
Karen

> On Apr 12, 2019, at 11:44 AM, Brian Goetz <brian.go...@oracle.com> wrote:
> 
> 
>  
>> 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
>> 
> 
> Let me try from the other direction, using the JVMS terminology rather than 
> appealing to as-if.  Where I think we're saying slightly different things is 
> in the interpretation of the lines I colored in blue above (hope the 
> formatting came through.)  You are talking about the E.m(Date) that appears 
> in E.class (good so far).  But I'm talking about the _members_ of E.  And the 
> E.m(Date) that appears in E.class should _not_ be considered a (new-to-E) 
> member of E.      Instead, that E.m(Date) gives rise to a synthetic member 
> E.m(LDT).   I have colored two cases in red because I think this is where our 
> assumptions really parted ways; will come back to this at the bottom.  
> 
> Here's why I'm harping on this distinction; we mark methods as "forwarders" 
> and do something special when we see something override a forwarder.  Taking 
> the same hierarchy:
> 
>     // before
>     class D {
>         void m(Date) { }
>     }
> 
>     class E extends D { 
>         void m(Date) { }
>     }
> 
>     // middle -- D migrates, but E not yet
>     class D {
>         void m(LDT) { }
>         @Forwarding( m(LDT) } void m(Date);
>     }
> 
>     class E extends D { 
>         void m(Date) { }
>     }
> 
>     // after -- E finally gets the memo
>     class D {
>         void m(LDT) { }
>         @Forwarding( m(LDT) } void m(Date);
>     }
> 
>     class E extends D { 
>         void m(LDT) { }
>     }
> 
> Now, let's draw inheritance diagrams (these are not vtables, they are member 
> tables).  I'll use your notation, where I think D.m(X) means "the Code 
> attribute declared in D for m(X)".  
> 
> Before
> m(Date)
> D
> D.m(Date)
> E
> E.m(Date)
> 
> This part is easy; D has m(Date), and E overrides it.  
> 
> Middle
> m(LDT)
> m(Date)
> D
> D.m(LDT)
> forwarder -> m(LDT)
> E
> reverser adapted from E.m(Date)
> inherits forwarder
> 
> Now, both D and E have both m(Date) and m(LDT).  D has a real method for 
> m(LDT), and a forwarder for m(Date).  E has an m(Date), which we see 
> overrides a forwarder.  So we adapt it to be an m(LDT), but we consider E to 
> have inherited the forwarder from D.  I'll come back to this in a minute.
> 
> After m(LDT)
> m(Date)
> D
> D.m(LDT)
> forwarder -> m(LDT)
> E
> E.m(LDT)
> inherits forwarder
> 
> In this nirvana, there is a forwarder still, but it doesn't affect E, because 
> E has already gotten the memo.  It sits around purely in the case that 
> someone calls m(Date).  
> 
> OK, so why am I saying that membership has to be tilted this way?  Let's go 
> back to the middle case, and add
> 
>     class F extends E {
>         void m(Date) { } // still didn't get the memo
>     }
> 
> 
> Middle
> m(LDT)
> m(Date)
> D
> D.m(LDT)
> forwarder -> m(LDT)
> E
> reverser adapted from E.m(Date)
> inherits forwarder
> F
> reverser adapted from F.m(Date)
> inherits forwarder
> 
> When we go to compute members, I want to see that _F.m(Date) overrides a 
> forwarder too_.  If we merely put E.m(Date) in the (E, m(Date)) box, then it 
> looks like F is overriding an ordinary member, and no reverser is generated.  
> (Or, we have to keep walking up the chain to see if E.m(Date) in turn 
> overrides a forwarder -- yuck, plus, that makes forwarder-overrides-forwarder 
> even messier.  
> 
> Now, back to your table.  The above interpretation of what is going on comes 
> to the same answer for all of the rows of your table, except these:
> 
>> 
>> invocation
>> dynamic receiver
>> resolution
>> NOT invoked
>> selection:
>> actual execution
>> invokevirtual D.m(Date)
>> E
>> D.m(Date)
>> E.m(Date)
>> invokevirtual E.m(Date)
>> E
>> E.m(Date)
>> E.m(Date) // original - unchanged behavior
>> 
> 
> You are thinking "E has a perfectly good m(Date), let's just select that".  
> Makes sense, but the cost of that is that it complicates calculation of 
> membership and overriding.  I think I am content to let invocations of 
> m(Date) on receivers of type E go through both rounds of adaptation: forward 
> the call (with adaptation) to m(LDT), which, in the case of E, does the 
> reverse adaptations and ends up at the original Code attribute of E.m(Date).  
> This sounds ugly (and we'd need to justify some potential failures) but leads 
> us to a simpler interpretation of migration.  
> 
> In your model, we basically have to split the box in two: 
> 
> 
> After m(LDT)
> m(Date)
> D
> D.m(LDT)
> forwarder -> m(LDT)
> E
> E.m(LDT)
> E.m(Date), but also is viewed as a forwarder by subclasses
> 
> I think its a good goal, but I was trying to eliminate that complexity by 
> accepting the round-trip adaptation -- which goes away when E gets the memo.  
> 
> 

Reply via email to