question on exports to

Jochen Theodorou blackdrag at gmx.org
Sat Jun 4 09:55:34 UTC 2016



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







More information about the jigsaw-dev mailing list