JDK-8013527[1] has somehow become the umbrella bug for "Using MethodHandles to call caller sensitive methods leads to interesting results".

To recap: A caller sensitive method knows about who called it, and can behave differently when called from an other context.
Examples are: Class.forName, MethodHandles.lookup, Method.invoke...

A MethodHandle on the other hand should not be caller sensitive.
To archive this, a MethodHandle will "bind" the lookup class as caller for caller sensitive methods.

This is currently done by injecting a hidden class (InjectedInvoker" that acts as a trampoline for calling caller sensitive methods.

This injected invoker shares many properties of the original caller:
Same ClassLoader, same Module, same Package, same ProtectionDomain, but it's not the same class or a nestmate of it.

For caller sensitive methods that do look at more than just the injected invoker, this leads to "unexpected" results when called through a MethodHandle:

* MethodHandles.lookup() returns a full privileged lookup for the injected invoker. * jlr.Field.get*/set*, jlr.Constructor.newInstance, jlr.Method.invoke may fail with an IllegalAccessException if the target is private. See JDK-8257874[2].

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

After reading one of John Rose's comments[3], I thought that this might be a way to solve this general problem.

So I implemented some of it here[4].

The basic idea is that there is a private overload of the caller sensitive method which accepts the caller as a tailing argument.

The good news:
* tier1 Tests pass.
* ((Lookup) lookup.findStatic(MethodHandles.class, "lookup", MethodType.methodType(Lookup.class)).invokeExact()).lookupClass() == lookup.lookupClass();
* JDK-8257874 can't be reproduced with Field.* or Constructor.
* Performance is likely better. (InjectedInvoker collects all arguments into an Object[].)

The bad news:
* If you use a MethodHandle to call Method.invoke for a caller sensitive method, then you can still observe the injected invoker.

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

Moving forward, there are 3 ways:
1. Do nothing. Won't fix any bug.
2. Use the current prototype, and accept Method.invoke is odd when calling it through a MethodHandle.
3. Go all in:
  * Require **every** caller sensitive method to have a private overload.
  * Method.invoke will also use that private overload.

The problems with the 3rd approach are:
* What about methods that can be called virtually? (Thread.getContextClassLoader()) * Requires a few changes to MethodAccessor. Maybe implementing JDK-6824466[5] first?
  * What about methods that do stack walks?

I have to think more about the problems listed above - but maybe you have some input that could help me on that.

- Johannes


[1]: https://bugs.openjdk.java.net/browse/JDK-8013527
[2]: https://bugs.openjdk.java.net/browse/JDK-8257874
[3]: https://bugs.openjdk.java.net/browse/JDK-8020968?focusedCommentId=13611844&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13611844
[4]: https://github.com/openjdk/jdk/pull/2117/files
[5]: https://bugs.openjdk.java.net/browse/JDK-6824466

Reply via email to