< prev index next >

src/java.base/share/classes/java/lang/invoke/MethodHandle.java

Print this page
rev 13059 : 8130227: Extend MethodHandle APIs

*** 870,913 **** * <a href="MethodHandle.html#maxarity">too many parameters</a> * @throws WrongMethodTypeException if the implied {@code asType} call fails * @see #asCollector */ public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) { ! MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength); ! int arity = type().parameterCount(); ! int spreadArgPos = arity - 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); return mh.copyWith(preSpreadType, lform); } /** * 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) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs < arrayLength || arrayLength < 0) throw newIllegalArgumentException("bad spread array length"); Class<?> arrayElement = arrayType.getComponentType(); MethodType mtype = type(); boolean match = true, fail = false; ! for (int i = nargs - arrayLength; i < nargs; i++) { Class<?> ptype = mtype.parameterType(i); if (ptype != arrayElement) { match = false; if (!MethodType.canConvert(arrayElement, ptype)) { fail = true; break; } } } if (match) return mtype; ! MethodType needType = mtype.asSpreaderType(arrayType, arrayLength); if (!fail) return needType; // elicit an error: this.asType(needType); throw newInternalError("should not return", null); } --- 870,951 ---- * <a href="MethodHandle.html#maxarity">too many parameters</a> * @throws WrongMethodTypeException if the implied {@code asType} call fails * @see #asCollector */ public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) { ! return asSpreader(type().parameterCount() - arrayLength, arrayType, arrayLength); ! } ! ! /** ! * Makes an <em>array-spreading</em> 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 <i>target</i>, ! * 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}. ! * <p> ! * 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. ! * <p> ! * Example: ! * <blockquote><pre>{@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<Integer> 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); ! * }</pre></blockquote> ! * @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 the resulting method handle's type would have ! * <a href="MethodHandle.html#maxarity">too many parameters</a> ! * @throws WrongMethodTypeException if the implied {@code asType} call fails ! * ! * @see #asSpreader(Class, int) ! */ ! 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, spreadArgPos + arrayLength, arrayType); return mh.copyWith(preSpreadType, lform); } /** * 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 pos, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs < arrayLength || arrayLength < 0) throw newIllegalArgumentException("bad spread array length"); Class<?> arrayElement = arrayType.getComponentType(); MethodType mtype = type(); boolean match = true, fail = false; ! for (int i = pos; i < arrayLength; i++) { Class<?> ptype = mtype.parameterType(i); if (ptype != arrayElement) { match = false; if (!MethodType.canConvert(arrayElement, ptype)) { fail = true; break; } } } if (match) return mtype; ! MethodType needType = mtype.asSpreaderType(arrayType, pos, arrayLength); if (!fail) return needType; // elicit an error: this.asType(needType); throw newInternalError("should not return", null); }
*** 996,1009 **** * @throws WrongMethodTypeException if the implied {@code asType} call fails * @see #asSpreader * @see #asVarargsCollector */ public MethodHandle asCollector(Class<?> arrayType, int arrayLength) { ! asCollectorChecks(arrayType, arrayLength); ! int collectArgPos = type().parameterCount() - 1; BoundMethodHandle mh = rebind(); ! MethodType resultType = type().asCollectorType(arrayType, arrayLength); MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength); LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray); if (lform != null) { return mh.copyWith(resultType, lform); } --- 1034,1088 ---- * @throws WrongMethodTypeException if the implied {@code asType} call fails * @see #asSpreader * @see #asVarargsCollector */ public MethodHandle asCollector(Class<?> arrayType, int arrayLength) { ! return asCollector(type().parameterCount() - 1, arrayType, arrayLength); ! } ! ! /** ! * Makes an <em>array-collecting</em> 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 ! * <i>target</i>, 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}. ! * <p> ! * 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. ! * <p> ! * Examples: ! * <blockquote><pre>{@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()); ! * }</pre></blockquote> ! * @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 the resulting method handle's type would have ! * <a href="MethodHandle.html#maxarity">too many parameters</a> ! * @throws WrongMethodTypeException if the implied {@code asType} call fails ! * ! * @see #asCollector(Class, int) ! */ ! public MethodHandle asCollector(int collectArgPos, Class<?> arrayType, int arrayLength) { ! asCollectorChecks(arrayType, collectArgPos, arrayLength); BoundMethodHandle mh = rebind(); ! MethodType resultType = type().asCollectorType(arrayType, collectArgPos, arrayLength); MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength); LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray); if (lform != null) { return mh.copyWith(resultType, lform); }
*** 1013,1031 **** /** * 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) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs != 0) { ! Class<?> lastParam = type().parameterType(nargs-1); ! if (lastParam == arrayType) return true; ! if (lastParam.isAssignableFrom(arrayType)) return false; } ! throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType); } /** * Makes a <em>variable arity</em> adapter which is able to accept * any number of trailing positional arguments and collect them --- 1092,1110 ---- /** * 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 pos, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs != 0) { ! Class<?> param = type().parameterType(pos); ! if (param == arrayType) return true; ! if (param.isAssignableFrom(arrayType)) return false; } ! throw newIllegalArgumentException("array type not assignable to argument", this, arrayType); } /** * Makes a <em>variable arity</em> adapter which is able to accept * any number of trailing positional arguments and collect them
*** 1176,1186 **** * @see #isVarargsCollector * @see #asFixedArity */ public MethodHandle asVarargsCollector(Class<?> arrayType) { Objects.requireNonNull(arrayType); ! boolean lastMatch = asCollectorChecks(arrayType, 0); if (isVarargsCollector() && lastMatch) return this; return MethodHandleImpl.makeVarargsCollector(this, arrayType); } --- 1255,1265 ---- * @see #isVarargsCollector * @see #asFixedArity */ public MethodHandle asVarargsCollector(Class<?> arrayType) { Objects.requireNonNull(arrayType); ! boolean lastMatch = asCollectorChecks(arrayType, type().parameterCount() - 1, 0); if (isVarargsCollector() && lastMatch) return this; return MethodHandleImpl.makeVarargsCollector(this, arrayType); }
< prev index next >