JDK-8239384 : Convert LambdaMetaFactory to create hidden classes
  • Type: Sub-task
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 15
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2020-02-19
  • Updated: 2020-04-03
  • Resolved: 2020-03-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
repo-valhallaFixed
Related Reports
Relates :  
Relates :  
Description
The current implementation of LambdaMetaFactory creates VM-anonymous class which does not conform to the standard access control rules.

This issue tracks the conversion of LambdaMetaFactory from using VM-anonymous class to hidden class.

A hidden class is subject to standard access control rules.  

One non-standard access that VM-anonymous class has is that
a VM-anonymous class can always access protected members of a superclass in a different package, which is not allowed by the rules of JVMS 5.4.4. 

Comments
An additional change was made in LMF when converting to hidden classes: 5. Special case for method reference on a protected method inherited from a "remote" supertype (i.e. in a different package) to invoke `implMethod` via `invokeExact` rather than bytecode invocation for binary compatibility JDK-8222411 raises a binary compatibility concern w.r.t. a method reference of this form: `<instance of C> :: <method of C's supertype>` Hidden nestmate class is "less powerful" than VM-anonymous class. A VM-anonymous class can access protected members of its host class even if the VM-anonymous class exists in a different run-time package and is not a subclass of the host class. In contrast, access-control rules are applied properly for hidden classes: a hidden class can only access protected members of another class if the hidden class is in the same run-time package as, or a subclass of, the other class. There is no special access for a hidden class to the protected members of the lookup class. Our prior discussion is to accept this incompatibility and javac to generate the access bridge (JDK-8234729). A method reference of this form: `<instance of C> :: <method of C's supertype D>` class C was compiled with class D when D's method is public. Then D's method is changed from public to protected in a separate compilation. When the accessor class `C` is a subtype of the accessee class D which declares the `protected` member; moreover, the code in `C` accesses the `protected` member through a receiver which is an instance of the accessor class (`C`). This meets the requirements for access to a `protected` member. As such, separately recompiling the supertype, so that it declares a `protected` member instead of a `public` member, must not cause a linkage error for the code in `C`. The change in the supertype is binary compatible from the point of view of this subtype; obviously the change in the supertype would not be binary compatible for millions of other classes. This scenario is rare but it is a binary incompatiblity. The following options have been considered: (1) Lookup::in supports nestmates that should not drop access (JDK-8199386). The accessor does not have an access bridge and a dynamic nestmate does not have access to the inherited protected member in a supertype in a different package. This is the exact scenario John raised w.r.t remote supertypes and interaction of Lookup::in and nests. LMF will spin a lambda proxy class to obtain a Lookup on C (calling Lookup::in) and then find a MethodHandle of this protected member. Then invoke via MethodHandle. (2) Invoke `implMethod` passed from JVM to LMF if it is to invoke a protected method inherited from a "remote" supertype (i.e. different package) Direct invocation of the `implMethod` method handle by the lambda proxy was explored in JDK 8 lambda development time. It is time to remeasure the performance of this approach (`MethodHandle::invokeExact`) compared with the current implementation translating to the bytecode invocation (JDK-8239580). This option is the simplest solution to maintain binary compatibility. If JDK-8239580 shows comparable performance, LMF can invoke `implMethod` via `MethodHandle::invokeExact` consistently for all cases. (3) Consider this is not a supported binary compatibility. That is, invoking a protected method in a remote supertype via bytecode is binary compatible whereas invoking via method handle is not binary compatibility. This inconsistency requires to know the implementation details to explain to the developers. 6. A method reference on a protected member inherited from a remote supertype needs to be desugared to synthesize a bridge for lambda proxy class to access. (This is related to 5 above). javac has been changed in JDK 14 to synthesize a bridge method if it's a method reference to access a protected member in a remote supertype (JDK-8234729).
03-04-2020

The following has been made in LambdaMetaFactory when converting to use hidden classes: 1. the lambda proxy class is a nestmate of the target class. The hidden class is as if the implementation body of the target class to implement lambdas and it can access its private members. 2. the lambda proxy class no longer needs to generate the "get$Lambda" factory method as the hidden class is a nestmate and the Lookup object on the target class has the access to the constructor of the hidden class. 3. Classes compiled prior to dynamic nestmate support invokes a private instance method with REF_invokeSpecial. However, as the lambda proxy class is now a nestmate, invokespecial can only be used to invoke private nestmate constructors. For binary compatibility, LMF will convert REF_invokeSpecial on an instance method of the target clss to spin `invokevirtual` or `invokeinterface` instruction instead. 4. the lambda proxy class has the strong relationship with the class loader (that will share the VM metaspace for its defining loader - implementation details)
24-03-2020