invoking a interface default method from within an InvocationHandler

Remi Forax forax at univ-mlv.fr
Thu Oct 9 19:19:09 UTC 2014


Thinking a little bit more about that, you can almost use a lambda proxy 
as a reflect proxy,
yes, almost because the lambda metafactory doesn't understand varargs.

class Main {
   public static void print(Object... args) {
     System.out.println(Arrays.toString(args));
   }

   public static void main(String[] args) {
     Consumer<Object> consumer = Main::print;
     consumer.accept("hello");
   }
}

here, the proxy that implements the Consumer doesn't call Main::print 
but a lambda that will call Main::print
which is rather ugly.

private static void lambda$MR$main$print$5c87cf9$1(java.lang.Object);
     Code:
        0: iconst_1
        1: anewarray     #8                  // class java/lang/Object
        4: dup
        5: iconst_0
        6: aload_0
        7: aastore
        8: invokestatic  #9                  // Method 
print:([Ljava/lang/Object;)V
       11: return

otherwise, this code works:
   private static Object trampoline(InvocationHandler handler, Object 
proxy, Method method, Object arg) throws Throwable {
     return handler.invoke(proxy, method, new Object[]{ arg });
   }

   public static void main(String[] args) throws Throwable {
     InvocationHandler handler = (Object proxy, Method method, Object[] 
array) -> {
       System.out.println(method);
       return null;
     };

     /* create a mh on InvocationHandler::invoke, sadly it doesn't work :(
     MethodHandle  implMethod = 
MethodHandles.publicLookup().findVirtual(InvocationHandler.class, "invoke",
         MethodType.methodType(Object.class, Object.class, Method.class, 
Object[].class));
     implMethod = implMethod.asVarargsCollector(Object[].class);
     */

     // create a mh on the trampoline !
     MethodHandle  implMethod = 
MethodHandles.lookup().findStatic(CallingADefaultMethodInAProxy.class, 
"trampoline",
         MethodType.methodType(Object.class, InvocationHandler.class, 
Object.class, Method.class, Object.class));

     Method method = Arrays.stream(Consumer.class.getMethods())
         .filter(m -> !m.isDefault())
         .findFirst()
         .get();
     MethodType mType = MethodType.methodType(method.getReturnType(), 
method.getParameterTypes());

     // ask for a lambda proxy
     CallSite cs = LambdaMetafactory.metafactory(MethodHandles.lookup(), 
"accept",
         MethodType.methodType(Consumer.class, InvocationHandler.class, 
Object.class, Method.class),
         mType,
         implMethod,
         mType);
     Consumer<Object> consumer = 
(Consumer<Object>)cs.getTarget().invokeExact(handler, (Object)null, method);

     consumer.accept("foo");
   }

cheers,
Rémi

On 10/09/2014 07:30 PM, Jochen Theodorou wrote:
> Am 09.10.2014 19:07, schrieb Remi Forax:
>> 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 :)
>
> lol.
>
> The other variant is to get the Lookup constructor accepting an int, 
> to make private level access possible. But is that really supposed to 
> be a standard solution? I mean I could then use Unsafe too ;)
>
>> 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.
>
> yes, I found the same thing strange... though given the special nature 
> of invokespecial I was thinking that this limitation is there to 
> ensure this "call has to be done from same class or subclass" logic. 
> So I would expect for example, that if I do 
> MethodHandles.lookup().in(Foo.class), while being in Foo or a subclass 
> that it would work. But of course, that is of zero use if Foo is an 
> interface and whole purpose of the exercise is to have a proxy that 
> acts as that interface instead of having to implement the interface 
> yourself. But I did not actually test if my assumption is right.
>
> bye Jochen
>



More information about the mlvm-dev mailing list