JDK-8271820 : Implementation of JEP 416: Reimplement Core Reflection with Method Handle
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2021-08-03
  • Updated: 2024-11-04
  • Resolved: 2021-10-28
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.
JDK 18
18 b22Fixed
Related Reports
CSR :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Sub Tasks
JDK-8277244 :  
Description
Implement JEP 416.   The old core reflection implementation will be removed in a later release.

JDK-8013527 and JDK-8257874 are also fixed in this patch.
Comments
Changeset: c6339cb8 Author: Mandy Chung <mchung@openjdk.org> Date: 2021-10-28 18:32:50 +0000 URL: https://git.openjdk.java.net/jdk/commit/c6339cb8a255d387bb182ad20dd69f3d460cf1ed
28-10-2021

This captures the implementation details of caller-sensitive methods. ### Caller-sensitive methods A _caller-sensitive_ method is a method that can behave differently depending upon the class of its immediate caller. The implementation of a caller-sensitive method does a stack walk to find its immediate caller, skipping any stack frames introduced by the internal reflection machinery. Here are some of the caller-sensitive methods in the platform: - `Class::forName(String)` uses its caller class's class loader to load the named class and to perform a permission check if a security manager is enabled. - `Method::invoke`, `Constructor::newInstance`, `Field::getX`, and `Field::setX` perform an access check against their caller's class unless the access check is suppressed via `setAccessible(true)`. - `MethodHandles::lookup` uses its caller's class as the lookup class of the returned `Lookup` object. This example code shows how a caller-sensitive method `CSM::returnCallerClass` is called via `Method::invoke`. ``` class CSM { @CallerSensitive static Class<?> returnCallerClass() { return Reflection.getCallerClass(); } } class Foo { void test() throws Throwable { // calling CSM::returnCallerClass via reflection var m = CSM.class.getMethod("returnCallerClass"); // expect Foo to be the caller class var caller = m.invoke(null); assert(caller == Foo.class); } } ``` First, `Method::invoke` finds `Foo` as its immediate caller to check. It checks if `CSM::returnCallerClass` is accessible to `Foo`. Then it invokes `CSM::returnCallerClass` reflectively. Since `CSM::returnCallerClass` is a caller-sensitive method, it finds its immediate caller class, skipping reflection frames, and returns it. In this case, `CSM::returnCallerClass` finds `Foo` as the caller class. The stack trace would look something like this: ``` CSM.returnCallerClass jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 jdk.internal.reflect.NativeMethodAccessorImpl.invoke jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke java.lang.reflect.Method.invoke Foo.test : : ``` Note that the stack walk to find the caller's class is done twice, once for `Method::invoke` and once for `CSM::returnCallerClass`. ### Method handle invocation for caller-sensitive methods If a method handle for a caller-sensitive method is requested then the general rules for bytecode behaviors apply, but they take account of the lookup class in a special way. The resulting method handle behaves as if it were called from an instruction contained in the lookup class, so that the caller-sensitive method detects the lookup class. (By contrast, the invoker of the method handle is disregarded.) Thus, in the case of caller-sensitive methods, different lookup classes may give rise to differently behaving method handles. Due to this behavior of caller-sensitive methods, an invocation of a target caller-sensitive method via `Method::invoke` which is called via a method handle does not work properly. For example, `Bar` calls `CSM::returnCallerClass` via a chained reflective call as shown below: ``` class Bar { void test() throws Throwable { // method handle for Method::invoke MethodHandle mh = MethodHandles.lookup() .findVirtual(Method.class, "invoke", methodType(Object.class, Object.class, Object[].class)); // reflective object for CSM::returnCallerClass Method m = CSM.class.getMethod("returnCallerClass"); // invoke Method::invoke via method handle and the target method // being invoked reflectively is CSM::returnCallerClass var caller = mh.invoke(m, null, null); assert(caller == Bar.class); // Fail! } } ``` It is reasonable to expect that this chained reflective call to invoke `CSM::returnCallerClass` should behave the same as when `CSM::returnCallerClass` is called statically, i.e., `Bar` should be the returned class. The current implementation, however, returns the incorrect caller class. The stack trace below shows the internal implementation, including hidden frames, that reveals what is found as the caller class through stack walking. On the other hand, `Method::invoke` is called via a method handle. `Method::invoke` should behave as if it were called by the lookup class of the `Lookup` object creating that method handle as specified, i.e. `Bar`. ``` CSM.returnCallerClass() jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 jdk.internal.reflect.NativeMethodAccessorImpl.invoke jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke java.lang.reflect.Method.invoke(mh) java.lang.invoke.DirectMethodHandle$Holder.invokeSpecial java.lang.invoke.LambdaForm$MH/0x0000000800003000.invoke java.lang.invoke.LambdaForm$MH/0x0000000800004400.invokeExact_MT Bar$$InjectedInvoker/0x0000000800003400.invoke_V <--- caller java.lang.invoke.DirectMethodHandle$Holder.invokeStatic java.lang.invoke.LambdaForm$MH/0x0000000800004000.invoke java.lang.invoke.LambdaForm$MH/0x0000000800003c00.invoke_MT Bar.test : : ``` This example shows the bug in the current implementation: Two caller-sensitive methods do not work properly if called via a chained reflective call when relying on stack walking to find the caller. The current implementation injects a hidden class `Bar$$InjectedInvoker/0x0000000800003400`, which is in the same runtime package as `Bar` and defined by the same defining loader as `Bar` with the same protection domain. Stack walking will find `Bar$$InjectedInvoker/0x0000000800003400` as the caller's class instead of `Bar`. This approach works for caller-sensitive methods which depend on the runtime package, the defining loader, or the protection domain of the caller's class, but it does not work for `MethodHandles::lookup` invocations which require the exact caller class (see [8013527] and [8257874] for details). [8013527]: https://bugs.openjdk.java.net/browse/JDK-8013527 [8257874]: https://bugs.openjdk.java.net/browse/JDK-8257874 ### Special calling sequence for caller-sensitive methods The new implementation introduces a special calling sequence for caller-sensitive methods. A caller-sensitive method can provide a private adaptor of the same name but with an additional `Class` parameter alongside. When a caller-sensitive method is invoked via core reflection or via a method handle, it will find if an adaptor method with a `Class` parameter is present. If found, it will invoke the adaptor method with the caller class argument instead. This special calling sequence ensures that the same caller class is passed to a caller-sensitive method via `Method::invoke`, `MethodHandle::invokeExact`, or a mix of these methods. For example, `CSM::returnCallerClass` and its adaptor method will look like this: ``` class CSM { @CallerSensitive static Class<?> returnCallerClass() { return returnCallerClass(Reflection.getCallerClass()); } private static Class<?> returnCallerClass(Class<?> caller) { return caller; } } ``` The stack trace for the examples above would look like this in the new implementation: ``` CSM.returnCallerClass(caller) <--- adaptor method java.lang.invoke.DirectMethodHandle$Holder.invokeStatic java.lang.invoke.Invokers$Holder.invokeExact_MT jdk.internal.reflect.DirectMethodAccessorImpl$CallerSensitiveWithCaller.invoke java.lang.reflect.Method.invoke Foo.test : : ``` and ``` CSM.returnCallerClass(caller) <--- adaptor method java.lang.invoke.DirectMethodHandle$Holder.invokeStatic java.lang.invoke.Invokers$Holder.invokeExact_MT jdk.internal.reflect.DirectMethodAccessorImpl$CallerSensitiveWithCaller.invoke java.lang.reflect.Method.invoke(caller, m) <--- adaptor method java.lang.invoke.DirectMethodHandle$Holder.invokeSpecial java.lang.invoke.LambdaForm$MH/0x0000000800004000.invoke java.lang.invoke.LambdaForm$MH/0x0000000800003c00.invoke_MT Bar.test : : ``` Both `CSM::returnCallerClass` and `Method::invoke` can have an adaptor method with a caller-class parameter defined. `Foo` calls `Method::invoke` which does the stack walking to find the caller's class. It will pass the caller's class directly to the adaptor method for `CSM::returnCallerClass`. Similarly, `Bar` calls `Method::invoke` via a method handle to invoke `CSM::returnCallerClass`. In that case, `MethodHandle::invokeExact` uses the lookup class of the `Lookup` object producing the method handle as the caller's class, so no stack walking involved. The lookup class is `Bar`. It will invoke the adaptor method of `Method::invoke` with `Bar` as the caller's class, which in turn invokes the adaptor method of `CSM::returnCallerClass` with `Bar` as the caller. The new implementation eliminates the need for multiple stack walks when a caller-sensitive method is invoked reflectively. For caller-sensitive methods that require an exact caller class, the adaptor method must be defined for correctness. `MethodHandles::lookup` and `ClassLoader::registerAsParallelCapable` are the only two methods in the JDK that need the exact caller class. On the other hand, for caller-sensitive methods that use the caller's class for access checks or security permission checks, i.e., based on its runtime package, defining loader, or protection domain, the adaptor method is optional. The new implementation will support reflective calls on caller-sensitive methods with and without an adaptor using the special calling sequence.
03-08-2021