JDK-8230501: Class data support for hidden classes

Mandy Chung mandy.chung at oracle.com
Tue Nov 10 19:54:09 UTC 2020

I like John's suggestion (an offline discussion) that no need to change
to MethodHandles::hasFullPrivilegeAccess and Lookup::toString. Most clients
can ignore ORIGINAL bit.

I revise the spec change that full privilege access remains unchanged
and it means private and module access.  The operations requiring
original access are:
- obtain class data associated with a lookup class
- create method handles for caller-sensitive methods.

The compatibility risk only applies to existing code that obtain
caller-sensitive methods using a Lookup created using privateLookupIn.
This should be very few, if not none.


On 11/9/20 4:12 PM, Mandy Chung wrote:
> I'd like to get the discussion on the proposal APIs for class data
> support for hidden classes before posting PR.
> The patch is in https://github.com/mlchung/jdk/tree/class-data.
> This patch also updates the implementation of lambda meta factory and
> Memory Access API to use class data.  I ran the jdk.incubator.foreign
> microbenchmarks and no performance difference.  I'm running more
> performance testing.
> Specdiff
> http://cr.openjdk.java.net/~mchung/jdk16/webrevs/8230501/specdiff/overview-summary.html 
> Summary
> -------
> Provide `Lookup::defineHiddenClassWithClassData` API that allow live 
> objects
> be shared between a hidden class and other classes.  A hidden class 
> can load
> these live objects as dynamically-computed constants via 
> `MethodHandles::classData`
> and `MethodHandles::classDataAt` bootstrap methods.
> With this class data support and hidden classes, 
> `sun.misc.Unsafe::defineAnonymousClass`
> will be deprecated for removal.  Existing libraries should replace their
> calls to `sun.misc.Unsafe::defineAnonymousClass` with 
> `Lookup::defineHiddenClass`
> or `Lookup::defineHiddenClassWithClassData`.
> Background
> ----------
> This is an enhancement following up JEP 371: Hidden Classes w.r.t.
> "Constant-pool patching" in the "Risks and Assumption" section.
> 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.
> Details
> -------
> Provide `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 for the element at the given index in the class 
> data
> of the given lookup's lookup class, if the class data is a `List`.
> The hidden class will be initialized when `classData` or `classDataAt` 
> method
> is called if the hidden class has not been initialized.
> Bootstrap methods for class data of other type such as `Map` can be 
> considered
> in the future while this version also provides a bootstrap method for
> class data list.
> Frameworks sometimes want to dynamically create a hidden class (HC) 
> and add it
> it the lookup class nest and have HC to carry secrets hidden from that 
> nest.
> In this case, frameworks should not to use private static finals (in 
> the HCs
> they spin) to hold secrets because a nestmate of HC may obtain access to
> such a private static final and observe the framework's secret. It should
> use condy.
> In addition, we need to differentiate if a lookup object is created from
> the original lookup class or created from teleporting e.g. `Lookup::in`
> and `MethodHandles::privateLookupIn`.
> This proposes to add a new `ORIGINAL` bit that is only set if the lookup
> object is created by `MethodHandles::lookup` or by bootstrap method 
> invocation.
> `Lookup::hasFullPrivilegeAccess` returns true only if it has private, 
> module
> and originl access.
> `MethodHandles::privateLookupIn` spec is updated to check if the 
> caller lookup
> has private and module access.  It does not check for original access 
> so that
> a caller can pass a lookup without original access to a framework for
> deep reflection if desired.  It addition, the resulting Lookup object
> does not have the original access.
> Compatibility Risks
> -------------------
> The Lookup object returned by `MethodHandles::privateLookupIn` does 
> not have
> the original access.  If the caller lookup class and target class is 
> in the same
> module, it will return a Lookup object with private and module access but
> no original access. The return Lookup object no longer has full 
> privilege access.
> Existing code calling `Lookup::hasFullPrivilegeAccess` on a Lookup object
> returned from `privateLookupIn` is impacted since the resulting Lookup 
> object
> has private access but not full privilege access. `privateLookupIn` is 
> designed
> for frameworks to teleport to a target class with private access for deep
> reflection.  It is believed that `hasFullPrivilegeAccess` is not 
> popularly
> used (it was introduced in 14) and the compatibility risk is expected 
> to be low.
> `Lookup::toString` on the return Lookup object from 
> `MethodHandles::privateLookupIn`
> will have "/allaccess" suffix as opposed to no suffix to indicate full
> privilege access.

More information about the valhalla-dev mailing list