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