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.

Reply via email to