creating proxies for interfaces with default methods
Jochen Theodorou
blackdrag at gmx.org
Wed May 25 20:12:05 UTC 2016
On 25.05.2016 20:08, Alex Buckley wrote:
> On 5/25/2016 12:22 AM, Jochen Theodorou wrote:
>> so in earlier mails to this list I described a bit the problems I got
>> into with the way Groovy produces proxies and handles default methods
>> for those.
>
> Pointer? The most relevant thread I could find was "Groovy with Jigsaw"
> from September 2015 but even it didn't talk much about proxies.
Look at "running Groovy on JDK9". There is a bit discussion about this,
but not really much yet, since I had first to rethink the situation. I
came to it because we hack the Lokup object to be able to do a
invokespecial invocation for default methods.... and this does not work
anymore in JDK9.... at least not in the way I did it.
>> I would like to see how the correct solution to this is supposed to
>> be....
>>
>> so we have code like m.sortReversed{a,b -> a.value <=> b.value}, where
>> sortReversed is getting a Comperator and will call reversed() on it.
>> This is not actual API, this is just an example! So far the logic has
>> been, that kind of lambda {a,b -> a.value <=> b.value}, is called for
>> every non-default, non-static method. If we are talking about functional
>> interfaces, there is only one such method. The problem now arises what
>> we are supposed to do if a default method is called.
>
> What is m, some collection of reflection objects for methods?
No, could be any collection or array. Groovy "adds" method to jdk
classes. The code above is to be understood as Groovy code with
similarities to Java code, but not a direct copy.
>> Dynamic proxies and invocation handlers won't do the job, as they do not
>> provide an implementation of the interface I can call the default method
>> on.
>
> Is this a general limitation of j.l.r.Proxy? At first glance it doesn't
> seem like you're hitting an accessibility issue due to Jigsaw.
I have not found a way to call a default method with reflection. I have
no instance of the interface in the Proxy, so what am I supposed to call
the method on? Java can use an invokespecial on the interface method,
but you cannot do an invokespecial with reflection (to me knowledge).
With MethodHandles you can in theory do that - it exceeds my knowledge
on what you actually call it, since there is no implementation. But then
again, what if the default method calls another method of the interface?
what is "this" there?
>> Which means I have to use LambdaMetaFactory or maybe
>> MethodHandleProxies. This may solve the problem...
>>
>> A second usage is for a kind of mocking. which means we have either a
>> map of lambdas, or a lambda which can accept any kind of argument
>> constellation...and now multiple non-static, non-default methods to
>> proxy. We did use dynamic proxies for that in the past. I do not see how
>> that can work with either of the three.
>>
>> So runtime generated classes is the only option left? Including all the
>> problems with class loaders....
>
> This doesn't jibe with your closing comments in the "Groovy with Jigsaw"
> thread, where you said "We use our own class loaders in several areas,
> as well as runtime generated classes. ... generating classes using a new
> classloader is the standard for us."
That´s because you do not know what tricks we have to leverage to get
what we want. There are basically 3 cases:
(1) scripts as classes, compiled at runtime
(2) callsite caching using the pre invokedynamic code
(3) proxies in certain cases
(1) is less a problem so far.. largely because we ignore modules for now
(2) will work only if "sun.reflect.MagicAccessorImpl" is accessible to
us.... I doubt that is still the case in JDK9. So this will essentially
run on reflection without runtime class generation for JDK9
(3) our own proxy generation code is not written to handle the case I am
describing here, these are written to catch all invocations and redirect
them to an dynamic implementation. The code can be extended to handle
default methods differently as option of course, but it also means this
proxy generator was not much in use in the past. I have problems
foreseeing if it will work properly in all cases the dynamic proxy
handles right now. I mean that already starts with the question of what
classloader to use or what the parent to the class loader should be that
is used.
Well, maybe I worry to much, since the unnamed modules read all modules....
but I wonder if it will every be possible to have a module written in
Groovy with private API, since the runtime still needs to access the
unexported parts.
bye Jochen
More information about the jigsaw-dev
mailing list