On 04.06.2016 10:54, Peter Levart wrote:
[...]
Reflection code (Method.invoke, Field.[get|set],
Constructor.newInstance) are @CallerSensitive methods. They "know" which
class is calling them and perform access checks on behalf of the caller
class that are "equivalent" (with mentioned exceptions) to what JVM does
when it resolves various invoke / [get|put]field bytecodes. You may peek
into sources to see what they do. In the end they invoke the method
using either JNI or bytecode generated accessor classes which are free
from access checking and access the fields using Unsafe which is free of
access checking too. So reflection does access checks on every access
and has its own logic written in Java to perform them which uses an
internal cache (see AccessibleObject.checkAccess / securityCheckCache).
Bytecode instruction access checks are performed lazily by JVM which is
very good at optimizing them "away" so they don't affect performance.

A couple of things I want to say here....

For a dynamic language, are anyone doing proxies, mocks and many other things @CallerSensitive is a major pain. Even if you do MethodHadles and invokedynamic, frames from your runtime may get in the way and then @CallerSensitive is getting the wrong information. I think there was an annotation in talk to let @CallerSensitive skip frames, but got rejected because of security concerns. If my information is right it means I have to do what I did for Class#forName for example, and emulate the logic in that method... in case of Class#forName it is easy, because I can just call the other variant. I do have the caller class information in my runtime, so it is no problem getting this class or the loader of it.

But these module API methods are a different beast. How am I ever supposed to make these work? I am talking about Groovy code callig for example addReads (I know I am supposed to forget about it, but it is the first one that I remember right now.. sorry)

The runtime will have to get all the exports and reads. I could now go and say that it then maybe makes no sense to let Groovy support the module system beyond the absolute minimum, especially no modules written in Groovy...

for example... why should I make the Groovy runtime a named module, if it needs to be able to read everything anyway? The answer so far for me has me, so that other named modules can depend on it during compile time. And I am not only talking about modules written in Groovy here. Many libraries for Groovy are actually written in Java, but use types from Groovy. And a lot of interfacing code from applications depends on classes coming from the Groovy runtime. If the Groovy runtime does not become a named module, all those cannot become named modules.

... anyway ...

why are "bytecode generated accessor classes which are free from access checking"? I assume they are special treated by the JVM, the module system, or other inaccessible internals. Which means a library cannot emulate that. Is that right?

As for optimizing away things in the JVM... Now I don't know about those access checks, but any optimization work that needs to be done, means the application is going to be slower before that happens. This increases warmup times. And even if the JVM is good at optimizing away things, it does not mean it can optimize away things completely. I see that for example in the primitive opts in Groovy. Basically they mean to compile a fast and a slow path into bytecode, guarded by a simple boolean. The JVM is very good in optimizing away the never used slow branch and always depend on the fast one... still that code is 50% slower compared to having only the fast branch.

So your question was about runtime helper classes for invocation. Yes,
this has not changed. And the trick is that these generated helper
classes all extend a special internal class:

MagicAccessorImpl, I see... we have used that in the past as well to speed up our non-indy callsitecaching. Though that was sun.reflect.MagicAccessorImpl and now it is jdk.internal.reflect.MagicAccessorImpl. The use of that class is optional in our runtime, since not all platforms support it. We used it mostly to save on the verification cost... this means less stable callsites will have a much worse performance now. Good for the invokedynamic version, which suffers from the high costs caused by creating MethodType objects. And I assume jdk.internal.reflect is protected by the module system....

bye Jochen





Reply via email to