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
--- 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,