invoking a interface default method from within an InvocationHandler

Remi Forax forax at univ-mlv.fr
Thu Oct 16 10:43:45 UTC 2014


On 10/15/2014 06:54 PM, Paul Sandoz wrote:
> Hi Remi,
>
> I did some brief evaluation of this area.
>
> MethodHandleProxies.asInterfaceInstance currently does not support proxying to default methods. An InternalError will be thrown if a default method is invoked. It should be possible to fix using a trusted internal IMPL_LOOKUP to look up MHs that invokespecial. I believe this should be safe under the context of proxying.

Another solution is to do not rely on j.l.r.Proxy to implement 
asInterfaceInstance but to generate a proxy using ASM,
in that case, handling default methods is easy, just do nothing (i.e. do 
not override a default method).
It will also solve the fact that the proxies generated by 
asInterfaceInstance are currently super slow
because of the InvocationHandler API require boxing.

The real solution in my opinion is to create yet another API, lets call 
it proxy 2.0 that takes an interface and a bootstrap method
as parameter and generate a class that will call the BSM when calling a 
method of the proxy class the first time
(the BSM will be called with a 4th parameter which is a method handle 
corresponding to the called method
so one can use MHs.reflectAs to get the corresponding j.l.r.Method 
object if necessary).
For a default method, the 4th parameter will be a method handle created 
with findSuper, so a user can choose
to implement it or to delegate to this mh.
And asInterfaceInstance is just a foldArguments between the method 
handle taken as parameter and
an exactInvoker (or a genericInvoker if parameter types mismatch).

Compared to a j.l.r.Proxy, the proxy 2.0 API also need a way to store 
fields (or at least values) inside the generated proxy class
and a way to query that fields inside the BSM. This can be done exactly 
like this is done by the lambda proxy,
instead of returning a proxy class, the proxy 2.0 API will return a 
method handle that acts as a factory for creating
proxy instances of the proxy class (if the factory takes no arguments, 
as for the lambda proxy, the same constant object can be returned).
To get a mh getter from inside the BSM, because the BSM already have the 
lookup object, all you need is a convention that says
that the field names are something like arg0, arg1, etc.

The nice thing about this API is that it cleanly separate the 
information that can be process from proxied interface(s)
(by example, JavaEE annotations) and the ones, more dynamic, that are 
specific to a proxy instance.
It also removes one level of indirection compared to the 
InvocationHandler proxy.

>
> The InvocationHandler case is a little more tricky (as you show below). There could be a static method on MethodHandleProxies that returns an InvocationHandler that if proxying to default methods gives the option to continue with the default or not. The InvocationHandler, on invocation, must verify that it's arguments are valid before proxying to a default method i.e. p is an instance of a proxy class. Under such circumstances i believe that should be safe.
>
> Paul.

Rémi

>
> On Oct 9, 2014, at 7:07 PM, Remi Forax <forax at univ-mlv.fr> wrote:
>
>> public static void main(String[] args) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
>>     Lookup lookup = MethodHandles.publicLookup().in(Consumer.class);
>>     Field allowedModes = Lookup.class.getDeclaredField("allowedModes");
>>     allowedModes.setAccessible(true);
>>     allowedModes.set(lookup, Modifier.PRIVATE);
>>
>>     @SuppressWarnings("unchecked")
>>     Consumer<Object> consumer = (Consumer<Object>)Proxy.newProxyInstance(
>>         CallingADefaultMethodInAProxy.class.getClassLoader(),
>>         new Class<?>[]{Consumer.class},
>>         (Object proxy, Method method, Object[] array) -> {
>>             if (method.isDefault()) {
>>               MethodHandle mh = lookup.unreflectSpecial(method, Consumer.class);
>>               return mh.invokeWithArguments(Stream.concat(Stream.of(proxy), Arrays.stream(array)).toArray());
>>             }
>>             System.out.println("hello");
>>             return null;
>>         });
>>
>>     consumer.andThen(System.out::println).accept("default method");
>>   }
>>
>> Not very pretty, if someone ask me I will deny to have written that code :)
>>
>> John, I've discovered that findSpecial/unreflectSpecial doesn't honor setAccessible,
>> given that the whole point of unreflectSpecial is to see a virtual call as a super call,
>> it looks like a bug to me.
>>
>> Rémi
>>
>> On 10/09/2014 04:42 PM, Jochen Theodorou wrote:
>>> too bad no-one knows. Has anyone an idea for a better place to ask this? (btw, because of the getSimpleName issue I can't use MethodHandleProxies)
>>>
>>> Am 06.10.2014 18:06, schrieb Jochen Theodorou:
>>>> Hi,
>>>>
>>>> I find this a little odd and I wonder how you are supposed to do it
>>>> right. Or if that is a bug.
>>>>
>>>> So I have a class implementing InvocationHandler and I used Proxy to
>>>> create a an proxied instance of Consumer. This is a functional interface
>>>> and I want to use its accept method for my purposes, while still keeping
>>>> the old andThen method as it is.
>>>>
>>>> going by the usual signature for the InvocationHandler implementation
>>>> main method (public Object invoke(Object proxy, Method method, Object[]
>>>> args) throws Throwable)
>>>>
>>>> I can do neither method.invoke on this, since that leads to that method
>>>> (overflow then) again, nor can I take the proxy object, since it
>>>> obviously has nothing to do with functional interface.
>>>>
>>>> Now... is that really on purpose? Are you indeed forced to split up your
>>>> code into a java7+ and pre java7 case to be able to use
>>>> MethodHandleProxies#asInterfaceInstance where it is available instead?
>>>> Shouldn't Proxy and InvocationHandler then be marked deprecated in
>>>> Java8? And how do you use MethodHandleProxies to implement multiple
>>>> interfaces?
>>>>
>>>> bye Jochen
>>>>
>>>
>> _______________________________________________
>> mlvm-dev mailing list
>> mlvm-dev at openjdk.java.net
>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>
>
> _______________________________________________
> mlvm-dev mailing list
> mlvm-dev at openjdk.java.net
> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/mlvm-dev/attachments/20141016/e0fbfb29/attachment.html>


More information about the mlvm-dev mailing list