[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