On Wed, 11 Nov 2020 18:52:10 GMT, Mandy Chung <mch...@openjdk.org> wrote:

> Provide the `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.
> 
> Specdiff
> http://cr.openjdk.java.net/~mchung/jdk16/webrevs/8230501/specdiff/overview-summary.html
> 
> 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`.
> 
> This patch also updates the implementation of lambda meta factory and
> `MemoryAccessVarHandleGenerator` to use class data.   No performance 
> difference
> observed in the jdk.incubator.foreign microbenchmarks.   A side note: 
> `MemoryAccessVarHandleGenerator` is removed in the upcoming integration of 
> JDK-8254162 but it helps validating the class data support.
> 
> 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
> -------
> 
> A new `Lookup::defineHiddenClassWithClassData` API 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 to load the class data of the given lookup's lookup 
> class.
> The hidden class will be initialized when `classData` method is called if
> the hidden class has not been initialized.
> 
> For a class data containing more than one single element, libraries can
> create their convenience method to load a single live object via condy.
> We can reconsider if such a convenience method is needed in the future.
> 
> 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.
> The operations only apply to a Lookup object with original access are:
>    - create method handles for caller-sensitve methods
>    - obtain class data associated with the lookup class
> 
> No change to `Lookup::hasFullPrivilegeAccess` and `Lookup::toString` which
> ignores the ORIGINAL bit.
> 
> 
> Compatibility Risks
> -------------------
> 
> `Lookup::lookupModes` includes a new `ORIGINAL` bit.  Most lookup operations
> ignore this original bit except creating method handles for caller-sensitive 
> methods
> that expects the lookup from the original lookup class.  Existing code  
> compares
> the return value of `lookupModes` to be a fixed value may be impacted.   
> However
> existing client has no need to expect a fixed value of lookup modes.
> The incompatibility risk of this spec change is low.

I took a look, since I'm interested in this API. Though, I can't really give a 
meaningful review for the access checking & ORIGINAL code changes.

src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java 
line 552:

> 550:                 Handle classDataBsm = new Handle(H_INVOKESTATIC, 
> Type.getInternalName(MethodHandles.class), "classData",
> 551:                                                  
> classDataMType.descriptorString(), false);
> 552:                 ConstantDynamic implMethodCondy = new 
> ConstantDynamic("implMethod", MethodHandle.class.descriptorString(), 
> classDataBsm);

Could be moved to clinit?

test/jdk/java/lang/invoke/AccessControlTest.java line 183:

> 181:          * then no members, not even public members, will be accessible
> 182:          * i.e. all access modes are lost.
> 183:          * [A8] If the new lookup class, the old lookup class and the 
> previous lookup class

Seems like a typo? There are 2 A8s now, and no A7, it seems.

src/java.base/share/classes/java/lang/invoke/MethodHandles.java line 319:

> 317:      */
> 318:      public static <T> T classData(Lookup caller, String name, Class<T> 
> type) throws IllegalAccessException {
> 319:          Objects.requireNonNull(name);

`name` is unused, but it can not be `null`. I think it would be good to add 
something about that to the javadoc. e.g.
     * @param name ignored (must not be {@code null})

FWIW, `classDataAt` doesn't check this. Should `caller` be checked as well?

src/java.base/share/classes/java/lang/invoke/MethodHandles.java line 403:

> 401: 
> 402:         @SuppressWarnings("unchecked")
> 403:         List<T> classData = List.class.cast(classdata);

Couldn't this call `classData` ?
Suggestion:

        @SuppressWarnings("unchecked")
        List<T> classData = classData(caller, name, List.class);
        if (classData == null) return null;

test/jdk/java/lang/invoke/MethodHandles/classData/ClassDataTest.java line 78:

> 76:     @Test
> 77:     public void noClassData() throws Throwable {
> 78:         assertTrue(MethodHandles.classData(LOOKUP, "dummy", Object.class) 
> == null);

Could be:
Suggestion:

        assertNull(MethodHandles.classData(LOOKUP, "dummy", Object.class));

FWIW

-------------

PR: https://git.openjdk.java.net/jdk/pull/1171

Reply via email to