The curious case of MHS.Lookup.unreflect on MethodHandle.invoke/invokeExact

Paul Sandoz paul.sandoz at oracle.com
Fri Mar 13 10:00:55 UTC 2015


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.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 841 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <http://mail.openjdk.java.net/pipermail/mlvm-dev/attachments/20150313/38eb76f3/signature-0001.asc>


More information about the mlvm-dev mailing list