Suggestion: allow accessible reflection on protected methods of exported types.

Jochen Theodorou blackdrag at gmx.org
Mon Jan 9 23:37:10 UTC 2017


On 09.01.2017 12:47, Andrew Dinn wrote:
[...]
> If you carefully review discussion on the subject of this (slightly
> false) dichotomy between static and dynamic capabilities I think you
> will find that there has been a lot of explicit discussion. In my view,
> your summary of the status quo is at best out of date and at worst
> misrepresents the goals of the Jigsaw project and the wider goals of the
> OpenJDK project as a whole.
>
> There is indeed a desire to limit opportunities for programs to rely on
> reflection.

The problem is simply with old reflective code. You tend to have - and I 
have seen that in many applications - a kind of central part doing 
invocation for you. You could call it a reflective runtime. For this 
kind of code doing rights via @CallerSensitive tends to be unusable, 
because you tend to have code that wraps the call itself. And once you 
have that, it is the rights of your runtime, not that of the calling 
code. It was simply not "normal" to do, what you can now do with 
MethodHandles, which is returning something, which will then be invoked 
instead of doing the invocation right away. And considering cases where 
you for example need the post processing of exceptions and such I don´t 
really see how you could have done that with reflection anyway.

Now having these "central invokers" means you have to work completely 
out of context as far @CallerSensitive is considered. And that means you 
are forced to use setAccessible, if you need to handle more than just 
public class members (or public class containers for that matter). And 
this kind of code is now practically crippled.

And as you wrote, that is exactly what was intended to be done.

But that means, that language runtimes tend to have a problem now if 
they depend on such central invoker patterns.

I won´t deny that maybe in some structures things got a bit out of hand. 
But fixing them after years of working without breaking changes is near 
impossible.

That is because I cannot simply change exported API to reach through 
Lookup objects, since they would be a breaking API change. Plus the 
Lookup objects can only do what their context allows them to do, they do 
not automatically give me the right to invoke methods on other objects I 
normally have no access rights for in that context. I would need the 
Lookup object for the goal, which might not be obtainable. And again, 
that is exactly what was intended to be broken.

example... Let us say you work with a GroovyIntereptable:

class A implements GroovyIntercepable {
   def invokeMethod(String name, args) {
    ...
   }
}

class B extends A {
   private someMethod() {}
}

for every method call on an instance of A or B (for example 
b.callSomemethod()) the invokeMethod method is called. I was now 
perfectly able to invoke someMethod on instances of B in the 
invokeMethod method of A by simply doing this.someMethod(). I assume the 
code will still work if B is in the same module as A, but if not, then 
this code will no longer work using central invoker patterns. Of course 
I could now claim that B may expose a helper method of some kind, which 
is automagically called and delegates to someMethod, or that I can 
replace the usage by MethodHandles to some extend. But if class B is 
written in Java, then I fail with the first approach and if the actual 
handling is not done in A, but in a delegate of A, I fail with the 
MethodHandles approach behind the scenes as well. Of course I could have 
the Lookup object in this case. But point being, the API does not allow 
for it without breaking changes.

Then how else can I add such a helper method for my runtime? Agents, as 
Remi said? Can agents add methods to an already loaded class? I am 
unaware of this being possible now. Which means I have to start an agent 
on VM startup, which means a more complicated command line for the user, 
but it means first and for all it counters the usage of Groovy as 
non-intrusive library. Right now, if I do not use anything from Groovy, 
the Groovy runtime won´t be involved. With having to have an agent to 
transform classes on load, this is changed.

In other words, we are destroying one feature to rescue another. And I 
do not see a good solution here, I doubt there can really exist one, 
because it is the very goal of jigsaw to "limit opportunities for 
programs to rely on reflection". And just to make this 100% clear... 
even the invokedynamic based version of Groovy has to use reflection to 
some degree to get everything running. Basically all those things that 
jigsaw forbids now.

> That desire is linked to the motivation for implementing
> Jigsaw. Reflection as it was provided in JDK8- does not really fit well
> with /the/ main goal of modularization i.e. ensuring that module
> internals -- in particular, JDK runtime internals -- can be guaranteed
> to remain private to client code. The concern is wider than simply
> meeting this Jigsaw requirement. That guarantee is critical to ensuring
> that module implementations, most especially JDK runtime modules
> implementations, can be maintained, extended and optimized without
> breaking backward compatibility. Some breakage now is being traded off
> against breakage later.

"some" is relative. For me it means to rewrite the extensible object 
protocol for Groovy (MOP). All signatures of all extension points will 
change. And even then I cannot do all I did before, because before I was 
able to call methods you ordinarily have no access to in a Java world 
without reflection.

> This change to reflective accessibility is not simply a wholesale
> removal of capability.It is being balanced by a mix of completed and
> in-progress changes which i) allow suitably privileged code (such as
> agents or containers) to enable reflective access where they see fit and
> ii) provide an alternative mechanism to achieve the same end result as
> reflection but instead based on method handles. The latter alternative
> is especially important as it will ensure more precisely controlled and
> recorded access and, as one very important consequence, enable better
> performance from the compiler(s).

that kind of balancing is a POV depending action you know. For Java this 
is simply a completely different story than it is for Groovy. 
Accordingly it must look unbalanced for Groovy. Additionally I do not so 
(i). How can a library add reflective access where they see fit in a 
non-intrusive way? And (ii) is simply not there. For example... can you 
call Object#clone on Object using MethodHandles? I actually did not try 
this, but since the method is protected and in a module I would assume 
no. You had been able to do so by reflection. Let us not discuss about 
if that makes sense or not. It just shows for me that (ii) is simply not 
there.

They cannot be there, because if you could do everything you could have 
done with reflection in a jigsaw world, it means you can bypass the 
module system. As such it would counter the design goal to have stronger 
encapsulation.

And on the performance of compilers... that part you have to explain to me.

> There have also been many other changes made to ensure that Jigsaw
> supports other important aspects of dynamic programs. the Layer API in
> particular has been extended significantly to allow greater control over
> loading and linking of dynamically deployed modules, including support
> for undeploying and redeploying modular code (as required by e.g. EE
> containers). You might want to look over the archives of discussions on
> the JSR expert group, observers and comments mail lists to review all
> these proposed changes in full or maybe even sign up to the last of
> these lists in order to monitor progress on work that still remains to
> be completed.
>
>    http://mail.openjdk.java.net/pipermail/jpms-spec-experts/
>    http://mail.openjdk.java.net/pipermail/jpms-spec-observers/
>    http://mail.openjdk.java.net/pipermail/jpms-spec-comments/

How do layers help me in calling a method like Object#clone? I can see 
that if you are doing a container of sorts or module system like osgi or 
maybe the jboss modules, that there might be some benefit by jigsaw and 
that it may indeed give you more control in certain places.

But for a simple command line script this simply does not matter.

bye Jochen


More information about the jigsaw-dev mailing list