That’s just a typo.  There’s just one attribute.  

> On Apr 7, 2019, at 5:30 PM, Remi Forax <fo...@univ-mlv.fr> wrote:
> 
> Hi Brian,
> with an hat of JVM language implementor,
> the attribute ForwardingBridge is important because it allows to simulate 
> things that are not co/contravariant for the VM but are from the language 
> point of view, like between an int and a Long.
> 
> I don't see the point of having two attributes, given that ForwardingBridge 
> supports a superset of what Forwarding support, it seems to be a premature 
> optimization to me.
> 
> The other thing is that Forwarding bridge should not use an adapter but a 
> bootstrap method.
> Historically, indy was using something similar to your adapter but this 
> design doesn't support sharing code because here the adapter code has to be 
> present in the same class as the forwarding method.
> If you want to share code, the shared code may want to access to the class 
> containing the forwarding method, that's why you need a Lookup object and the 
> share code may want to have some specific arguments, aka the bootstrap 
> arguments.
> 
> I think John has already proposed that we should support a bootstrap method 
> that returns a MethodHandle instead of a CallSite, this is exactly what we 
> need here.
> 
> regards,
> Rémi
> 
> ----- Mail original -----
>> De: "Brian Goetz" <brian.go...@oracle.com>
>> À: "valhalla-spec-experts" <valhalla-spec-experts@openjdk.java.net>
>> Envoyé: Jeudi 4 Avril 2019 14:33:39
>> Objet: Updated VM-bridges document
> 
>> At the BUR meeting, we discussed reshuffling the dependency graph to do
>> forwarding+reversing bridges earlier, which has the effect of taking some
>> pressure off of the descriptor language.  Here’s an updated doc on
>> forwarding-reversing bridges in the VM.
>> 
>> I’ve dropped, for the time being, any discussion of replacing existing 
>> generic
>> bridges with this mechanism; we can revisit that later if it makes sense.
>> Instead, I’ve focused solely on the migration aspects.  I’ve also dropped any
>> mention of implementation strategy, and instead appealed to “as if” behavior.
>> 
>> 
>> ## From Bridges to Forwarders
>> 
>> In the Java 1.0 days, `javac` was little more than an "assembler" for
>> the classfile format, translating source code to bytecode in a mostly
>> 1:1 manner.  And, we liked it that way; the more predictable the
>> translation scheme, the more effective the runtime optimizations.
>> Even the major upgrade of Java 5 didn't significantly affect the
>> transparency of the resulting classfiles.
>> 
>> Over time, we've seen small divergences between the language model and
>> the classfile model, and each of these is a source of sharp edges.  In
>> Java 1.1 the addition of inner classes, and the mismatch between the
>> accessibility model in the language and the JVM (the language treated
>> a nest as a single entity; the JVM treat nest members as separate
>> classes) required _access bridges_ (`access$000` methods), which have
>> been the source of various issues over the years.  Twenty years later,
>> these methods were obviated by [_Nest-based Access Control_][jep181]
>> -- which represents the choice to align the VM model to the language
>> model, so these adaptation artifacts are no longer required.
>> 
>> In Java 5, while we were able to keep the translation largely stable
>> and transparent through the use of erasure, there was one point of
>> misalignment; several situations (covariant overrides, instantiated
>> generic supertypes) could give rise to the situation where two or more
>> method descriptors -- which the JVM treats as distinct methods -- are
>> treated by the language as if they correspond to the same method.  To
>> fool the VM, the compiler emits _bridge methods_ which forward
>> invocations from one signature to another.  And, as often happens when
>> we try to fool the VM, it ultimately has its revenge.
>> 
>> #### Example: covariant overrides
>> 
>> Java 5 introduced the ability to override a method but to provide a
>> more specific return type.  (Java 8 later extended this to bridges in
>> interfaces as well.)  For example:
>> 
>> ```{.java}
>> class Parent {
>>   Object m() { ... }
>> }
>> 
>> class Child extends Parent {
>>   @Override
>>   String m() { ... }
>> }
>> ```
>> 
>> `Parent` declares a method whose descriptor is `()Object`, and `Child`
>> declares a method with the same name whose descriptor is `()String`.
>> If we compiled this class in the obvious way, the method in `Child`
>> would not override the method in `Parent`, and anyone calling
>> `Parent.m()` would find themselves executing the wrong implementation.
>> 
>> The compiler addresses this by providing an additional implementation
>> of `m()`, whose descriptor is `()Object` (an actual override), marked
>> with `ACC_SYNTHETIC` and `ACC_BRIDGE`, whose body invokes `m()String`
>> (with `invokevirtual`), redirecting calls to the right implementation.
>> 
>> #### Example: generic substitution
>> 
>> A similar situation arises when we have a generic substitution with a
>> superclass.  For example:
>> 
>> ```{.java}
>> interface Parent<T> {
>>   void m(T x);
>> }
>> 
>> class Child extends Parent<String> {
>>   @Override
>>   void m(String x) { ... }
>> }
>> ```
>> 
>> At the language level, it is clear that `Child::m` intends to override
>> `Parent::m`.  But the descriptor of `Parent::m` is `(Object)V`, and
>> the descriptor of `Child::m` is `(String)V`, so again a bridge is
>> needed.
>> 
>> Because the two signatures -- `m(Object)V` and `m(String)V` -- have
>> been "merged" in this manner, the compiler will prevent subclasses
>> from overriding the bridge signature, in order to maintain the
>> integrity of the bridging scheme.  (The first time you encounter  an
>> error message informing you of an illegal override in this situation,
>> it can be extremely confusing!)
>> 
>> #### Anatomy of a bridge method
>> 
>> The bridge methods that are generated by the compiler today operate by
>> _forwarding_.  That is, a bridge method `m(X)` is always defined
>> relative to some other method `m(Y)`, and the body of a bridge method
>> pushes its arguments on the stack, adapting them (widening, casting,
>> boxing, etc) the arguments from X to Y, invoking `m(Y)` with
>> `invokevirtual`, and adapting the return type from Y to X, and
>> returning that.  Because the bridge uses `invokevirtual`, it need only
>> be generated once, and invocations of the bridge may select a method
>> in a subclass.  (The bridge is generated at the "highest" place in the
>> inheritance hierarchy where the need for a bridge is identified,
>> which may be a class or an interface.)
>> 
>> #### Bridges are brittle
>> 
>> Bridges can be brittle under separate compilation (and, there was a
>> nontrivial bug tail initially.)  Separate compilation can move bridges
>> from where already-compiled code expects them to be to places it does
>> not expect them.  This can cause the wrong method body to be invoked,
>> or can cause "bridge loops" (resulting in `StackOverflowError`).
>> (These anomalies disappear if the entire hierarchy is consistently
>> recompiled; they are solely an artifact of inconsistent separate
>> compilation.)
>> 
>> The basic problem with bridge methods is that the language views the
>> two method descriptors as two faces of the same actual method, whereas
>> the JVM sees them as distinct methods.  (And, reflection also has to
>> participate in the charade.)
>> 
>> #### Limits of bridge methods
>> 
>> Bridge methods have worked well enough for the uses to which we've put
>> them, but there are a number of desirable scenarios where bridge
>> methods ultimately run out of gas.  These scenarios stem from various
>> forms of _migration_, and the desire to make these migrations
>> binary-compatible.
>> 
>> The problem of migration arises both from language evolution (Valhalla
>> aims to enable compatible migrating from value-based classes to value
>> types, and from erased generics to specialized), as well as from the
>> ordinary evolution of libraries.
>> 
>> An example of the "ordinary migration" problem is the replacement of
>> the old `Date` classes with `LocalDateTime` and friends.  We can
>> easily add new the classes to the JDK, along with conversions to and
>> from the old types, but there are existing APIs that still deal in
>> `Date` -- and if we ever want to be able to deprecate the old
>> versions, we have to find a way to compatibly migrate APIs that deal
>> in `Date` to the new types.  (The extreme form of this is the
>> "Collections 2.0" problem; we could surely write a new Collections
>> library, but when nearly every API deals in `List`, unless we can
>> migrate these away, what would be the point?)
>> 
>> Migration scenarios like these pose two problems that bridge methods
>> cannot solve:
>> 
>> - **Fields.**  While we can often reroute method invocations with
>>   bridges, we have no similar mechanism for fields.  If a field
>>   signature changes (whether due to changes in the translation
>>   strategy, or changes in the API), there is no way to make this
>>   binary-compatible.
>> - **Overrides.**  Bridges allow us to reroute _invocations_ of
>>   methods, but not _overrides_ of methods.  If a method descriptor
>>   in a non-final class changes, but has subclasses in a separate
>>   maintenance domain that continue to use the old descriptor, what
>>   is intended to be an override may accidentally become an overload,
>>   or might override the bridge instead of the actual method.
>> 
>> #### Wildcards and polymorphic fields
>> 
>> A non-migration application for bridges that comes out of Valhalla is
>> _wildcards_.  For a class `C<T>` with a method `m(T)`, the wildcard
>> `C<?>` (the class type) has an abstract method `m(Object)`, which
>> needs to be implemented by each species type.  This is, effectively, a
>> bridge; the method `m(Object)` generated for the species adapts the
>> arguments and forwards to the "real" (`m(T)`) method.  While this
>> could be implemented using straightforward code generation in the
>> static compiler, it may be preferable to treat this as a bridge as
>> well.
>> 
>> More importantly, the same is true for fields; if `C<T>` has a field
>> of type `T`, then the wildcard `C<?>` will expose this field as if it
>> were of type `Object`.  This cannot be implemented using
>> straightforward code generation in the static compiler (without
>> undermining the promise of migration compatibility.)
>> 
>> ## Forwarding
>> 
>> In this document, we attempt to learn from the history of bridges, and
>> create a new mechanism -- _forwarders_ -- that work with the JVM
>> instead of against it.  This raises the level of expressivity of
>> classfiles and opens the possibility of greater laziness.  It is
>> possible that traditional bridging scenarios can eventually be handled
>> by forwarders too, but for purposes of this document, we will focus
>> exclusively on the migration scenarios.
>> 
>> A _forwarder_ is a non-abstract method that, instead of a `Code`
>> attribute, has a `Forwarding` attribute:
>> 
>> ```
>> Forwarding {
>>   u2 name;
>>   u4 length;
>>   u2 forwardeeDescriptor;
>> }
>> ```
>> 
>> Let's assume that forwarders have the `ACC_FORWARDER` and
>> `ACC_SYNTHETIC` bits (in reality we will likely overload
>> `ACC_BRIDGE`).
>> 
>> When compiling a method (concrete or abstract) that has been migrated
>> from an old descriptor to a new descriptor (such as migrating
>> `m(Object)V` to `m(String)V`), the compiler would generate an ordinary
>> method with the new descriptor, and a forwarder with the old
>> descriptor which forwarders to the new descriptor.  This captures the
>> statement that there used to be a method called `m` with the old
>> descriptor, but it migrated to the new descriptor -- so that the JVM
>> can transparently adjust the behavior of clients and overriders that
>> were not aware of the migration.
>> 
>> #### Invocation of forwarders
>> 
>> Given a forwarder in a class with name `N` and descriptor `D` that
>> forwards to descriptor `E`, define `M` by:
>> 
>>   MethodHandle M = MethodHandles.lookup()
>>                                 .findVirtual(thisClass, N, E);
>> 
>> If the forwarder is _selected_ as the target of an `invokevirtual`,
>> the behavior should be _as if_ the caller invoked `M.asType(D)`, where
>> the arguments of `D` are adapted to their counterparts in `E`, and the
>> return type in `E` is adapted back to the return type in `D`.  (We may
>> wish to reduce the set of built-in adaptations to a smaller set than
>> those implemented by `MethodHandle::asType`, for simplicity, based on
>> requirements.)
>> 
>> Because forwarders exist for migration, we hope that over time,
>> callers will migrate from the old descriptor to the new, rendering
>> forwarders vestigial.  As a result, we may wish to defer as much of
>> the bridge generation logic as possible to first-selection time.
>> 
>> #### Forwarders for fields
>> 
>> The forwarding strategy can be applied to fields as well.  In this
>> case, the forwardee descriptor is that of a field descriptor, and the
>> behavior has the same semantics as adapting a target field accessor
>> method handle to the type of the bridge descriptor.  (If the forwarder
>> field is static, then the field should be static too.)
>> 
>> #### Overriding of forwarders
>> 
>> Capturing forwarding information declaratively enables us to detect
>> when a class overrides a forwarder descriptor with a non-forwarder
>> (which indicates that the subclass is out of date with its supertypes)
>> and redirect the override to the actual method (with arguments and
>> return values adapted.)
>> 
>> Given a forwarder in a class `A` with name `N` and descriptor `D` that
>> forwards to descriptor `E`, suppose a subclass `B` overrides the
>> forwarder with `N(D)`.  Let `M` be the method handle that corresponds
>> to the `Code` attribute of `B.N(D)`.  We would like it to behave as if
>> `B` had instead specified a method `N(E)`, whose `Code` attribute
>> corresponded to `M.asType(E)`.
>> 
>> #### Additional adaptations
>> 
>> The uses we anticipate for L100 all can be done with `asType()`
>> adaptations (in fact, with a subset of `asType()` adaptations).
>> However, if we wish to support user-provided migrations (such as
>> migrating libraries that use `Date` to `LocalDateTime`) or migrate
>> complex JDK APIs such as `Stream`, we may need to provide additional
>> adaptation logic in the `ForwardingBridge` attribute.  Let's extend
>> the `Forwarding` attribute:
>> 
>> ```
>> Forwarding {
>>   u2 name;
>>   u4 length;
>>   u2 forwardeeDescriptor;
>>   u2 adapter;
>> }
>> ```
> 
> it's ForwardingBridge here ?
> 
>> 
>> where `adaptor` is the constant pool index of a method handle whose
>> type is `(MethodHandle;MethodType;)MethodHandle;` (note that the
>> method handle for `MethodHandle::asType` has this shape).  If
>> `adapter` is zero, we use the built-in adaptations; if it is nonzero,
>> we use the referred-to method handle to adapt between the forwarder
>> and forwardee descriptors (in both directions).
>> 
>> #### Adaptation failures and limitations
>> 
>> Whatever adaptations we are prepared to do between forwarder and
>> forwardee, we need to be prepared to do them in both directions; if a
>> method `m(int)` is migrated to `m(long)`, invocation arguments will be
>> adapted `int` to `long`, but if overridden, we'll do the reverse
>> adaptation on the (out of date) overrider `m(int)`.  Given that most
>> adaptations are not between isomorphic domains, there will be cases in
>> one direction or the other that cannot be represented  (`long` to
>> `int` is lossy; `Integer` to `int` can NPE; `Object` to  `String` can
>> CCE.)
>> 
>> Our guidance is that adaptations should form a projection/embedding
>> pair; this gives us the nice property that we can repeat adaptations
>> with impunity (if the first adaptation doesn't fail, adapting back and
>> back again is guaranteed to be an identity.)  Even within this,
>> though, there are often multiple ways to implement the adaptation; an
>> embedding can throw on an out-of-range value, or it could pick an
>> in-range target and map to that.  So, for example, if we migrated
>> `Collection::size` to return `long`, for `int`-desiring clients, we
>> could clamp values greater than `MAX_VALUE` to `MAX_VALUE`, rather
>> than throwing -- and this would likely be a better outcome for most
>> clients.  The choice of adaptation should ultimately be left to
>> metadata present at the declaration of the migrated method.
>> 
>> #### Type checking and corner cases
>> 
>> A forwarder should always forward to a non-forwarder method (concrete
>> or abstract) _in the same class_.  (Because they are in the same
>> class, there is no chance that separate compilation can cause a
>> forwarder to point to another forwarder.)
>> 
>> In general, we expect that forwarders are only ever overridden by
>> non-forwarder methods (and then, only in out-of-date classfiles).
>> (This means that invocations that resolve to the forwarder will
>> generally select the forwarder.)
>> 
>> - If a forwarder method is overridden by another forwarder method,
>>   this is probably a result of a migration happening in a subclass
>>   and then later the same migration happens in a superclass.  We can
>>   let the override proceed.
>> - If a forwarder is overridden by a legacy bridge, we have a few bad
>>   choices.  We could accept the bridge (which would interfere with
>>   forwarding), or discard the bridge (which could cause other
>>   anomalies.)  If we leave existing bridge generation alone, this
>>   case is unlikely and accepting the bridge is probably a reasonable
>>   answer; if we migrate bridges to use forwarding, we'd probably
>>   want to err in the other direction.
>> - If a forwarder has a forwardee descriptor that is exactly the
>>   same as the forwarder, the forwarder should be discarded.  (These
>>   can arise from specialization situations.)
>> 
>> 
>> 
>> 
>> [jep181]: https://openjdk.java.net/jeps/181

Reply via email to