JDK-8066746 : MHs.explicitCastArguments does incorrect type checks for VarargsCollector
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 8u40,9
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2014-12-04
  • Updated: 2016-07-28
  • Resolved: 2014-12-09
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 JDK 9
8u40Fixed 9 b43Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_40-ea"
Java(TM) SE Runtime Environment (build 1.8.0_40-ea-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b18, mixed mode)


FULL OS VERSION :
Linux macnux 3.16.3-031603-generic #201409171435 SMP Wed Sep 17 18:36:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
First of all, reported on 8u40 but he bug occurs with JDK 1.8.0u40 and JDK 1.9.0b39.

It is a blocker for Groovy (invokedynamic version), that we test against the latest EAP releases. The test case attached runs fine on 8u20, but fails on the above versions with the following stack trace:

java.lang.ClassCastException: Cannot cast [Ljava.lang.String; to java.lang.String
	at java.lang.invoke.MethodHandleImpl.newClassCastException(MethodHandleImpl.java:365)
	at java.lang.invoke.MethodHandleImpl.castReference(MethodHandleImpl.java:360)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:625)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:215)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at TestScript0.getStringArrayIndirectlyWithType_Length(TestScript0.groovy:6)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:625)
	at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:215)
	at TestScript0.run(TestScript0.groovy:8)
	at groovy.lang.GroovyShell.evaluate(GroovyShell.java:589)
	at groovy.lang.GroovyShell.evaluate(GroovyShell.java:627)
	at groovy.lang.GroovyShell.evaluate(GroovyShell.java:608)
	at groovy.test.GroovyAssert.assertScript(GroovyAssert.java:80)
	at groovy.util.GroovyTestCase.assertScript(GroovyTestCase.java:200)

THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Did not try

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Did not try

REGRESSION.  Last worked in version 8u20

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Download https://dl.dropboxusercontent.com/u/20288797/bug_jdk9.tgz
2. tar xzvf bug_jdk9.tgz
3. cd bug_jdk9
4. ./gradlew test

The test will fail and generate a report with the exception above.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.ClassCastException: Cannot cast [Ljava.lang.String; to java.lang.String
	at java.lang.invoke.MethodHandleImpl.newClassCastException(MethodHandleImpl.java:365)
	at java.lang.invoke.MethodHandleImpl.castReference(MethodHandleImpl.java:360)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:625)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:215)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at TestScript0.getStringArrayIndirectlyWithType_Length(TestScript0.groovy:6)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:681)
	at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:625)
	at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:215)
	at TestScript0.run(TestScript0.groovy:8)
	at groovy.lang.GroovyShell.evaluate(GroovyShell.java:589)
	at groovy.lang.GroovyShell.evaluate(GroovyShell.java:627)
	at groovy.lang.GroovyShell.evaluate(GroovyShell.java:608)
	at groovy.test.GroovyAssert.assertScript(GroovyAssert.java:80)
	at groovy.util.GroovyTestCase.assertScript(GroovyTestCase.java:200)

REPRODUCIBILITY :
This bug can be reproduced always.


Comments
MHs.explicitCastArguments does not do arity conversion; it only does pairwise conversions. The term "pairwise" is used (here and in the doc for MH.asType) in contrast to the arity conversion performed by varargs collectors. Suggested fix: diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -2028,7 +2028,7 @@ MethodType oldType = target.type(); if (oldType == newType) return target; if (oldType.explicitCastEquivalentToAsType(newType)) { - return target.asType(newType); + return target.asFixedArity().asType(newType); } return MethodHandleImpl.makePairwiseConvert(target, newType, false); }
06-12-2014

Test case: static String[] f(String... args) { return args; } MethodType mt = MethodType.methodType(String[].class, String[].class); MethodHandle mh = MethodHandles.lookup().findStatic(ExplicitCastArgs.class, "f", mt); mh = MethodHandles.explicitCastArguments(mh, MethodType.methodType(void.class, Object.class)); mh.invokeWithArguments((Object)(new String[] { "str1", "str2"}));
06-12-2014

MHs.explicitCastArguments() doesn't take into account that the target can be a varargs collector (MethodHandleImpl$AsVarargsCollector). That's the case where the test fails: target: MethodHandle(TestScript0,String[])String[] class java.lang.invoke.MethodHandleImpl$AsVarargsCollector newType: (TestScript0,Object)Object
05-12-2014

The bug is in MethodType.explicitCastEquivalentToAsType(). It erroneously triggers more strict checks than necessary: src/java.base/share/classes/java/lang/invoke/MethodHandles.java: 2025 MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { 2026 explicitCastArgumentsChecks(target, newType); 2027 // use the asTypeCache when possible: 2028 MethodType oldType = target.type(); 2029 if (oldType == newType) return target; 2030 if (oldType.explicitCastEquivalentToAsType(newType)) { 2031 return target.asType(newType); 2032 } 2033 return MethodHandleImpl.makePairwiseConvert(target, newType, false); 2034 }
05-12-2014

ILW=HLH = P2 I=H: regression L=L: some special case in java.lang.invoke functionality W=H: none
05-12-2014

It seems to also work with 8u25. Indeed breaks with both 8u40 and 9.
05-12-2014

Moved to core-libs -> java.lang.invoke
05-12-2014