MethodHandles.java 52.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
/*
 * Copyright 2008-2009 Sun Microsystems, Inc.  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.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package java.dyn;

import java.lang.reflect.Constructor;
import sun.dyn.Access;
import sun.dyn.MemberName;
import sun.dyn.MethodHandleImpl;
import sun.dyn.util.VerifyAccess;
import sun.dyn.util.Wrapper;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import sun.dyn.Invokers;
import sun.dyn.MethodTypeImpl;
import sun.reflect.Reflection;
import static sun.dyn.MemberName.newIllegalArgumentException;
import static sun.dyn.MemberName.newNoAccessException;

/**
 * Fundamental operations and utilities for MethodHandle.
 * <p>
 * <em>API Note:</em>  The matching of method types in this API cannot
 * be completely checked by Java's generic type system for three reasons:
 * <ol>
 * <li>Method types range over all possible arities,
 * from no arguments to an arbitrary number of arguments.
 * Generics are not variadic, and so cannot represent this.</li>
 * <li>Method types can specify arguments of primitive types,
 * which Java generic types cannot range over.</li>
 * <li>Method types can optionally specify varargs (ellipsis).</li>
 * </ol>
 * @author John Rose, JSR 292 EG
 */
public class MethodHandles {

    private MethodHandles() { }  // do not instantiate

    private static final Access IMPL_TOKEN = Access.getToken();
    private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
    static { MethodHandleImpl.initStatics(); }
    // See IMPL_LOOKUP below.

    //// Method handle creation from ordinary methods.

    public static Lookup lookup() {
        return new Lookup();
    }

    /**
     * A factory object for creating method handles, when the creation
     * requires access checking.  Method handles do not perform
     * access checks when they are called; this is a major difference
     * from reflective {@link Method}, which performs access checking
     * against every caller, on every call.  Method handle access
     * restrictions are enforced when a method handle is created.
     * The caller class against which those restrictions are enforced
     * is known as the "lookup class".  {@link Lookup} embodies an
     * authenticated lookup class, and can be used to create any number
     * of access-checked method handles, all checked against a single
     * lookup class.
     * <p>
     * A class which needs to create method handles will call
     * {@code MethodHandles.lookup()} to create a factory for itself.
     * It may then use this factory to create method handles on
     * all of its methods, including private ones.
     * It may also delegate the lookup (e.g., to a metaobject protocol)
     * by passing the {@code Lookup} object to other code.
     * If this other code creates method handles, they will be access
     * checked against the original lookup class, and not with any higher
     * privileges.
     * <p>
     * Note that access checks only apply to named and reflected methods.
     * Other method handle creation methods, such as {@link #convertArguments},
     * do not require any access checks, and can be done independently
     * of any lookup class.
     * <p>
     * <em>A note about error conditions:<em>  A lookup can fail, because
     * the containing class is not accessible to the lookup class, or
     * because the desired class member is missing, or because the
     * desired class member is not accessible to the lookup class.
     * It can also fail if a security manager is installed and refuses
     * access.  In any of these cases, an exception will be
     * thrown from the attempted lookup.
     * In general, the conditions under which a method handle may be
     * created for a method M are exactly as restrictive as the conditions
     * under which the lookup class could have compiled a call to M.
     */
    public static final
    class Lookup {
        private final Class<?> lookupClass;

        /** Which class is performing the lookup?  It is this class against
         *  which checks are performed for visibility and access permissions.
         *  <p>
         *  This value is null if and only if this lookup is {@link #PUBLIC_LOOKUP}.
         */
        public Class<?> lookupClass() {
            return lookupClass;
        }

        /** Embody the current class (the lookupClass) as a lookup class
         * for method handle creation.
         * Must be called by from a method in this package,
         * which in turn is called by a method not in this package.
         * Also, don't make it private, lest javac interpose
         * an access$N method.
         */
        Lookup() {
            Class caller = getCallerClassAtEntryPoint();
            // make sure we haven't accidentally picked up this class:
            checkUnprivilegedlookupClass(caller);
            this.lookupClass = caller;
        }

        private Lookup(Class<?> lookupClass) {
            this.lookupClass = lookupClass;
        }

        /** Version of lookup which is trusted minimally.
         *  It can only be used to create method handles to
         *  publicly accessible members.
         */
        public static final Lookup PUBLIC_LOOKUP = new Lookup(null);

        /** Package-private version of lookup which is trusted. */
        static final Lookup IMPL_LOOKUP = new Lookup(Access.class);
        static { MethodHandleImpl.initLookup(IMPL_TOKEN, IMPL_LOOKUP); }

        private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
            if (lookupClass == null ||
                lookupClass == Access.class ||
                lookupClass.getName().startsWith("java.dyn."))
                throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
        }

        @Override
        public String toString() {
            if (lookupClass == null)
                return "public";
            return lookupClass.getName();
        }

        // call this from an entry point method in Lookup with extraFrames=0.
        private static Class<?> getCallerClassAtEntryPoint() {
            final int CALLER_DEPTH = 4;
            // 0: Reflection.getCC, 1: getCallerClassAtEntryPoint,
            // 2: Lookup.<init>, 3: MethodHandles.*, 4: caller
            // Note:  This should be the only use of getCallerClass in this file.
            return Reflection.getCallerClass(CALLER_DEPTH);
        }

        /**
         * Produce a method handle for a static method.
         * The type of the method handle will be that of the method.
         * The method and all its argument types must be accessible to the lookup class.
         * If the method's class has not yet been initialized, that is done
         * immediately, before the method handle is returned.
         * @param defc the class from which the method is accessed
         * @param name the name of the method
         * @param type the type of the method
         * @return the desired method handle
         * @exception SecurityException <em>TBD</em>
         * @exception NoAccessException if the method does not exist or access checking fails
         */
        public
        MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws NoAccessException {
            MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
            checkStatic(true, method, lookupClass);
            //throw NoSuchMethodException
            return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
        }

        /**
         * Produce a method handle for a virtual method.
         * The type of the method handle will be that of the method,
         * with the receiver type ({@code defc}) prepended.
         * The method and all its argument types must be accessible to the lookup class.
         * <p>
         * When called, the handle will treat the first argument as a receiver
         * and dispatch on the receiver's type to determine which method
         * implementation to enter.
         * (The dispatching action is identical with that performed by an
         * {@code invokevirtual} or {@code invokeinterface} instruction.)
         * @param defc the class or interface from which the method is accessed
         * @param name the name of the method
         * @param type the type of the method, with the receiver argument omitted
         * @return the desired method handle
         * @exception SecurityException <em>TBD</em>
         * @exception NoAccessException if the method does not exist or access checking fails
         */
        public MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws NoAccessException {
            MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), true, lookupClass);
            checkStatic(false, method, lookupClass);
            return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
        }

        /**
         * Produce an early-bound method handle for a virtual method,
         * or a handle for a constructor, as if called from an {@code invokespecial}
         * instruction from {@code caller}.
         * The type of the method handle will be that of the method or constructor,
         * with a suitably restricted receiver type (such as {@code caller}) prepended.
         * The method or constructor and all its argument types must be accessible
         * to the caller.
         * <p>
         * When called, the handle will treat the first argument as a receiver,
         * but will not dispatch on the receiver's type.
         * (This direct invocation action is identical with that performed by an
         * {@code invokespecial} instruction.)
         * <p>
         * If the explicitly specified caller class is not identical with the
         * lookup class, a security check TBD is performed.
         * @param defc the class or interface from which the method is accessed
         * @param name the name of the method, or "<init>" for a constructor
         * @param type the type of the method, with the receiver argument omitted
         * @param specialCaller the proposed calling class to perform the {@code invokespecial}
         * @return the desired method handle
         * @exception SecurityException <em>TBD</em>
         * @exception NoAccessException if the method does not exist or access checking fails
         */
        public MethodHandle findSpecial(Class<?> defc, String name, MethodType type,
                                        Class<?> specialCaller) throws NoAccessException {
            checkSpecialCaller(specialCaller, lookupClass);
            MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), false, specialCaller);
            checkStatic(false, method, lookupClass);
            if (name.equals("<init>")) {
                if (defc != specialCaller)
                    throw newNoAccessException("constructor must be local to lookup class", method, lookupClass);
            } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) {
                throw newNoAccessException("method must be in a superclass of lookup class", method, lookupClass);
            }
            return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, specialCaller);
        }

        /**
         * Produce an early-bound method handle for a non-static method.
         * The receiver must have a supertype {@code defc} in which a method
         * of the given name and type is accessible to the lookup class.
         * The method and all its argument types must be accessible to the lookup class.
         * The type of the method handle will be that of the method.
         * The given receiver will be bound into the method handle.
         * <p>
         * Equivalent to the following expression:
         * <code>
         * {@link #insertArgument}({@link #findVirtual}(defc, name, type), receiver)
         * </code>
         * @param receiver the object from which the method is accessed
         * @param name the name of the method
         * @param type the type of the method, with the receiver argument omitted
         * @return the desired method handle
         * @exception SecurityException <em>TBD</em>
         * @exception NoAccessException if the method does not exist or access checking fails
         */
        public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
            Class<? extends Object> rcvc = receiver.getClass(); // may get NPE
            MemberName reference = new MemberName(rcvc, name, type);
            MemberName method = IMPL_NAMES.resolveOrFail(reference, true, lookupClass);
            checkStatic(false, method, lookupClass);
            MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
            MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
            if (bmh == null)
                throw newNoAccessException(method, lookupClass);
            return bmh;
        }

        /**
         * Make a direct method handle to <i>m</i>, if the lookup class has permission.
         * If <i>m</i> is non-static, the receiver argument is treated as an initial argument.
         * If <i>m</i> is virtual, overriding is respected on every call.
         * Unlike the Core Reflection API, exceptions are <em>not</em> wrapped.
         * The type of the method handle will be that of the method,
         * with the receiver type prepended (but only if it is non-static).
         * If the method's {@code accessible} flag is not set,
         * access checking is performed immediately on behalf of the lookup class.
         * If <i>m</i> is not public, do not share the resulting handle with untrusted parties.
         * @param m the reflected method
         * @return a method handle which can invoke the reflected method
         * @exception NoAccessException if access checking fails
         */
        public MethodHandle unreflect(Method m) throws NoAccessException {
            return unreflectImpl(new MemberName(m), m.isAccessible(), true, lookupClass);
        }

        /**
         * Produce a method handle for a reflected method.
         * It will bypass checks for overriding methods on the receiver,
         * as if by the {@code invokespecial} instruction.
         * The type of the method handle will be that of the method,
         * with the receiver type prepended.
         * If the method's {@code accessible} flag is not set,
         * access checking is performed immediately on behalf of the lookup class,
         * as if {@code invokespecial} instruction were being linked.
         * @param m the reflected method
         * @return a method handle which can invoke the reflected method
         * @exception NoAccessException if access checking fails
         */
        public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws NoAccessException {
            checkSpecialCaller(specialCaller, lookupClass);
            MemberName mname = new MemberName(m);
            checkStatic(false, mname, lookupClass);
            return unreflectImpl(mname, m.isAccessible(), false, specialCaller);
        }

        /**
         * Produce a method handle for a reflected constructor.
         * The type of the method handle will be that of the constructor.
         * The method handle will perform a {@code newInstance} operation,
         * creating a new instance of the constructor's class on the
         * arguments passed to the method handle.
         * <p>
         * If the constructor's {@code accessible} flag is not set,
         * access checking is performed immediately on behalf of the lookup class,
         * as if {@code invokespecial} instruction were being linked.
         * @param ctor the reflected constructor
         * @return a method handle which can invoke the reflected constructor
         * @exception NoAccessException if access checking fails
         */
        public MethodHandle unreflectConstructor(Constructor ctor) throws NoAccessException {
            MemberName m = new MemberName(ctor);
            return unreflectImpl(m, ctor.isAccessible(), false, lookupClass);
        }

        /**
         * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
         * Produce a method handle giving read access to a reflected field.
         * The type of the method handle will have a return type of the field's
         * value type.  Its sole argument will be the field's containing class
         * (but only if it is non-static).
         * If the method's {@code accessible} flag is not set,
         * access checking is performed immediately on behalf of the lookup class.
         * @param f the reflected field
         * @return a method handle which can load values from the reflected field
         * @exception NoAccessException if access checking fails
         */
        public MethodHandle unreflectGetter(Field f) throws NoAccessException {
            return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, lookupClass);
        }

        /**
         * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
         * Produce a method handle giving write access to a reflected field.
         * The type of the method handle will have a void return type.
         * Its last argument will be the field's value type.
         * Its other argument will be the field's containing class
         * (but only if it is non-static).
         * If the method's {@code accessible} flag is not set,
         * access checking is performed immediately on behalf of the lookup class.
         * @param f the reflected field
         * @return a method handle which can store values into the reflected field
         * @exception NoAccessException if access checking fails
         */
        public MethodHandle unreflectSetter(Field f) throws NoAccessException {
            return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, lookupClass);
        }

    }

    static /*must not be public*/
    MethodHandle findStaticFrom(Class<?> lookupClass,
                                Class<?> defc, String name, MethodType type) throws NoAccessException {
        MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
        checkStatic(true, method, lookupClass);
        return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
    }

    static void checkStatic(boolean wantStatic, MemberName m, Class<?> lookupClass) {
        if (wantStatic != m.isStatic()) {
            String message = wantStatic ? "expected a static method" : "expected a non-static method";
            throw newNoAccessException(message, m, lookupClass);
        }
    }

    static void checkSpecialCaller(Class<?> specialCaller, Class<?> lookupClass) {
        if (lookupClass == Lookup.IMPL_LOOKUP.lookupClass())
            return;  // privileged action
        if (lookupClass == null ||  // public-only access
            !VerifyAccess.isSamePackageMember(specialCaller, lookupClass))
            throw newNoAccessException("no private access", new MemberName(specialCaller), lookupClass);
    }

    // Helper for creating handles on reflected methods and constructors.
    static MethodHandle unreflectImpl(MemberName m, boolean isAccessible,
                                      boolean doDispatch, Class<?> lookupClass) {
        MethodType mtype = m.getInvocationType();
        Class<?> defc = m.getDeclaringClass();
        int mods = m.getModifiers();
        if (m.isStatic()) {
            if (!isAccessible &&
                    VerifyAccess.isAccessible(defc, mods, false, lookupClass) == null)
                throw newNoAccessException(m, lookupClass);
        } else {
            Class<?> constraint;
            if (isAccessible) {
                // abbreviated access check for "unlocked" method
                constraint = doDispatch ? defc : lookupClass;
            } else {
                constraint = VerifyAccess.isAccessible(defc, mods, doDispatch, lookupClass);
            }
            if (constraint != defc && !constraint.isAssignableFrom(defc)) {
                if (!defc.isAssignableFrom(constraint))
                    throw newNoAccessException("receiver must be in caller class", m, lookupClass);
                mtype = mtype.changeParameterType(0, constraint);
            }
        }
        return MethodHandleImpl.findMethod(IMPL_TOKEN, m, doDispatch, lookupClass);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle giving read access to elements of an array.
     * The type of the method handle will have a return type of the array's
     * element type.  Its first argument will be the array type,
     * and the second will be {@code int}.
     * @param arrayClass an array type
     * @return a method handle which can load values from the given array type
     * @throws  IllegalArgumentException if arrayClass is not an array type
     */
    public static
    MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
        return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle giving write access to elements of an array.
     * The type of the method handle will have a void return type.
     * Its last argument will be the array's element type.
     * The first and second arguments will be the array type and int.
     * @return a method handle which can store values into the array type
     * @throws IllegalArgumentException if arrayClass is not an array type
     */
    public static
    MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
        return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true);
    }


    /// method handle invocation (reflective style)

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Call the {@code invoke} method of a given method handle,
     * with arguments that exactly match the parameter types of the method handle.
     * The length of the arguments array must equal the parameter count
     * of the target's type.
     * The arguments array is spread into separate arguments, and
     * basic reference and unboxing conversions are applied.
     * <p>
     * In order to match the type of the target, the following argument
     * conversions are applied as necessary:
     * <ul>
     * <li>reference casting
     * <li>unboxing
     * </ul>
     * The following conversions are not applied:
     * <ul>
     * <li>primitive conversions (e.g., {@code byte} to {@code int}
     * <li>varargs conversions other than the initial spread
     * <li>any application-specific conversions (e.g., string to number)
     * </ul>
     * The result returned by the call is boxed if it is a primitive,
     * or forced to null if the return type is void.
     * <p>
     * This call is a convenience method for the following code:
     * <pre>
     *   MethodHandle invoker = MethodHandles.genericInvoker(target.type(), 0, true);
     *   Object result = invoker.invoke(arguments);
     * </pre>
     * @param target the method handle to invoke
     * @param arguments the arguments to pass to the target
     * @return the result returned by the target
     */
    public static
    Object invoke(MethodHandle target, Object... arguments) {
        int argc = arguments == null ? 0 : arguments.length;
        MethodType type = target.type();
        if (argc <= 4) {
            MethodHandle invoker = invokers(type).genericInvoker();
            switch (argc) {
                case 0:  return invoker.<Object>invoke(target);
                case 1:  return invoker.<Object>invoke(target,
                                    arguments[0]);
                case 2:  return invoker.<Object>invoke(target,
                                    arguments[0], arguments[1]);
                case 3:  return invoker.<Object>invoke(target,
                                    arguments[0], arguments[1], arguments[2]);
                case 4:  return invoker.<Object>invoke(target,
                                    arguments[0], arguments[1], arguments[2], arguments[3]);
            }
        }
        MethodHandle invoker = invokers(type).varargsInvoker();
        return invoker.<Object>invoke(target, arguments);
    }

    public static
    Object invoke_0(MethodHandle target) {
        MethodHandle invoker = invokers(target.type()).genericInvoker();
        return invoker.<Object>invoke(target);
    }
    public static
    Object invoke_1(MethodHandle target, Object a0) {
        MethodHandle invoker = invokers(target.type()).genericInvoker();
        return invoker.<Object>invoke(target, a0);
    }
    public static
    Object invoke_2(MethodHandle target, Object a0, Object a1) {
        MethodHandle invoker = invokers(target.type()).genericInvoker();
        return invoker.<Object>invoke(target, a0, a1);
    }
    public static
    Object invoke_3(MethodHandle target, Object a0, Object a1, Object a2) {
        MethodHandle invoker = invokers(target.type()).genericInvoker();
        return invoker.<Object>invoke(target, a0, a1, a2);
    }
    public static
    Object invoke_4(MethodHandle target, Object a0, Object a1, Object a2, Object a3) {
        MethodHandle invoker = invokers(target.type()).genericInvoker();
        return invoker.<Object>invoke(target, a0, a1, a2, a3);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Give a method handle which will invoke any method handle of the
     * given type on a standard set of {@code Object} type arguments.
     * The the resulting invoker will be a method handle with the following
     * arguments:
     * <ul>
     * <li>a single {@code MethodHandle} target
     * <li>zero or more {@code Object} values
     * <li>an optional {@code Object[]} array containing more arguments
     * </ul>
     * The invoker will spread the varargs array (if present), apply
     * reference casts as necessary, and unbox primitive arguments.
     * The return value of the invoker will be an {@code Object} reference,
     * boxing a primitive value if the original type returns a primitive,
     * and always null if the original type returns void.
     * <p>
     * This is a convenience method equivalent to the following code:
     * <pre>
     * MethodHandle invoker = exactInvoker(type);
     * MethodType genericType = MethodType.makeGeneric(objectArgCount, varargs);
     * genericType = genericType.insertParameterType(0, MethodHandle.class);
     * if (!varargs)
     *     return convertArguments(invoker, genericType);
     * else
     *     return spreadArguments(invoker, genericType);
     * </pre>
     * @param type the desired target type
     * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments
     * @param varargs if true, the invoker will accept a final {@code Object[]} argument
     * @return a method handle suitable for invoking any method handle of the given type
     */
    static public
    MethodHandle genericInvoker(MethodType type, int objectArgCount, boolean varargs) {
        return invokers(type).genericInvoker();
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Give a method handle which will take a invoke any method handle of the
     * given type.  The resulting invoker will have a type which is
     * exactly equal to the desired type, except that it will accept
     * an additional leading argument of type {@code MethodHandle}.
     * <p>
     * This is a convenience method equivalent to the following code:
     * <pre>
     *     MethodHandles.lookup().findVirtual(MethodHandle.class, "invoke", type);
     * </pre>
     * @param type the desired target type
     * @return a method handle suitable for invoking any method handle of the given type
     */
    static public
    MethodHandle exactInvoker(MethodType type) {
        return invokers(type).exactInvoker();
    }

    static private Invokers invokers(MethodType type) {
        return MethodTypeImpl.invokers(IMPL_TOKEN, type);
    }

    /**
     * <em>WORK IN PROGRESS:</em>
     * Perform value checking, exactly as if for an adapted method handle.
     * It is assumed that the given value is either null, of type T0,
     * or (if T0 is primitive) of the wrapper type corresponding to T0.
     * The following checks and conversions are made:
     * <ul>
     * <li>If T0 and T1 are references, then a cast to T1 is applied.
     *     (The types do not need to be related in any particular way.)
     * <li>If T0 and T1 are primitives, then a widening or narrowing
     *     conversion is applied, if one exists.
     * <li>If T0 is a primitive and T1 a reference, and
     *     T0 has a wrapper type TW, a boxing conversion to TW is applied,
     *     possibly followed by a reference conversion.
     *     T1 must be TW or a supertype.
     * <li>If T0 is a reference and T1 a primitive, and
     *     T1 has a wrapper type TW, an unboxing conversion is applied,
     *     possibly preceded by a reference conversion.
     *     T0 must be TW or a supertype.
     * <li>If T1 is void, the return value is discarded
     * <li>If T0 is void and T1 a reference, a null value is introduced.
     * <li>If T0 is void and T1 a primitive, a zero value is introduced.
     * </ul>
     * If the value is discarded, null will be returned.
     * @param valueType
     * @param value
     * @return the value, converted if necessary
     * @throws java.lang.ClassCastException if a cast fails
     */
    static
    <T0, T1> T1 checkValue(Class<T0> t0, Class<T1> t1, Object value)
       throws ClassCastException
    {
        if (t0 == t1) {
            // no conversion needed; just reassert the same type
            if (t0.isPrimitive())
                return Wrapper.asPrimitiveType(t1).cast(value);
            else
                return Wrapper.OBJECT.cast(value, t1);
        }
        boolean prim0 = t0.isPrimitive(), prim1 = t1.isPrimitive();
        if (!prim0) {
            // check contract with caller
            Wrapper.OBJECT.cast(value, t0);
            if (!prim1) {
                return Wrapper.OBJECT.cast(value, t1);
            }
            // convert reference to primitive by unboxing
            Wrapper w1 = Wrapper.forPrimitiveType(t1);
            return w1.cast(value, t1);
        }
        // check contract with caller:
        Wrapper.asWrapperType(t0).cast(value);
        Wrapper w1 = Wrapper.forPrimitiveType(t1);
        return w1.cast(value, t1);
    }

    static
    Object checkValue(Class<?> T1, Object value)
       throws ClassCastException
    {
        Class<?> T0;
        if (value == null)
            T0 = Object.class;
        else
            T0 = value.getClass();
        return checkValue(T0, T1, value);
    }

    /// method handle modification (creation from other method handles)

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle which adapts the type of the
     * given method handle to a new type, by pairwise argument conversion,
     * and/or varargs conversion.
     * The original type and new type must have the same number of
     * arguments, or else one or both them the must be varargs types.
     * The resulting method handle is guaranteed to confess a type
     * which is equal to the desired new type, with any varargs property erased.
     * <p>
     * If the original type and new type are equal, returns target.
     * <p>
     * The following conversions are applied as needed both to
     * arguments and return types.  Let T0 and T1 be the differing
     * new and old parameter types (or old and new return types)
     * for corresponding values passed by the new and old method types.
     * <p>
     * If an ordinary (non-varargs) parameter of the new type is
     * to be boxed in a varargs parameter of the old type of type T1[],
     * then T1 is the element type of the varargs array.
     * Otherwise, if a varargs parameter of the new type of type T0[]
     * is to be spread into one or more outgoing old type parameters,
     * then T0 is the element type of the
     * If the new type is varargs and the old type is not, the varargs
     * argument will be checked and must be a non-null array of exactly
     * the right length.  If there are no parameters in the old type
     * corresponding to the new varargs parameter, the varargs argument
     * is also allowed to be null.
     * <p>
     * Given those types T0, T1, one of the following conversions is applied
     * if possible:
     * <ul>
     * <li>If T0 and T1 are references, then a cast to T2 is applied,
     *     where T2 is Object if T1 is an interface, else T1.
     *     (The types do not need to be related in any particular way.
     *     The treatment of interfaces follows the usage of the bytecode verifier.)
     * <li>If T0 and T1 are primitives, then a Java casting
     *     conversion (JLS 5.5) is applied, if one exists.
     * <li>If T0 and T1 are primitives and one is boolean,
     *     the boolean is treated as a one-bit unsigned integer.
     *     (This treatment follows the usage of the bytecode verifier.)
     *     A conversion from another primitive type behaves as if
     *     it first converts to byte, and then masks all but the low bit.
     * <li>If T0 is a primitive and T1 a reference, a boxing
     *     conversion is applied if one exists, possibly followed by
     *     an reference conversion to a superclass.
     *     T1 must be a wrapper class or a supertype of one.
     *     If T1 is a wrapper class, T0 is converted if necessary
     *     to T1's primitive type by one of the preceding conversions.
     *     Otherwise, T0 is boxed, and its wrapper converted to T1.
     * <li>If T0 is a reference and T1 a primitive, an unboxing
     *     conversion is applied if one exists, possibly preceded by
     *     a reference conversion to a wrapper class.
     *     T0 must be a wrapper class or a supertype of one.
     *     If T0 is a wrapper class, its primitive value is converted
     *     if necessary to T1 by one of the preceding conversions.
     *     Otherwise, T0 is converted directly to the wrapper type for T1,
     *     which is then unboxed.
     * <li>If T1 is void, any returned value is discarded
     * <li>If T0 is void and T1 a reference, a null value is introduced.
     * <li>If T0 is void and T1 a primitive, a zero value is introduced.
     * </ul>
     * @param target the method handle to invoke after arguments are retyped
     * @param newType the expected type of the new method handle
     * @return a method handle which delegates to {@code target} after performing
     *           any necessary argument conversions, and arranges for any
     *           necessary return value conversions
     * @throws WrongMethodTypeException if the conversion cannot be made
     */
    public static
    MethodHandle convertArguments(MethodHandle target, MethodType newType) {
        MethodType oldType = target.type();
        if (oldType.equals(newType))
            return target;
        MethodHandle res = MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
                                                 newType, oldType, null);
        if (res == null)
            throw newIllegalArgumentException("cannot convert to "+newType+": "+target);
        return res;
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle which adapts the calling sequence of the
     * given method handle to a new type, by reordering the arguments.
     * The resulting method handle is guaranteed to confess a type
     * which is equal to the desired new type.
     * <p>
     * The given array controls the reordering.
     * Call {@code #I} the number of incoming parameters (the value
     * {@code newType.parameterCount()}, and call {@code #O} the number
     * of outgoing parameters (the value {@code target.type().parameterCount()}).
     * Then the length of the reordering array must be {@code #O},
     * and each element must be a non-negative number less than {@code #I}.
     * For every {@code N} less than {@code #O}, the {@code N}-th
     * outgoing argument will be taken from the {@code I}-th incoming
     * argument, where {@code I} is {@code reorder[N]}.
     * <p>
     * The reordering array need not specify an actual permutation.
     * An incoming argument will be duplicated if its index appears
     * more than once in the array, and an incoming argument will be dropped
     * if its index does not appear in the array.
     * <p>
     * Pairwise conversions are applied as needed to arguments and return
     * values, as with {@link #convertArguments}.
     * @param target the method handle to invoke after arguments are reordered
     * @param newType the expected type of the new method handle
     * @param reorder a string which controls the reordering
     * @return a method handle which delegates to {@code target} after performing
     *           any necessary argument motion and conversions, and arranges for any
     *           necessary return value conversions
     */
    public static
    MethodHandle permuteArguments(MethodHandle target, MethodType newType, int[] reorder) {
        MethodType oldType = target.type();
        checkReorder(reorder, newType, oldType);
        return MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
                                                 newType, oldType,
                                                 reorder);
    }

    private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
        if (reorder.length == oldType.parameterCount()) {
            int limit = newType.parameterCount();
            boolean bad = false;
            for (int i : reorder) {
                if (i < 0 || i >= limit) {
                    bad = true; break;
                }
            }
            if (!bad)  return;
        }
        throw newIllegalArgumentException("bad reorder array");
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle which adapts the type of the
     * given method handle to a new type, by spreading the final argument.
     * The resulting method handle is guaranteed to confess a type
     * which is equal to the desired new type.
     * <p>
     * The final parameter type of the new type must be an array type T[].
     * This is the type of what is called the <i>spread</i> argument.
     * All other arguments of the new type are called <i>ordinary</i> arguments.
     * <p>
     * The ordinary arguments of the new type are pairwise converted
     * to the initial parameter types of the old type, according to the
     * rules in {@link #convertArguments}.
     * Any additional arguments in the old type
     * are converted from the array element type T,
     * again according to the rules in {@link #convertArguments}.
     * The return value is converted according likewise.
     * <p>
     * The call verifies that the spread argument is in fact an array
     * of exactly the type length, i.e., the excess number of
     * arguments in the old type over the ordinary arguments in the new type.
     * If there are no excess arguments, the spread argument is also
     * allowed to be null.
     * @param target the method handle to invoke after the argument is prepended
     * @param newType the expected type of the new method handle
     * @return a new method handle which spreads its final argument,
     *         before calling the original method handle
     */
    public static
    MethodHandle spreadArguments(MethodHandle target, MethodType newType) {
        MethodType oldType = target.type();
        int inargs  = newType.parameterCount();
        int outargs = oldType.parameterCount();
        int spreadPos = inargs - 1;
        int numSpread = (outargs - spreadPos);
        MethodHandle res = null;
        if (spreadPos >= 0 && numSpread >= 0) {
            res = MethodHandleImpl.spreadArguments(IMPL_TOKEN, target, newType, spreadPos);
        }
        if (res == null) {
            throw newIllegalArgumentException("cannot spread "+newType+" to " +oldType);
        }
        return res;
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle which adapts the type of the
     * given method handle to a new type, by collecting a series of
     * trailing arguments into an array.
     * The resulting method handle is guaranteed to confess a type
     * which is equal to the desired new type.
     * <p>
     * This method is inverse to {@link #spreadArguments}.
     * The final parameter type of the old type must be an array type T[],
     * which is the type of what is called the <i>spread</i> argument.
     * The trailing arguments of the new type which correspond to
     * the spread argument are all converted to type T and collected
     * into an array before the original method is called.
     * <p>
     * ISSUE: Unify this with combineArguments.  CollectArguments
     * is combineArguments with (a) new Object[]{...} as a combiner,
     * and (b) the combined arguments dropped, in favor of the combined result.
     * @param target the method handle to invoke after the argument is prepended
     * @param newType the expected type of the new method handle
     * @return a new method handle which collects some trailings argument
     *         into an array, before calling the original method handle
     */
    public static
    MethodHandle collectArguments(MethodHandle target, MethodType newType) {
        MethodType oldType = target.type();
        int inargs  = newType.parameterCount();
        int outargs = oldType.parameterCount();
        int collectPos = outargs - 1;
        int numCollect = (inargs - collectPos);
        if (collectPos < 0 || numCollect < 0)
            throw newIllegalArgumentException("wrong number of arguments");
        return MethodHandleImpl.collectArguments(IMPL_TOKEN, target, newType, collectPos);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle which calls the original method handle,
     * after inserting the given argument at the given position.
     * The type of the new method handle will drop the corresponding argument
     * type from the original handle's type.
     * <p>
     * The given argument object must match the dropped argument type.
     * If the dropped argument type is a primitive, the argument object
     * must be a wrapper, and is unboxed to produce the primitive.
     * <p>
     * The  <i>pos</i> may range between zero and <i>N</i> (inclusively),
     * where <i>N</i> is the number of argument types in <i>target</i>,
     * meaning to insert the new argument as the first or last (respectively),
     * or somewhere in between.
     * @param target the method handle to invoke after the argument is inserted
     * @param pos where to insert the argument (zero for the first)
     * @param value the argument to insert
     * @return a new method handle which inserts an additional argument,
     *         before calling the original method handle
     */
    public static
    MethodHandle insertArgument(MethodHandle target, int pos, Object value) {
        MethodType oldType = target.type();
        ArrayList<Class<?>> ptypes =
                new ArrayList<Class<?>>(oldType.parameterList());
        int outargs = oldType.parameterCount();
        int inargs  = outargs - 1;
        if (pos < 0 || pos >= outargs)
            throw newIllegalArgumentException("no argument type to append");
        Class<?> valueType = ptypes.remove(pos);
        value = checkValue(valueType, value);
        if (pos == 0 && !valueType.isPrimitive()) {
            // At least for now, make bound method handles a special case.
            // This lets us get by with minimal JVM support, at the expense
            // of generating signature-specific adapters as Java bytecodes.
            MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, target, value);
            if (bmh != null)  return bmh;
            // else fall through to general adapter machinery
        }
        return MethodHandleImpl.bindArgument(IMPL_TOKEN, target, pos, value);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle which calls the original method handle,
     * after dropping the given argument(s) at the given position.
     * The type of the new method handle will insert the given argument
     * type(s), at that position, into the original handle's type.
     * <p>
     * The <i>pos</i> may range between zero and <i>N-1</i>,
     * where <i>N</i> is the number of argument types in <i>target</i>,
     * meaning to drop the first or last argument (respectively),
     * or an argument somewhere in between.
     * @param target the method handle to invoke after the argument is dropped
     * @param valueTypes the type(s) of the argument to drop
     * @param pos which argument to drop (zero for the first)
     * @return a new method handle which drops an argument of the given type,
     *         before calling the original method handle
     */
    public static
    MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
        if (valueTypes.length == 0)  return target;
        MethodType oldType = target.type();
        int outargs = oldType.parameterCount();
        int inargs  = outargs + valueTypes.length;
        if (pos < 0 || pos >= inargs)
            throw newIllegalArgumentException("no argument type to remove");
        ArrayList<Class<?>> ptypes =
                new ArrayList<Class<?>>(oldType.parameterList());
        ptypes.addAll(pos, Arrays.asList(valueTypes));
        MethodType newType = MethodType.make(oldType.returnType(), ptypes);
        return MethodHandleImpl.dropArguments(IMPL_TOKEN, target, newType, pos);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Make a method handle which adapts a target method handle,
     * by guarding it with a test, a boolean-valued method handle.
     * If the guard fails, a fallback handle is called instead.
     * All three method handles must have the same corresponding
     * argument and return types, except that the return type
     * of the test must be boolean.
     * <p> Here is pseudocode for the resulting adapter:
     * <blockquote><pre>
     * signature T(A...);
     * boolean test(A...);
     * T target(A...);
     * T fallback(A...);
     * T adapter(A... a) {
     *   if (test(a...))
     *     return target(a...);
     *   else
     *     return fallback(a...);
     * }
     * </pre></blockquote>
     * @param test method handle used for test, must return boolean
     * @param target method handle to call if test passes
     * @param fallback method handle to call if test fails
     * @return method handle which incorporates the specified if/then/else logic
     * @throws IllegalArgumentException if {@code test} does not return boolean,
     *          or if all three method types do not match (with the return
     *          type of {@code test} changed to match that of {@code target}).
     */
    public static
    MethodHandle guardWithTest(MethodHandle test,
                               MethodHandle target,
                               MethodHandle fallback) {
        if (target.type() != fallback.type())
            throw newIllegalArgumentException("target and fallback types do not match");
        if (target.type().changeReturnType(boolean.class) != test.type())
            throw newIllegalArgumentException("target and test types do not match");
        /* {
            MethodHandle invoke = findVirtual(MethodHandle.class, "invoke", target.type());
            static MethodHandle choose(boolean z, MethodHandle t, MethodHandle f) {
                return z ? t : f;
            }
            static MethodHandle compose(MethodHandle f, MethodHandle g) {
                Class<?> initargs = g.type().parameterArray();
                f = dropArguments(f, 1, initargs);  // ignore 2nd copy of args
                return combineArguments(f, g);
            }
            // choose = \z.(z ? target : fallback)
            MethodHandle choose = findVirtual(MethodHandles.class, "choose",
                    MethodType.make(boolean.class, MethodHandle.class, MethodHandle.class));
            choose = appendArgument(choose, target);
            choose = appendArgument(choose, fallback);
            MethodHandle dispatch = compose(choose, test);
            // dispatch = \(a...).(test(a...) ? target : fallback)
            return combineArguments(invoke, dispatch, 0);
            // return \(a...).((test(a...) ? target : fallback).invoke(a...))
        } */
        return MethodHandleImpl.makeGuardWithTest(IMPL_TOKEN, test, target, fallback);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Adapt a target method handle {@code target} by first processing
     * its arguments, and then calling the target.
     * The initial processing is performed by a second method handle, the {@code combiner}.
     * After this, control passes to the {@code target}, with the same arguments.
     * <p>
     * The return value of the {@code combiner} is inserted into the argument list
     * for the {@code target} at the indicated position {@code pos}, if it is non-negative.
     * Except for this inserted argument (if any), the argument types of
     * the target {@code target} and the {@code combiner} must be identical.
     * <p>
     * (Note that {@link #dropArguments} can be used to remove any arguments
     * that either the {@code combiner} or {@code target} does not wish to receive.)
     * <p>
     * The combiner handle must have the same argument types as the
     * target handle, but must return {@link MethodHandle} instead of
     * the ultimate return type.  The returned method handle, in turn,
     * is required to have exactly the given final method type.
     * <p> Here is pseudocode for the resulting adapter:
     * <blockquote><pre>
     * signature V(A[pos]..., B...);
     * signature T(A[pos]..., V, B...);
     * T target(A... a, V v, B... b);
     * V combiner(A..., B...);
     * T adapter(A... a, B... b) {
     *   V v = combiner(a..., b...);
     *   return target(a..., v, b...);
     * }
     * </pre></blockquote>
     * @param target the method handle to invoke after arguments are combined
     * @param pos where the return value of {@code combiner} is to
     *          be inserted as an argument to {@code target}
     * @param combiner method handle to call initially on the incoming arguments
     * @return method handle which incorporates the specified dispatch logic
     * @throws IllegalArgumentException if {@code combiner} does not itself
     *          return either void or the {@code pos}-th argument of {@code target},
     *          or does not have the same argument types as {@code target}
     *          (minus the inserted argument)
     */
    public static
    MethodHandle combineArguments(MethodHandle target, int pos, MethodHandle combiner) {
        MethodType mhType = target.type();
        Class<?> combineType = combiner.type().returnType();
        MethodType incomingArgs;
        if (pos < 0) {
            // No inserted argument; target & combiner must have same argument types.
            incomingArgs = mhType;
            if (!incomingArgs.changeReturnType(combineType).equals(combiner.type()))
                throw newIllegalArgumentException("target and combiner types do not match");
        } else {
            // Inserted argument.
            if (pos >= mhType.parameterCount()
                || mhType.parameterType(pos) != combineType)
                throw newIllegalArgumentException("inserted combiner argument does not match target");
            incomingArgs = mhType.dropParameterType(pos);
        }
        if (!incomingArgs.changeReturnType(combineType).equals(combiner.type())) {
            throw newIllegalArgumentException("target and combiner types do not match");
        }
        return MethodHandleImpl.combineArguments(IMPL_TOKEN, target, combiner, pos);
    }

}