JDK-8256214 : Class data support for hidden classes
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 16
  • Submitted: 2020-11-11
  • Updated: 2020-11-25
  • Resolved: 2020-11-25
Related Reports
CSR :  
Description
Summary
-------

Provide `Lookup::defineHiddenClassWithClassData` API that allows live objects
be shared between a hidden class and other classes.  A hidden class can load
these live objects as dynamically-computed constants via this API.

`sun.misc.Unsafe::defineAnonymousClass` will be deprecated for removal. 
Existing libraries depending on the constant pool patching of VM-anonymous
class should replace their calls to `sun.misc.Unsafe::defineAnonymousClass`
with `Lookup::defineHiddenClassWithClassData`.

Problem
-------

This is a follow up enhancement to JEP 371: Hidden Classes as documented
in the "Risks and Assumption" section:

___Constant-pool patching___

A VM-anonymous class can be defined with its constant-pool entries already
resolved to concrete values. This allows critical constants to be shared
between a VM-anonymous class and the language runtime that defines it, and
between multiple VM-anonymous classes. For example, a language runtime will
often have `MethodHandle` objects in its address space that would be useful
to newly-defined VM-anonymous classes. Instead of the runtime serializing
the objects to constant-pool entries in VM-anonymous classes and then
generating bytecode in those classes to laboriously `ldc` the entries,
the runtime can simply supply `Unsafe::defineAnonymousClass` with references
to its live objects. The relevant constant-pool entries in the newly-defined
VM-anonymous class are pre-linked to those objects, improving performance
and reducing footprint. In addition, this allows VM-anonymous classes to
refer to each other: Constant-pool entries in a class file are based on names.
They thus cannot refer to nameless VM-anonymous classes. A language runtime can,
however, easily track the live Class objects for its VM-anonymous classes and
supply them to `Unsafe::defineAnonymousClass`, thus pre-linking the new class's
constant pool entries to other VM-anonymous classes.

This extends the hidden classes to allow live objects to be injected
in a hidden class and loaded them via condy.

Solution
--------

Define a new `Lookup::defineHiddenClassWithClassData` API that takes 
additional `classData` argument compared to `Lookup::defineHiddenClass`.
Class data can be method handles, lookup objects, arbitrary user objects
or collections of all of the above.

This method behaves as if calling `Lookup::defineHiddenClass` to define
a hidden class with a private static unnamed field that is initialized with `classData` at the first instruction of the class initializer.

`MethodHandles::classData(Lookup lookup, String name, Class<?> type)`
is a bootstrap method for the class data of the given lookup's lookup class.
`MethodHandles::classDataAt(Lookup lookup, String name, Class<?> type, int index)`
is a bootstrap method to load an element at the specified index from the class data
if it's a list.   It's a convenience method to load one element from class data 
via condy.

The hidden class will be initialized when `classData` or `classDataAt` method
is called if the hidden class has not been initialized.


Specification
-------------

See attached specdiff for the spec changes for:

 1. In java.lang.invoke.MethodHandles.Lookup class:
   - New `Lookup::defineHiddenClassWithClassData` method
   - New `Lookup::ORIGINAL` mode and the following methods are updated to reflect this new mode
       - `Lookup::lookupModes`
       - `Lookup::in`
       - `Lookup::dropLookupIn`
 2. java.lang.invoke.MethodHandles class
    - New `MethodHandles::classData(Lookup lookup, String name, Class<?> type)` static method
    - New `MethodHandles::classDataAt(Lookup lookup, String name, Class<?> type, int index)` static method
    - `MethodHandles::lookup` and `MethodHandles::privateLookupIn` are updated to reflect the new `ORIGINAL` mode

Compatibility Risks
-------------

The `Lookup` object produced via teleporting will drop `ORIGINAL` bit (including `Lookup::in`, `Lookup::dropLookupIn` and `MethodHandles::privateLookupIn`)

A `Lookup` with original access ensures that this lookup is created by the original lookup class and the bootstrap method invoked by the VM. Such a lookup with original access also has private and module access which has the following additional capability:

   - create method handles which invoke caller sensitive methods, such as Class.forName
   - obtain the class data associated with the lookup class

Prior to the new `ORIGINAL` bit, a `Lookup` returned by `MethodHandles::privateLookupIn` with both private and module access can lookup caller-sensitive methods.  The behavior of  the `Lookup` returned  by `MethodHandles::privateLookupIn` with both private and module access is changed and it will not be able to lookup any caller-sensitive method.   

The behavior of a caller-sensitive method is dependent on the lookup context.  So a method handle of caller-sensitive methods is bound with the lookup context at creation time and the lookup context is evaluated at runtime instead of the caller at method invocation time.  Therefore a lookup object with the original access must be provided; otherwise, `IllegalAccessException` will be thrown.   The intent for `MethodHandles::privateLookupIn` is to allow module authors to authorize libraries or frameworks to do deep reflection on private and module-internal members.  Existing code should use `MethodHandles::lookup` (its own lookup with original full privilege access) to obtain caller-sensitive methods instead.  The compatibility risk of this behavioral change is low because the full privilege access requirement to lookup caller-sensitive methods is prior to Java SE 9 when `privateLookupIn` is introduced.

Comments
Moving to Approved.
25-11-2020