2491 * A[i] filter[i](V[i]);
2492 * T adapter(P... p, V[i]... v[i], B... b) {
2493 * return target(p..., f[i](v[i])..., b...);
2494 * }
2495 * }</pre></blockquote>
2496 *
2497 * @param target the method handle to invoke after arguments are filtered
2498 * @param pos the position of the first argument to filter
2499 * @param filters method handles to call initially on filtered arguments
2500 * @return method handle which incorporates the specified argument filtering logic
2501 * @throws NullPointerException if the target is null
2502 * or if the {@code filters} array is null
2503 * @throws IllegalArgumentException if a non-null element of {@code filters}
2504 * does not match a corresponding argument type of target as described above,
2505 * or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()},
2506 * or if the resulting method handle's type would have
2507 * <a href="MethodHandle.html#maxarity">too many parameters</a>
2508 */
2509 public static
2510 MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
2511 MethodType targetType = target.type();
2512 MethodHandle adapter = target;
2513 MethodType adapterType = null;
2514 assert((adapterType = targetType) != null);
2515 int maxPos = targetType.parameterCount();
2516 if (pos + filters.length > maxPos)
2517 throw newIllegalArgumentException("too many filters");
2518 int curPos = pos-1; // pre-incremented
2519 for (MethodHandle filter : filters) {
2520 curPos += 1;
2521 if (filter == null) continue; // ignore null elements of filters
2522 adapter = filterArgument(adapter, curPos, filter);
2523 assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null);
2524 }
2525 assert(adapterType.equals(adapter.type()));
2526 return adapter;
2527 }
2528
2529 /*non-public*/ static
2530 MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
2531 MethodType targetType = target.type();
2532 MethodType filterType = filter.type();
2533 if (filterType.parameterCount() != 1
2534 || filterType.returnType() != targetType.parameterType(pos))
2535 throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
2536 return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
2537 }
2538
2539 /**
2540 * Adapts a target method handle by pre-processing
2541 * a sub-sequence of its arguments with a filter (another method handle).
2542 * The pre-processed arguments are replaced by the result (if any) of the
2543 * filter function.
2544 * The target is then called on the modified (usually shortened) argument list.
2545 * <p>
2546 * If the filter returns a value, the target must accept that value as
2547 * its argument in position {@code pos}, preceded and/or followed by
2548 * any arguments not passed to the filter.
2549 * If the filter returns void, the target must accept all arguments
2550 * not passed to the filter.
2551 * No arguments are reordered, and a result returned from the filter
2552 * replaces (in order) the whole subsequence of arguments originally
2553 * passed to the adapter.
2554 * <p>
2555 * The argument types (if any) of the filter
2556 * replace zero or one argument types of the target, at position {@code pos},
2696 * }
2697 * // and if the filter has a void return:
2698 * V target3(A...);
2699 * void filter3(V);
2700 * void adapter3(A... a) {
2701 * V v = target3(a...);
2702 * filter3(v);
2703 * }
2704 * }</pre></blockquote>
2705 * @param target the method handle to invoke before filtering the return value
2706 * @param filter method handle to call on the return value
2707 * @return method handle which incorporates the specified return value filtering logic
2708 * @throws NullPointerException if either argument is null
2709 * @throws IllegalArgumentException if the argument list of {@code filter}
2710 * does not match the return type of target as described above
2711 */
2712 public static
2713 MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
2714 MethodType targetType = target.type();
2715 MethodType filterType = filter.type();
2716 Class<?> rtype = targetType.returnType();
2717 int filterValues = filterType.parameterCount();
2718 if (filterValues == 0
2719 ? (rtype != void.class)
2720 : (rtype != filterType.parameterType(0)))
2721 throw newIllegalArgumentException("target and filter types do not match", target, filter);
2722 // result = fold( lambda(retval, arg...) { filter(retval) },
2723 // lambda( arg...) { target(arg...) } )
2724 return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
2725 }
2726
2727 /**
2728 * Adapts a target method handle by pre-processing
2729 * some of its arguments, and then calling the target with
2730 * the result of the pre-processing, inserted into the original
2731 * sequence of arguments.
2732 * <p>
2733 * The pre-processing is performed by {@code combiner}, a second method handle.
2734 * Of the arguments passed to the adapter, the first {@code N} arguments
2735 * are copied to the combiner, which is then called.
2736 * (Here, {@code N} is defined as the parameter count of the combiner.)
2737 * After this, control passes to the target, with any result
2738 * from the combiner inserted before the original {@code N} incoming
2739 * arguments.
2740 * <p>
2741 * If the combiner returns a value, the first parameter type of the target
2742 * must be identical with the return type of the combiner, and the next
2743 * {@code N} parameter types of the target must exactly match the parameters
2744 * of the combiner.
2785 * T target2(A[N]..., B...);
2786 * void combiner2(A...);
2787 * T adapter2(A... a, B... b) {
2788 * combiner2(a...);
2789 * return target2(a..., b...);
2790 * }
2791 * }</pre></blockquote>
2792 * @param target the method handle to invoke after arguments are combined
2793 * @param combiner method handle to call initially on the incoming arguments
2794 * @return method handle which incorporates the specified argument folding logic
2795 * @throws NullPointerException if either argument is null
2796 * @throws IllegalArgumentException if {@code combiner}'s return type
2797 * is non-void and not the same as the first argument type of
2798 * the target, or if the initial {@code N} argument types
2799 * of the target
2800 * (skipping one matching the {@code combiner}'s return type)
2801 * are not identical with the argument types of {@code combiner}
2802 */
2803 public static
2804 MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
2805 int pos = 0;
2806 MethodType targetType = target.type();
2807 MethodType combinerType = combiner.type();
2808 int foldPos = pos;
2809 int foldArgs = combinerType.parameterCount();
2810 int foldVals = combinerType.returnType() == void.class ? 0 : 1;
2811 int afterInsertPos = foldPos + foldVals;
2812 boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
2813 if (ok && !(combinerType.parameterList()
2814 .equals(targetType.parameterList().subList(afterInsertPos,
2815 afterInsertPos + foldArgs))))
2816 ok = false;
2817 if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0)))
2818 ok = false;
2819 if (!ok)
2820 throw misMatchedTypes("target and combiner types", targetType, combinerType);
2821 MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
2822 return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
2823 }
2824
2825 /**
2826 * Makes a method handle which adapts a target method handle,
2827 * by guarding it with a test, a boolean-valued method handle.
2828 * If the guard fails, a fallback handle is called instead.
2829 * All three method handles must have the same corresponding
2830 * argument and return types, except that the return type
2831 * of the test must be boolean, and the test is allowed
2832 * to have fewer arguments than the other two method handles.
2833 * <p> Here is pseudocode for the resulting adapter:
2834 * <blockquote><pre>{@code
2835 * boolean test(A...);
2836 * T target(A...,B...);
2837 * T fallback(A...,B...);
2838 * T adapter(A... a,B... b) {
2839 * if (test(a...))
2840 * return target(a..., b...);
2841 * else
2842 * return fallback(a..., b...);
|
2491 * A[i] filter[i](V[i]);
2492 * T adapter(P... p, V[i]... v[i], B... b) {
2493 * return target(p..., f[i](v[i])..., b...);
2494 * }
2495 * }</pre></blockquote>
2496 *
2497 * @param target the method handle to invoke after arguments are filtered
2498 * @param pos the position of the first argument to filter
2499 * @param filters method handles to call initially on filtered arguments
2500 * @return method handle which incorporates the specified argument filtering logic
2501 * @throws NullPointerException if the target is null
2502 * or if the {@code filters} array is null
2503 * @throws IllegalArgumentException if a non-null element of {@code filters}
2504 * does not match a corresponding argument type of target as described above,
2505 * or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()},
2506 * or if the resulting method handle's type would have
2507 * <a href="MethodHandle.html#maxarity">too many parameters</a>
2508 */
2509 public static
2510 MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
2511 filterArgumentsCheckArity(target, pos, filters);
2512 MethodHandle adapter = target;
2513 int curPos = pos-1; // pre-incremented
2514 for (MethodHandle filter : filters) {
2515 curPos += 1;
2516 if (filter == null) continue; // ignore null elements of filters
2517 adapter = filterArgument(adapter, curPos, filter);
2518 }
2519 return adapter;
2520 }
2521
2522 /*non-public*/ static
2523 MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
2524 filterArgumentChecks(target, pos, filter);
2525 return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
2526 }
2527
2528 private static void filterArgumentsCheckArity(MethodHandle target, int pos, MethodHandle[] filters) {
2529 MethodType targetType = target.type();
2530 int maxPos = targetType.parameterCount();
2531 if (pos + filters.length > maxPos)
2532 throw newIllegalArgumentException("too many filters");
2533 }
2534
2535 private static void filterArgumentChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
2536 MethodType targetType = target.type();
2537 MethodType filterType = filter.type();
2538 if (filterType.parameterCount() != 1
2539 || filterType.returnType() != targetType.parameterType(pos))
2540 throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
2541 }
2542
2543 /**
2544 * Adapts a target method handle by pre-processing
2545 * a sub-sequence of its arguments with a filter (another method handle).
2546 * The pre-processed arguments are replaced by the result (if any) of the
2547 * filter function.
2548 * The target is then called on the modified (usually shortened) argument list.
2549 * <p>
2550 * If the filter returns a value, the target must accept that value as
2551 * its argument in position {@code pos}, preceded and/or followed by
2552 * any arguments not passed to the filter.
2553 * If the filter returns void, the target must accept all arguments
2554 * not passed to the filter.
2555 * No arguments are reordered, and a result returned from the filter
2556 * replaces (in order) the whole subsequence of arguments originally
2557 * passed to the adapter.
2558 * <p>
2559 * The argument types (if any) of the filter
2560 * replace zero or one argument types of the target, at position {@code pos},
2700 * }
2701 * // and if the filter has a void return:
2702 * V target3(A...);
2703 * void filter3(V);
2704 * void adapter3(A... a) {
2705 * V v = target3(a...);
2706 * filter3(v);
2707 * }
2708 * }</pre></blockquote>
2709 * @param target the method handle to invoke before filtering the return value
2710 * @param filter method handle to call on the return value
2711 * @return method handle which incorporates the specified return value filtering logic
2712 * @throws NullPointerException if either argument is null
2713 * @throws IllegalArgumentException if the argument list of {@code filter}
2714 * does not match the return type of target as described above
2715 */
2716 public static
2717 MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
2718 MethodType targetType = target.type();
2719 MethodType filterType = filter.type();
2720 filterReturnValueChecks(targetType, filterType);
2721 return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
2722 }
2723
2724 private static void filterReturnValueChecks(MethodType targetType, MethodType filterType) throws RuntimeException {
2725 Class<?> rtype = targetType.returnType();
2726 int filterValues = filterType.parameterCount();
2727 if (filterValues == 0
2728 ? (rtype != void.class)
2729 : (rtype != filterType.parameterType(0)))
2730 throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
2731 }
2732
2733 /**
2734 * Adapts a target method handle by pre-processing
2735 * some of its arguments, and then calling the target with
2736 * the result of the pre-processing, inserted into the original
2737 * sequence of arguments.
2738 * <p>
2739 * The pre-processing is performed by {@code combiner}, a second method handle.
2740 * Of the arguments passed to the adapter, the first {@code N} arguments
2741 * are copied to the combiner, which is then called.
2742 * (Here, {@code N} is defined as the parameter count of the combiner.)
2743 * After this, control passes to the target, with any result
2744 * from the combiner inserted before the original {@code N} incoming
2745 * arguments.
2746 * <p>
2747 * If the combiner returns a value, the first parameter type of the target
2748 * must be identical with the return type of the combiner, and the next
2749 * {@code N} parameter types of the target must exactly match the parameters
2750 * of the combiner.
2791 * T target2(A[N]..., B...);
2792 * void combiner2(A...);
2793 * T adapter2(A... a, B... b) {
2794 * combiner2(a...);
2795 * return target2(a..., b...);
2796 * }
2797 * }</pre></blockquote>
2798 * @param target the method handle to invoke after arguments are combined
2799 * @param combiner method handle to call initially on the incoming arguments
2800 * @return method handle which incorporates the specified argument folding logic
2801 * @throws NullPointerException if either argument is null
2802 * @throws IllegalArgumentException if {@code combiner}'s return type
2803 * is non-void and not the same as the first argument type of
2804 * the target, or if the initial {@code N} argument types
2805 * of the target
2806 * (skipping one matching the {@code combiner}'s return type)
2807 * are not identical with the argument types of {@code combiner}
2808 */
2809 public static
2810 MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
2811 int foldPos = 0;
2812 MethodType targetType = target.type();
2813 MethodType combinerType = combiner.type();
2814 Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
2815 return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
2816 }
2817
2818 private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
2819 int foldArgs = combinerType.parameterCount();
2820 Class<?> rtype = combinerType.returnType();
2821 int foldVals = rtype == void.class ? 0 : 1;
2822 int afterInsertPos = foldPos + foldVals;
2823 boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
2824 if (ok && !(combinerType.parameterList()
2825 .equals(targetType.parameterList().subList(afterInsertPos,
2826 afterInsertPos + foldArgs))))
2827 ok = false;
2828 if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
2829 ok = false;
2830 if (!ok)
2831 throw misMatchedTypes("target and combiner types", targetType, combinerType);
2832 return rtype;
2833 }
2834
2835 /**
2836 * Makes a method handle which adapts a target method handle,
2837 * by guarding it with a test, a boolean-valued method handle.
2838 * If the guard fails, a fallback handle is called instead.
2839 * All three method handles must have the same corresponding
2840 * argument and return types, except that the return type
2841 * of the test must be boolean, and the test is allowed
2842 * to have fewer arguments than the other two method handles.
2843 * <p> Here is pseudocode for the resulting adapter:
2844 * <blockquote><pre>{@code
2845 * boolean test(A...);
2846 * T target(A...,B...);
2847 * T fallback(A...,B...);
2848 * T adapter(A... a,B... b) {
2849 * if (test(a...))
2850 * return target(a..., b...);
2851 * else
2852 * return fallback(a..., b...);
|