Hi,

I am wondering if people can help shed some light on the MHS.Lookup.unreflect 
implementation for the MethodHandle.invoke/invokeExact.

It's clear that one should not be able to operate reflectively on polysig 
methods and when doing do a USO is throw (as specified), but the implementation 
seems a little odd.


A call to the following succeeds:

  Method m = MethodHandle.class.getMethod("invokeExact", Object[].class);
  MethodHandle rmh = MethodHandles.lookup().unreflect(m);

The type of "rmh" is "(MethodHandle,Object[])Object", thus any 
"rmh.invokeExact" that does not match that type will fail with a 
WrongMethodTypeException.

A call to the following:

  Object o = rmh.invokeExact((MethodHandle) null, new Object[]{});

Will result in a:

  java.lang.UnsupportedOperationException: cannot reflectively invoke 
MethodHandle

However, the stack trace corresponds to the stack where the call to "unreflect" 
was performed and not where the invocation occurs.


The implementation caches the MethodHandle instances for invokeExact/invoke 
methods:

static
MethodHandle throwException(MethodType type) {
    assert(Throwable.class.isAssignableFrom(type.parameterType(0)));
    int arity = type.parameterCount();
    if (arity > 1) {
        MethodHandle mh = throwException(type.dropParameterTypes(1, arity));
        mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, 
arity));
        return mh;
    }
    return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, 
false, true);
}

static <T extends Throwable> Empty throwException(T t) throws T { throw t; }

static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2];
static MethodHandle fakeMethodHandleInvoke(MemberName method) {
    int idx;
    assert(method.isMethodHandleInvoke());
    switch (method.getName()) {
    case "invoke":       idx = 0; break;
    case "invokeExact":  idx = 1; break;
    default:             throw new InternalError(method.getName());
    }
    MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE[idx];
    if (mh != null)  return mh;
    MethodType type = MethodType.methodType(Object.class, 
UnsupportedOperationException.class,
                                            MethodHandle.class, Object[].class);
    mh = throwException(type);
    mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively 
invoke MethodHandle"));
    if (!method.getInvocationType().equals(mh.type()))
        throw new InternalError(method.toString());
    mh = mh.withInternalMemberName(method, false);
    mh = mh.asVarargsCollector(Object[].class);
    assert(method.isVarargs());
    FAKE_METHOD_HANDLE_INVOKE[idx] = mh;
    return mh;
}

Further it does "mh.withInternalMemberName(method, false)", that i cannot 
explain. Why do we need to re-associate the MH throwing the USO with the member 
name corresponding to the MH.invokeExact/invoke method?


Would it not be simpler if the implementation was something like:

public static MethodHandle unsupportedOperation(MethodType type, String 
message) throws Exception {
    MethodHandle mh = MethodHandles.lookup().findStatic(
            X.class, "throwUOE", MethodType.methodType(void.class, 
String.class));
    return MethodHandles.dropArguments(mh, 1, type.parameterList())
            .bindTo(message)
            .asType(type)
            .asVarargsCollector(Object[].class);
}

public static void throwUOE(String s) {
    throw new UnsupportedOperationException(s);
}

For such edge cases perhaps caching is not required.

?


The motivation behind the above is i need to provide similar behaviour for 
VarHandle polysig methods.

Thanks,
Paul.

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

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

Reply via email to