JDK-8167974 : MethodHandles.iteratedLoop(...) fails with CCE in the case of iterating over array
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2016-10-13
  • Updated: 2017-07-13
  • Resolved: 2016-11-02
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 9
9 b143Fixed
Related Reports
Relates :  
Description
According to MethodHandles.iteratedLoop(...) documentation's last constraint, following input is allowed:
iterator=null
init=()I
body=(I, String[])I

Please, observe two examples (Example0 and Example1) showing that loop run fails in this case. Note that loop handle was created successfully in both examples.

Thrown exception: Exception in thread "main" java.lang.ClassCastException: Cannot cast [Ljava.lang.String; to java.lang.Iterable

Was found on jdk9b140
Comments
The phrase "or an array type" is a leftover from an unimplemented intention. So is the use of enhanced-for loop in the pseudocode. Note that the psdeudocode is incorrect: The E-for loop uses an Iterable, not an Iterator. The iteratedLoop doesn't require an Iterable.
29-10-2016

Relevant section of iteratedLoop JavaDoc: * <li>As a special case, if the body contributes only {@code V} and {@code T} types, * with no additional {@code A} types, then the internal parameter list is extended by * the argument types {@code A...} of the {@code iterator} handle; if it is {@code null} the * single type {@code Iterable} is added and constitutes the {@code A...} list. ... * <li>If the {@code iterator} handle is non-{@code null}, it must have the return * type {@code java.util.Iterator} or a subtype thereof. * The iterator it produces when the loop is executed will be assumed * to yield values which can be converted to type {@code T}. * <li>The parameter list of an {@code iterator} that is non-{@code null} (of some form {@code (A*)}) must be * effectively identical to the external parameter list {@code (A...)}. * <li>If {@code iterator} is {@code null} it defaults to a method handle which behaves * like {@link java.lang.Iterable#iterator()}. In that case, the internal parameter list * {@code (V T A...)} must have at least one {@code A} type, and the default iterator * handle parameter is adjusted to accept the leading {@code A} type, as if by * the {@link MethodHandle#asType asType} conversion method. * The leading {@code A} type must be {@code Iterable} or a subtype thereof, or an array type. * This conversion step, done at loop construction time, must not throw a {@code WrongMethodTypeException}. For the case when the body only contributes V and T then an (A...) of consisting of Iterable is synthesized. Otherwise, an explicit (A...) must be declared with the leading A being Iterator or a subtype of, *or an array type*. Here are the three cases in code: // Passes static class SyntheticIteratorArgument { static void run() throws Throwable { MethodHandle handle = MethodHandles.iteratedLoop( null, // iterator null, // init MethodHandles.lookup().findStatic( SyntheticIteratorArgument.class, "body", MethodType.methodType(void.class, String.class)) ); handle.invoke(List.of("A", "B")); } static void body(/* void v, */ String t) { System.out.println(t); } } // Passes static class ExplicitIteratorArgument { static void run() throws Throwable { MethodHandle handle = MethodHandles.iteratedLoop( null, // iterator null, // init MethodHandles.lookup().findStatic( ExplicitIteratorArgument.class, "body", MethodType.methodType(void.class, String.class, List.class)) ); handle.invoke(List.of("A", "B")); } static void body(/* void v, */ String t, List<String> a) { System.out.println(t); } } // Fails static class ExplicitArrayArgument { static void run() throws Throwable { MethodHandle handle = MethodHandles.iteratedLoop( null, // iterator null, // init MethodHandles.lookup().findStatic( ExplicitArrayArgument.class, "body", MethodType.methodType(void.class, String.class, String[].class)) ); handle.invoke(new String[]{"A", "B"}); } static void body(/* void v, */ String t, String[] a) { System.out.println(t); } } The last case ExplicitArrayArgument is not implemented, which i think is an oversight, especially given that i suspect this loop combinator was intended to have parity with the Java for-each loop language feature. Support for array types can be supported by wrapping the array in an Iterator, which is easy for reference types (List.of(refArray).iterator()), but for primitive types it will require some boxing. The non-compiled implementation could use reflection to access array contents (to reduce code). The compiled implementation could be smarter, but might require more work, depending on how much Iterator is assumed throughout the code.
28-10-2016

Following tests will be excluded: api/java_lang/invoke/MethodHandles/index.html#IteratedLoopTests[testBodyUpdatesLoopState] api/java_lang/invoke/MethodHandles/index.html#IteratedLoopTests[testDefaultsForArrayIteration] api/java_lang/invoke/MethodHandles/index.html#IteratedLoopTests[testInitInitializesLoopState] api/java_lang/invoke/MethodHandles/index.html#IteratedLoopTests[testIteratedOverElements] api/java_lang/invoke/MethodHandles/index.html#IteratedLoopTests[testResultIsLastLocalState]
13-10-2016