8167974: MethodHandles.iteratedLoop(...) fails with CCE in the case of iterating over array
authorpsandoz
Tue, 01 Nov 2016 17:29:48 -0700
changeset 41829 224facf0da6e
parent 41828 0436ea0c6099
child 41830 12d848e1dd2e
8167974: MethodHandles.iteratedLoop(...) fails with CCE in the case of iterating over array 8167966: MethodHandles.iteratedLoop fails with IAE in the case of correct arguments Reviewed-by: redestad
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/test/java/lang/invoke/LoopCombinatorTest.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Tue Nov 01 14:36:26 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Tue Nov 01 17:29:48 2016 -0700
@@ -5347,7 +5347,7 @@
      * {@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.
+     * The leading {@code A} type must be {@code Iterable} or a subtype thereof.
      * This conversion step, done at loop construction time, must not throw a {@code WrongMethodTypeException}.
      * </ul>
      * <p>
@@ -5374,7 +5374,8 @@
      * V iteratedLoop(A... a...) {
      *   Iterator<T> it = iterator(a...);
      *   V v = init(a...);
-     *   for (T t : it) {
+     *   while (it.hasNext()) {
+     *     T t = it.next();
      *     v = body(v, t, a...);
      *   }
      *   return v;
@@ -5483,49 +5484,59 @@
         Objects.requireNonNull(body);
         MethodType bodyType = body.type();
         Class<?> returnType = bodyType.returnType();
-        List<Class<?>> innerList = bodyType.parameterList();
+        List<Class<?>> internalParamList = bodyType.parameterList();
         // strip leading V value if present
         int vsize = (returnType == void.class ? 0 : 1);
-        if (vsize != 0 && (innerList.size() == 0 || innerList.get(0) != returnType)) {
+        if (vsize != 0 && (internalParamList.size() == 0 || internalParamList.get(0) != returnType)) {
             // argument list has no "V" => error
             MethodType expected = bodyType.insertParameterTypes(0, returnType);
             throw misMatchedTypes("body function", bodyType, expected);
-        } else if (innerList.size() <= vsize) {
+        } else if (internalParamList.size() <= vsize) {
             // missing T type => error
             MethodType expected = bodyType.insertParameterTypes(vsize, Object.class);
             throw misMatchedTypes("body function", bodyType, expected);
         }
-        //Class<?> elementType = innerList.get(vsize);  // do not need this
-        List<Class<?>> outerList = innerList.subList(vsize + 1, innerList.size());
-        if (outerList.isEmpty()) {
-            // special case; take lists from iterator handle
-            outerList = ((iterator != null)
-                    ? iterator.type().parameterList()
-                    : Arrays.asList(Iterable.class));
-            innerList = bodyType.insertParameterTypes(vsize + 1, outerList).parameterList();
-        }
+        List<Class<?>> externalParamList = internalParamList.subList(vsize + 1, internalParamList.size());
+        Class<?> iterableType = null;
         if (iterator != null) {
+            // special case; if the body handle only declares V and T then
+            // the external parameter list is obtained from iterator handle
+            if (externalParamList.isEmpty()) {
+                externalParamList = iterator.type().parameterList();
+            }
             MethodType itype = iterator.type();
             if (!Iterator.class.isAssignableFrom(itype.returnType())) {
                 throw newIllegalArgumentException("iteratedLoop first argument must have Iterator return type");
             }
-            if (!itype.effectivelyIdenticalParameters(0, outerList)) {
-                MethodType expected = methodType(itype.returnType(), outerList);
+            if (!itype.effectivelyIdenticalParameters(0, externalParamList)) {
+                MethodType expected = methodType(itype.returnType(), externalParamList);
                 throw misMatchedTypes("iterator parameters", itype, expected);
             }
+        } else {
+            if (externalParamList.isEmpty()) {
+                // special case; if the iterator handle is null and the body handle
+                // only declares V and T then the external parameter list consists
+                // of Iterable
+                externalParamList = Arrays.asList(Iterable.class);
+                iterableType = Iterable.class;
+            } else {
+                // special case; if the iterator handle is null and the external
+                // parameter list is not empty then the first parameter must be
+                // assignable to Iterable
+                iterableType = externalParamList.get(0);
+                if (!Iterable.class.isAssignableFrom(iterableType)) {
+                    throw newIllegalArgumentException(
+                            "inferred first loop argument must inherit from Iterable: " + iterableType);
+                }
+            }
         }
         if (init != null) {
             MethodType initType = init.type();
             if (initType.returnType() != returnType ||
-                    !initType.effectivelyIdenticalParameters(0, outerList)) {
-                throw misMatchedTypes("loop initializer", initType, methodType(returnType, outerList));
+                    !initType.effectivelyIdenticalParameters(0, externalParamList)) {
+                throw misMatchedTypes("loop initializer", initType, methodType(returnType, externalParamList));
             }
         }
-        Class<?> iterableType = outerList.isEmpty() ? null : outerList.get(0);
-        if (iterableType != null && !Iterable.class.isAssignableFrom(iterableType) && !iterableType.isArray()) {
-            throw newIllegalArgumentException(
-                    "inferred first loop argument must be an array or inherit from Iterable: " + iterableType);
-        }
         return iterableType;  // help the caller a bit
     }
 
--- a/jdk/test/java/lang/invoke/LoopCombinatorTest.java	Tue Nov 01 14:36:26 2016 -0700
+++ b/jdk/test/java/lang/invoke/LoopCombinatorTest.java	Tue Nov 01 17:29:48 2016 -0700
@@ -33,6 +33,7 @@
  * @bug 8153637
  * @bug 8154751
  * @bug 8154754
+ * @bug 8167974
  * @run testng/othervm -ea -esa test.java.lang.invoke.LoopCombinatorTest
  */
 
@@ -800,7 +801,8 @@
                 {l_it, l_i, isl_i, ""},
                 {l_it, null, sl_v, ""},
                 {li_it, li_i, isli_i, ""},
-                {il_it, null, sil_v, "inferred first loop argument must inherit from Iterable: int"},
+                {null, null, sil_v, "inferred first loop argument must inherit from Iterable: int"},
+                {il_it, null, sil_v, ""},
                 {li_it, null, sli_v, ""},
                 {sl_v, null, sl_v, "iteratedLoop first argument must have Iterator return type"},
                 {li_it, l_it, sl_v,