--- old/src/java.base/share/classes/java/lang/invoke/MethodHandle.java 2015-11-19 22:13:59.000000000 +0100 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandle.java 2015-11-19 22:13:59.000000000 +0100 @@ -872,13 +872,54 @@ * @see #asCollector */ public MethodHandle asSpreader(Class arrayType, int arrayLength) { - MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength); - int arity = type().parameterCount(); - int spreadArgPos = arity - arrayLength; + return asSpreader(type().parameterCount() - arrayLength, arrayType, arrayLength); + } + + /** + * Makes an array-spreading method handle, which accepts an array argument at a given position and spreads + * its elements as positional arguments in place of the array. The new method handle adapts, as its target, + * the current method handle. The type of the adapter will be the same as the type of the target, except that the + * {@code arrayLength} parameters of the target's type, starting at the zero-based position {@code spreadArgPos}, + * are replaced by a single array parameter of type {@code arrayType}. + *

+ * This method behaves very much like {@link #asSpreader(Class, int)}, but accepts an additional {@code spreadArgPos} + * argument to indicate at which position in the parameter list the spreading should take place. + *

+ * @apiNote Example: + *

{@code
+    MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
+    MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
+    Object[] ints = new Object[]{3, 9, 7, 7};
+    Comparator cmp = (a, b) -> a - b;
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
+     * }
+ * @param spreadArgPos the position (zero-based index) in the argument list at which spreading should start. + * @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments + * @param arrayLength the number of arguments to spread from an incoming array argument + * @return a new method handle which spreads an array argument at a given position, + * before calling the original method handle + * @throws NullPointerException if {@code arrayType} is a null reference + * @throws IllegalArgumentException if {@code arrayType} is not an array type, + * or if target does not have at least + * {@code arrayLength} parameter types, + * or if {@code arrayLength} is negative, + * or if {@code spreadArgPos} has an illegal value (negative, or together with arrayLength exceeding the + * number of arguments), + * or if the resulting method handle's type would have + * too many parameters + * @throws WrongMethodTypeException if the implied {@code asType} call fails + * + * @see #asSpreader(Class, int) + * @since 9 + */ + public MethodHandle asSpreader(int spreadArgPos, Class arrayType, int arrayLength) { + MethodType postSpreadType = asSpreaderChecks(arrayType, spreadArgPos, arrayLength); MethodHandle afterSpread = this.asType(postSpreadType); BoundMethodHandle mh = afterSpread.rebind(); LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength); - MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType); + MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, spreadArgPos + arrayLength, arrayType); return mh.copyWith(preSpreadType, lform); } @@ -886,15 +927,18 @@ * See if {@code asSpreader} can be validly called with the given arguments. * Return the type of the method handle call after spreading but before conversions. */ - private MethodType asSpreaderChecks(Class arrayType, int arrayLength) { + private MethodType asSpreaderChecks(Class arrayType, int pos, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs < arrayLength || arrayLength < 0) throw newIllegalArgumentException("bad spread array length"); + if (pos < 0 || pos + arrayLength > nargs) { + throw newIllegalArgumentException("bad spread position"); + } Class arrayElement = arrayType.getComponentType(); MethodType mtype = type(); boolean match = true, fail = false; - for (int i = nargs - arrayLength; i < nargs; i++) { + for (int i = pos; i < arrayLength; i++) { Class ptype = mtype.parameterType(i); if (ptype != arrayElement) { match = false; @@ -905,7 +949,7 @@ } } if (match) return mtype; - MethodType needType = mtype.asSpreaderType(arrayType, arrayLength); + MethodType needType = mtype.asSpreaderType(arrayType, pos, arrayLength); if (!fail) return needType; // elicit an error: this.asType(needType); @@ -998,10 +1042,53 @@ * @see #asVarargsCollector */ public MethodHandle asCollector(Class arrayType, int arrayLength) { - asCollectorChecks(arrayType, arrayLength); - int collectArgPos = type().parameterCount() - 1; + return asCollector(type().parameterCount() - 1, arrayType, arrayLength); + } + + /** + * Makes an array-collecting method handle, which accepts a given number of positional arguments starting + * at a given position, and collects them into an array argument. The new method handle adapts, as its + * target, the current method handle. The type of the adapter will be the same as the type of the target, + * except that the parameter at the position indicated by {@code collectArgPos} (usually of type {@code arrayType}) + * is replaced by {@code arrayLength} parameters whose type is element type of {@code arrayType}. + *

+ * This method behaves very much like {@link #asCollector(Class, int)}, but differs in that its {@code + * collectArgPos} argument indicates at which position in the parameter list arguments should be collected. This + * index is zero-based. + *

+ * @apiNote Examples: + *

{@code
+    StringWriter swr = new StringWriter();
+    MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr);
+    MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
+    swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
+    assertEquals("BC", swr.toString());
+    swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
+    assertEquals("BCPQRS", swr.toString());
+    swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
+    assertEquals("BCPQRSZ", swr.toString());
+     * }
+ * @param collectArgPos the zero-based position in the parameter list at which to start collecting. + * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments + * @param arrayLength the number of arguments to collect into a new array argument + * @return a new method handle which collects some arguments + * into an array, before calling the original method handle + * @throws NullPointerException if {@code arrayType} is a null reference + * @throws IllegalArgumentException if {@code arrayType} is not an array type + * or {@code arrayType} is not assignable to this method handle's array parameter type, + * or {@code arrayLength} is not a legal array size, + * or {@code collectArgPos} has an illegal value (negative, or greater than the number of arguments), + * or the resulting method handle's type would have + * too many parameters + * @throws WrongMethodTypeException if the implied {@code asType} call fails + * + * @see #asCollector(Class, int) + * @since 9 + */ + public MethodHandle asCollector(int collectArgPos, Class arrayType, int arrayLength) { + asCollectorChecks(arrayType, collectArgPos, arrayLength); BoundMethodHandle mh = rebind(); - MethodType resultType = type().asCollectorType(arrayType, arrayLength); + MethodType resultType = type().asCollectorType(arrayType, collectArgPos, arrayLength); MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength); LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray); if (lform != null) { @@ -1015,15 +1102,18 @@ * See if {@code asCollector} can be validly called with the given arguments. * Return false if the last parameter is not an exact match to arrayType. */ - /*non-public*/ boolean asCollectorChecks(Class arrayType, int arrayLength) { + /*non-public*/ boolean asCollectorChecks(Class arrayType, int pos, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); + if (pos < 0 || pos >= nargs) { + throw newIllegalArgumentException("bad collect position"); + } if (nargs != 0) { - Class lastParam = type().parameterType(nargs-1); - if (lastParam == arrayType) return true; - if (lastParam.isAssignableFrom(arrayType)) return false; + Class param = type().parameterType(pos); + if (param == arrayType) return true; + if (param.isAssignableFrom(arrayType)) return false; } - throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType); + throw newIllegalArgumentException("array type not assignable to argument", this, arrayType); } /** @@ -1178,7 +1268,7 @@ */ public MethodHandle asVarargsCollector(Class arrayType) { Objects.requireNonNull(arrayType); - boolean lastMatch = asCollectorChecks(arrayType, 0); + boolean lastMatch = asCollectorChecks(arrayType, type().parameterCount() - 1, 0); if (isVarargsCollector() && lastMatch) return this; return MethodHandleImpl.makeVarargsCollector(this, arrayType); @@ -1341,7 +1431,6 @@ // cannot be cracked into MethodHandleInfo. assert viewAsTypeChecks(newType, strict); BoundMethodHandle mh = rebind(); - assert(!((MethodHandle)mh instanceof DirectMethodHandle)); return mh.copyWith(newType, mh.form); } --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2015-11-19 22:14:01.000000000 +0100 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2015-11-19 22:14:00.000000000 +0100 @@ -27,16 +27,17 @@ import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; +import java.util.List; import java.util.function.Function; +import java.util.stream.Collectors; import sun.invoke.empty.Empty; import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; -import jdk.internal.HotSpotIntrinsicCandidate; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; import static java.lang.invoke.LambdaForm.*; @@ -1297,7 +1298,7 @@ @Override public MethodHandle asCollector(Class arrayType, int arrayLength) { if (intrinsicName == Intrinsic.IDENTITY) { - MethodType resultType = type().asCollectorType(arrayType, arrayLength); + MethodType resultType = type().asCollectorType(arrayType, type().parameterCount() - 1, arrayLength); MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength); return newArray.asType(resultType); } @@ -1619,17 +1620,251 @@ } } + /** + * Assembles a loop method handle from the given handles and type information. This works by binding and configuring + * the {@linkplain #looper(MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], int, int, Object[]) "most + * generic loop"}. + * + * @param tloop the return type of the loop. + * @param targs types of the arguments to be passed to the loop. + * @param tvars types of loop-local variables. + * @param init sanitized array of initializers for loop-local variables. + * @param step sanitited array of loop bodies. + * @param pred sanitized array of predicates. + * @param fini sanitized array of loop finalizers. + * + * @return a handle that, when invoked, will execute the loop. + */ + static MethodHandle makeLoop(Class tloop, List> targs, List> tvars, List init, + List step, List pred, List fini) { + MethodHandle[] ainit = toArrayArgs(init); + MethodHandle[] astep = toArrayArgs(step); + MethodHandle[] apred = toArrayArgs(pred); + MethodHandle[] afini = toArrayArgs(fini); + + MethodHandle l = getConstantHandle(MH_looper); + + // Bind the statically known arguments. + l = MethodHandles.insertArguments(l, 0, ainit, astep, apred, afini, tvars.size(), targs.size()); + + // Turn the args array into an argument list. + l = l.asCollector(Object[].class, targs.size()); + + // Finally, make loop type. + MethodType loopType = MethodType.methodType(tloop, targs); + l = l.asType(loopType); + + return l; + } + + /** + * Converts all handles in the {@code hs} array to handles that accept an array of arguments. + * + * @param hs method handles to be converted. + * + * @return the {@code hs} array, with all method handles therein converted. + */ + static MethodHandle[] toArrayArgs(List hs) { + return hs.stream().map(h -> h.asSpreader(Object[].class, h.type().parameterCount())).toArray(MethodHandle[]::new); + } + + /** + * This method embodies the most generic loop for use by {@link MethodHandles#loop(MethodHandle[][])}. A handle on + * it will be transformed into a handle on a concrete loop instantiation by {@link #makeLoop}. + * + * @param init loop-local variable initializers. + * @param step bodies. + * @param pred predicates. + * @param fini finalizers. + * @param varSize number of loop-local variables. + * @param nArgs number of arguments passed to the loop. + * @param args arguments to the loop invocation. + * + * @return the result of executing the loop. + */ + static Object looper(MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini, + int varSize, int nArgs, Object[] args) throws Throwable { + Object[] varsAndArgs = new Object[varSize + nArgs]; + for (int i = 0, v = 0; i < init.length; ++i) { + if (init[i].type().returnType() == void.class) { + init[i].invoke(args); + } else { + varsAndArgs[v++] = init[i].invoke(args); + } + } + System.arraycopy(args, 0, varsAndArgs, varSize, nArgs); + final int nSteps = step.length; + for (; ; ) { + for (int i = 0, v = 0; i < nSteps; ++i) { + MethodHandle p = pred[i]; + MethodHandle s = step[i]; + MethodHandle f = fini[i]; + if (s.type().returnType() == void.class) { + s.invoke(varsAndArgs); + } else { + varsAndArgs[v++] = s.invoke(varsAndArgs); + } + if (!(boolean) p.invoke(varsAndArgs)) { + return f.invoke(varsAndArgs); + } + } + } + } + + /** + * This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, + * MethodHandle) counting loops}. + * + * @param counter the counter parameter, passed in during loop execution. + * @param limit the upper bound of the parameter, statically bound at loop creation time. + * + * @return whether the counter has reached the limit. + */ + static boolean countedLoopPredicate(int counter, int limit) { + return counter <= limit; + } + + /** + * This method is bound as the step function in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, + * MethodHandle) counting loops} to increment the counter. + * + * @param counter the loop counter. + * + * @return the loop counter incremented by 1. + */ + static int countedLoopStep(int counter, int limit) { + return counter + 1; + } + + /** + * This is bound to initialize the loop-local iterator in {@linkplain MethodHandles#iteratedLoop iterating loops}. + * + * @param it the {@link Iterable} over which the loop iterates. + * + * @return an {@link Iterator} over the argument's elements. + */ + static Iterator initIterator(Iterable it) { + return it.iterator(); + } + + /** + * This method is bound as the predicate in {@linkplain MethodHandles#iteratedLoop iterating loops}. + * + * @param it the iterator to be checked. + * + * @return {@code true} iff there are more elements to iterate over. + */ + static boolean iteratePredicate(Iterator it) { + return it.hasNext(); + } + + /** + * This method is bound as the step for retrieving the current value from the iterator in {@linkplain + * MethodHandles#iteratedLoop iterating loops}. + * + * @param it the iterator. + * + * @return the next element from the iterator. + */ + static Object iterateNext(Iterator it) { + return it.next(); + } + + /** + * Makes a {@code try-finally} handle that conforms to the type constraints. + * + * @param target the target to execute in a {@code try-finally} block. + * @param cleanup the cleanup to execute in the {@code finally} block. + * @param type the result type of the entire construct. + * @param argTypes the types of the arguments. + * + * @return a handle on the constructed {@code try-finally} block. + */ + static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Class type, List> argTypes) { + MethodHandle tf = getConstantHandle(type == void.class ? MH_tryFinallyVoidExec : MH_tryFinallyExec); + + // Bind the statically known arguments. + tf = MethodHandles.insertArguments(tf, 0, target, cleanup); + + // Turn the args array into an argument list. + tf = tf.asCollector(Object[].class, argTypes.size()); + + // Finally, make try-finally type. + MethodType tfType = MethodType.methodType(type, argTypes); + tf = tf.asType(tfType); + + return tf; + } + + /** + * A method that will be bound during construction of a {@code try-finally} handle with non-{@code void} return type + * by {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}. + * + * @param target the handle to wrap in a {@code try-finally} block. This will be bound. + * @param cleanup the handle to run in any case before returning. This will be bound. + * @param args the arguments to the call. These will remain as the argument list. + * + * @return whatever the execution of the {@code target} returned (it may have been modified by the execution of + * {@code cleanup}). + * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be + * passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit. + */ + static Object tryFinallyExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable { + Throwable t = null; + Object r = null; + try { + r = target.invoke(args); + } catch (Throwable thrown) { + t = thrown; + throw t; + } finally { + r = cleanup.invoke(t, r, args); + } + return r; + } + + /** + * A method that will be bound during construction of a {@code try-finally} handle with {@code void} return type by + * {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}. + * + * @param target the handle to wrap in a {@code try-finally} block. This will be bound. + * @param cleanup the handle to run in any case before returning. This will be bound. + * @param args the arguments to the call. These will remain as the argument list. + * + * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be + * passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit. + */ + static void tryFinallyVoidExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable { + Throwable t = null; + try { + target.invoke(args); + } catch (Throwable thrown) { + t = thrown; + throw t; + } finally { + cleanup.invoke(t, args); + } + } + // Indexes into constant method handles: - private static final int + static final int MH_cast = 0, MH_selectAlternative = 1, MH_copyAsPrimitiveArray = 2, MH_fillNewTypedArray = 3, MH_fillNewArray = 4, MH_arrayIdentity = 5, - MH_LIMIT = 6; + MH_looper = 6, + MH_countedLoopPred = 7, + MH_countedLoopStep = 8, + MH_iteratePred = 9, + MH_initIterator = 10, + MH_iterateNext = 11, + MH_tryFinallyExec = 12, + MH_tryFinallyVoidExec = 13, + MH_LIMIT = 14; - private static MethodHandle getConstantHandle(int idx) { + static MethodHandle getConstantHandle(int idx) { MethodHandle handle = HANDLES[idx]; if (handle != null) { return handle; @@ -1672,6 +1907,31 @@ return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative", MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)), Intrinsic.SELECT_ALTERNATIVE); + case MH_looper: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "looper", MethodType.methodType(Object.class, + MethodHandle[].class, MethodHandle[].class, MethodHandle[].class, MethodHandle[].class, + int.class, int.class, Object[].class)); + case MH_countedLoopPred: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopPredicate", + MethodType.methodType(boolean.class, int.class, int.class)); + case MH_countedLoopStep: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep", + MethodType.methodType(int.class, int.class, int.class)); + case MH_iteratePred: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate", + MethodType.methodType(boolean.class, Iterator.class)); + case MH_initIterator: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator", + MethodType.methodType(Iterator.class, Iterable.class)); + case MH_iterateNext: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext", + MethodType.methodType(Object.class, Iterator.class)); + case MH_tryFinallyExec: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyExecutor", + MethodType.methodType(Object.class, MethodHandle.class, MethodHandle.class, Object[].class)); + case MH_tryFinallyVoidExec: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyVoidExecutor", + MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, Object[].class)); } } catch (ReflectiveOperationException ex) { throw newInternalError(ex); --- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2015-11-19 22:14:02.000000000 +0100 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2015-11-19 22:14:02.000000000 +0100 @@ -26,10 +26,7 @@ package java.lang.invoke; import java.lang.reflect.*; -import java.util.BitSet; -import java.util.List; -import java.util.Arrays; -import java.util.Objects; +import java.util.*; import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyAccess; @@ -39,11 +36,13 @@ import sun.reflect.misc.ReflectUtil; import sun.security.util.SecurityConstants; import java.lang.invoke.LambdaForm.BasicType; -import static java.lang.invoke.LambdaForm.BasicType.*; + import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleImpl.Intrinsic; import static java.lang.invoke.MethodHandleNatives.Constants.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * This class consists exclusively of static methods that operate on or return @@ -176,7 +175,7 @@ * equivalent of a particular bytecode behavior. * (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.) * Here is a summary of the correspondence between these factory methods and - * the behavior the resulting method handles: + * the behavior of the resulting method handles: * * * @@ -235,6 +234,10 @@ * * * + * + * + * + * *
lookup expression{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}({@code static})?
{@code T m(A*);}
{@code (T) aMethod.invoke(thisOrNull, arg*);}
{@link java.lang.invoke.MethodHandles.Lookup#findClass lookup.findClass("C")}{@code class C { ... }}{@code C.class;}
* * Here, the type {@code C} is the class or interface being searched for a member, @@ -255,6 +258,10 @@ * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand * for reflective objects corresponding to the given members. *

+ * The bytecode behavior for a {@code findClass} operation is a load of a constant class, + * as if by {@code ldc CONSTANT_Class}. + * The behavior is represented, not as a method handle, but directly as a {@code Class} constant. + *

* In cases where the given member is of variable arity (i.e., a method or constructor) * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}. * In all other cases, the returned method handle will be of fixed arity. @@ -423,7 +430,7 @@ * and the Core Reflection API * (as found on {@link java.lang.Class Class}). *

- * If a security manager is present, member lookups are subject to + * If a security manager is present, member and class lookups are subject to * additional checks. * From one to three calls are made to the security manager. * Any of these calls can refuse access by throwing a @@ -433,6 +440,8 @@ * {@code refc} as the containing class in which the member * is being sought, and {@code defc} as the class in which the * member is actually defined. + * (If a class or other type is being accessed, + * the {@code refc} and {@code defc} values are the class itself.) * The value {@code lookc} is defined as not present * if the current lookup object does not have * private access. @@ -444,11 +453,16 @@ * then {@link SecurityManager#checkPackageAccess * smgr.checkPackageAccess(refcPkg)} is called, * where {@code refcPkg} is the package of {@code refc}. - *

  • Step 2: + *
  • Step 2a: * If the retrieved member is not public and * {@code lookc} is not present, then * {@link SecurityManager#checkPermission smgr.checkPermission} * with {@code RuntimePermission("accessDeclaredMembers")} is called. + *
  • Step 2b: + * If the retrieved class has a {@code null} class loader, + * and {@code lookc} is not present, then + * {@link SecurityManager#checkPermission smgr.checkPermission} + * with {@code RuntimePermission("getClassLoader")} is called. *
  • Step 3: * If the retrieved member is not public, * and if {@code lookc} is not present, @@ -458,9 +472,9 @@ * where {@code defcPkg} is the package of {@code defc}. * * Security checks are performed after other access checks have passed. - * Therefore, the above rules presuppose a member that is public, + * Therefore, the above rules presuppose a member or class that is public, * or else that is being accessed from a lookup class that has - * rights to access the member. + * rights to access the member or class. * *

    Caller sensitive methods

    * A small number of Java methods have a special property called caller sensitivity. @@ -922,6 +936,49 @@ } /** + * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static + * initializer of the class is not run. + * + * @param targetName the fully qualified name of the class to be looked up. + * @return the requested class. + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws LinkageError if the linkage fails + * @throws ClassNotFoundException if the class does not exist. + * @throws IllegalAccessException if the class is not accessible, using the allowed access + * modes. + * @exception SecurityException if a security manager is present and it + * refuses access + * @since 9 + */ + public Class findClass(String targetName) throws ClassNotFoundException, IllegalAccessException { + Class targetClass = Class.forName(targetName, false, lookupClass.getClassLoader()); + return accessClass(targetClass); + } + + /** + * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The + * static initializer of the class is not run. + * + * @param targetClass the class to be access-checked + * + * @return the class that has been access-checked + * + * @throws IllegalAccessException if the class is not accessible from the lookup class, using the allowed access + * modes. + * @exception SecurityException if a security manager is present and it + * refuses access + * @since 9 + */ + public Class accessClass(Class targetClass) throws IllegalAccessException { + if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, allowedModes)) { + throw new MemberName(targetClass).makeAccessException("access violation", this); + } + checkSecurityManager(targetClass, null); + return targetClass; + } + + /** * Produces an early-bound method handle for a virtual method. * It will bypass checks for overriding methods on the receiver, * as if called from an {@code invokespecial} @@ -995,7 +1052,7 @@ */ public MethodHandle findSpecial(Class refc, String name, MethodType type, Class specialCaller) throws NoSuchMethodException, IllegalAccessException { - checkSpecialCaller(specialCaller); + checkSpecialCaller(specialCaller, refc); Lookup specialLookup = this.in(specialCaller); MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type); return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerClass(method)); @@ -1224,7 +1281,7 @@ * @throws NullPointerException if any argument is null */ public MethodHandle unreflectSpecial(Method m, Class specialCaller) throws IllegalAccessException { - checkSpecialCaller(specialCaller); + checkSpecialCaller(specialCaller, null); Lookup specialLookup = this.in(specialCaller); MemberName method = new MemberName(m, true); assert(method.isMethod()); @@ -1444,7 +1501,15 @@ ReflectUtil.checkPackageAccess(refc); } - // Step 2: + if (m == null) { // findClass or accessClass + // Step 2b: + if (!fullPowerLookup) { + smgr.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); + } + return; + } + + // Step 2a: if (m.isPublic()) return; if (!fullPowerLookup) { smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); @@ -1557,11 +1622,13 @@ private static final boolean ALLOW_NESTMATE_ACCESS = false; - private void checkSpecialCaller(Class specialCaller) throws IllegalAccessException { + private void checkSpecialCaller(Class specialCaller, Class refc) throws IllegalAccessException { int allowedModes = this.allowedModes; if (allowedModes == TRUSTED) return; if (!hasPrivateAccess() || (specialCaller != lookupClass() + // ensure non-abstract methods in superinterfaces can be special-invoked + && !(refc != null && refc.isInterface() && refc.isAssignableFrom(specialCaller)) && !(ALLOW_NESTMATE_ACCESS && VerifyAccess.isSamePackageMember(specialCaller, lookupClass())))) throw new MemberName(specialCaller). @@ -1888,7 +1955,7 @@ MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { if (leadingArgCount < 0 || leadingArgCount > type.parameterCount()) throw newIllegalArgumentException("bad argument count", leadingArgCount); - type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount); + type = type.asSpreaderType(Object[].class, leadingArgCount, type.parameterCount() - leadingArgCount); return type.invokers().spreadInvoker(leadingArgCount); } @@ -2924,19 +2991,7 @@ */ public static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { - int foldPos = 0; - MethodType targetType = target.type(); - MethodType combinerType = combiner.type(); - Class rtype = foldArgumentChecks(foldPos, targetType, combinerType); - BoundMethodHandle result = target.rebind(); - boolean dropResult = (rtype == void.class); - // Note: This may cache too many distinct LFs. Consider backing off to varargs code. - LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType()); - MethodType newType = targetType; - if (!dropResult) - newType = newType.dropParameterTypes(foldPos, foldPos + 1); - result = result.copyWithExtendL(newType, lform, combiner); - return result; + return foldArguments(target, 0, combiner); } private static Class foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) { @@ -2949,7 +3004,7 @@ .equals(targetType.parameterList().subList(afterInsertPos, afterInsertPos + foldArgs)))) ok = false; - if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0)) + if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos)) ok = false; if (!ok) throw misMatchedTypes("target and combiner types", targetType, combinerType); @@ -3011,7 +3066,7 @@ return MethodHandleImpl.makeGuardWithTest(test, target, fallback); } - static RuntimeException misMatchedTypes(String what, MethodType t1, MethodType t2) { + static RuntimeException misMatchedTypes(String what, T t1, T t2) { return newIllegalArgumentException(what + " must match: " + t1 + " != " + t2); } @@ -3057,6 +3112,7 @@ * the given exception type, or if the method handle types do * not match in their return types and their * corresponding parameters + * @see MethodHandles#tryFinally(MethodHandle, MethodHandle) */ public static MethodHandle catchException(MethodHandle target, @@ -3100,4 +3156,897 @@ throw new ClassCastException(exType.getName()); return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType)); } + + /** + * Constructs a method handle representing a loop with several loop variables that are updated and checked upon each + * iteration. Upon termination of the loop due to one of the predicates, a corresponding finalizer is run and + * delivers the loop's result, which is the return value of the resulting handle. + *

    + * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop + * exit. Each iteration of the loop executes each clause in order. A clause can optionally update its iteration + * variable; it can also optionally perform a test and conditional loop exit. In order to express this logic in + * terms of method handles, each clause will determine four actions:

      + *
    • Before the loop executes, the initialization of an iteration variable or loop invariant local. + *
    • When a clause executes, an update step for the iteration variable. + *
    • When a clause executes, a predicate execution to test for loop exit. + *
    • If a clause causes a loop exit, a finalizer execution to compute the loop's return value. + *
    + *

    + * Some of these clause parts may be omitted according to certain rules, and useful default behavior is provided in + * this case. See below for a detailed description. + *

    + * Each clause function, with the exception of clause initializers, is able to observe the entire loop state, + * because it will be passed all current iteration variable values, as well as all incoming loop + * parameters. Most clause functions will not need all of this information, but they will be formally connected as + * if by {@link #dropArguments}. + *

    + * Given a set of clauses, there is a number of checks and adjustments performed to connect all the parts of the + * loop. They are spelled out in detail in the steps below. In these steps, every occurrence of the word "must" + * corresponds to a place where {@link IllegalArgumentException} may be thrown if the required constraint is not met + * by the inputs to the loop combinator. The term "effectively identical", applied to parameter type lists, means + * that they must be identical, or else one list must be a proper prefix of the other. + *

    + * Step 0: Determine clause structure.

      + *
    1. The clause array (of type {@code MethodHandle[][]} must be non-{@code null} and contain at least one element. + *
    2. The clause array may not contain {@code null}s or sub-arrays longer than four elements. + *
    3. Clauses shorter than four elements are treated as if they were padded by {@code null} elements to length + * four. Padding takes place by appending elements to the array. + *
    4. Clauses with all {@code null}s are disregarded. + *
    5. Each clause is treated as a four-tuple of functions, called "init", "step", "pred", and "fini". + *
    + *

    + * Step 1A: Determine iteration variables.

      + *
    1. Examine init and step function return types, pairwise, to determine each clause's iteration variable type. + *
    2. If both functions are omitted, use {@code void}; else if one is omitted, use the other's return type; else + * use the common return type (they must be identical). + *
    3. Form the list of return types (in clause order), omitting all occurrences of {@code void}. + *
    4. This list of types is called the "common prefix". + *
    + *

    + * Step 1B: Determine loop parameters.

      + *
    1. Examine init function parameter lists. + *
    2. Omitted init functions are deemed to have {@code null} parameter lists. + *
    3. All init function parameter lists must be effectively identical. + *
    4. The longest parameter list (which is necessarily unique) is called the "common suffix". + *
    + *

    + * Step 1C: Determine loop return type.

      + *
    1. Examine fini function return types, disregarding omitted fini functions. + *
    2. If there are no fini functions, use {@code void} as the loop return type. + *
    3. Otherwise, use the common return type of the fini functions; they must all be identical. + *
    + *

    + * Step 1D: Check other types.

      + *
    1. There must be at least one non-omitted pred function. + *
    2. Every non-omitted pred function must have a {@code boolean} return type. + *
    + *

    + * (Implementation Note: Steps 1A, 1B, 1C, 1D are logically independent of each other, and may be performed in any + * order.) + *

    + * Step 2: Determine parameter lists.

      + *
    1. The parameter list for the resulting loop handle will be the "common suffix". + *
    2. The parameter list for init functions will be adjusted to the "common suffix". (Note that their parameter + * lists are already effectively identical to the common suffix.) + *
    3. The parameter list for non-init (step, pred, and fini) functions will be adjusted to the common prefix + * followed by the common suffix, called the "common parameter sequence". + *
    4. Every non-init, non-omitted function parameter list must be effectively identical to the common parameter + * sequence. + *
    + *

    + * Step 3: Fill in omitted functions.

      + *
    1. If an init function is omitted, use a {@linkplain #constant constant function} of the appropriate + * {@code null}/zero/{@code false}/{@code void} type. (For this purpose, a constant {@code void} is simply a + * function which does nothing and returns {@code void}; it can be obtained from another constant function by + * {@linkplain MethodHandle#asType type conversion}.) + *
    2. If a step function is omitted, use an {@linkplain #identity identity function} of the clause's iteration + * variable type; insert dropped argument parameters before the identity function parameter for the non-{@code void} + * iteration variables of preceding clauses. (This will turn the loop variable into a local loop invariant.) + *
    3. If a pred function is omitted, the corresponding fini function must also be omitted. + *
    4. If a pred function is omitted, use a constant {@code true} function. (This will keep the loop going, as far + * as this clause is concerned.) + *
    5. If a fini function is omitted, use a constant {@code null}/zero/{@code false}/{@code void} function of the + * loop return type. + *
    + *

    + * Step 4: Fill in missing parameter types.

      + *
    1. At this point, every init function parameter list is effectively identical to the common suffix, but some + * lists may be shorter. For every init function with a short parameter list, pad out the end of the list by + * {@linkplain #dropArguments dropping arguments}. + *
    2. At this point, every non-init function parameter list is effectively identical to the common parameter + * sequence, but some lists may be shorter. For every non-init function with a short parameter list, pad out the end + * of the list by {@linkplain #dropArguments dropping arguments}. + *
    + *

    + * Final observations.

      + *
    1. After these steps, all clauses have been adjusted by supplying omitted functions and arguments. + *
    2. All init functions have a common parameter type list, which the final loop handle will also have. + *
    3. All fini functions have a common return type, which the final loop handle will also have. + *
    4. All non-init functions have a common parameter type list, which is the common parameter sequence, of + * (non-{@code void}) iteration variables followed by loop parameters. + *
    5. Each pair of init and step functions agrees in their return types. + *
    6. Each non-init function will be able to observe the current values of all iteration variables, by means of the + * common prefix. + *
    + *

    + * Loop execution.

      + *
    1. When the loop is called, the loop input values are saved in locals, to be passed (as the common suffix) to + * every clause function. These locals are loop invariant. + *
    2. Each init function is executed in clause order (passing the common suffix) and the non-{@code void} values + * are saved (as the common prefix) into locals. These locals are loop varying (unless their steps are identity + * functions, as noted above). + *
    3. All function executions (except init functions) will be passed the common parameter sequence, consisting of + * the non-{@code void} iteration values (in clause order) and then the loop inputs (in argument order). + *
    4. The step and pred functions are then executed, in clause order (step before pred), until a pred function + * returns {@code false}. + *
    5. The non-{@code void} result from a step function call is used to update the corresponding loop variable. The + * updated value is immediately visible to all subsequent function calls. + *
    6. If a pred function returns {@code false}, the corresponding fini function is called, and the resulting value + * is returned from the loop as a whole. + *
    + *

    + * Here is pseudocode for the resulting loop handle: + *

    {@code
    +     * V... init...(A...);
    +     * boolean pred...(V..., A...);
    +     * V... step...(V..., A...);
    +     * R fini...(V..., A...);
    +     * R loop(A... a) {
    +     *   V... v... = init...(a...);
    +     *   for (;;) {
    +     *     for ((v, p, s, f) in (v..., pred..., step..., fini...)) {
    +     *       v = s(v..., a...);
    +     *       if (!p(v..., a...)) {
    +     *         return f(v..., a...);
    +     *       }
    +     *     }
    +     *   }
    +     * }
    +     * }
    + *

    + * @apiNote Example: + *

    {@code
    +     * // iterative implementation of the factorial function as a loop handle
    +     * static int one(int k) { return 1; }
    +     * int inc(int i, int acc, int k) { return i + 1; }
    +     * int mult(int i, int acc, int k) { return i * acc; }
    +     * boolean pred(int i, int acc, int k) { return i < k; }
    +     * int fin(int i, int acc, int k) { return acc; }
    +     * // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
    +     * // null initializer for counter, should initialize to 0
    +     * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
    +     * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
    +     * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
    +     * assertEquals(120, loop.invoke(5));
    +     * }
    + * + * @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above. + * + * @return a method handle embodying the looping behavior as defined by the arguments. + * + * @throws IllegalArgumentException in case any of the constraints described above is violated. + * + * @see MethodHandles#whileLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#doWhileLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#iteratedLoop(MethodHandle, MethodHandle, MethodHandle) + * @since 9 + */ + public static MethodHandle loop(MethodHandle[]... clauses) { + // Step 0: determine clause structure. + checkLoop0(clauses); + + List init = new ArrayList<>(); + List step = new ArrayList<>(); + List pred = new ArrayList<>(); + List fini = new ArrayList<>(); + + Stream.of(clauses).filter(c -> Stream.of(c).anyMatch(Objects::nonNull)).forEach(clause -> { + init.add(clause[0]); // all clauses have at least length 1 + step.add(clause.length <= 1 ? null : clause[1]); + pred.add(clause.length <= 2 ? null : clause[2]); + fini.add(clause.length <= 3 ? null : clause[3]); + }); + + assert Stream.of(init, step, pred, fini).map(List::size).distinct().count() == 1; + final int nclauses = init.size(); + + // Step 1A: determine iteration variables. + final List> iterationVariableTypes = new ArrayList<>(); + for (int i = 0; i < nclauses; ++i) { + MethodHandle in = init.get(i); + MethodHandle st = step.get(i); + if (in == null && st == null) { + iterationVariableTypes.add(void.class); + } else if (in != null && st != null) { + checkLoop1a(i, in, st); + iterationVariableTypes.add(in.type().returnType()); + } else { + iterationVariableTypes.add(in == null ? st.type().returnType() : in.type().returnType()); + } + } + final List> commonPrefix = iterationVariableTypes.stream().filter(t -> t != void.class). + collect(Collectors.toList()); + + // Step 1B: determine loop parameters. + final List> empty = new ArrayList<>(); + final List> commonSuffix = init.stream().filter(Objects::nonNull).map(MethodHandle::type). + map(MethodType::parameterList).reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty); + checkLoop1b(init, commonSuffix); + + // Step 1C: determine loop return type. + // Step 1D: check other types. + final Class loopReturnType = fini.stream().filter(Objects::nonNull).map(MethodHandle::type). + map(MethodType::returnType).findFirst().orElse(void.class); + checkLoop1cd(pred, fini, loopReturnType); + + // Step 2: determine parameter lists. + final List> commonParameterSequence = new ArrayList<>(commonPrefix); + commonParameterSequence.addAll(commonSuffix); + checkLoop2(step, pred, fini, commonParameterSequence); + + // Step 3: fill in omitted functions. + for (int i = 0; i < nclauses; ++i) { + Class t = iterationVariableTypes.get(i); + if (init.get(i) == null) { + init.set(i, zeroHandle(t)); + } + if (step.get(i) == null) { + step.set(i, dropArguments(t == void.class ? zeroHandle(t) : identity(t), 0, commonPrefix.subList(0, i))); + } + if (pred.get(i) == null) { + pred.set(i, constant(boolean.class, true)); + } + if (fini.get(i) == null) { + fini.set(i, zeroHandle(t)); + } + } + + // Step 4: fill in missing parameter types. + List finit = fillParameterTypes(init, commonSuffix); + List fstep = fillParameterTypes(step, commonParameterSequence); + List fpred = fillParameterTypes(pred, commonParameterSequence); + List ffini = fillParameterTypes(fini, commonParameterSequence); + + assert finit.stream().map(MethodHandle::type).map(MethodType::parameterList). + allMatch(pl -> pl.equals(commonSuffix)); + assert Stream.of(fstep, fpred, ffini).flatMap(List::stream).map(MethodHandle::type).map(MethodType::parameterList). + allMatch(pl -> pl.equals(commonParameterSequence)); + + return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, commonPrefix, finit, fstep, fpred, ffini); + } + + private static List fillParameterTypes(List hs, final List> targetParams) { + return hs.stream().map(h -> { + int pc = h.type().parameterCount(); + int tpsize = targetParams.size(); + return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h; + }).collect(Collectors.toList()); + } + + /** + * Constructs a {@code while} loop from an initializer, a body, and a predicate. This is a convenience wrapper for + * the {@linkplain #loop(MethodHandle[][]) generic loop combinator}. + *

    + * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}. + * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle + * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code + * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][]) + * generic loop combinator}. + *

    + * Here is pseudocode for the resulting loop handle: + *

    {@code
    +     * V init(A);
    +     * boolean pred(V, A);
    +     * V body(V, A);
    +     * V whileLoop(A a) {
    +     *   V v = init(a);
    +     *   while (pred(v, a)) {
    +     *     v = body(v, a);
    +     *   }
    +     *   return v;
    +     * }
    +     * }
    + *

    + * @apiNote Example: + *

    {@code
    +     * // implement the zip function for lists as a loop handle
    +     * List initZip(Iterator a, Iterator b) { return new ArrayList<>(); }
    +     * boolean zipPred(List zip, Iterator a, Iterator b) { return a.hasNext() && b.hasNext(); }
    +     * List zipStep(List zip, Iterator a, Iterator b) {
    +     *   zip.add(a.next());
    +     *   zip.add(b.next());
    +     *   return zip;
    +     * }
    +     * // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods
    +     * MethodHandle loop = MethodHandles.doWhileLoop(MH_initZip, MH_zipPred, MH_zipStep);
    +     * List a = Arrays.asList("a", "b", "c", "d");
    +     * List b = Arrays.asList("e", "f", "g", "h");
    +     * List zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
    +     * assertEquals(zipped, (List) loop.invoke(a.iterator(), b.iterator()));
    +     * }
    + * + *

    + * The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
    +     *     MethodHandle[]
    +     *         checkExit = {null, null, pred, identity(init.type().returnType())},
    +     *         varBody = {init, body};
    +     *     return loop(checkExit, varBody);
    +     * }
    +     * }
    + * + * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's + * result type. Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param pred condition for the loop, which may not be {@code null}. + * @param body body of the loop, which may not be {@code null}. + * + * @return the value of the loop variable as the loop terminates. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @see MethodHandles#loop(MethodHandle[][]) + * @since 9 + */ + public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) { + MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType()); + MethodHandle[] checkExit = {null, null, pred, fin}; + MethodHandle[] varBody = {init, body}; + return loop(checkExit, varBody); + } + + /** + * Constructs a {@code do-while} loop from an initializer, a body, and a predicate. This is a convenience wrapper + * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *

    + * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}. + * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle + * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code + * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][]) + * generic loop combinator}. + *

    + * Here is pseudocode for the resulting loop handle: + *

    {@code
    +     * V init(A);
    +     * boolean pred(V, A);
    +     * V body(V, A);
    +     * V doWhileLoop(A a) {
    +     *   V v = init(a);
    +     *   do {
    +     *     v = body(v, a);
    +     *   } while (pred(v, a));
    +     *   return v;
    +     * }
    +     * }
    + *

    + * @apiNote Example: + *

    {@code
    +     * // int i = 0; while (i < limit) { ++i; } return i; => limit
    +     * int zero(int limit) { return 0; }
    +     * int step(int i, int limit) { return i + 1; }
    +     * boolean pred(int i, int limit) { return i < limit; }
    +     * // assume MH_zero, MH_step, and MH_pred are handles to the above methods
    +     * MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
    +     * assertEquals(23, loop.invoke(23));
    +     * }
    + * + * *

    + * The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
    +     *     MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) };
    +     *     return loop(clause);
    +     * }
    +     * }
    + * + * + * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's + * result type. Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param pred condition for the loop, which may not be {@code null}. + * @param body body of the loop, which may not be {@code null}. + * + * @return the value of the loop variable as the loop terminates. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @see MethodHandles#loop(MethodHandle[][]) + * @since 9 + */ + public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) { + MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType()); + MethodHandle[] clause = {init, body, pred, fin}; + return loop(clause); + } + + /** + * Constructs a loop that runs a given number of iterations. The loop counter is an {@code int} initialized from the + * {@code iterations} handle evaluation result. The counter is passed to the {@code body} function, so that must + * accept an initial {@code int} argument. The result of the loop execution is the final value of the additional + * local state. This is a convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *

    + * The result type and parameter type list of {@code init} determine those of the resulting handle. The {@code + * iterations} handle must accept the same parameter types as {@code init} but return an {@code int}. The {@code + * body} handle must accept the same parameter types as well, preceded by an {@code int} parameter for the counter, + * and a parameter of the same type as {@code init}'s result. These constraints follow directly from those described + * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *

    + * Here is pseudocode for the resulting loop handle: + *

    {@code
    +     * int iterations(A);
    +     * V init(A);
    +     * V body(int, V, A);
    +     * V countedLoop(A a) {
    +     *   int end = iterations(a);
    +     *   V v = init(a);
    +     *   for (int i = 0; i < end; ++i) {
    +     *     v = body(i, v, a);
    +     *   }
    +     *   return v;
    +     * }
    +     * }
    + *

    + * @apiNote Example: + *

    {@code
    +     * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
    +     * // => a variation on a well known theme
    +     * String start(String arg) { return arg; }
    +     * String step(int counter, String v, String arg) { return "na " + v; }
    +     * // assume MH_start and MH_step are handles to the two methods above
    +     * MethodHandle loop = MethodHandles.countedLoop(13, MH_start, MH_step);
    +     * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
    +     * }
    + * + *

    + * The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
    +     *     return countedLoop(null, iterations, init, body);  // null => constant zero
    +     * }
    +     * }
    + * + * @param iterations a handle to return the number of iterations this loop should run. + * @param init initializer for additional loop state. This determines the loop's result type. + * Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param body the body of the loop, which must not be {@code null}. + * It must accept an initial {@code int} parameter (for the counter), and then any + * additional loop-local variable plus loop parameters. + * + * @return a method handle representing the loop. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @since 9 + */ + public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { + return countedLoop(null, iterations, init, body); + } + + /** + * Constructs a loop that counts over a range of numbers. The loop counter is an {@code int} that will be + * initialized to the {@code int} value returned from the evaluation of the {@code start} handle and run to the + * value returned from {@code end} (exclusively) with a step width of 1. The counter value is passed to the {@code + * body} function in each iteration; it has to accept an initial {@code int} parameter + * for that. The result of the loop execution is the final value of the additional local state + * obtained by running {@code init}. + * This is a + * convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *

    + * The constraints for the {@code init} and {@code body} handles are the same as for {@link + * #countedLoop(MethodHandle, MethodHandle, MethodHandle)}. Additionally, the {@code start} and {@code end} handles + * must return an {@code int} and accept the same parameters as {@code init}. + *

    + * Here is pseudocode for the resulting loop handle: + *

    {@code
    +     * int start(A);
    +     * int end(A);
    +     * V init(A);
    +     * V body(int, V, A);
    +     * V countedLoop(A a) {
    +     *   int s = start(a);
    +     *   int e = end(a);
    +     *   V v = init(a);
    +     *   for (int i = s; i < e; ++i) {
    +     *     v = body(i, v, a);
    +     *   }
    +     *   return v;
    +     * }
    +     * }
    + * + *

    + * The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
    +     *     MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class);
    +     *     // assume MH_increment and MH_lessThan are handles to x+1 and x
    + * + * @param start a handle to return the start value of the loop counter. + * If it is {@code null}, a constant zero is assumed. + * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to {@code end-1}). + * @param init initializer for additional loop state. This determines the loop's result type. + * Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param body the body of the loop, which must not be {@code null}. + * It must accept an initial {@code int} parameter (for the counter), and then any + * additional loop-local variable plus loop parameters. + * + * @return a method handle representing the loop. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @since 9 + */ + public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { + MethodHandle returnVar = dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), + 0, int.class, int.class); + MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)}; + MethodHandle[] loopLimit = {end, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), returnVar}; + MethodHandle[] bodyClause = {init, dropArguments(body, 1, int.class)}; + return loop(indexVar, loopLimit, bodyClause); + } + + /** + * Constructs a loop that ranges over the elements produced by an {@code Iterator}. + * The iterator will be produced by the evaluation of the {@code iterator} handle. + * If this handle is passed as {@code null} the method {@link Iterable#iterator} will be used instead, + * and will be applied to a leading argument of the loop handle. + * Each value produced by the iterator is passed to the {@code body}, which must accept an initial {@code T} parameter. + * The result of the loop execution is the final value of the additional local state + * obtained by running {@code init}. + *

    + * This is a convenience wrapper for the + * {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}, and the constraints imposed on the {@code body} + * handle follow directly from those described for the latter. + *

    + * Here is pseudocode for the resulting loop handle: + *

    {@code
    +     * Iterator iterator(A);  // defaults to Iterable::iterator
    +     * V init(A);
    +     * V body(T,V,A);
    +     * V iteratedLoop(A a) {
    +     *   Iterator it = iterator(a);
    +     *   V v = init(a);
    +     *   for (T t : it) {
    +     *     v = body(t, v, a);
    +     *   }
    +     *   return v;
    +     * }
    +     * }
    + *

    + * The type {@code T} may be either a primitive or reference. + * Since type {@code Iterator} is erased in the method handle representation to the raw type + * {@code Iterator}, the {@code iteratedLoop} combinator adjusts the leading argument type for {@code body} + * to {@code Object} as if by the {@link MethodHandle#asType asType} conversion method. + * Therefore, if an iterator of the wrong type appears as the loop is executed, + * runtime exceptions may occur as the result of dynamic conversions performed by {@code asType}. + *

    + * @apiNote Example: + *

    {@code
    +     * // reverse a list
    +     * List reverseStep(String e, List r) {
    +     *   r.add(0, e);
    +     *   return r;
    +     * }
    +     * List newArrayList() { return new ArrayList<>(); }
    +     * // assume MH_reverseStep, MH_newArrayList are handles to the above methods
    +     * MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
    +     * List list = Arrays.asList("a", "b", "c", "d", "e");
    +     * List reversedList = Arrays.asList("e", "d", "c", "b", "a");
    +     * assertEquals(reversedList, (List) loop.invoke(list));
    +     * }
    + *

    + * The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
    +     *     // assume MH_next and MH_hasNext are handles to methods of Iterator
    +     *     Class itype = iterator.type().returnType();
    +     *     Class ttype = body.type().parameterType(0);
    +     *     MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, itype);
    +     *     MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
    +     *     MethodHandle[]
    +     *         iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext)
    +     *         bodyClause = {init, filterArgument(body, 0, nextVal)};  // v = body(t, v, a);
    +     *     return loop(iterVar, bodyClause);
    +     * }
    +     * }
    + * + * @param iterator a handle to return the iterator to start the loop. + * Passing {@code null} will make the loop call {@link Iterable#iterator()} on the first + * incoming value. + * @param init initializer for additional loop state. This determines the loop's result type. + * Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param body the body of the loop, which must not be {@code null}. + * It must accept an initial {@code T} parameter (for the iterated values), and then any + * additional loop-local variable plus loop parameters. + * + * @return a method handle embodying the iteration loop functionality. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @since 9 + */ + public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) { + checkIteratedLoop(body); + + MethodHandle initit = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator); + MethodHandle initIterator = iterator == null ? + initit.asType(initit.type().changeParameterType(0, body.type().parameterType(init == null ? 1 : 2))) : + iterator; + Class itype = initIterator.type().returnType(); + Class ttype = body.type().parameterType(0); + + MethodHandle returnVar = + dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), 0, itype); + MethodHandle initnx = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext); + MethodHandle nextVal = initnx.asType(initnx.type().changeReturnType(ttype)); + + MethodHandle[] iterVar = {initIterator, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred), returnVar}; + MethodHandle[] bodyClause = {init, filterArgument(body, 0, nextVal)}; + + return loop(iterVar, bodyClause); + } + + /** + * Makes a method handle that adapts a {@code target} method handle by wrapping it in a {@code try-finally} block. + * Another method handle, {@code cleanup}, represents the functionality of the {@code finally} block. Any exception + * thrown during the execution of the {@code target} handle will be passed to the {@code cleanup} handle. The + * exception will be rethrown, unless {@code cleanup} handle throws an exception first. The + * value returned from the {@code cleanup} handle's execution will be the result of the execution of the + * {@code try-finally} handle. + *

    + * The {@code cleanup} handle will be passed one or two additional leading arguments. + * The first is the exception thrown during the + * execution of the {@code target} handle, or {@code null} if no exception was thrown. + * The second is the result of the execution of the {@code target} handle, or, if it throws an exception, + * a {@code null}, zero, or {@code false} value of the required type is supplied as a placeholder. + * The second argument is not present if the {@code target} handle has a {@code void} return type. + * (Note that, except for argument type conversions, combinators represent {@code void} values in parameter lists + * by omitting the corresponding paradoxical arguments, not by inserting {@code null} or zero values.) + *

    + * The {@code target} and {@code cleanup} handles' return types must be the same. Their parameter type lists also + * must be the same, but the {@code cleanup} handle must accept one or two more leading parameters:

      + *
    • a {@code Throwable}, which will carry the exception thrown by the {@code target} handle (if any); and + *
    • a parameter of the same type as the return type of both {@code target} and {@code cleanup}, which will carry + * the result from the execution of the {@code target} handle. + * This parameter is not present if the {@code target} returns {@code void}. + *
    + *

    + * The pseudocode for the resulting adapter looks like this: + *

    {@code
    +     * T target(A..., B...);
    +     * T cleanup(Throwable, T, A...);
    +     * T adapter(A... a, B... b) {
    +     *   T result = (zero value for T);
    +     *   Throwable throwable = null;
    +     *   try {
    +     *     result = target(a..., b...);
    +     *   } catch (Throwable t) {
    +     *     throwable = t;
    +     *     throw t;
    +     *   } finally {
    +     *     result = cleanup(throwable, result, a...);
    +     *   }
    +     *   return result;
    +     * }
    +     * }
    + *

    + * Note that the saved arguments ({@code a...} in the pseudocode) cannot + * be modified by execution of the target, and so are passed unchanged + * from the caller to the cleanup, if it is invoked. + *

    + * The target and cleanup must return the same type, even if the cleanup + * always throws. + * To create such a throwing cleanup, compose the cleanup logic + * with {@link #throwException throwException}, + * in order to create a method handle of the correct return type. + *

    + * Note that {@code tryFinally} never converts exceptions into normal returns. + * In rare cases where exceptions must be converted in that way, first wrap + * the target with {@link #catchException(MethodHandle, Class, MethodHandle)} + * to capture an outgoing exception, and then wrap with {@code tryFinally}. + * + * @param target the handle whose execution is to be wrapped in a {@code try} block. + * @param cleanup the handle that is invoked in the finally block. + * + * @return a method handle embodying the {@code try-finally} block composed of the two arguments. + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if {@code cleanup} does not accept + * the required leading arguments, or if the method handle types do + * not match in their return types and their + * corresponding trailing parameters + * + * @see MethodHandles#catchException(MethodHandle, Class, MethodHandle) + * @since 9 + */ + public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup) { + List> targetParamTypes = target.type().parameterList(); + List> cleanupParamTypes = cleanup.type().parameterList(); + Class rtype = target.type().returnType(); + + checkTryFinally(target, cleanup); + + // Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments. + int tpSize = targetParamTypes.size(); + int cpPrefixLength = rtype == void.class ? 1 : 2; + int cpSize = cleanupParamTypes.size(); + MethodHandle aCleanup = cpSize - cpPrefixLength < tpSize ? + dropArguments(cleanup, cpSize, targetParamTypes.subList(tpSize - (cpSize - cpPrefixLength), tpSize)) : + cleanup; + + MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount()); + aCleanup = aCleanup.asSpreader(Object[].class, tpSize); + + return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, rtype, targetParamTypes); + } + + /** + * Adapts a target method handle by pre-processing some of its arguments, starting at a given position, and then + * calling the target with the result of the pre-processing, inserted into the original sequence of arguments just + * before the folded arguments. + *

    + * This method is closely related to {@link #foldArguments(MethodHandle, MethodHandle)}, but allows to control the + * position in the parameter list at which folding takes place. The argument controlling this, {@code pos}, is a + * zero-based index. The aforementioned method {@link #foldArguments(MethodHandle, MethodHandle)} assumes position + * 0. + *

    + * @apiNote Example: + *

    {@code
    +    import static java.lang.invoke.MethodHandles.*;
    +    import static java.lang.invoke.MethodType.*;
    +    ...
    +    MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
    +    "println", methodType(void.class, String.class))
    +    .bindTo(System.out);
    +    MethodHandle cat = lookup().findVirtual(String.class,
    +    "concat", methodType(String.class, String.class));
    +    assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
    +    MethodHandle catTrace = foldArguments(cat, 1, trace);
    +    // also prints "jum":
    +    assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
    +     * }
    + *

    Here is pseudocode for the resulting adapter: + *

    {@code
    +     * // there are N arguments in A...
    +     * T target(Z..., V, A[N]..., B...);
    +     * V combiner(A...);
    +     * T adapter(Z... z, A... a, B... b) {
    +     *   V v = combiner(a...);
    +     *   return target(z..., v, a..., b...);
    +     * }
    +     * // and if the combiner has a void return:
    +     * T target2(Z..., A[N]..., B...);
    +     * void combiner2(A...);
    +     * T adapter2(Z... z, A... a, B... b) {
    +     *   combiner2(a...);
    +     *   return target2(z..., a..., b...);
    +     * }
    +     * }
    + * + * @param target the method handle to invoke after arguments are combined + * @param pos the position at which to start folding and at which to insert the folding result; if this is {@code + * 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}. + * @param combiner method handle to call initially on the incoming arguments + * @return method handle which incorporates the specified argument folding logic + * @throws NullPointerException if either argument is null + * @throws IllegalArgumentException if {@code combiner}'s return type + * is non-void and not the same as the argument type at position {@code pos} of + * the target signature, or if the {@code N} argument types at position {@code pos} + * of the target signature + * (skipping one matching the {@code combiner}'s return type) + * are not identical with the argument types of {@code combiner} + * + * @see #foldArguments(MethodHandle, MethodHandle) + * @since 9 + */ + public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner) { + MethodType targetType = target.type(); + MethodType combinerType = combiner.type(); + Class rtype = foldArgumentChecks(pos, targetType, combinerType); + BoundMethodHandle result = target.rebind(); + boolean dropResult = rtype == void.class; + LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType()); + MethodType newType = targetType; + if (!dropResult) { + newType = newType.dropParameterTypes(pos, pos + 1); + } + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } + + /** + * Wrap creation of a proper zero handle for a given type. + * + * @param type the type. + * + * @return a zero value for the given type. + */ + static MethodHandle zeroHandle(Class type) { + return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type); + } + + private static void checkLoop0(MethodHandle[][] clauses) { + if (clauses == null || clauses.length == 0) { + throw newIllegalArgumentException("null or no clauses passed"); + } + if (Stream.of(clauses).anyMatch(Objects::isNull)) { + throw newIllegalArgumentException("null clauses are not allowed"); + } + if (Stream.of(clauses).anyMatch(c -> c.length > 4)) { + throw newIllegalArgumentException("All loop clauses must be represented as MethodHandle arrays with at most 4 elements."); + } + } + + private static void checkLoop1a(int i, MethodHandle in, MethodHandle st) { + if (in.type().returnType() != st.type().returnType()) { + throw misMatchedTypes("clause " + i + ": init and step return types", in.type().returnType(), + st.type().returnType()); + } + } + + private static void checkLoop1b(List init, List> commonSuffix) { + if (init.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::parameterList). + anyMatch(pl -> !pl.equals(commonSuffix.subList(0, pl.size())))) { + throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init + + " (common suffix: " + commonSuffix + ")"); + } + } + + private static void checkLoop1cd(List pred, List fini, Class loopReturnType) { + if (fini.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType). + anyMatch(t -> t != loopReturnType)) { + throw newIllegalArgumentException("found non-identical finalizer return types: " + fini + " (return type: " + + loopReturnType + ")"); + } + + if (!pred.stream().filter(Objects::nonNull).findFirst().isPresent()) { + throw newIllegalArgumentException("no predicate found", pred); + } + if (pred.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType). + anyMatch(t -> t != boolean.class)) { + throw newIllegalArgumentException("predicates must have boolean return type", pred); + } + } + + private static void checkLoop2(List step, List pred, List fini, List> commonParameterSequence) { + if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type). + map(MethodType::parameterList).anyMatch(pl -> !pl.equals(commonParameterSequence.subList(0, pl.size())))) { + throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step + + "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")"); + } + } + + private static void checkIteratedLoop(MethodHandle body) { + if (null == body) { + throw newIllegalArgumentException("iterated loop body must not be null"); + } + } + + private static void checkTryFinally(MethodHandle target, MethodHandle cleanup) { + Class rtype = target.type().returnType(); + if (rtype != cleanup.type().returnType()) { + throw misMatchedTypes("target and return types", cleanup.type().returnType(), rtype); + } + List> cleanupParamTypes = cleanup.type().parameterList(); + if (!Throwable.class.isAssignableFrom(cleanupParamTypes.get(0))) { + throw misMatchedTypes("cleanup first argument and Throwable", cleanup.type(), Throwable.class); + } + if (rtype != void.class && cleanupParamTypes.get(1) != rtype) { + throw misMatchedTypes("cleanup second argument and target return type", cleanup.type(), rtype); + } + // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the + // target parameter list. + int cleanupArgIndex = rtype == void.class ? 1 : 2; + if (!cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size()). + equals(target.type().parameterList().subList(0, cleanupParamTypes.size() - cleanupArgIndex))) { + throw misMatchedTypes("cleanup parameters after (Throwable,result) and target parameter list prefix", + cleanup.type(), target.type()); + } + } + } --- old/src/java.base/share/classes/java/lang/invoke/MethodType.java 2015-11-19 22:14:03.000000000 +0100 +++ new/src/java.base/share/classes/java/lang/invoke/MethodType.java 2015-11-19 22:14:03.000000000 +0100 @@ -469,12 +469,13 @@ /** Replace the last arrayLength parameter types with the component type of arrayType. * @param arrayType any array type + * @param pos position at which to spread * @param arrayLength the number of parameter types to change * @return the resulting type */ - /*non-public*/ MethodType asSpreaderType(Class arrayType, int arrayLength) { + /*non-public*/ MethodType asSpreaderType(Class arrayType, int pos, int arrayLength) { assert(parameterCount() >= arrayLength); - int spreadPos = ptypes.length - arrayLength; + int spreadPos = pos; if (arrayLength == 0) return this; // nothing to change if (arrayType == Object[].class) { if (isGeneric()) return this; // nothing to change @@ -489,10 +490,10 @@ } Class elemType = arrayType.getComponentType(); assert(elemType != null); - for (int i = spreadPos; i < ptypes.length; i++) { + for (int i = spreadPos; i < spreadPos + arrayLength; i++) { if (ptypes[i] != elemType) { Class[] fixedPtypes = ptypes.clone(); - Arrays.fill(fixedPtypes, i, ptypes.length, elemType); + Arrays.fill(fixedPtypes, i, spreadPos + arrayLength, elemType); return methodType(rtype, fixedPtypes); } } @@ -512,12 +513,14 @@ /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType. * @param arrayType any array type + * @param pos position at which to insert parameters * @param arrayLength the number of parameter types to insert * @return the resulting type */ - /*non-public*/ MethodType asCollectorType(Class arrayType, int arrayLength) { + /*non-public*/ MethodType asCollectorType(Class arrayType, int pos, int arrayLength) { assert(parameterCount() >= 1); - assert(lastParameterType().isAssignableFrom(arrayType)); + assert(pos < ptypes.length); + assert(ptypes[pos].isAssignableFrom(arrayType)); MethodType res; if (arrayType == Object[].class) { res = genericMethodType(arrayLength); @@ -532,7 +535,11 @@ if (ptypes.length == 1) { return res; } else { - return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1)); + // insert after (if need be), then before + if (pos < parameterList().size() - 1) { + res = res.insertParameterTypes(arrayLength, parameterList().subList(pos + 1, parameterList().size())); + } + return res.insertParameterTypes(0, parameterList().subList(0, pos)); } } --- old/test/java/lang/invoke/AccessControlTest.java 2015-11-19 22:14:04.000000000 +0100 +++ new/test/java/lang/invoke/AccessControlTest.java 2015-11-19 22:14:04.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -225,6 +225,31 @@ System.out.println(this+" willAccess "+lc+" m1="+m1+" m2="+m2+" => "+((m2 & m1) != 0)); return (m2 & m1) != 0; } + + /** Predict the success or failure of accessing this class. */ + public boolean willAccessClass(Class c2, boolean load) { + Class c1 = lookupClass(); + if (load && c1.getClassLoader() == null) { + return false; + } + LookupCase lc = this.in(c2); + int m1 = lc.lookupModes(); + boolean r = false; + if (m1 == 0) { + r = false; + } else { + int m2 = fixMods(c2.getModifiers()); + if ((m2 & PUBLIC) != 0) { + r = true; + } else if ((m1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage()) { + r = true; + } + } + if (verbosity >= 2) { + System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r); + } + return r; + } } private static Class topLevelClass(Class cls) { @@ -342,6 +367,8 @@ Method method = targetMethod(targetClass, targetAccess, methodType); // Try to access target method from various contexts. for (LookupCase sourceCase : CASES) { + testOneAccess(sourceCase, method, "findClass"); + testOneAccess(sourceCase, method, "accessClass"); testOneAccess(sourceCase, method, "find"); testOneAccess(sourceCase, method, "unreflect"); } @@ -356,11 +383,19 @@ Class targetClass = method.getDeclaringClass(); String methodName = method.getName(); MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes()); - boolean willAccess = sourceCase.willAccess(method); + boolean isFindOrAccessClass = "findClass".equals(kind) || "accessClass".equals(kind); + boolean willAccess = isFindOrAccessClass ? + sourceCase.willAccessClass(targetClass, "findClass".equals(kind)) : sourceCase.willAccess(method); boolean didAccess = false; ReflectiveOperationException accessError = null; try { switch (kind) { + case "accessClass": + sourceCase.lookup().accessClass(targetClass); + break; + case "findClass": + sourceCase.lookup().findClass(targetClass.getName()); + break; case "find": if ((method.getModifiers() & Modifier.STATIC) != 0) sourceCase.lookup().findStatic(targetClass, methodName, methodType); @@ -378,8 +413,8 @@ accessError = ex; } if (willAccess != didAccess) { - System.out.println(sourceCase+" => "+targetClass.getSimpleName()+"."+methodName+methodType); - System.out.println("fail on "+method+" ex="+accessError); + System.out.println(sourceCase+" => "+targetClass.getSimpleName()+(isFindOrAccessClass?"":"."+methodName+methodType)); + System.out.println("fail "+(isFindOrAccessClass?kind:"on "+method)+" ex="+accessError); assertEquals(willAccess, didAccess); } testCount++; --- old/test/java/lang/invoke/BigArityTest.java 2015-11-19 22:14:05.000000000 +0100 +++ new/test/java/lang/invoke/BigArityTest.java 2015-11-19 22:14:05.000000000 +0100 @@ -58,6 +58,8 @@ return x == null ? dflt : x; } + static final MethodType MT_A = MethodType.methodType(Object.class, Object.class, Object[].class, Object.class); + static Object hashArguments(Object... args) { return Objects.hash(args); } @@ -108,9 +110,36 @@ } } // Sizes not in the above array are good: - target.asCollector(Object[].class, minbig-1); + target.asCollector(Object[].class, minbig - 1); for (int i = 2; i <= 10; i++) - target.asCollector(Object[].class, minbig-i); + target.asCollector(Object[].class, minbig - i); + } + + static void asciae02target(Object[] a, Object b) { + // naught + } + + @Test + public void asCollectorIAE02() throws ReflectiveOperationException { + final int[] INVALID_ARRAY_LENGTHS = { + Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -2, -1, 254, 255, Integer.MAX_VALUE - 1, Integer.MAX_VALUE + }; + MethodHandle target = MethodHandles.lookup().findStatic(BigArityTest.class, "asciae02target", + MethodType.methodType(void.class, Object[].class, Object.class)); + int minbig = Integer.MAX_VALUE; + for (int invalidLength : INVALID_ARRAY_LENGTHS) { + if (minbig > invalidLength && invalidLength > 100) minbig = invalidLength; + try { + target.asCollector(0, Object[].class, invalidLength); + assert(false) : invalidLength; + } catch (IllegalArgumentException ex) { + System.out.println("OK: "+ex); + } + } + // Sizes not in the above array are good: + for (int i = 1; i <= 10; ++i) { + target.asCollector(0, Object[].class, minbig - i); + } } @Test @@ -216,51 +245,86 @@ Class cls = (Class) cls0; //Class cls = Object[].class.asSubclass(cls0); int nargs = args.length, skip; + Object hr; MethodHandle smh = mh.asSpreader(cls, nargs - (skip = 0)); + MethodHandle hsmh = mh.asSpreader(0, cls, nargs - skip); Object[] tail = Arrays.copyOfRange(args, skip, nargs, cls); - if (cls == Object[].class) + Object[] head = Arrays.copyOfRange(args, 0, nargs - skip, cls); + if (cls == Object[].class) { r = smh.invokeExact(tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head); + } else if (cls == Integer[].class) { r = smh.invokeExact((Integer[]) tail); //warning OK, see 8019340 - else + hr = hsmh.invokeExact((Integer[]) head); + } else { r = smh.invoke(tail); + hr = hsmh.invoke(head); + } assertEquals(r0, r); + assertEquals(r0, hr); smh = mh.asSpreader(cls, nargs - (skip = 1)); + hsmh = mh.asSpreader(0, cls, nargs - skip); tail = Arrays.copyOfRange(args, skip, nargs, cls); - if (cls == Object[].class) + head = Arrays.copyOfRange(args, 0, nargs - skip, cls); + if (cls == Object[].class) { r = smh.invokeExact(args[0], tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head, args[2]); + } else if (cls == Integer[].class) { r = smh.invokeExact(args[0], (Integer[]) tail); - else + hr = hsmh.invokeExact((Integer[]) head, args[2]); + } else { r = smh.invoke(args[0], tail); + hr = hsmh.invoke(head, args[2]); + } assertEquals(r0, r); + assertEquals(r0, hr); smh = mh.asSpreader(cls, nargs - (skip = 2)); + hsmh = mh.asSpreader(0, cls, nargs - skip); tail = Arrays.copyOfRange(args, skip, nargs, cls); - if (cls == Object[].class) + head = Arrays.copyOfRange(args, 0, nargs - skip, cls); + if (cls == Object[].class) { r = smh.invokeExact(args[0], args[1], tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head, args[1], args[2]); + } else if (cls == Integer[].class) { r = smh.invokeExact(args[0], args[1], (Integer[]) tail); - else + hr = hsmh.invokeExact((Integer[]) head, args[1], args[2]); + } else { r = smh.invoke(args[0], args[1], tail); + hr = hsmh.invoke(head, args[1], args[2]); + } assertEquals(r0, r); + assertEquals(r0, hr); smh = mh.asSpreader(cls, nargs - (skip = 3)); + hsmh = mh.asSpreader(0, cls, nargs - skip); tail = Arrays.copyOfRange(args, skip, nargs, cls); - if (cls == Object[].class) + head = Arrays.copyOfRange(args, 0, nargs - skip, cls); + if (cls == Object[].class) { r = smh.invokeExact(args[0], args[1], args[2], tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head, args[0], args[1], args[2]); + } else if (cls == Integer[].class) { r = smh.invokeExact(args[0], args[1], args[2], (Integer[]) tail); - else + hr = hsmh.invokeExact((Integer[]) head, args[0], args[1], args[2]); + } else { r = smh.invoke(args[0], args[1], args[2], tail); + hr = hsmh.invoke(head, args[0], args[1], args[2]); + } assertEquals(r0, r); + assertEquals(r0, hr); // Try null array in addition to zero-length array: tail = null; - if (cls == Object[].class) + head = null; + if (cls == Object[].class) { r = smh.invokeExact(args[0], args[1], args[2], tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head, args[0], args[1], args[2]); + } else if (cls == Integer[].class) { r = smh.invokeExact(args[0], args[1], args[2], (Integer[]) tail); - else + hr = hsmh.invokeExact((Integer[]) head, args[0], args[1], args[2]); + } else { r = smh.invoke(args[0], args[1], args[2], tail); + hr = hsmh.invoke(head, args[0], args[1], args[2]); + } assertEquals(r0, r); + assertEquals(r0, hr); } } @@ -292,7 +356,7 @@ @Test public void testArities() throws Throwable { System.out.println("testing spreaders and collectors on high arities..."); - int iterations = ITERATION_COUNT; + int iterations = ITERATION_COUNT; testArities(Object[].class, MIN_ARITY-10, MIN_ARITY-1, iterations / 1000); testArities(Object[].class, MIN_ARITY, SLOW_ARITY-1, iterations); testArities(Object[].class, SLOW_ARITY, MAX_ARITY, iterations / 1000); @@ -307,8 +371,13 @@ Class cls = (Class) cls0; System.out.println("array class: "+cls.getSimpleName()); int iterations = ITERATION_COUNT / 1000; - testArities(cls, MIN_ARITY, SLOW_ARITY-1, iterations); - testArities(cls, SLOW_ARITY, MAX_ARITY, iterations / 100); + try { + testArities(cls, MIN_ARITY, SLOW_ARITY - 1, iterations); + testArities(cls, SLOW_ARITY, MAX_ARITY, iterations / 100); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } } } @@ -321,11 +390,14 @@ if (verbose) System.out.println("arity="+arity); MethodHandle mh = MH_hashArguments(cls, arity); MethodHandle mh_VA = mh.asSpreader(cls, arity); + MethodHandle mh_VA_h = mh.asSpreader(0, cls, arity-1); assert(mh_VA.type().parameterType(0) == cls); - testArities(cls, arity, iterations, verbose, mh, mh_VA); + assert(mh_VA_h.type().parameterType(0) == cls); + testArities(cls, arity, iterations, verbose, mh, mh_VA, mh_VA_h); // mh_CA will collect arguments of a particular type and pass them to mh_VA MethodHandle mh_CA = mh_VA.asCollector(cls, arity); MethodHandle mh_VA2 = mh_CA.asSpreader(cls, arity); + MethodHandle mh_VA2_h = mh_CA.asSpreader(0, cls, arity-1); assert(mh_CA.type().equals(mh.type())); assert(mh_VA2.type().equals(mh_VA.type())); if (cls != Object[].class) { @@ -336,7 +408,7 @@ } } int iterations_VA = iterations / 100; - testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2); + testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2, mh_VA2_h); } } @@ -357,13 +429,16 @@ * @param verbose are we printing extra output? * @param mh a fixed-arity version of {@code hashArguments} * @param mh_VA a variable-arity version of {@code hashArguments}, accepting the given array type {@code cls} + * @param mh_VA_h a version of {@code hashArguments} that has a leading {@code cls} array and one final {@code cls} + * argument */ private void testArities(Class cls, int arity, int iterations, boolean verbose, MethodHandle mh, - MethodHandle mh_VA + MethodHandle mh_VA, + MethodHandle mh_VA_h ) throws Throwable { if (iterations < 4) iterations = 4; final int MAX_MH_ARITY = MAX_JVM_ARITY - 1; // mh.invoke(arg*[N]) @@ -373,6 +448,7 @@ args = Arrays.copyOf(args, arity, cls); Object r0 = Objects.hash(args); Object r; + Object hr; MethodHandle ximh = null; MethodHandle gimh = null; if (arity <= MAX_INVOKER_ARITY) { @@ -397,13 +473,18 @@ Object[] mh_args = cat(mh, args); assert(arity <= MAX_MH_ARITY); for (int i = 0; i < iterations; ++i) { - if (cls == Object[].class) + if (cls == Object[].class) { r = mh_VA.invokeExact(args); - else if (cls == Integer[].class) - r = mh_VA.invokeExact((Integer[])args); //warning OK, see 8019340 - else + hr = mh_VA_h.invokeExact(Arrays.copyOfRange(args, 0, arity - 1), args[arity - 1]); + } else if (cls == Integer[].class) { + r = mh_VA.invokeExact((Integer[]) args); //warning OK, see 8019340 + hr = mh_VA_h.invokeExact((Integer[]) Arrays.copyOfRange(args, 0, arity - 1), (Integer) args[arity - 1]); + } else { r = mh_VA.invoke(args); + hr = mh_VA_h.invoke(Arrays.copyOfRange(args, 0, arity - 1), args[arity - 1]); + } assertEquals(r0, r); + assertEquals(r0, hr); r = mh.invokeWithArguments(args); assertEquals(r0, r); if (ximh != null) { @@ -473,6 +554,43 @@ // xF8, xF9, xFA, xFB); } + static Object hashArguments_252_a(Object x00, Object[] x01_FA, Object xFB) { + return Objects.hash( + // + x00, x01_FA[0], x01_FA[1], x01_FA[2], x01_FA[3], x01_FA[4], x01_FA[5], x01_FA[6], x01_FA[7], x01_FA[8], + x01_FA[9], x01_FA[10], x01_FA[11], x01_FA[12], x01_FA[13], x01_FA[14], x01_FA[15], x01_FA[16], + x01_FA[17], x01_FA[18], x01_FA[19], x01_FA[20], x01_FA[21], x01_FA[22], x01_FA[23], x01_FA[24], + x01_FA[25], x01_FA[26], x01_FA[27], x01_FA[28], x01_FA[29], x01_FA[30], x01_FA[31], x01_FA[32], + x01_FA[33], x01_FA[34], x01_FA[35], x01_FA[36], x01_FA[37], x01_FA[38], x01_FA[39], x01_FA[40], + x01_FA[41], x01_FA[42], x01_FA[43], x01_FA[44], x01_FA[45], x01_FA[46], x01_FA[47], x01_FA[48], + x01_FA[49], x01_FA[50], x01_FA[51], x01_FA[52], x01_FA[53], x01_FA[54], x01_FA[55], x01_FA[56], + x01_FA[57], x01_FA[58], x01_FA[59], x01_FA[60], x01_FA[61], x01_FA[62], x01_FA[63], x01_FA[64], + x01_FA[65], x01_FA[66], x01_FA[67], x01_FA[68], x01_FA[69], x01_FA[70], x01_FA[71], x01_FA[72], + x01_FA[73], x01_FA[74], x01_FA[75], x01_FA[76], x01_FA[77], x01_FA[78], x01_FA[79], x01_FA[80], + x01_FA[81], x01_FA[82], x01_FA[83], x01_FA[84], x01_FA[85], x01_FA[86], x01_FA[87], x01_FA[88], + x01_FA[89], x01_FA[90], x01_FA[91], x01_FA[92], x01_FA[93], x01_FA[94], x01_FA[95], x01_FA[96], + x01_FA[97], x01_FA[98], x01_FA[99], x01_FA[100], x01_FA[101], x01_FA[102], x01_FA[103], x01_FA[104], + x01_FA[105], x01_FA[106], x01_FA[107], x01_FA[108], x01_FA[109], x01_FA[110], x01_FA[111], x01_FA[112], + x01_FA[113], x01_FA[114], x01_FA[115], x01_FA[116], x01_FA[117], x01_FA[118], x01_FA[119], x01_FA[120], + x01_FA[121], x01_FA[122], x01_FA[123], x01_FA[124], x01_FA[125], x01_FA[126], x01_FA[127], x01_FA[128], + x01_FA[129], x01_FA[130], x01_FA[131], x01_FA[132], x01_FA[133], x01_FA[134], x01_FA[135], x01_FA[136], + x01_FA[137], x01_FA[138], x01_FA[139], x01_FA[140], x01_FA[141], x01_FA[142], x01_FA[143], x01_FA[144], + x01_FA[145], x01_FA[146], x01_FA[147], x01_FA[148], x01_FA[149], x01_FA[150], x01_FA[151], x01_FA[152], + x01_FA[153], x01_FA[154], x01_FA[155], x01_FA[156], x01_FA[157], x01_FA[158], x01_FA[159], x01_FA[160], + x01_FA[161], x01_FA[162], x01_FA[163], x01_FA[164], x01_FA[165], x01_FA[166], x01_FA[167], x01_FA[168], + x01_FA[169], x01_FA[170], x01_FA[171], x01_FA[172], x01_FA[173], x01_FA[174], x01_FA[175], x01_FA[176], + x01_FA[177], x01_FA[178], x01_FA[179], x01_FA[180], x01_FA[181], x01_FA[182], x01_FA[183], x01_FA[184], + x01_FA[185], x01_FA[186], x01_FA[187], x01_FA[188], x01_FA[189], x01_FA[190], x01_FA[191], x01_FA[192], + x01_FA[193], x01_FA[194], x01_FA[195], x01_FA[196], x01_FA[197], x01_FA[198], x01_FA[199], x01_FA[200], + x01_FA[201], x01_FA[202], x01_FA[203], x01_FA[204], x01_FA[205], x01_FA[206], x01_FA[207], x01_FA[208], + x01_FA[209], x01_FA[210], x01_FA[211], x01_FA[212], x01_FA[213], x01_FA[214], x01_FA[215], x01_FA[216], + x01_FA[217], x01_FA[218], x01_FA[219], x01_FA[220], x01_FA[221], x01_FA[222], x01_FA[223], x01_FA[224], + x01_FA[225], x01_FA[226], x01_FA[227], x01_FA[228], x01_FA[229], x01_FA[230], x01_FA[231], x01_FA[232], + x01_FA[233], x01_FA[234], x01_FA[235], x01_FA[236], x01_FA[237], x01_FA[238], x01_FA[239], x01_FA[240], + x01_FA[241], x01_FA[242], x01_FA[243], x01_FA[244], x01_FA[245], x01_FA[246], x01_FA[247], x01_FA[248], + // + x01_FA[249], xFB); + } @Test public void test252() throws Throwable { @@ -507,6 +625,8 @@ test252(mh, a, r0); MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY); test252(mh_CA, a, r0); + MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2); + test252(mh_a, a, r0); } public void test252(MethodHandle mh, Object[] a, Object r0) throws Throwable { Object r; @@ -686,6 +806,43 @@ // xF8, xF9, xFA, xFB, xFC); } + static Object hashArguments_253_a(Object x00, Object[] x01_FB, Object xFC) { + return Objects.hash( + // + x00, x01_FB[0], x01_FB[1], x01_FB[2], x01_FB[3], x01_FB[4], x01_FB[5], x01_FB[6], x01_FB[7], x01_FB[8], + x01_FB[9], x01_FB[10], x01_FB[11], x01_FB[12], x01_FB[13], x01_FB[14], x01_FB[15], x01_FB[16], + x01_FB[17], x01_FB[18], x01_FB[19], x01_FB[20], x01_FB[21], x01_FB[22], x01_FB[23], x01_FB[24], + x01_FB[25], x01_FB[26], x01_FB[27], x01_FB[28], x01_FB[29], x01_FB[30], x01_FB[31], x01_FB[32], + x01_FB[33], x01_FB[34], x01_FB[35], x01_FB[36], x01_FB[37], x01_FB[38], x01_FB[39], x01_FB[40], + x01_FB[41], x01_FB[42], x01_FB[43], x01_FB[44], x01_FB[45], x01_FB[46], x01_FB[47], x01_FB[48], + x01_FB[49], x01_FB[50], x01_FB[51], x01_FB[52], x01_FB[53], x01_FB[54], x01_FB[55], x01_FB[56], + x01_FB[57], x01_FB[58], x01_FB[59], x01_FB[60], x01_FB[61], x01_FB[62], x01_FB[63], x01_FB[64], + x01_FB[65], x01_FB[66], x01_FB[67], x01_FB[68], x01_FB[69], x01_FB[70], x01_FB[71], x01_FB[72], + x01_FB[73], x01_FB[74], x01_FB[75], x01_FB[76], x01_FB[77], x01_FB[78], x01_FB[79], x01_FB[80], + x01_FB[81], x01_FB[82], x01_FB[83], x01_FB[84], x01_FB[85], x01_FB[86], x01_FB[87], x01_FB[88], + x01_FB[89], x01_FB[90], x01_FB[91], x01_FB[92], x01_FB[93], x01_FB[94], x01_FB[95], x01_FB[96], + x01_FB[97], x01_FB[98], x01_FB[99], x01_FB[100], x01_FB[101], x01_FB[102], x01_FB[103], x01_FB[104], + x01_FB[105], x01_FB[106], x01_FB[107], x01_FB[108], x01_FB[109], x01_FB[110], x01_FB[111], x01_FB[112], + x01_FB[113], x01_FB[114], x01_FB[115], x01_FB[116], x01_FB[117], x01_FB[118], x01_FB[119], x01_FB[120], + x01_FB[121], x01_FB[122], x01_FB[123], x01_FB[124], x01_FB[125], x01_FB[126], x01_FB[127], x01_FB[128], + x01_FB[129], x01_FB[130], x01_FB[131], x01_FB[132], x01_FB[133], x01_FB[134], x01_FB[135], x01_FB[136], + x01_FB[137], x01_FB[138], x01_FB[139], x01_FB[140], x01_FB[141], x01_FB[142], x01_FB[143], x01_FB[144], + x01_FB[145], x01_FB[146], x01_FB[147], x01_FB[148], x01_FB[149], x01_FB[150], x01_FB[151], x01_FB[152], + x01_FB[153], x01_FB[154], x01_FB[155], x01_FB[156], x01_FB[157], x01_FB[158], x01_FB[159], x01_FB[160], + x01_FB[161], x01_FB[162], x01_FB[163], x01_FB[164], x01_FB[165], x01_FB[166], x01_FB[167], x01_FB[168], + x01_FB[169], x01_FB[170], x01_FB[171], x01_FB[172], x01_FB[173], x01_FB[174], x01_FB[175], x01_FB[176], + x01_FB[177], x01_FB[178], x01_FB[179], x01_FB[180], x01_FB[181], x01_FB[182], x01_FB[183], x01_FB[184], + x01_FB[185], x01_FB[186], x01_FB[187], x01_FB[188], x01_FB[189], x01_FB[190], x01_FB[191], x01_FB[192], + x01_FB[193], x01_FB[194], x01_FB[195], x01_FB[196], x01_FB[197], x01_FB[198], x01_FB[199], x01_FB[200], + x01_FB[201], x01_FB[202], x01_FB[203], x01_FB[204], x01_FB[205], x01_FB[206], x01_FB[207], x01_FB[208], + x01_FB[209], x01_FB[210], x01_FB[211], x01_FB[212], x01_FB[213], x01_FB[214], x01_FB[215], x01_FB[216], + x01_FB[217], x01_FB[218], x01_FB[219], x01_FB[220], x01_FB[221], x01_FB[222], x01_FB[223], x01_FB[224], + x01_FB[225], x01_FB[226], x01_FB[227], x01_FB[228], x01_FB[229], x01_FB[230], x01_FB[231], x01_FB[232], + x01_FB[233], x01_FB[234], x01_FB[235], x01_FB[236], x01_FB[237], x01_FB[238], x01_FB[239], x01_FB[240], + x01_FB[241], x01_FB[242], x01_FB[243], x01_FB[244], x01_FB[245], x01_FB[246], x01_FB[247], x01_FB[248], + // + x01_FB[249], x01_FB[250], xFC); + } @Test public void test253() throws Throwable { @@ -720,6 +877,8 @@ test253(mh, a, r0); MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY); test253(mh_CA, a, r0); + MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2); + test253(mh_a, a, r0); } public void test253(MethodHandle mh, Object[] a, Object r0) throws Throwable { Object r; @@ -899,6 +1058,43 @@ // xF8, xF9, xFA, xFB, xFC, xFD); } + static Object hashArguments_254_a(Object x00, Object[] x01_FC, Object xFD) { + return Objects.hash( + // + x00, x01_FC[0], x01_FC[1], x01_FC[2], x01_FC[3], x01_FC[4], x01_FC[5], x01_FC[6], x01_FC[7], x01_FC[8], + x01_FC[9], x01_FC[10], x01_FC[11], x01_FC[12], x01_FC[13], x01_FC[14], x01_FC[15], x01_FC[16], + x01_FC[17], x01_FC[18], x01_FC[19], x01_FC[20], x01_FC[21], x01_FC[22], x01_FC[23], x01_FC[24], + x01_FC[25], x01_FC[26], x01_FC[27], x01_FC[28], x01_FC[29], x01_FC[30], x01_FC[31], x01_FC[32], + x01_FC[33], x01_FC[34], x01_FC[35], x01_FC[36], x01_FC[37], x01_FC[38], x01_FC[39], x01_FC[40], + x01_FC[41], x01_FC[42], x01_FC[43], x01_FC[44], x01_FC[45], x01_FC[46], x01_FC[47], x01_FC[48], + x01_FC[49], x01_FC[50], x01_FC[51], x01_FC[52], x01_FC[53], x01_FC[54], x01_FC[55], x01_FC[56], + x01_FC[57], x01_FC[58], x01_FC[59], x01_FC[60], x01_FC[61], x01_FC[62], x01_FC[63], x01_FC[64], + x01_FC[65], x01_FC[66], x01_FC[67], x01_FC[68], x01_FC[69], x01_FC[70], x01_FC[71], x01_FC[72], + x01_FC[73], x01_FC[74], x01_FC[75], x01_FC[76], x01_FC[77], x01_FC[78], x01_FC[79], x01_FC[80], + x01_FC[81], x01_FC[82], x01_FC[83], x01_FC[84], x01_FC[85], x01_FC[86], x01_FC[87], x01_FC[88], + x01_FC[89], x01_FC[90], x01_FC[91], x01_FC[92], x01_FC[93], x01_FC[94], x01_FC[95], x01_FC[96], + x01_FC[97], x01_FC[98], x01_FC[99], x01_FC[100], x01_FC[101], x01_FC[102], x01_FC[103], x01_FC[104], + x01_FC[105], x01_FC[106], x01_FC[107], x01_FC[108], x01_FC[109], x01_FC[110], x01_FC[111], x01_FC[112], + x01_FC[113], x01_FC[114], x01_FC[115], x01_FC[116], x01_FC[117], x01_FC[118], x01_FC[119], x01_FC[120], + x01_FC[121], x01_FC[122], x01_FC[123], x01_FC[124], x01_FC[125], x01_FC[126], x01_FC[127], x01_FC[128], + x01_FC[129], x01_FC[130], x01_FC[131], x01_FC[132], x01_FC[133], x01_FC[134], x01_FC[135], x01_FC[136], + x01_FC[137], x01_FC[138], x01_FC[139], x01_FC[140], x01_FC[141], x01_FC[142], x01_FC[143], x01_FC[144], + x01_FC[145], x01_FC[146], x01_FC[147], x01_FC[148], x01_FC[149], x01_FC[150], x01_FC[151], x01_FC[152], + x01_FC[153], x01_FC[154], x01_FC[155], x01_FC[156], x01_FC[157], x01_FC[158], x01_FC[159], x01_FC[160], + x01_FC[161], x01_FC[162], x01_FC[163], x01_FC[164], x01_FC[165], x01_FC[166], x01_FC[167], x01_FC[168], + x01_FC[169], x01_FC[170], x01_FC[171], x01_FC[172], x01_FC[173], x01_FC[174], x01_FC[175], x01_FC[176], + x01_FC[177], x01_FC[178], x01_FC[179], x01_FC[180], x01_FC[181], x01_FC[182], x01_FC[183], x01_FC[184], + x01_FC[185], x01_FC[186], x01_FC[187], x01_FC[188], x01_FC[189], x01_FC[190], x01_FC[191], x01_FC[192], + x01_FC[193], x01_FC[194], x01_FC[195], x01_FC[196], x01_FC[197], x01_FC[198], x01_FC[199], x01_FC[200], + x01_FC[201], x01_FC[202], x01_FC[203], x01_FC[204], x01_FC[205], x01_FC[206], x01_FC[207], x01_FC[208], + x01_FC[209], x01_FC[210], x01_FC[211], x01_FC[212], x01_FC[213], x01_FC[214], x01_FC[215], x01_FC[216], + x01_FC[217], x01_FC[218], x01_FC[219], x01_FC[220], x01_FC[221], x01_FC[222], x01_FC[223], x01_FC[224], + x01_FC[225], x01_FC[226], x01_FC[227], x01_FC[228], x01_FC[229], x01_FC[230], x01_FC[231], x01_FC[232], + x01_FC[233], x01_FC[234], x01_FC[235], x01_FC[236], x01_FC[237], x01_FC[238], x01_FC[239], x01_FC[240], + x01_FC[241], x01_FC[242], x01_FC[243], x01_FC[244], x01_FC[245], x01_FC[246], x01_FC[247], x01_FC[248], + // + x01_FC[249], x01_FC[250], x01_FC[251], xFD); + } @Test public void test254() throws Throwable { @@ -933,6 +1129,8 @@ test254(mh, a, r0); MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY); test254(mh_CA, a, r0); + MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2); + test254(mh_a, a, r0); } public void test254(MethodHandle mh, Object[] a, Object r0) throws Throwable { Object r; @@ -1094,6 +1292,43 @@ // xF8, xF9, xFA, xFB, xFC, xFD, xFE); } + static Object hashArguments_255_a(Object x00, Object[] x01_FD, Object xFE) { + return Objects.hash( + // + x00, x01_FD[0], x01_FD[1], x01_FD[2], x01_FD[3], x01_FD[4], x01_FD[5], x01_FD[6], x01_FD[7], x01_FD[8], + x01_FD[9], x01_FD[10], x01_FD[11], x01_FD[12], x01_FD[13], x01_FD[14], x01_FD[15], x01_FD[16], + x01_FD[17], x01_FD[18], x01_FD[19], x01_FD[20], x01_FD[21], x01_FD[22], x01_FD[23], x01_FD[24], + x01_FD[25], x01_FD[26], x01_FD[27], x01_FD[28], x01_FD[29], x01_FD[30], x01_FD[31], x01_FD[32], + x01_FD[33], x01_FD[34], x01_FD[35], x01_FD[36], x01_FD[37], x01_FD[38], x01_FD[39], x01_FD[40], + x01_FD[41], x01_FD[42], x01_FD[43], x01_FD[44], x01_FD[45], x01_FD[46], x01_FD[47], x01_FD[48], + x01_FD[49], x01_FD[50], x01_FD[51], x01_FD[52], x01_FD[53], x01_FD[54], x01_FD[55], x01_FD[56], + x01_FD[57], x01_FD[58], x01_FD[59], x01_FD[60], x01_FD[61], x01_FD[62], x01_FD[63], x01_FD[64], + x01_FD[65], x01_FD[66], x01_FD[67], x01_FD[68], x01_FD[69], x01_FD[70], x01_FD[71], x01_FD[72], + x01_FD[73], x01_FD[74], x01_FD[75], x01_FD[76], x01_FD[77], x01_FD[78], x01_FD[79], x01_FD[80], + x01_FD[81], x01_FD[82], x01_FD[83], x01_FD[84], x01_FD[85], x01_FD[86], x01_FD[87], x01_FD[88], + x01_FD[89], x01_FD[90], x01_FD[91], x01_FD[92], x01_FD[93], x01_FD[94], x01_FD[95], x01_FD[96], + x01_FD[97], x01_FD[98], x01_FD[99], x01_FD[100], x01_FD[101], x01_FD[102], x01_FD[103], x01_FD[104], + x01_FD[105], x01_FD[106], x01_FD[107], x01_FD[108], x01_FD[109], x01_FD[110], x01_FD[111], x01_FD[112], + x01_FD[113], x01_FD[114], x01_FD[115], x01_FD[116], x01_FD[117], x01_FD[118], x01_FD[119], x01_FD[120], + x01_FD[121], x01_FD[122], x01_FD[123], x01_FD[124], x01_FD[125], x01_FD[126], x01_FD[127], x01_FD[128], + x01_FD[129], x01_FD[130], x01_FD[131], x01_FD[132], x01_FD[133], x01_FD[134], x01_FD[135], x01_FD[136], + x01_FD[137], x01_FD[138], x01_FD[139], x01_FD[140], x01_FD[141], x01_FD[142], x01_FD[143], x01_FD[144], + x01_FD[145], x01_FD[146], x01_FD[147], x01_FD[148], x01_FD[149], x01_FD[150], x01_FD[151], x01_FD[152], + x01_FD[153], x01_FD[154], x01_FD[155], x01_FD[156], x01_FD[157], x01_FD[158], x01_FD[159], x01_FD[160], + x01_FD[161], x01_FD[162], x01_FD[163], x01_FD[164], x01_FD[165], x01_FD[166], x01_FD[167], x01_FD[168], + x01_FD[169], x01_FD[170], x01_FD[171], x01_FD[172], x01_FD[173], x01_FD[174], x01_FD[175], x01_FD[176], + x01_FD[177], x01_FD[178], x01_FD[179], x01_FD[180], x01_FD[181], x01_FD[182], x01_FD[183], x01_FD[184], + x01_FD[185], x01_FD[186], x01_FD[187], x01_FD[188], x01_FD[189], x01_FD[190], x01_FD[191], x01_FD[192], + x01_FD[193], x01_FD[194], x01_FD[195], x01_FD[196], x01_FD[197], x01_FD[198], x01_FD[199], x01_FD[200], + x01_FD[201], x01_FD[202], x01_FD[203], x01_FD[204], x01_FD[205], x01_FD[206], x01_FD[207], x01_FD[208], + x01_FD[209], x01_FD[210], x01_FD[211], x01_FD[212], x01_FD[213], x01_FD[214], x01_FD[215], x01_FD[216], + x01_FD[217], x01_FD[218], x01_FD[219], x01_FD[220], x01_FD[221], x01_FD[222], x01_FD[223], x01_FD[224], + x01_FD[225], x01_FD[226], x01_FD[227], x01_FD[228], x01_FD[229], x01_FD[230], x01_FD[231], x01_FD[232], + x01_FD[233], x01_FD[234], x01_FD[235], x01_FD[236], x01_FD[237], x01_FD[238], x01_FD[239], x01_FD[240], + x01_FD[241], x01_FD[242], x01_FD[243], x01_FD[244], x01_FD[245], x01_FD[246], x01_FD[247], x01_FD[248], + // + x01_FD[249], x01_FD[250], x01_FD[251], x01_FD[252], xFE); + } @Test public void test255() throws Throwable { @@ -1163,5 +1398,38 @@ } catch (IllegalArgumentException ex) { System.out.println("OK: "+ex); } + MethodHandle mh_a; + try { + mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2); + throw new AssertionError("should not create an arity 255 collector method handle"); + } catch (IllegalArgumentException ex) { + System.out.println("OK: "+ex); + mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-3); + } + try { + r = mh_a.invokeExact( + // + a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], a[0x05], a[0x06], a[0x07], a[0x08], a[0x09], a[0x0A], a[0x0B], a[0x0C], a[0x0D], a[0x0E], a[0x0F], + a[0x10], a[0x11], a[0x12], a[0x13], a[0x14], a[0x15], a[0x16], a[0x17], a[0x18], a[0x19], a[0x1A], a[0x1B], a[0x1C], a[0x1D], a[0x1E], a[0x1F], + a[0x20], a[0x21], a[0x22], a[0x23], a[0x24], a[0x25], a[0x26], a[0x27], a[0x28], a[0x29], a[0x2A], a[0x2B], a[0x2C], a[0x2D], a[0x2E], a[0x2F], + a[0x30], a[0x31], a[0x32], a[0x33], a[0x34], a[0x35], a[0x36], a[0x37], a[0x38], a[0x39], a[0x3A], a[0x3B], a[0x3C], a[0x3D], a[0x3E], a[0x3F], + a[0x40], a[0x41], a[0x42], a[0x43], a[0x44], a[0x45], a[0x46], a[0x47], a[0x48], a[0x49], a[0x4A], a[0x4B], a[0x4C], a[0x4D], a[0x4E], a[0x4F], + a[0x50], a[0x51], a[0x52], a[0x53], a[0x54], a[0x55], a[0x56], a[0x57], a[0x58], a[0x59], a[0x5A], a[0x5B], a[0x5C], a[0x5D], a[0x5E], a[0x5F], + a[0x60], a[0x61], a[0x62], a[0x63], a[0x64], a[0x65], a[0x66], a[0x67], a[0x68], a[0x69], a[0x6A], a[0x6B], a[0x6C], a[0x6D], a[0x6E], a[0x6F], + a[0x70], a[0x71], a[0x72], a[0x73], a[0x74], a[0x75], a[0x76], a[0x77], a[0x78], a[0x79], a[0x7A], a[0x7B], a[0x7C], a[0x7D], a[0x7E], a[0x7F], + a[0x80], a[0x81], a[0x82], a[0x83], a[0x84], a[0x85], a[0x86], a[0x87], a[0x88], a[0x89], a[0x8A], a[0x8B], a[0x8C], a[0x8D], a[0x8E], a[0x8F], + a[0x90], a[0x91], a[0x92], a[0x93], a[0x94], a[0x95], a[0x96], a[0x97], a[0x98], a[0x99], a[0x9A], a[0x9B], a[0x9C], a[0x9D], a[0x9E], a[0x9F], + a[0xA0], a[0xA1], a[0xA2], a[0xA3], a[0xA4], a[0xA5], a[0xA6], a[0xA7], a[0xA8], a[0xA9], a[0xAA], a[0xAB], a[0xAC], a[0xAD], a[0xAE], a[0xAF], + a[0xB0], a[0xB1], a[0xB2], a[0xB3], a[0xB4], a[0xB5], a[0xB6], a[0xB7], a[0xB8], a[0xB9], a[0xBA], a[0xBB], a[0xBC], a[0xBD], a[0xBE], a[0xBF], + a[0xC0], a[0xC1], a[0xC2], a[0xC3], a[0xC4], a[0xC5], a[0xC6], a[0xC7], a[0xC8], a[0xC9], a[0xCA], a[0xCB], a[0xCC], a[0xCD], a[0xCE], a[0xCF], + a[0xD0], a[0xD1], a[0xD2], a[0xD3], a[0xD4], a[0xD5], a[0xD6], a[0xD7], a[0xD8], a[0xD9], a[0xDA], a[0xDB], a[0xDC], a[0xDD], a[0xDE], a[0xDF], + a[0xE0], a[0xE1], a[0xE2], a[0xE3], a[0xE4], a[0xE5], a[0xE6], a[0xE7], a[0xE8], a[0xE9], a[0xEA], a[0xEB], a[0xEC], a[0xED], a[0xEE], a[0xEF], + a[0xF0], a[0xF1], a[0xF2], a[0xF3], a[0xF4], a[0xF5], a[0xF6], a[0xF7], + // + a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD], a[0xFE]); + throw new AssertionError("should not call an arity 255 collector method handle"); + } catch (LinkageError ex) { + System.out.println("OK: "+ex); + } } } --- old/test/java/lang/invoke/MethodHandlesTest.java 2015-11-19 22:14:06.000000000 +0100 +++ new/test/java/lang/invoke/MethodHandlesTest.java 2015-11-19 22:14:06.000000000 +0100 @@ -32,6 +32,7 @@ import test.java.lang.invoke.remote.RemoteExample; import java.lang.invoke.*; +import static java.lang.invoke.MethodType.methodType; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.*; import java.util.*; @@ -448,6 +449,7 @@ } public static interface IntExample { public void v0(); + public default void vd() { called("vd", this); } public static class Impl implements IntExample { public void v0() { called("Int/v0", this); } final String name; @@ -719,9 +721,10 @@ public void testFindSpecial0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("findSpecial"); - testFindSpecial(SubExample.class, Example.class, void.class, "v0"); - testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0"); - testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0"); + testFindSpecial(SubExample.class, Example.class, void.class, false, "v0"); + testFindSpecial(SubExample.class, Example.class, void.class, false, "pkg_v0"); + testFindSpecial(RemoteExample.class, PubExample.class, void.class, false, "Pub/pro_v0"); + testFindSpecial(Example.class, IntExample.class, void.class, true, "vd"); // Do some negative testing: for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) { testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0"); @@ -729,11 +732,12 @@ testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "", int.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "", Void.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0"); + testFindSpecial(false, lookup, Example.class, IntExample.class, void.class, "v0"); } } void testFindSpecial(Class specialCaller, - Class defc, Class ret, String name, Class... params) throws Throwable { + Class defc, Class ret, boolean dflt, String name, Class... params) throws Throwable { if (specialCaller == RemoteExample.class) { testFindSpecial(false, EXAMPLE, specialCaller, defc, ret, name, params); testFindSpecial(false, PRIVATE, specialCaller, defc, ret, name, params); @@ -742,11 +746,11 @@ testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); return; } - testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params); - testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params); - testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params); - testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params); - testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); + testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params); + testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params); + testFindSpecial(false || dflt, PACKAGE, specialCaller, defc, ret, name, params); + testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params); + testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); } void testFindSpecial(boolean positive, Lookup lookup, Class specialCaller, Class defc, Class ret, String name, Class... params) throws Throwable { @@ -1834,6 +1838,7 @@ @Test // SLOW public void testSpreadArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0); + CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments1); } public void testSpreadArguments0() throws Throwable { @@ -1842,44 +1847,27 @@ for (Class argType : new Class[]{Object.class, Integer.class, int.class}) { if (verbosity >= 3) System.out.println("spreadArguments "+argType); + Class arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); for (int nargs = 0; nargs < 50; nargs++) { if (CAN_TEST_LIGHTLY && nargs > 11) break; for (int pos = 0; pos <= nargs; pos++) { if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) continue; - testSpreadArguments(argType, pos, nargs); + testSpreadArguments(argType, arrayType, pos, nargs); } } } } - public void testSpreadArguments(Class argType, int pos, int nargs) throws Throwable { + public void testSpreadArguments(Class argType, Class arrayType, int pos, int nargs) throws Throwable { countTest(); - Class arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); MethodHandle target2 = varargsArray(arrayType, nargs); MethodHandle target = target2.asType(target2.type().generic()); if (verbosity >= 3) System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); Object[] args = randomArgs(target2.type().parameterArray()); // make sure the target does what we think it does: - if (pos == 0 && nargs < 5 && !argType.isPrimitive()) { - Object[] check = (Object[]) target.invokeWithArguments(args); - assertArrayEquals(args, check); - switch (nargs) { - case 0: - check = (Object[]) (Object) target.invokeExact(); - assertArrayEquals(args, check); - break; - case 1: - check = (Object[]) (Object) target.invokeExact(args[0]); - assertArrayEquals(args, check); - break; - case 2: - check = (Object[]) (Object) target.invokeExact(args[0], args[1]); - assertArrayEquals(args, check); - break; - } - } + checkTarget(argType, pos, nargs, target, args); List> newParams = new ArrayList<>(target2.type().parameterList()); { // modify newParams in place List> spreadParams = newParams.subList(pos, nargs); @@ -1898,6 +1886,78 @@ args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); returnValue = result.invokeWithArguments(args1); } + checkReturnValue(argType, args, result, returnValue); + } + public void testSpreadArguments1() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("spreadArguments/pos"); + for (Class argType : new Class[]{Object.class, Integer.class, int.class}) { + if (verbosity >= 3) + System.out.println("spreadArguments "+argType); + Class arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); + for (int nargs = 0; nargs < 50; nargs++) { + if (CAN_TEST_LIGHTLY && nargs > 11) break; + for (int pos = 0; pos <= nargs; pos++) { + if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; + if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) + continue; + for (int spr = 1; spr < nargs - pos; ++spr) { + if (spr > 4 && spr != 7 && spr != 11 && spr != 20 && spr < nargs - pos - 4) continue; + testSpreadArguments(argType, arrayType, pos, spr, nargs); + } + } + } + } + } + public void testSpreadArguments(Class argType, Class arrayType, int pos, int spread, int nargs) throws Throwable { + countTest(); + MethodHandle target2 = varargsArray(arrayType, nargs); + MethodHandle target = target2.asType(target2.type().generic()); + if (verbosity >= 3) + System.out.println("spread into " + target2 + " [" + pos + ".." + (pos + spread) + "["); + Object[] args = randomArgs(target2.type().parameterArray()); + // make sure the target does what we think it does: + checkTarget(argType, pos, nargs, target, args); + List> newParams = new ArrayList<>(target2.type().parameterList()); + { // modify newParams in place + List> spreadParams = newParams.subList(pos, pos + spread); + spreadParams.clear(); + spreadParams.add(arrayType); + } + MethodType newType = MethodType.methodType(arrayType, newParams); + MethodHandle result = target2.asSpreader(pos, arrayType, spread); + assert (result.type() == newType) : Arrays.asList(result, newType); + result = result.asType(newType.generic()); + // args1 has nargs-spread entries, plus one for the to-be-spread array + int args1Length = nargs - (spread - 1); + Object[] args1 = new Object[args1Length]; + System.arraycopy(args, 0, args1, 0, pos); + args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, pos + spread)); + System.arraycopy(args, pos + spread, args1, pos + 1, nargs - spread - pos); + Object returnValue = result.invokeWithArguments(args1); + checkReturnValue(argType, args, result, returnValue); + } + private static void checkTarget(Class argType, int pos, int nargs, MethodHandle target, Object[] args) throws Throwable { + if (pos == 0 && nargs < 5 && !argType.isPrimitive()) { + Object[] check = (Object[]) target.invokeWithArguments(args); + assertArrayEquals(args, check); + switch (nargs) { + case 0: + check = (Object[]) (Object) target.invokeExact(); + assertArrayEquals(args, check); + break; + case 1: + check = (Object[]) (Object) target.invokeExact(args[0]); + assertArrayEquals(args, check); + break; + case 2: + check = (Object[]) (Object) target.invokeExact(args[0], args[1]); + assertArrayEquals(args, check); + break; + } + } + } + private static void checkReturnValue(Class argType, Object[] args, MethodHandle result, Object returnValue) { String argstr = Arrays.toString(args); if (!argType.isPrimitive()) { Object[] rv = (Object[]) returnValue; @@ -1932,6 +1992,7 @@ @Test // SLOW public void testAsCollector() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testAsCollector0); + CodeCacheOverflowProcessor.runMHTest(this::testAsCollector1); } public void testAsCollector0() throws Throwable { @@ -1974,6 +2035,51 @@ // collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]); assertArrayEquals(collectedArgs, returnValue); } + public void testAsCollector1() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("asCollector/pos"); + for (Class argType : new Class[]{Object.class, Integer.class, int.class}) { + if (verbosity >= 3) + System.out.println("asCollector/pos "+argType); + for (int nargs = 0; nargs < 50; nargs++) { + if (CAN_TEST_LIGHTLY && nargs > 11) break; + for (int pos = 0; pos <= nargs; pos++) { + if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; + if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) + continue; + for (int coll = 1; coll < nargs - pos; ++coll) { + if (coll > 4 && coll != 7 && coll != 11 && coll != 20 && coll < nargs - pos - 4) continue; + testAsCollector(argType, pos, coll, nargs); + } + } + } + } + } + public void testAsCollector(Class argType, int pos, int collect, int nargs) throws Throwable { + countTest(); + // fake up a MH with the same type as the desired adapter: + MethodHandle fake = varargsArray(nargs); + fake = changeArgTypes(fake, argType); + MethodType newType = fake.type(); + Object[] args = randomArgs(newType.parameterArray()); + // here is what should happen: + // new arg list has "collect" less arguments, but one extra for collected arguments array + int collectedLength = nargs-(collect-1); + Object[] collectedArgs = new Object[collectedLength]; + System.arraycopy(args, 0, collectedArgs, 0, pos); + collectedArgs[pos] = Arrays.copyOfRange(args, pos, pos+collect); + System.arraycopy(args, pos+collect, collectedArgs, pos+1, args.length-(pos+collect)); + // here is the MH which will witness the collected argument part (not tail!): + MethodHandle target = varargsArray(collectedLength); + target = changeArgTypes(target, 0, pos, argType); + target = changeArgTypes(target, pos, pos+1, Object[].class); + target = changeArgTypes(target, pos+1, collectedLength, argType); + if (verbosity >= 3) + System.out.println("collect "+collect+" from "+Arrays.asList(args)+" ["+pos+".."+(pos+collect)+"["); + MethodHandle result = target.asCollector(pos, Object[].class, collect).asType(newType); + Object[] returnValue = (Object[]) result.invokeWithArguments(args); + assertArrayEquals(collectedArgs, returnValue); + } @Test // SLOW public void testInsertArguments() throws Throwable { @@ -2117,21 +2223,29 @@ public void testCollectArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("collectArguments"); - testFoldOrCollectArguments(true); + testFoldOrCollectArguments(true, false); } @Test public void testFoldArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments0); + CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments1); } public void testFoldArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("foldArguments"); - testFoldOrCollectArguments(false); + testFoldOrCollectArguments(false, false); + } + + public void testFoldArguments1() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("foldArguments/pos"); + testFoldOrCollectArguments(false, true); } - void testFoldOrCollectArguments(boolean isCollect) throws Throwable { + void testFoldOrCollectArguments(boolean isCollect, boolean withFoldPos) throws Throwable { + assert !(isCollect && withFoldPos); // exclude illegal argument combination for (Class lastType : new Class[]{ Object.class, String.class, int.class }) { for (Class collectType : new Class[]{ Object.class, String.class, int.class, void.class }) { int maxArity = 10; @@ -2146,7 +2260,7 @@ if (!mixArgs(argTypes, mix, argTypesSeen)) continue; for (int collect = 0; collect <= nargs; collect++) { for (int pos = 0; pos <= nargs - collect; pos++) { - testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect); + testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect, withFoldPos); } } } @@ -2186,13 +2300,14 @@ int pos, int fold, // position and length of the folded arguments Class combineType, // type returned from the combiner Class lastType, // type returned from the target - boolean isCollect) throws Throwable { + boolean isCollect, + boolean withFoldPos) throws Throwable { int nargs = argTypes.size(); - if (pos != 0 && !isCollect) return; // can fold only at pos=0 for now + if (pos != 0 && !isCollect && !withFoldPos) return; // test MethodHandles.foldArguments(MH,MH) only for pos=0 countTest(); List> combineArgTypes = argTypes.subList(pos, pos + fold); List> targetArgTypes = new ArrayList<>(argTypes); - if (isCollect) // does targret see arg[pos..pos+cc-1]? + if (isCollect) // does target see arg[pos..pos+cc-1]? targetArgTypes.subList(pos, pos + fold).clear(); if (combineType != void.class) targetArgTypes.add(pos, combineType); @@ -2205,7 +2320,7 @@ if (isCollect) target2 = MethodHandles.collectArguments(target, pos, combine); else - target2 = MethodHandles.foldArguments(target, combine); + target2 = withFoldPos ? MethodHandles.foldArguments(target, pos, combine) : MethodHandles.foldArguments(target, combine); // Simulate expected effect of combiner on arglist: List expectedList = new ArrayList<>(argsToPass); List argsToFold = expectedList.subList(pos, pos + fold); @@ -2541,6 +2656,203 @@ } @Test + public void testGenericLoopCombinator() throws Throwable { + CodeCacheOverflowProcessor.runMHTest(this::testGenericLoopCombinator0); + } + public void testGenericLoopCombinator0() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("loop"); + // Test as follows: + // * Have an increasing number of loop-local state. Local state type diversity grows with the number. + // * Initializers set the starting value of loop-local state from the corresponding loop argument. + // * For each local state element, there is a predicate - for all state combinations, exercise all predicates. + // * Steps modify each local state element in each iteration. + // * Finalizers group all local state elements into a resulting array. Verify end values. + // * Exercise both pre- and post-checked loops. + // Local state types, start values, predicates, and steps: + // * int a, 0, a < 7, a = a + 1 + // * double b, 7.0, b > 0.5, b = b / 2.0 + // * String c, "start", c.length <= 9, c = c + a + final Class[] argTypes = new Class[] {int.class, double.class, String.class}; + final Object[][] args = new Object[][] { + new Object[]{0 }, + new Object[]{0, 7.0 }, + new Object[]{0, 7.0, "start"} + }; + // These are the expected final state tuples for argument type tuple / predicate combinations, for pre- and + // post-checked loops: + final Object[][] preCheckedResults = new Object[][] { + new Object[]{7 }, // (int) / int + new Object[]{7, 0.0546875 }, // (int,double) / int + new Object[]{5, 0.4375 }, // (int,double) / double + new Object[]{7, 0.0546875, "start1234567"}, // (int,double,String) / int + new Object[]{5, 0.4375, "start1234" }, // (int,double,String) / double + new Object[]{6, 0.109375, "start12345" } // (int,double,String) / String + }; + final Object[][] postCheckedResults = new Object[][] { + new Object[]{7 }, // (int) / int + new Object[]{7, 0.109375 }, // (int,double) / int + new Object[]{4, 0.4375 }, // (int,double) / double + new Object[]{7, 0.109375, "start123456"}, // (int,double,String) / int + new Object[]{4, 0.4375, "start123" }, // (int,double,String) / double + new Object[]{5, 0.21875, "start12345" } // (int,double,String) / String + }; + final Lookup l = MethodHandles.lookup(); + final Class MHT = MethodHandlesTest.class; + final Class B = boolean.class; + final Class I = int.class; + final Class D = double.class; + final Class S = String.class; + final MethodHandle hip = l.findStatic(MHT, "loopIntPred", methodType(B, I)); + final MethodHandle hdp = l.findStatic(MHT, "loopDoublePred", methodType(B, I, D)); + final MethodHandle hsp = l.findStatic(MHT, "loopStringPred", methodType(B, I, D, S)); + final MethodHandle his = l.findStatic(MHT, "loopIntStep", methodType(I, I)); + final MethodHandle hds = l.findStatic(MHT, "loopDoubleStep", methodType(D, I, D)); + final MethodHandle hss = l.findStatic(MHT, "loopStringStep", methodType(S, I, D, S)); + final MethodHandle[] preds = new MethodHandle[] {hip, hdp, hsp}; + final MethodHandle[] steps = new MethodHandle[] {his, hds, hss}; + for (int nargs = 1, useResultsStart = 0; nargs <= argTypes.length; useResultsStart += nargs++) { + Class[] useArgTypes = Arrays.copyOf(argTypes, nargs, Class[].class); + MethodHandle[] usePreds = Arrays.copyOf(preds, nargs, MethodHandle[].class); + MethodHandle[] useSteps = Arrays.copyOf(steps, nargs, MethodHandle[].class); + Object[] useArgs = args[nargs - 1]; + Object[][] usePreCheckedResults = new Object[nargs][]; + Object[][] usePostCheckedResults = new Object[nargs][]; + System.arraycopy(preCheckedResults, useResultsStart, usePreCheckedResults, 0, nargs); + System.arraycopy(postCheckedResults, useResultsStart, usePostCheckedResults, 0, nargs); + testGenericLoopCombinator(nargs, useArgTypes, usePreds, useSteps, useArgs, usePreCheckedResults, + usePostCheckedResults); + } + } + void testGenericLoopCombinator(int nargs, Class[] argTypes, MethodHandle[] preds, MethodHandle[] steps, + Object[] args, Object[][] preCheckedResults, Object[][] postCheckedResults) + throws Throwable { + List> lArgTypes = Arrays.asList(argTypes); + // Predicate and step handles are passed in as arguments, initializer and finalizer handles are constructed here + // from the available information. + MethodHandle[] inits = new MethodHandle[nargs]; + for (int i = 0; i < nargs; ++i) { + MethodHandle h; + // Initializers are meant to return whatever they are passed at a given argument position. This means that + // additional arguments may have to be appended and prepended. + h = MethodHandles.identity(argTypes[i]); + if (i < nargs - 1) { + h = MethodHandles.dropArguments(h, 1, lArgTypes.subList(i + 1, nargs)); + } + if (i > 0) { + h = MethodHandles.dropArguments(h, 0, lArgTypes.subList(0, i)); + } + inits[i] = h; + } + // Finalizers are all meant to collect all of the loop-local state in a single array and return that. Local + // state is passed before the loop args. Construct such a finalizer by first taking a varargsArray collector for + // the number of local state arguments, and then appending the loop args as to-be-dropped arguments. + MethodHandle[] finis = new MethodHandle[nargs]; + MethodHandle genericFini = MethodHandles.dropArguments( + varargsArray(nargs).asType(methodType(Object[].class, lArgTypes)), nargs, lArgTypes); + Arrays.fill(finis, genericFini); + // The predicate and step handles' signatures need to be extended. They currently just accept local state args; + // append possibly missing local state args and loop args using dropArguments. + for (int i = 0; i < nargs; ++i) { + List> additionalLocalStateArgTypes = lArgTypes.subList(i + 1, nargs); + preds[i] = MethodHandles.dropArguments( + MethodHandles.dropArguments(preds[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes); + steps[i] = MethodHandles.dropArguments( + MethodHandles.dropArguments(steps[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes); + } + // Iterate over all of the predicates, using only one of them at a time. + for (int i = 0; i < nargs; ++i) { + MethodHandle[] usePreds; + if (nargs == 1) { + usePreds = preds; + } else { + // Create an all-null preds array, and only use one predicate in this iteration. The null entries will + // be substituted with true predicates by the loop combinator. + usePreds = new MethodHandle[nargs]; + usePreds[i] = preds[i]; + } + // Go for it. + if (verbosity >= 3) { + System.out.println("calling loop for argument types " + lArgTypes + " with predicate at index " + i); + if (verbosity >= 5) { + System.out.println("predicates: " + Arrays.asList(usePreds)); + } + } + MethodHandle[] preInits = new MethodHandle[nargs + 1]; + MethodHandle[] prePreds = new MethodHandle[nargs + 1]; + MethodHandle[] preSteps = new MethodHandle[nargs + 1]; + MethodHandle[] preFinis = new MethodHandle[nargs + 1]; + System.arraycopy(inits, 0, preInits, 1, nargs); + System.arraycopy(usePreds, 0, prePreds, 0, nargs); // preds are offset by 1 for pre-checked loops + System.arraycopy(steps, 0, preSteps, 1, nargs); + System.arraycopy(finis, 0, preFinis, 0, nargs); // finis are also offset by 1 for pre-checked loops + // Convert to clause-major form. + MethodHandle[][] preClauses = new MethodHandle[nargs+1][4]; + MethodHandle[][] postClauses = new MethodHandle[nargs][4]; + toClauseMajor(preClauses, preInits, preSteps, prePreds, preFinis); + toClauseMajor(postClauses, inits, steps, usePreds, finis); + MethodHandle pre = MethodHandles.loop(preClauses); + MethodHandle post = MethodHandles.loop(postClauses); + Object[] preResults = (Object[]) pre.invokeWithArguments(args); + if (verbosity >= 4) { + System.out.println("pre-checked: expected " + Arrays.asList(preCheckedResults[i]) + ", actual " + + Arrays.asList(preResults)); + } + Object[] postResults = (Object[]) post.invokeWithArguments(args); + if (verbosity >= 4) { + System.out.println("post-checked: expected " + Arrays.asList(postCheckedResults[i]) + ", actual " + + Arrays.asList(postResults)); + } + assertArrayEquals(preCheckedResults[i], preResults); + assertArrayEquals(postCheckedResults[i], postResults); + } + } + static void toClauseMajor(MethodHandle[][] clauses, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini) { + for (int i = 0; i < clauses.length; ++i) { + clauses[i][0] = init[i]; + clauses[i][1] = step[i]; + clauses[i][2] = pred[i]; + clauses[i][3] = fini[i]; + } + } + static boolean loopIntPred(int a) { + if (verbosity >= 5) { + System.out.println("int pred " + a + " -> " + (a < 7)); + } + return a < 7; + } + static boolean loopDoublePred(int a, double b) { + if (verbosity >= 5) { + System.out.println("double pred (a=" + a + ") " + b + " -> " + (b > 0.5)); + } + return b > 0.5; + } + static boolean loopStringPred(int a, double b, String c) { + if (verbosity >= 5) { + System.out.println("String pred (a=" + a + ",b=" + b + ") " + c + " -> " + (c.length() <= 9)); + } + return c.length() <= 9; + } + static int loopIntStep(int a) { + if (verbosity >= 5) { + System.out.println("int step " + a + " -> " + (a + 1)); + } + return a + 1; + } + static double loopDoubleStep(int a, double b) { + if (verbosity >= 5) { + System.out.println("double step (a=" + a + ") " + b + " -> " + (b / 2.0)); + } + return b / 2.0; + } + static String loopStringStep(int a, double b, String c) { + if (verbosity >= 5) { + System.out.println("String step (a=" + a + ",b=" + b + ") " + c + " -> " + (c + a)); + } + return c + a; + } + + @Test public void testThrowException() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testThrowException0); } @@ -2576,12 +2888,107 @@ } @Test + public void testTryFinally() throws Throwable { + CodeCacheOverflowProcessor.runMHTest(this::testTryFinally0); + } + public void testTryFinally0() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("tryFinally"); + String inputMessage = "returned"; + String augmentedMessage = "augmented"; + String thrownMessage = "thrown"; + String rethrownMessage = "rethrown"; + // Test these cases: + // * target returns, cleanup passes through + // * target returns, cleanup augments + // * target throws, cleanup augments and returns + // * target throws, cleanup augments and rethrows + MethodHandle target = MethodHandles.identity(String.class); + MethodHandle targetThrow = MethodHandles.dropArguments( + MethodHandles.throwException(String.class, Exception.class).bindTo(new Exception(thrownMessage)), 0, String.class); + MethodHandle cleanupPassThrough = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, + Throwable.class, String.class); + MethodHandle cleanupAugment = MethodHandles.dropArguments(MethodHandles.constant(String.class, augmentedMessage), + 0, Throwable.class, String.class, String.class); + MethodHandle cleanupCatch = MethodHandles.dropArguments(MethodHandles.constant(String.class, thrownMessage), 0, + Throwable.class, String.class, String.class); + MethodHandle cleanupThrow = MethodHandles.dropArguments(MethodHandles.throwException(String.class, Exception.class). + bindTo(new Exception(rethrownMessage)), 0, Throwable.class, String.class, String.class); + testTryFinally(target, cleanupPassThrough, inputMessage, inputMessage, false); + testTryFinally(target, cleanupAugment, inputMessage, augmentedMessage, false); + testTryFinally(targetThrow, cleanupCatch, inputMessage, thrownMessage, true); + testTryFinally(targetThrow, cleanupThrow, inputMessage, rethrownMessage, true); + // Test the same cases as above for void targets and cleanups. + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class C = this.getClass(); + MethodType targetType = methodType(void.class, String[].class); + MethodType cleanupType = methodType(void.class, Throwable.class, String[].class); + MethodHandle vtarget = lookup.findStatic(C, "vtarget", targetType); + MethodHandle vtargetThrow = lookup.findStatic(C, "vtargetThrow", targetType); + MethodHandle vcleanupPassThrough = lookup.findStatic(C, "vcleanupPassThrough", cleanupType); + MethodHandle vcleanupAugment = lookup.findStatic(C, "vcleanupAugment", cleanupType); + MethodHandle vcleanupCatch = lookup.findStatic(C, "vcleanupCatch", cleanupType); + MethodHandle vcleanupThrow = lookup.findStatic(C, "vcleanupThrow", cleanupType); + testTryFinally(vtarget, vcleanupPassThrough, inputMessage, inputMessage, false); + testTryFinally(vtarget, vcleanupAugment, inputMessage, augmentedMessage, false); + testTryFinally(vtargetThrow, vcleanupCatch, inputMessage, thrownMessage, true); + testTryFinally(vtargetThrow, vcleanupThrow, inputMessage, rethrownMessage, true); + } + void testTryFinally(MethodHandle target, MethodHandle cleanup, String input, String msg, boolean mustCatch) + throws Throwable { + countTest(); + MethodHandle tf = MethodHandles.tryFinally(target, cleanup); + String result = null; + boolean isVoid = target.type().returnType() == void.class; + String[] argArray = new String[]{input}; + try { + if (isVoid) { + tf.invoke(argArray); + } else { + result = (String) tf.invoke(input); + } + } catch (Throwable t) { + assertTrue(mustCatch); + assertEquals(msg, t.getMessage()); + return; + } + assertFalse(mustCatch); + if (isVoid) { + assertEquals(msg, argArray[0]); + } else { + assertEquals(msg, result); + } + } + static void vtarget(String[] a) { + // naught, akin to identity + } + static void vtargetThrow(String[] a) throws Exception { + throw new Exception("thrown"); + } + static void vcleanupPassThrough(Throwable t, String[] a) { + assertNull(t); + // naught, akin to identity + } + static void vcleanupAugment(Throwable t, String[] a) { + assertNull(t); + a[0] = "augmented"; + } + static void vcleanupCatch(Throwable t, String[] a) { + assertNotNull(t); + a[0] = "caught"; + } + static void vcleanupThrow(Throwable t, String[] a) throws Exception { + assertNotNull(t); + throw new Exception("rethrown"); + } + + @Test public void testInterfaceCast() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testInterfaceCast0); } public void testInterfaceCast0() throws Throwable { - //if (CAN_SKIP_WORKING) return; + if (CAN_SKIP_WORKING) return; startTest("interfaceCast"); assert( (((Object)"foo") instanceof CharSequence)); assert(!(((Object)"foo") instanceof Iterable)); --- /dev/null 2015-11-19 22:14:07.000000000 +0100 +++ new/test/java/lang/invoke/FindClassSecurityManager.java 2015-11-19 22:14:07.000000000 +0100 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @run main/othervm/policy=findclass.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.FindClassSecurityManager + */ + +package test.java.lang.invoke; + +import java.lang.invoke.MethodHandles; + +public class FindClassSecurityManager { + public static void main(String[] args) throws Throwable { + assert null != System.getSecurityManager(); + Class thisClass = FindClassSecurityManager.class; + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class lookedUp = lookup.findClass(thisClass.getName()); + assert thisClass == lookedUp; + Class accessed = lookup.accessClass(thisClass); + assert thisClass == accessed; + } +} --- /dev/null 2015-11-19 22:14:08.000000000 +0100 +++ new/test/java/lang/invoke/T8139885.java 2015-11-19 22:14:08.000000000 +0100 @@ -0,0 +1,1082 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @run testng/othervm -ea -esa test.java.lang.invoke.T8139885 + */ + +package test.java.lang.invoke; + +import java.io.StringWriter; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.util.*; + +import static java.lang.invoke.MethodType.methodType; + +import static org.testng.AssertJUnit.*; + +import org.testng.annotations.*; + +/** + * Example-scale and negative tests for JEP 274 extensions. + */ +public class T8139885 { + + static final Lookup LOOKUP = MethodHandles.lookup(); + + // + // Tests. + // + + @Test + public static void testLoopFac() throws Throwable { + MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc}; + MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; + MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); + assertEquals(Fac.MT_fac, loop.type()); + assertEquals(120, loop.invoke(5)); + } + + @Test + public static void testLoopFacNullInit() throws Throwable { + // null initializer for counter, should initialize to 0 + MethodHandle[] counterClause = new MethodHandle[]{null, Fac.MH_inc}; + MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; + MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); + assertEquals(Fac.MT_fac, loop.type()); + assertEquals(120, loop.invoke(5)); + } + + @Test + public static void testLoopVoid1() throws Throwable { + // construct a post-checked loop that only does one iteration and has a void body and void local state + MethodHandle loop = MethodHandles.loop(new MethodHandle[]{Empty.MH_f, Empty.MH_f, Empty.MH_pred, null}); + assertEquals(MethodType.methodType(void.class), loop.type()); + loop.invoke(); + } + + @Test + public static void testLoopVoid2() throws Throwable { + // construct a post-checked loop that only does one iteration and has a void body and void local state, + // initialized implicitly from the step type + MethodHandle loop = MethodHandles.loop(new MethodHandle[]{null, Empty.MH_f, Empty.MH_pred, null}); + assertEquals(MethodType.methodType(void.class), loop.type()); + loop.invoke(); + } + + @Test + public static void testLoopFacWithVoidState() throws Throwable { + // like testLoopFac, but with additional void state that outputs a dot + MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc}; + MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; + MethodHandle[] dotClause = new MethodHandle[]{null, Fac.MH_dot}; + MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause, dotClause); + assertEquals(Fac.MT_fac, loop.type()); + assertEquals(120, loop.invoke(5)); + } + + @Test + public static void testLoopNegative() throws Throwable { + MethodHandle mh_loop = + LOOKUP.findStatic(MethodHandles.class, "loop", methodType(MethodHandle.class, MethodHandle[][].class)); + MethodHandle i0 = MethodHandles.constant(int.class, 0); + MethodHandle ii = MethodHandles.dropArguments(i0, 0, int.class, int.class); + MethodHandle id = MethodHandles.dropArguments(i0, 0, int.class, double.class); + MethodHandle i3 = MethodHandles.dropArguments(i0, 0, int.class, int.class, int.class); + List inits = Arrays.asList(ii, id, i3); + List> ints = Arrays.asList(int.class, int.class, int.class); + List finis = Arrays.asList(Fac.MH_fin, Fac.MH_inc, Counted.MH_step); + List preds1 = Arrays.asList(null, null, null); + List preds2 = Arrays.asList(null, Fac.MH_fin, null); + MethodHandle eek = MethodHandles.dropArguments(i0, 0, int.class, int.class, double.class); + List nesteps = Arrays.asList(Fac.MH_inc, eek, Fac.MH_dot); + List nepreds = Arrays.asList(null, Fac.MH_pred, null); + List nefinis = Arrays.asList(null, Fac.MH_fin, null); + MethodHandle[][][] cases = { + null, + {}, + {{null, Fac.MH_inc}, {Fac.MH_one, null, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}}, + {{null, Fac.MH_inc}, null}, + {{Fac.MH_zero, Fac.MH_dot}}, + {{ii}, {id}, {i3}}, + {{null, Fac.MH_inc, null, Fac.MH_fin}, {null, Fac.MH_inc, null, Fac.MH_inc}, + {null, Counted.MH_start, null, Counted.MH_step}}, + {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, null, Fac.MH_fin}, {null, Fac.MH_dot}}, + {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, Fac.MH_fin, Fac.MH_fin}, {null, Fac.MH_dot}}, + {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, eek, Fac.MH_pred, Fac.MH_fin}, {null, Fac.MH_dot}} + }; + String[] messages = { + "null or no clauses passed", + "null or no clauses passed", + "All loop clauses must be represented as MethodHandle arrays with at most 4 elements.", + "null clauses are not allowed", + "clause 0: init and step return types must match: int != void", + "found non-effectively identical init parameter type lists: " + inits + " (common suffix: " + ints + ")", + "found non-identical finalizer return types: " + finis + " (return type: int)", + "no predicate found: " + preds1, + "predicates must have boolean return type: " + preds2, + "found non-effectively identical parameter type lists:\nstep: " + nesteps + "\npred: " + nepreds + + "\nfini: " + nefinis + " (common parameter sequence: " + ints + ")" + }; + for (int i = 0; i < cases.length; ++i) { + boolean caught = false; + try { + mh_loop.invokeWithArguments(cases[i]); + } catch (IllegalArgumentException iae) { + assertEquals(messages[i], iae.getMessage()); + caught = true; + } + assertTrue(caught); + } + } + + @Test + public static void testWhileLoop() throws Throwable { + // int i = 0; while (i < limit) { ++i; } return i; => limit + MethodHandle loop = MethodHandles.whileLoop(While.MH_zero, While.MH_pred, While.MH_step); + assertEquals(While.MT_while, loop.type()); + assertEquals(23, loop.invoke(23)); + } + + @Test + public static void testWhileLoopNoIteration() throws Throwable { + // a while loop that never executes its body because the predicate evaluates to false immediately + MethodHandle loop = MethodHandles.whileLoop(While.MH_initString, While.MH_predString, While.MH_stepString); + assertEquals(While.MT_string, loop.type()); + assertEquals("a", loop.invoke()); + } + + @Test + public static void testDoWhileLoop() throws Throwable { + // int i = 0; do { ++i; } while (i < limit); return i; => limit + MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zero, While.MH_step, While.MH_pred); + assertEquals(While.MT_while, loop.type()); + assertEquals(23, loop.invoke(23)); + } + + @Test + public static void testWhileZip() throws Throwable { + MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zipInitZip, While.MH_zipStep, While.MH_zipPred); + assertEquals(While.MT_zip, loop.type()); + List a = Arrays.asList("a", "b", "c", "d"); + List b = Arrays.asList("e", "f", "g", "h"); + List zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h"); + assertEquals(zipped, (List) loop.invoke(a.iterator(), b.iterator())); + } + + @Test + public static void testCountedLoop() throws Throwable { + // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; => a variation on a well known theme + MethodHandle fit13 = MethodHandles.constant(int.class, 13); + MethodHandle loop = MethodHandles.countedLoop(fit13, Counted.MH_start, Counted.MH_step); + assertEquals(Counted.MT_counted, loop.type()); + assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); + } + + @Test + public static void testCountedArrayLoop() throws Throwable { + // int[] a = new int[]{0}; for (int i = 0; i < 13; ++i) { ++a[0]; } => a[0] == 13 + MethodHandle fit13 = MethodHandles.dropArguments(MethodHandles.constant(int.class, 13), 0, int[].class); + MethodHandle loop = MethodHandles.countedLoop(fit13, null, Counted.MH_stepUpdateArray); + assertEquals(Counted.MT_arrayCounted, loop.type()); + int[] a = new int[]{0}; + loop.invoke(a); + assertEquals(13, a[0]); + } + + @Test + public static void testCountedPrintingLoop() throws Throwable { + MethodHandle fit5 = MethodHandles.constant(int.class, 5); + MethodHandle loop = MethodHandles.countedLoop(fit5, null, Counted.MH_printHello); + assertEquals(Counted.MT_countedPrinting, loop.type()); + loop.invoke(); + } + + @Test + public static void testCountedRangeLoop() throws Throwable { + // String s = "Lambdaman!"; for (int i = -5; i < 8; ++i) { s = "na " + s; } return s; => a well known theme + MethodHandle fitm5 = MethodHandles.dropArguments(Counted.MH_m5, 0, String.class); + MethodHandle fit8 = MethodHandles.dropArguments(Counted.MH_8, 0, String.class); + MethodHandle loop = MethodHandles.countedLoop(fitm5, fit8, Counted.MH_start, Counted.MH_step); + assertEquals(Counted.MT_counted, loop.type()); + assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); + } + + @Test + public static void testIterateSum() throws Throwable { + // Integer[] a = new Integer[]{1,2,3,4,5,6}; int sum = 0; for (int e : a) { sum += e; } return sum; => 21 + MethodHandle loop = MethodHandles.iteratedLoop(Iterate.MH_sumIterator, Iterate.MH_sumInit, Iterate.MH_sumStep); + assertEquals(Iterate.MT_sum, loop.type()); + assertEquals(21, loop.invoke(new Integer[]{1, 2, 3, 4, 5, 6})); + } + + @Test + public static void testIterateReverse() throws Throwable { + MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_reverseInit, Iterate.MH_reverseStep); + assertEquals(Iterate.MT_reverse, loop.type()); + List list = Arrays.asList("a", "b", "c", "d", "e"); + List reversedList = Arrays.asList("e", "d", "c", "b", "a"); + assertEquals(reversedList, (List) loop.invoke(list)); + } + + @Test + public static void testIterateLength() throws Throwable { + MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_lengthInit, Iterate.MH_lengthStep); + assertEquals(Iterate.MT_length, loop.type()); + List list = Arrays.asList(23.0, 148.0, 42.0); + assertEquals(list.size(), (int) loop.invoke(list)); + } + + @Test + public static void testIterateMap() throws Throwable { + MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_mapInit, Iterate.MH_mapStep); + assertEquals(Iterate.MT_map, loop.type()); + List list = Arrays.asList("Hello", "world", "!"); + List upList = Arrays.asList("HELLO", "WORLD", "!"); + assertEquals(upList, (List) loop.invoke(list)); + } + + @Test + public static void testIteratePrint() throws Throwable { + MethodHandle loop = MethodHandles.iteratedLoop(null, null, Iterate.MH_printStep); + assertEquals(Iterate.MT_print, loop.type()); + loop.invoke(Arrays.asList("hello", "world")); + } + + @Test + public static void testIterateNullBody() { + boolean caught = false; + try { + MethodHandles.iteratedLoop(MethodHandles.identity(int.class), MethodHandles.identity(int.class), null); + } catch (IllegalArgumentException iae) { + assertEquals("iterated loop body must not be null", iae.getMessage()); + caught = true; + } + assertTrue(caught); + } + + @Test + public static void testTryFinally() throws Throwable { + MethodHandle hello = MethodHandles.tryFinally(TryFinally.MH_greet, TryFinally.MH_exclaim); + assertEquals(TryFinally.MT_hello, hello.type()); + assertEquals("Hello, world!", hello.invoke("world")); + } + + @Test + public static void testTryFinallyVoid() throws Throwable { + MethodHandle tfVoid = MethodHandles.tryFinally(TryFinally.MH_print, TryFinally.MH_printMore); + assertEquals(TryFinally.MT_printHello, tfVoid.type()); + tfVoid.invoke("world"); + } + + @Test + public static void testTryFinallySublist() throws Throwable { + MethodHandle helloMore = MethodHandles.tryFinally(TryFinally.MH_greetMore, TryFinally.MH_exclaimMore); + assertEquals(TryFinally.MT_moreHello, helloMore.type()); + assertEquals("Hello, world and universe (but world first)!", helloMore.invoke("world", "universe")); + } + + @Test + public static void testTryFinallyNegative() { + MethodHandle intid = MethodHandles.identity(int.class); + MethodHandle intco = MethodHandles.constant(int.class, 0); + MethodHandle errTarget = MethodHandles.dropArguments(intco, 0, int.class, double.class, String.class, int.class); + MethodHandle errCleanup = MethodHandles.dropArguments(MethodHandles.constant(int.class, 0), 0, Throwable.class, + int.class, double.class, Object.class); + MethodHandle[][] cases = { + {intid, MethodHandles.identity(double.class)}, + {intid, MethodHandles.dropArguments(intid, 0, String.class)}, + {intid, MethodHandles.dropArguments(intid, 0, Throwable.class, double.class)}, + {errTarget, errCleanup} + }; + String[] messages = { + "target and return types must match: double != int", + "cleanup first argument and Throwable must match: (String,int)int != class java.lang.Throwable", + "cleanup second argument and target return type must match: (Throwable,double,int)int != int", + "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " + + errCleanup.type() + " != " + errTarget.type() + }; + for (int i = 0; i < cases.length; ++i) { + boolean caught = false; + try { + MethodHandles.tryFinally(cases[i][0], cases[i][1]); + } catch (IllegalArgumentException iae) { + assertEquals(messages[i], iae.getMessage()); + caught = true; + } + assertTrue(caught); + } + } + + @Test + public static void testFold0a() throws Throwable { + // equivalence to foldArguments(MethodHandle,MethodHandle) + MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 0, Fold.MH_adder); + assertEquals(Fold.MT_folded1, fold.type()); + assertEquals(720, (int) fold.invoke(3, 4, 5)); + } + + @Test + public static void testFold1a() throws Throwable { + // test foldArguments for folding position 1 + MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 1, Fold.MH_adder1); + assertEquals(Fold.MT_folded1, fold.type()); + assertEquals(540, (int) fold.invoke(3, 4, 5)); + } + + @Test + public static void testFold0b() throws Throwable { + // test foldArguments equivalence with multiple types + MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 0, Fold.MH_comb); + assertEquals(Fold.MT_folded2, fold.type()); + assertEquals(23, (int) fold.invoke("true", true, 23)); + } + + @Test + public static void testFold1b() throws Throwable { + // test folgArguments for folding position 1, with multiple types + MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 1, Fold.MH_comb2); + assertEquals(Fold.MT_folded3, fold.type()); + assertEquals(1, (int) fold.invoke(true, true, 1)); + assertEquals(-1, (int) fold.invoke(true, false, -1)); + } + + @Test + public static void testFoldArgumentsExample() throws Throwable { + // test the JavaDoc foldArguments-with-pos example + StringWriter swr = new StringWriter(); + MethodHandle trace = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, String.class)).bindTo(swr); + MethodHandle cat = LOOKUP.findVirtual(String.class, "concat", methodType(String.class, String.class)); + assertEquals("boojum", (String) cat.invokeExact("boo", "jum")); + MethodHandle catTrace = MethodHandles.foldArguments(cat, 1, trace); + assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); + assertEquals("jum", swr.toString()); + } + + @Test + public static void testAsSpreader() throws Throwable { + MethodHandle spreader = SpreadCollect.MH_forSpreading.asSpreader(1, int[].class, 3); + assertEquals(SpreadCollect.MT_spreader, spreader.type()); + assertEquals("A456B", (String) spreader.invoke("A", new int[]{4, 5, 6}, "B")); + } + + @Test + public static void testAsSpreaderExample() throws Throwable { + // test the JavaDoc asSpreader-with-pos example + MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class)); + MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2); + Object[] ints = new Object[]{3, 9, 7, 7}; + Comparator cmp = (a, b) -> a - b; + assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0); + assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0); + assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0); + } + + @Test + public static void testAsSpreaderIllegalPos() throws Throwable { + int[] illegalPos = {-7, 3, 19}; + int caught = 0; + for (int p : illegalPos) { + try { + SpreadCollect.MH_forSpreading.asSpreader(p, Object[].class, 3); + } catch (IllegalArgumentException iae) { + assertEquals("bad spread position", iae.getMessage()); + ++caught; + } + } + assertEquals(illegalPos.length, caught); + } + + @Test + public static void testAsCollector() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1); + assertEquals(SpreadCollect.MT_collector1, collector.type()); + assertEquals("A4B", (String) collector.invoke("A", 4, "B")); + collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2); + assertEquals(SpreadCollect.MT_collector2, collector.type()); + assertEquals("A45B", (String) collector.invoke("A", 4, 5, "B")); + collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3); + assertEquals(SpreadCollect.MT_collector3, collector.type()); + assertEquals("A456B", (String) collector.invoke("A", 4, 5, 6, "B")); + } + + @Test + public static void testAsCollectorInvokeWithArguments() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1); + assertEquals(SpreadCollect.MT_collector1, collector.type()); + assertEquals("A4B", (String) collector.invokeWithArguments("A", 4, "B")); + collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2); + assertEquals(SpreadCollect.MT_collector2, collector.type()); + assertEquals("A45B", (String) collector.invokeWithArguments("A", 4, 5, "B")); + collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3); + assertEquals(SpreadCollect.MT_collector3, collector.type()); + assertEquals("A456B", (String) collector.invokeWithArguments("A", 4, 5, 6, "B")); + } + + @Test + public static void testAsCollectorLeading() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1); + assertEquals(SpreadCollect.MT_collectorLeading1, collector.type()); + assertEquals("7Q", (String) collector.invoke(7, "Q")); + collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2); + assertEquals(SpreadCollect.MT_collectorLeading2, collector.type()); + assertEquals("78Q", (String) collector.invoke(7, 8, "Q")); + collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3); + assertEquals(SpreadCollect.MT_collectorLeading3, collector.type()); + assertEquals("789Q", (String) collector.invoke(7, 8, 9, "Q")); + } + + @Test + public static void testAsCollectorLeadingInvokeWithArguments() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1); + assertEquals(SpreadCollect.MT_collectorLeading1, collector.type()); + assertEquals("7Q", (String) collector.invokeWithArguments(7, "Q")); + collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2); + assertEquals(SpreadCollect.MT_collectorLeading2, collector.type()); + assertEquals("78Q", (String) collector.invokeWithArguments(7, 8, "Q")); + collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3); + assertEquals(SpreadCollect.MT_collectorLeading3, collector.type()); + assertEquals("789Q", (String) collector.invokeWithArguments(7, 8, 9, "Q")); + } + + @Test + public static void testAsCollectorNone() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 0); + assertEquals(SpreadCollect.MT_collector0, collector.type()); + assertEquals("AB", (String) collector.invoke("A", "B")); + } + + @Test + public static void testAsCollectorIllegalPos() throws Throwable { + int[] illegalPos = {-1, 17}; + int caught = 0; + for (int p : illegalPos) { + try { + SpreadCollect.MH_forCollecting.asCollector(p, int[].class, 0); + } catch (IllegalArgumentException iae) { + assertEquals("bad collect position", iae.getMessage()); + ++caught; + } + } + assertEquals(illegalPos.length, caught); + } + + @Test + public static void testAsCollectorExample() throws Throwable { + // test the JavaDoc asCollector-with-pos example + StringWriter swr = new StringWriter(); + MethodHandle swWrite = LOOKUP. + findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)). + bindTo(swr); + MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4); + swWrite4.invoke('A', 'B', 'C', 'D', 1, 2); + assertEquals("BC", swr.toString()); + swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4); + assertEquals("BCPQRS", swr.toString()); + swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1); + assertEquals("BCPQRSZ", swr.toString()); + } + + @Test + public static void testFindSpecial() throws Throwable { + FindSpecial.C c = new FindSpecial.C(); + assertEquals("I1.m", c.m()); + MethodType t = MethodType.methodType(String.class); + MethodHandle ci1m = LOOKUP.findSpecial(FindSpecial.I1.class, "m", t, FindSpecial.C.class); + assertEquals("I1.m", (String) ci1m.invoke(c)); + } + + @Test + public static void testFindSpecialAbstract() throws Throwable { + FindSpecial.C c = new FindSpecial.C(); + assertEquals("q", c.q()); + MethodType t = MethodType.methodType(String.class); + boolean caught = false; + try { + MethodHandle ci3q = LOOKUP.findSpecial(FindSpecial.I3.class, "q", t, FindSpecial.C.class); + } catch (Throwable thrown) { + if (!(thrown instanceof IllegalAccessException) || !FindSpecial.ABSTRACT_ERROR.equals(thrown.getMessage())) { + throw new AssertionError(thrown.getMessage(), thrown); + } + caught = true; + } + assertTrue(caught); + } + + @Test + public static void testFindClassCNFE() throws Throwable { + boolean caught = false; + try { + LOOKUP.findClass("does.not.Exist"); + } catch (ClassNotFoundException cnfe) { + caught = true; + } + assertTrue(caught); + } + + // + // Methods used to assemble tests. + // + + static class Empty { + + static void f() { } + + static boolean pred() { + return false; + } + + static final Class EMPTY = Empty.class; + + static final MethodType MT_f = methodType(void.class); + static final MethodType MT_pred = methodType(boolean.class); + + static final MethodHandle MH_f; + static final MethodHandle MH_pred; + + static { + try { + MH_f = LOOKUP.findStatic(EMPTY, "f", MT_f); + MH_pred = LOOKUP.findStatic(EMPTY, "pred", MT_pred); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + } + + static class Fac { + + static int zero(int k) { + return 0; + } + + static int one(int k) { + return 1; + } + + static boolean pred(int i, int acc, int k) { + return i < k; + } + + static int inc(int i, int acc, int k) { + return i + 1; + } + + static int mult(int i, int acc, int k) { + return i * acc; + } + + static void dot(int i, int acc, int k) { + System.out.print('.'); + } + + static int fin(int i, int acc, int k) { + return acc; + } + + static final Class FAC = Fac.class; + + static final MethodType MT_init = methodType(int.class, int.class); + static final MethodType MT_fn = methodType(int.class, int.class, int.class, int.class); + static final MethodType MT_dot = methodType(void.class, int.class, int.class, int.class); + static final MethodType MT_pred = methodType(boolean.class, int.class, int.class, int.class); + + static final MethodHandle MH_zero; + static final MethodHandle MH_one; + static final MethodHandle MH_pred; + static final MethodHandle MH_inc; + static final MethodHandle MH_mult; + static final MethodHandle MH_dot; + static final MethodHandle MH_fin; + + static final MethodType MT_fac = methodType(int.class, int.class); + + static { + try { + MH_zero = LOOKUP.findStatic(FAC, "zero", MT_init); + MH_one = LOOKUP.findStatic(FAC, "one", MT_init); + MH_pred = LOOKUP.findStatic(FAC, "pred", MT_pred); + MH_inc = LOOKUP.findStatic(FAC, "inc", MT_fn); + MH_mult = LOOKUP.findStatic(FAC, "mult", MT_fn); + MH_dot = LOOKUP.findStatic(FAC, "dot", MT_dot); + MH_fin = LOOKUP.findStatic(FAC, "fin", MT_fn); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class While { + + static int zero(int limit) { + return 0; + } + + static boolean pred(int i, int limit) { + return i < limit; + } + + static int step(int i, int limit) { + return i + 1; + } + + static String initString() { + return "a"; + } + + static boolean predString(String s) { + return s.length() != 1; + } + + static String stepString(String s) { + return s + "a"; + } + + static List zipInitZip(Iterator a, Iterator b) { + return new ArrayList<>(); + } + + static boolean zipPred(List zip, Iterator a, Iterator b) { + return a.hasNext() && b.hasNext(); + } + + static List zipStep(List zip, Iterator a, Iterator b) { + zip.add(a.next()); + zip.add(b.next()); + return zip; + } + + static final Class WHILE = While.class; + + static final MethodType MT_zero = methodType(int.class, int.class); + static final MethodType MT_pred = methodType(boolean.class, int.class, int.class); + static final MethodType MT_fn = methodType(int.class, int.class, int.class); + static final MethodType MT_initString = methodType(String.class); + static final MethodType MT_predString = methodType(boolean.class, String.class); + static final MethodType MT_stepString = methodType(String.class, String.class); + static final MethodType MT_zipInitZip = methodType(List.class, Iterator.class, Iterator.class); + static final MethodType MT_zipPred = methodType(boolean.class, List.class, Iterator.class, Iterator.class); + static final MethodType MT_zipStep = methodType(List.class, List.class, Iterator.class, Iterator.class); + + static final MethodHandle MH_zero; + static final MethodHandle MH_pred; + static final MethodHandle MH_step; + static final MethodHandle MH_initString; + static final MethodHandle MH_predString; + static final MethodHandle MH_stepString; + static final MethodHandle MH_zipInitZip; + static final MethodHandle MH_zipPred; + static final MethodHandle MH_zipStep; + + static final MethodType MT_while = methodType(int.class, int.class); + static final MethodType MT_string = methodType(String.class); + static final MethodType MT_zip = methodType(List.class, Iterator.class, Iterator.class); + + static { + try { + MH_zero = LOOKUP.findStatic(WHILE, "zero", MT_zero); + MH_pred = LOOKUP.findStatic(WHILE, "pred", MT_pred); + MH_step = LOOKUP.findStatic(WHILE, "step", MT_fn); + MH_initString = LOOKUP.findStatic(WHILE, "initString", MT_initString); + MH_predString = LOOKUP.findStatic(WHILE, "predString", MT_predString); + MH_stepString = LOOKUP.findStatic(WHILE, "stepString", MT_stepString); + MH_zipInitZip = LOOKUP.findStatic(WHILE, "zipInitZip", MT_zipInitZip); + MH_zipPred = LOOKUP.findStatic(WHILE, "zipPred", MT_zipPred); + MH_zipStep = LOOKUP.findStatic(WHILE, "zipStep", MT_zipStep); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class Counted { + + static String start(String arg) { + return arg; + } + + static String step(int counter, String v, String arg) { + return "na " + v; + } + + static void stepUpdateArray(int counter, int[] a) { + ++a[0]; + } + + static void printHello(int counter) { + System.out.print("hello"); + } + + static final Class COUNTED = Counted.class; + + static final MethodType MT_start = methodType(String.class, String.class); + static final MethodType MT_step = methodType(String.class, int.class, String.class, String.class); + static final MethodType MT_stepUpdateArray = methodType(void.class, int.class, int[].class); + static final MethodType MT_printHello = methodType(void.class, int.class); + + static final MethodHandle MH_13; + static final MethodHandle MH_m5; + static final MethodHandle MH_8; + static final MethodHandle MH_start; + static final MethodHandle MH_step; + static final MethodHandle MH_stepUpdateArray; + static final MethodHandle MH_printHello; + + static final MethodType MT_counted = methodType(String.class, String.class); + static final MethodType MT_arrayCounted = methodType(void.class, int[].class); + static final MethodType MT_countedPrinting = methodType(void.class); + + static { + try { + MH_13 = MethodHandles.constant(int.class, 13); + MH_m5 = MethodHandles.constant(int.class, -5); + MH_8 = MethodHandles.constant(int.class, 8); + MH_start = LOOKUP.findStatic(COUNTED, "start", MT_start); + MH_step = LOOKUP.findStatic(COUNTED, "step", MT_step); + MH_stepUpdateArray = LOOKUP.findStatic(COUNTED, "stepUpdateArray", MT_stepUpdateArray); + MH_printHello = LOOKUP.findStatic(COUNTED, "printHello", MT_printHello); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class Iterate { + + static Iterator sumIterator(Integer[] a) { + return Arrays.asList(a).iterator(); + } + + static int sumInit(Integer[] a) { + return 0; + } + + static int sumStep(int s, int e, Integer[] a) { + return s + e; + } + + static List reverseInit(List l) { + return new ArrayList<>(); + } + + static List reverseStep(String e, List r, List l) { + r.add(0, e); + return r; + } + + static int lengthInit(List l) { + return 0; + } + + static int lengthStep(Object o, int len, List l) { + return len + 1; + } + + static List mapInit(List l) { + return new ArrayList<>(); + } + + static List mapStep(String e, List r, List l) { + r.add(e.toUpperCase()); + return r; + } + + static void printStep(String s, List l) { + System.out.print(s); + } + + static final Class ITERATE = Iterate.class; + + static final MethodType MT_sumIterator = methodType(Iterator.class, Integer[].class); + + static final MethodType MT_sumInit = methodType(int.class, Integer[].class); + static final MethodType MT_reverseInit = methodType(List.class, List.class); + static final MethodType MT_lenghInit = methodType(int.class, List.class); + static final MethodType MT_mapInit = methodType(List.class, List.class); + + static final MethodType MT_sumStep = methodType(int.class, int.class, int.class, Integer[].class); + static final MethodType MT_reverseStep = methodType(List.class, String.class, List.class, List.class); + static final MethodType MT_lengthStep = methodType(int.class, Object.class, int.class, List.class); + static final MethodType MT_mapStep = methodType(List.class, String.class, List.class, List.class); + static final MethodType MT_printStep = methodType(void.class, String.class, List.class); + + static final MethodHandle MH_sumIterator; + static final MethodHandle MH_sumInit; + static final MethodHandle MH_sumStep; + static final MethodHandle MH_printStep; + + static final MethodHandle MH_reverseInit; + static final MethodHandle MH_reverseStep; + + static final MethodHandle MH_lengthInit; + static final MethodHandle MH_lengthStep; + + static final MethodHandle MH_mapInit; + static final MethodHandle MH_mapStep; + + static final MethodType MT_sum = methodType(int.class, Integer[].class); + static final MethodType MT_reverse = methodType(List.class, List.class); + static final MethodType MT_length = methodType(int.class, List.class); + static final MethodType MT_map = methodType(List.class, List.class); + static final MethodType MT_print = methodType(void.class, List.class); + + static { + try { + MH_sumIterator = LOOKUP.findStatic(ITERATE, "sumIterator", MT_sumIterator); + MH_sumInit = LOOKUP.findStatic(ITERATE, "sumInit", MT_sumInit); + MH_sumStep = LOOKUP.findStatic(ITERATE, "sumStep", MT_sumStep); + MH_reverseInit = LOOKUP.findStatic(ITERATE, "reverseInit", MT_reverseInit); + MH_reverseStep = LOOKUP.findStatic(ITERATE, "reverseStep", MT_reverseStep); + MH_lengthInit = LOOKUP.findStatic(ITERATE, "lengthInit", MT_lenghInit); + MH_lengthStep = LOOKUP.findStatic(ITERATE, "lengthStep", MT_lengthStep); + MH_mapInit = LOOKUP.findStatic(ITERATE, "mapInit", MT_mapInit); + MH_mapStep = LOOKUP.findStatic(ITERATE, "mapStep", MT_mapStep); + MH_printStep = LOOKUP.findStatic(ITERATE, "printStep", MT_printStep); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class TryFinally { + + static String greet(String whom) { + return "Hello, " + whom; + } + + static String exclaim(Throwable t, String r, String whom) { + return r + "!"; + } + + static void print(String what) { + System.out.print("Hello, " + what); + } + + static void printMore(Throwable t, String what) { + System.out.println("!"); + } + + static String greetMore(String first, String second) { + return "Hello, " + first + " and " + second; + } + + static String exclaimMore(Throwable t, String r, String first) { + return r + " (but " + first + " first)!"; + } + + static final Class TRY_FINALLY = TryFinally.class; + + static final MethodType MT_greet = methodType(String.class, String.class); + static final MethodType MT_exclaim = methodType(String.class, Throwable.class, String.class, String.class); + static final MethodType MT_print = methodType(void.class, String.class); + static final MethodType MT_printMore = methodType(void.class, Throwable.class, String.class); + static final MethodType MT_greetMore = methodType(String.class, String.class, String.class); + static final MethodType MT_exclaimMore = methodType(String.class, Throwable.class, String.class, String.class); + + static final MethodHandle MH_greet; + static final MethodHandle MH_exclaim; + static final MethodHandle MH_print; + static final MethodHandle MH_printMore; + static final MethodHandle MH_greetMore; + static final MethodHandle MH_exclaimMore; + + static final MethodType MT_hello = methodType(String.class, String.class); + static final MethodType MT_printHello = methodType(void.class, String.class); + static final MethodType MT_moreHello = methodType(String.class, String.class, String.class); + + static { + try { + MH_greet = LOOKUP.findStatic(TRY_FINALLY, "greet", MT_greet); + MH_exclaim = LOOKUP.findStatic(TRY_FINALLY, "exclaim", MT_exclaim); + MH_print = LOOKUP.findStatic(TRY_FINALLY, "print", MT_print); + MH_printMore = LOOKUP.findStatic(TRY_FINALLY, "printMore", MT_printMore); + MH_greetMore = LOOKUP.findStatic(TRY_FINALLY, "greetMore", MT_greetMore); + MH_exclaimMore = LOOKUP.findStatic(TRY_FINALLY, "exclaimMore", MT_exclaimMore); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class Fold { + + static int adder(int a, int b, int c) { + return a + b + c; + } + + static int adder1(int a, int b) { + return a + b; + } + + static int multer(int x, int q, int r, int s) { + return x * q * r * s; + } + + static int str(boolean b1, String s, boolean b2, int x) { + return b1 && s.equals(String.valueOf(b2)) ? x : -x; + } + + static boolean comb(String s, boolean b2) { + return !s.equals(b2); + } + + static String comb2(boolean b2, int x) { + int ib = b2 ? 1 : 0; + return ib == x ? "true" : "false"; + } + + static final Class FOLD = Fold.class; + + static final MethodType MT_adder = methodType(int.class, int.class, int.class, int.class); + static final MethodType MT_adder1 = methodType(int.class, int.class, int.class); + static final MethodType MT_multer = methodType(int.class, int.class, int.class, int.class, int.class); + static final MethodType MT_str = methodType(int.class, boolean.class, String.class, boolean.class, int.class); + static final MethodType MT_comb = methodType(boolean.class, String.class, boolean.class); + static final MethodType MT_comb2 = methodType(String.class, boolean.class, int.class); + + static final MethodHandle MH_adder; + static final MethodHandle MH_adder1; + static final MethodHandle MH_multer; + static final MethodHandle MH_str; + static final MethodHandle MH_comb; + static final MethodHandle MH_comb2; + + static final MethodType MT_folded1 = methodType(int.class, int.class, int.class, int.class); + static final MethodType MT_folded2 = methodType(int.class, String.class, boolean.class, int.class); + static final MethodType MT_folded3 = methodType(int.class, boolean.class, boolean.class, int.class); + + static { + try { + MH_adder = LOOKUP.findStatic(FOLD, "adder", MT_adder); + MH_adder1 = LOOKUP.findStatic(FOLD, "adder1", MT_adder1); + MH_multer = LOOKUP.findStatic(FOLD, "multer", MT_multer); + MH_str = LOOKUP.findStatic(FOLD, "str", MT_str); + MH_comb = LOOKUP.findStatic(FOLD, "comb", MT_comb); + MH_comb2 = LOOKUP.findStatic(FOLD, "comb2", MT_comb2); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + } + + static class SpreadCollect { + + static String forSpreading(String s1, int i1, int i2, int i3, String s2) { + return s1 + i1 + i2 + i3 + s2; + } + + static String forCollecting(String s1, int[] is, String s2) { + StringBuilder sb = new StringBuilder(s1); + for (int i : is) { + sb.append(i); + } + return sb.append(s2).toString(); + } + + static String forCollectingLeading(int[] is, String s) { + return forCollecting("", is, s); + } + + static final Class SPREAD_COLLECT = SpreadCollect.class; + + static final MethodType MT_forSpreading = methodType(String.class, String.class, int.class, int.class, int.class, String.class); + static final MethodType MT_forCollecting = methodType(String.class, String.class, int[].class, String.class); + static final MethodType MT_forCollectingLeading = methodType(String.class, int[].class, String.class); + + static final MethodHandle MH_forSpreading; + static final MethodHandle MH_forCollecting; + static final MethodHandle MH_forCollectingLeading; + + static final MethodType MT_spreader = methodType(String.class, String.class, int[].class, String.class); + static final MethodType MT_collector0 = methodType(String.class, String.class, String.class); + static final MethodType MT_collector1 = methodType(String.class, String.class, int.class, String.class); + static final MethodType MT_collector2 = methodType(String.class, String.class, int.class, int.class, String.class); + static final MethodType MT_collector3 = methodType(String.class, String.class, int.class, int.class, int.class, String.class); + static final MethodType MT_collectorLeading1 = methodType(String.class, int.class, String.class); + static final MethodType MT_collectorLeading2 = methodType(String.class, int.class, int.class, String.class); + static final MethodType MT_collectorLeading3 = methodType(String.class, int.class, int.class, int.class, String.class); + + static final String NONE_ERROR = "zero array length in MethodHandle.asCollector"; + + static { + try { + MH_forSpreading = LOOKUP.findStatic(SPREAD_COLLECT, "forSpreading", MT_forSpreading); + MH_forCollecting = LOOKUP.findStatic(SPREAD_COLLECT, "forCollecting", MT_forCollecting); + MH_forCollectingLeading = LOOKUP.findStatic(SPREAD_COLLECT, "forCollectingLeading", MT_forCollectingLeading); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class FindSpecial { + + interface I1 { + default String m() { + return "I1.m"; + } + } + + interface I2 { + default String m() { + return "I2.m"; + } + } + + interface I3 { + String q(); + } + + static class C implements I1, I2, I3 { + public String m() { + return I1.super.m(); + } + public String q() { + return "q"; + } + } + + static final String ABSTRACT_ERROR = "no such method: test.java.lang.invoke.T8139885$FindSpecial$I3.q()String/invokeSpecial"; + + } + + // + // Auxiliary methods. + // + + static MethodHandle[] mha(MethodHandle... mhs) { + return mhs; + } + +} --- /dev/null 2015-11-19 22:14:09.000000000 +0100 +++ new/test/java/lang/invoke/findclass.security.policy 2015-11-19 22:14:09.000000000 +0100 @@ -0,0 +1,9 @@ +/* + * Security policy used by the FindClassSecurityManager test. + * Must allow file reads so that jtreg itself can run, and getting class loaders. + */ + +grant { + permission java.io.FilePermission "*", "read"; + permission java.lang.RuntimePermission "getClassLoader"; +};