JDK-8028129 : Type checking: signature polymorphic MethodHandle.invoke(Object[])
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 8
  • Priority: P2
  • Status: Resolved
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 2013-11-11
  • Updated: 2013-12-09
  • Resolved: 2013-11-20
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8Resolved
Related Reports
Relates :  
Relates :  
Description
Let's consider following code:

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;

interface MyFunctionalInterface {
    Object invokeMethodReference(Object... args) throws Throwable;
}

public class Test {
    public Object m(Object ... args) {
        System.out.println("m: " + Arrays.toString(args));
        return "m";
    }
    public static void main(String argv[]) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType mt = MethodType.methodType(String.class, char.class, char.class);
        MethodHandle ms = lookup.findVirtual(String.class, "replace", mt);

        System.out.println("result: " + ms.invoke("some string to search", 's', 'o'));

        MyFunctionalInterface methodRefSimple = new Test()::m;

        methodRefSimple.invokeMethodReference("first", "second");
        MyFunctionalInterface methodRefSignPoly = ms::invoke;
        System.out.println(methodRefSignPoly.invokeMethodReference("some string to search", 's', 'o'));
    }
}

it causes following output:

result: oome otring to oearch
m: [first, second]
Exception in thread "main" java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(String,char,char)String to (Object[])Object
	at java.lang.invoke.MethodHandle.asTypeUncached(MethodHandle.java:776)
	at java.lang.invoke.MethodHandle.asType(MethodHandle.java:770)
	at java.lang.invoke.Invokers.checkGenericType(Invokers.java:366)
	at Test$$Lambda$2/1761291320.invokeMethodReference(Unknown Source)
	at Test.main(Test.java:26)

The following line:

"System.out.println(methodRefSignPoly.invokeMethodReference("some string to search", 's', 'o'));"

causes java.lang.invoke.WrongMethodTypeException to be thrown.

JLS doesn't prohibit method references to signature polymorphic methods in any way. So I could believe it's a bug.

The code above is attached for your convenience.
Comments
We have identified some incorrect behavior in a different scenario in which the functional interface parameters are _not_ (Object[]). See JDK-8028739.
20-11-2013

The current behavior of the compiler, VM, and LambdaMetafactory is correct. See Lambda Spec, Part E, 15.28.3. "The body of the invocation method has the effect of invoking the compile-time declaration of the method reference" ... "the invocation arguments are the parameters of the invocation method." That is, the argument to MethodHandle.invoke in the original example is a single Object[], args. A program like the following has the same behavior: static Object invokeMethodReference(MethodHandle mh, Object... args) throws Throwable { return mh.invoke(args); } MethodHandle ms = lookup.findVirtual(String.class, "replace", mt); System.out.println(invokeMethodReference(ms, "some string to search", 's', 'o')); (The same comment applies to MethodHandle invocations appearing in lambda bodies -- it's no different than a MethodHandle invocation appearing in a method body.) That said, it would be helpful if the parameter/return types associated with 'invoke' by the compiler, as described in 15.12.3, were more clearly spelled out for method references. I've filed a separate spec bug for that: JDK-8028690.
20-11-2013

At lunch Alex raised the related (but subtler) question of what this means: foo( ()-> mh.invoke() ). The problem is with the target type of the call to mh.invoke. It's not enough to say "type variable erased to Object", since that's not compatible with any other type, such as either int or String. Another related question is this one: foo( a -> (String)mh.invoke(a ) ). Here the descriptor for mh.invoke will mention String as a return type, but what is the argument type? Again, "type variable erased to Object" is almost certainly the wrong answer, and "boxed varargs list new Object[]{a}" is even worse. If the above spec. question is open, then these may also be open.
15-11-2013

Dan, please check spec (fix?) and assign accordingly.
14-11-2013

The previous code example is a legal program using method handles that throws the correct WMTE. (The method handle is to String::replace, which is not a varargs method, and which therefore cannot be invoked on a boxed argument list.) So I doubt that it is a useful representation of the problem, other than showing how the back-end of the system throws a correct WMTE when presented with bad code from the front-end. The construct mh::invoke (where mh is a jli.MethodHandle) should fail to compile, because a signature-polymorphic method cannot be type-inferenced; it behaves like an overloaded method with an infinite number of overloadings. The javac frontend is looking at mh::invoke and seeing the placeholder native method, seeing that it is not overloaded (as a placeholder) and is requesting a binding to it. But this is not allowed for a source code processor. See: http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html Bytecode generators, including the compiler back end, are required to emit untransformed symbolic type descriptors for these methods. Tools which determine symbolic linkage are required to accept such untransformed descriptors, without reporting linkage errors. and: Since invokevirtual instructions can natively invoke method handles under any symbolic type descriptor, this reflective view conflicts with the normal presentation of these methods via bytecodes. Thus, these two native methods, when reflectively viewed by Class.getDeclaredMethod, may be regarded as placeholders only. As I said before, this appears to be a javac bug, and possibly an omission in the spec.
14-11-2013

This has nothing actually to do with method-references. Problem can be reproduced with the test below. Will change area, title, and assignee. import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.Arrays; public class T3 { public Object m(Object ... args) { System.out.println("m: " + Arrays.toString(args)); return "m"; } public static void main(String argv[]) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(String.class, char.class, char.class); MethodHandle ms = lookup.findVirtual(String.class, "replace", mt); System.out.println(imr(ms, "some string to search", 's', 'o')); } static Object imr(MethodHandle mh, Object... args) throws Throwable { return mh.invoke(args); } }
14-11-2013

The generated lambda class is also effectively identical (again, modulo class MH vs MethodHandle). The failure is MethodHandle processing. On the surface, I'm not seeing the method-reference specific nature of the problem. Will try to reproduce without it.
14-11-2013

Changed the component to core-libs. With the metafactory now the presumed villain and the 292 code the only other option.
14-11-2013

Changing: MyFunctionalInterface methodRefSignPoly = mh1::invoke; To: MH mh1 = new MH(); MyFunctionalInterface methodRefSignPoly = mh1::invoke; The resulting class file has effectively identical bootstrap methods, invokedynamic call, and invocation sequence. In other words, the problem does not appear to be in the class file, and thus not in the compiler.
14-11-2013

The problem is specific to MethodHandle. It does not impact other signature polymorphic references.
14-11-2013

Following JCK tests fail with error: "ClassCastException: Cannot cast...": lang/LMBD/lmbd144/lmbd14401m21/lmbd14401m21 lang/LMBD/lmbd144/lmbd14401m11/lmbd14401m11 lang/LMBD/lmbd144/lmbd14401m01/lmbd14401m01 The minimized test: import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; interface MyFunctionalInterface { Object invokeMethodReference(Object... args) throws Throwable; } public class Test4 { public static void main(String argv[]) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(String.class); MethodHandle ms = lookup.findVirtual(Integer.class, "toString", mt); System.out.println(ms.invoke(new Integer(23))); MyFunctionalInterface instance = ms::invoke; Object result = instance.invokeMethodReference(new Integer(23)); } } The output: 23 Exception in thread "main" java.lang.ClassCastException: Cannot cast [Ljava.lang.Object; to java.lang.Integer at sun.invoke.util.ValueConversions.newClassCastException(ValueConversions.java:461) at sun.invoke.util.ValueConversions.castReference(ValueConversions.java:456) at Test4$$Lambda$1/705265961.invokeMethodReference(Unknown Source) at Test4.main(Test4.java:17) Following line causes the error: "Object result = instance.invokeMethodReference(new Integer(23));" The test is attached for your convenience.
12-11-2013

Following JCK tests also fail with message: "WrongMethodTypeException: expected ... but found ...": lang/LMBD/lmbd144/lmbd14401m321/lmbd14401m321 lang/LMBD/lmbd144/lmbd14401m331/lmbd14401m331 lang/LMBD/lmbd144/lmbd14401m231/lmbd14401m231 lang/LMBD/lmbd144/lmbd14401m311/lmbd14401m311 lang/LMBD/lmbd144/lmbd14401m301/lmbd14401m301 lang/LMBD/lmbd144/lmbd14401m031/lmbd14401m031 lang/LMBD/lmbd144/lmbd14401m221/lmbd14401m221 lang/LMBD/lmbd144/lmbd14401m021/lmbd14401m021 lang/LMBD/lmbd144/lmbd14401m131/lmbd14401m131 lang/LMBD/lmbd144/lmbd14401m201/lmbd14401m201 lang/LMBD/lmbd144/lmbd14401m211/lmbd14401m211 lang/LMBD/lmbd144/lmbd14401m011/lmbd14401m011 lang/LMBD/lmbd144/lmbd14401m121/lmbd14401m121 lang/LMBD/lmbd144/lmbd14401m111/lmbd14401m111 lang/LMBD/lmbd144/lmbd14401m101/lmbd14401m101 lang/LMBD/lmbd144/lmbd14401m001/lmbd14401m001 Minimized tests causing such error: import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; interface MyFunctionalInterface { Object invokeMethodReference(Object... args) throws Throwable; } class MyClass { public MyClass add(String a, int b, long c) { return new MyClass(); } } public class Test3 { public static void main(String argv[]) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(MyClass.class, String.class, int.class, long.class); MethodHandle ms = lookup.findVirtual(MyClass.class, "add", mt); MyFunctionalInterface instance = (ms)::invokeExact; MyClass result2 = (MyClass)ms.invokeExact(new MyClass(), "b", 4, 5l); Object result = (MyClass)instance.invokeMethodReference(new MyClass(), "b", 4, 5); } } Output: Exception in thread "main" java.lang.invoke.WrongMethodTypeException: expected (MyClass,String,int,long)MyClass but found (Object[])Object at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:340) at java.lang.invoke.Invokers.checkExactType(Invokers.java:351) at Test3$$Lambda$1/705265961.invokeMethodReference(Unknown Source) at Test3.main(Test3.java:24) Following line causes the error: "Object result = (MyClass)instance.invokeMethodReference(new MyClass(), "b", 4, 5);" The minimized test is attached for your convenience.
12-11-2013

Following JCK tests also fail with a bit another message "java.lang.invoke.WrongMethodTypeException: cannot build collector": lang/LMBD/lmbd144/lmbd14401m33/lmbd14401m33 lang/LMBD/lmbd144/lmbd14401m23/lmbd14401m23 lang/LMBD/lmbd144/lmbd14401m03/lmbd14401m03 lang/LMBD/lmbd144/lmbd14401m13/lmbd14401m13 Minimized test: import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.Arrays; interface MyFunctionalInterface { Object invokeMethodReference(Object... args) throws Throwable; } class MyClass { public String add(String a, int b, long... cs) { return this.toString() + ", " + a + ", " + b + ", " + Arrays.toString(cs); } } public class Test2 { public static void main(String argv[]) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(String.class, String.class, int.class, long[].class); MethodHandle ms = lookup.findVirtual(MyClass.class, "add", mt); MyFunctionalInterface instance = (ms)::invoke; System.out.println(ms.invoke(new MyClass(), "b", 4, 5)); Object result = instance.invokeMethodReference(new MyClass(), "b", 4, 5); } } Output: MyClass@1517365b, b, 4, [5] Exception in thread "main" java.lang.invoke.WrongMethodTypeException: cannot build collector at java.lang.invoke.MethodHandleImpl$AsVarargsCollector.asTypeUncached(MethodHandleImpl.java:359) at java.lang.invoke.MethodHandle.asType(MethodHandle.java:770) at java.lang.invoke.Invokers.checkGenericType(Invokers.java:366) at Test2$$Lambda$1/783286238.invokeMethodReference(Unknown Source) at Test2.main(Test2.java:23) Caused by: java.lang.IllegalArgumentException: array length is not legal: -2 at java.lang.invoke.MethodHandleStatics.newIllegalArgumentException(MethodHandleStatics.java:112) at java.lang.invoke.MethodHandle.spreadArrayChecks(MethodHandle.java:906) at java.lang.invoke.MethodHandle.asCollectorChecks(MethodHandle.java:997) at java.lang.invoke.MethodHandle.asCollector(MethodHandle.java:986) at java.lang.invoke.MethodHandleImpl$AsVarargsCollector.asTypeUncached(MethodHandleImpl.java:356) ... 4 more The error is caused by line: "Object result = instance.invokeMethodReference(new MyClass(), "b", 4, 5);" The minimized test is attached for your convenience.
12-11-2013

This is P2 bug because it's a conformance issue; following JCK tests fail due to it: lang/LMBD/lmbd144/lmbd14401m32/lmbd14401m32 lang/LMBD/lmbd144/lmbd14401m3/lmbd14401m3 lang/LMBD/lmbd144/lmbd14401m22/lmbd14401m22 lang/LMBD/lmbd144/lmbd14401m2/lmbd14401m2 lang/LMBD/lmbd144/lmbd14401m02/lmbd14401m02 lang/LMBD/lmbd144/lmbd14401m12/lmbd14401m12 lang/LMBD/lmbd144/lmbd14401m1/lmbd14401m1 lang/LMBD/lmbd144/lmbd14401m0/lmbd14401m0
12-11-2013

javac is treating MethodHandle::invoke as if it were not S-P, which is an error. It may be a hole in the spec., or may be an implementation problem. The runtime is treating this correctly, so it's not a libraries or JVM compiler bug. It's either a javac bug or a spec. bug (or both).
11-11-2013

Georgiy, can you add justification (ILW) why do you think it's P2, please?
11-11-2013