[LW100] Specialized generics -- migration

Brian Goetz brian.goetz at oracle.com
Wed Oct 17 20:29:11 UTC 2018


And, number 3… some of the stories we cooked up for migration. These are 
just stories at this point, aimed at mitigating method and field access 
against specialized instances from legacy (erased) code.


    Bridge attributes

Rather than generating bridge methods as bytecode, which is both brittle 
and verbose, instead we could express bridges as attributes on the 
bridgee. For example, using the Model 3 notation, we’d translate:

|T identity(T t) { return t; } |

as

|[Bridge[(LObject)LObject)]] TypeVar[T//LObject] 
identity(TypeVar[T//LObject]) { aload_1 areturn } |

When we specialize for T=QPoint, we’d get:

|[Bridge[(LObject)LObject)]] QPoint identity(QPoint) { aload_1 areturn } |

This means that our class has a method |identity(QPoint)QPoint|, but it 
also “responds to” invocations of |identity(Object)Object|.

Linkage sketch: when we go to resolve the erased method (with signature 
E), we won’t find it, so we’ll search again looking for methods of that 
name that have a bridge attribute that matches what we’re looking for. 
If we find it (call it M), what we effectively want to call is 
|MethodHandle[M].asType(E)|. The adaptations from |QPoint| to/from 
|LObject| are known to |asType()|, so this is easily computable. We 
install this as the linkage target. (Yes, I know linkage doesn’t work 
this way now.)

So, what we’ve done is turned eager bridges into lazy ones, and reified 
the bridge descriptors with the bridgee, and we compute bridges at the 
invocation site, when they are invoked. This reduces the classfile 
footprint for the declaration, and puts the linkage cost on legacy 
invokers.


        Bonus — loop-free bridges

It also potentially does something else, which we’ve wanted for a long 
time — loop-resistent bridges. The brittleness of existing bridges can 
lead to “bridge loops” under separate compilation, because we are 
prematurely selecting an invocation mode and burning it into the bridge. 
By pushing bridge computation to the invocation site, we can use the 
invocation mode present at the invocation site — no loops!

This is essentially a multi-way win:

  * Kill legacy bridges
      o Smaller class files
      o Reifies relationship between bridge and bridgee
  * Loop-free bridges
  * Support legacy erased invocation of specialized methods


    Field bridges

Just as we have a problem with invoking erased methods on specialized 
instances from legacy code, we have the same problem with field access. 
We can run essentially the same play for “field bridges” as well:

|[Bridge[Object]] [TypeVar[T//Object]] f |

When we encounter a

|getfield Foo.f:Object |

after the initial resolution failure, we again look for fields with that 
name and a bridge attribute of the desired type, and if we find it, we 
replace the field operation with the equivalent of

|M.asType(E) |

where M is the field-access method handle, and E is the erased 
descriptor we want to bridge it to. This is messier as the linkage state 
for fields is “thinner” than that for methods, but conceptually the same 
play.

​


More information about the valhalla-spec-observers mailing list