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


_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to