On 09.07.2017 13:13, Uwe Schindler wrote:
Hi,

thanks for the insight. I was just not sure where this synthetic class name came from.

I think we should ask on the Jigsaw mailing list if the warning was intended to be also printed by the trySetAccessible call. IMHO, trySetAccessible is a new API, so it should **not** grant access nor print a warning. The Jigsaw kill switch should have no effect on trySetAccessible! In contrast, setAccessible should behave as it does with the Jigsaw kill switch enabled.

maybe they did it wrong, since they changed the kill switch suddenly.

Nevertheless, I’d change the code in the following way: Instead of trySetAccessible method handle, use a methodhandle to get the Module instance of the Groovy runtime (fetch it once in the static initializer -- should be the unnamed module, but we should be flexible here) and when trying to make everything accessible in a class, use the same methodhandle to get the module, too. If the module if different, don’t try to make anything accessible at all and just return with empty array. In the future we may try improve the whole thing my looking into the module properties (“open” packages), but as a quick fix this is fine.

well... let us assume you have a class

class IAmInJavaBase {
  final protected void callMe()
}

in java.base and this

class GroovyWithModules extends IAmInJavaBase {
  void foo(){callMe()}
}

The Groovy runtime will no longer be allowed to call "callMe", because while the unnamed module reads java.base, it does not make java.base automatically an open module. And even if IAmInJavaBase is in an exported package, that does not include "callMe"

The two ways to make this call work is by using the Lookup object provided by GroovyWithModules and a direct method call. Even if you get that right, you still have to handle inner classes and Closures. (Technically you can do this with reflection as well, but then the invokeMethod has to come from GroovyWithModules directly)

Now if you stop trying to make things accessible and just return an empty array, then "callMe" will not exist anymore. That means, that even in indy, where you have this lookup object, you will get a MissingMethodException.

If you go with the approach of reacting to AccessibleObject.canAccess returning true... I would be very surprised to see canAccess returning true for that method.

the major pain with the central invoker simply is, that if X can access foo and Y cannot access foo, you have trouble to make a system in which both are allowed to make their call... especially if you do not know by 100% who the original caller actually is.

[...]
Finally I have the question: Is it really needed to keep trying to make everything accessible by default in Groovy 3 ???

The big problem is giving up the central invoker pattern completely. It also means dragging Lookup instances through the whole MOP to finally reach the place of invocation... or to change the whole MOP to return something you can invoke.

My original plan for MOP2 was actually trying to change the MOP to always return something you can then invoke. But...

class GroovyWithModules extends IAmInJavaBase {
  void foo(){
    def cl = new Helper().build()
    cl.setDelegate(this)
    cl.call()
  }
}

class Helper {
  def build() {
    return { callMe () }
  }
}

new GroovyWithModules().foo()

This code works in Groovy today, but with modules... So even though I do let build return something that can call callMe() I still have to give it the rights to do so. So either I transport the Lookup object through the whole MOP and let it for example return a MethodHandle, or I create another intermediate layer here that I can control from the caller and that then will produce the MethodHandle with the appropriate Lookup object. So I do have to at least combine the two approaches now.

And frankly, in the code above I use Helper just as an demonstration of what the runtime would have to do. If I actually want to get the above working I am pretty screwed. In { callMe () } the best lookup object I can automatically use is the one from Helper, and that one is not having access.

I could introduce something like:

class Helper {
  def build(x) {
    return { inTheNameOf(x) {callMe ()} }
  }
}

A where x is actually a GroovyWithModules instance with very special logic for inTheNameOf, which would basically try to somehow get the Lookup object with enough rights from x to be able to do the call. This looks majorly awkward and you actually rip open all the holes, the module system is supposed to keep under the covers. In the end the whole concept of setting a delegate is now broken for the module system. Because in the end, each of our Closures are specialized central invoker. And something like x."$foo"() takes the specialization away completely. Since central invoker do not work properly anymore in JDK9, Closures will also produce trouble in some cases.

Maybe you should break backwards compatibility and no longer do this by default.

You know, if breaking the API would be our only problem here, it wouldn't be a big deal. Having to overthrow basic concepts of the language is the kind of backwards compatibility break we are talking about here.

This was IMHO a mis-design of the Groovy language from the beginning.

It was the way to do it back in 2004 with a security manager. And the security manager mechanism almost forced this structure upon you.

This is also a security issue, as every groovy module can corrupt the whole JVM. This is also one reason why Elasticsearch moved to their own scripting language, as the cowboy “setAccessible” everywhere is a serious security issue!

Yes, they do not know how to use a security manager or simply do not want to bother with one.

bye Jochen

Reply via email to