/*
 * 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 impl.java.dyn.Access;
import impl.java.dyn.MemberName;
import impl.java.dyn.MethodHandleImpl;
import impl.java.dyn.util.MethodHandleInvoker;
import impl.java.dyn.util.VerifyAccess;
import impl.java.dyn.util.Wrappers;
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.reflect.Reflection;
import static impl.java.dyn.MemberName.newIllegalArgumentException;
import static impl.java.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_LOOKUP = MemberName.getFactory(IMPL_TOKEN);

    //// Method handle creation from ordinary methods.

    /**
     * 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 caller.
     * 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, or null if no such method exists
     * @exception SecurityException <em>TBD</em>
     * @exception NoAccessException if access checking fails
     */
    public static
    MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws NoAccessException {
        Class<?> caller = Reflection.getCallerClass(2);
        MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, caller);
        checkStatic(true, method, caller);
        return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, caller);
    }

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

    /**
     * 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 caller.
     * <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, or null if no such method exists
     * @exception SecurityException <em>TBD</em>
     * @exception NoAccessException if access checking fails
     */
    public static
    MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws NoAccessException {
        Class<?> caller = Reflection.getCallerClass(2);
        MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type), true, caller);
        checkStatic(false, method, caller);
        return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, caller);
    }

    /**
     * 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 actual
     * caller of {@code findSpecial}, 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, or null if no such method exists
     * @exception SecurityException <em>TBD</em>
     * @exception NoAccessException if access checking fails
     */
    public static
    MethodHandle findSpecial(Class<?> defc, String name, MethodType type,
            Class<?> specialCaller) throws NoAccessException {
        Class<?> caller = Reflection.getCallerClass(2);
        checkSpecialCaller(specialCaller, caller);
        MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type), false, specialCaller);
        checkStatic(false, method, caller);
        if (name.equals("<init>")) {
            if (defc != specialCaller)
                throw newNoAccessException("constructor must be local to caller", method, caller);
        } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) {
            throw newNoAccessException("method must be in a superclass of caller", method, caller);
        }
        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 caller.
     * The method and all its argument types must be accessible to the caller.
     * 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, or null if no such method exists
     * @exception SecurityException <em>TBD</em>
     * @exception NoAccessException if access checking fails
     */
    public static
    MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
        Class<?> caller = Reflection.getCallerClass(2);
        Class<? extends Object> rcvc = receiver.getClass(); // may get NPE
        MemberName reference = new MemberName(rcvc, name, type);
        MemberName method = IMPL_LOOKUP.resolveOrFail(reference, true, caller);
        checkStatic(false, method, caller);
        MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, caller);
        MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
        if (bmh == null)
            throw newNoAccessException(method, caller);
        return bmh;
    }

    /**
     * Make a direct method handle to <i>m</i>, if the current caller 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 caller.
     * If <i>m</i> is not public, do not share the resulting handle with untrusted callers.
     * @param m the reflected method
     * @return a method handle which can invoke the reflected method
     * @exception NoAccessException if access checking fails
     */
    public static
    MethodHandle unreflect(Method m) throws NoAccessException {
        Class<?> caller = Reflection.getCallerClass(2);
        return unreflect(new MemberName(m), m.isAccessible(), true, caller);
    }

    /**
     * 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 caller,
     * 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 static
    MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws NoAccessException {
        Class<?> caller = Reflection.getCallerClass(2);
        checkSpecialCaller(specialCaller, caller);
        MemberName mname = new MemberName(m);
        checkStatic(false, mname, caller);
        return unreflect(mname, m.isAccessible(), false, specialCaller);
    }

//    /**
//     * Produce a method handle for a reflected constructor.
//     * It will allow direct access to the constructor,
//     * 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 constructor's {@code accessible} flag is not set,
//     * access checking is performed immediately on behalf of the caller,
//     * as if {@code invokespecial} instruction were being linked.
//     * @param ctor the reflected constructor
//     * @param specialCaller the proposed calling class to perform the {@code invokespecial}
//     * @return a method handle which can invoke the reflected constructor
//     * @exception NoAccessException if access checking fails
//     */
//    public static
//    MethodHandle unreflectSpecial(Constructor ctor, Class<?> specialCaller) throws NoAccessException {
//        Class<?> caller = Reflection.getCallerClass(2);
//        checkSpecialCaller(specialCaller, caller);
//        MemberName m = new MemberName(ctor);
//        checkStatic(false, m, caller);
//        return unreflect(m, ctor.isAccessible(), false, specialCaller);
//    }

    private static
    void checkSpecialCaller(Class<?> specialCaller, Class<?> caller) {
        if (caller != null && !VerifyAccess.isSamePackageMember(specialCaller, caller))
            throw newNoAccessException("no private access", new MemberName(specialCaller), caller);
    }

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

    /**
     * <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 caller.
     * @param f the reflected field
     * @return a method handle which can load values from the reflected field
     * @exception NoAccessException if access checking fails
     */
    public static
    MethodHandle unreflectGetter(Field f) throws NoAccessException {
        Class<?> caller = Reflection.getCallerClass(2);
        return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, (Class)caller);
    }

    /**
     * <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 caller.
     * @param f the reflected field
     * @return a method handle which can store values into the reflected field
     * @exception NoAccessException if access checking fails
     */
    public static
    MethodHandle unreflectSetter(Field f) throws NoAccessException {
        Class<?> caller = Reflection.getCallerClass(2);
        return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, (Class)caller);
    }

    /**
     * <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 {
        Class<?> caller = Reflection.getCallerClass(2);
        return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false, (Class)caller);
    }

    /**
     * <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 {
        Class<?> caller = Reflection.getCallerClass(2);
        return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true, (Class)caller);
    }


    /// method handle invocation (reflective style)

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Call the {@code invoke} method of a given method handle.
     * The given arguments must exactly match the parameter types of the method handle.
     * <em>TBD: Fill in the details.</em>
     * No conversions are performed except reference casting and unboxing of primitives.
     * @param target method handle to invoke
     * @param arguments arguments to pass to the target (with any primitives wrapped)
     * @return the result of the method (with any primitive wrapped)
     */
    public static
    Object invoke(MethodHandle target, Object... arguments) {
        // TO DO: Remove this checking logic; must be part of the invoker anyway.
        int length = arguments.length;
        MethodType type = target.type();
        if (type.parameterCount() != length)
            throw new WrongMethodTypeException("wrong number of arguments");
        for (int i = 0; i < length; i++) {
            Object argument = arguments[i];
            Class<?> ptype = type.parameterType(i);
            if (ptype.isPrimitive()) {
                argument.getClass();  // provoke NPE if null
                ptype = Wrappers.asWrapperType(ptype);
            } else if (ptype.isInterface()) {
                ptype = Object.class;   // no check
            }
            if (ptype != Object.class) {
                ptype.cast(argument);
            }
        }
        // End of checking logic.
        return MethodHandleInvoker.make(target.type()).invoke(target, arguments);
    }

    /**
     * <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
    Object checkValue(Class<?> T0, Class<?> T1, Object value)
       throws ClassCastException
    {
        if (T0 == T1) {
            // no conversion needed
            return value;
        }
        boolean prim0 = T0.isPrimitive(), prim1 = T1.isPrimitive();
        Class<?> TW;
        if (!prim0) {
            if (!prim1) {
                return T1.cast(T0.cast(value));
            }
            // convert reference to primitive by unboxing
            TW = Wrappers.asWrapperType(T0);
            return T1.cast(TW.cast(value));
        }
        if (!prim1) {
            // convert primitive to reference type by boxing
            TW = Wrappers.asWrapperType(T1);
            return TW.cast(T0.cast(value));
        }
        // convert primitive to primitive; this requires a real value change
        if (value == null)
            return Wrappers.zeroValue(T1);
        // convert non-Number primitives to Integer:
        if (value instanceof Character) {
            char ch = (char) (Character) value;
            value = Integer.valueOf(ch);
            if (T1 == Integer.class)
                return value;
        } else if (value instanceof Boolean) {
            boolean z = (boolean) (Boolean) value;
            value = Integer.valueOf(z ? 1 : 0);
            if (T1 == Integer.class)
                return value;
        }
        Number numval = (Number) value;
        switch (Wrappers.basicTypeChar(T1)) {
        case 'Z': return (numval.intValue() != 0);
        case 'B': return numval.byteValue();
        case 'C': return (char) numval.intValue();
        case 'S': return numval.shortValue();
        case 'I': return numval.intValue();
        case 'J': return numval.longValue();
        case 'F': return numval.floatValue();
        case 'D': return numval.doubleValue();
        }
        return null;
    }

    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
     */
    public static
    MethodHandle convertArguments(MethodHandle target, MethodType newType) {
        MethodType oldType = target.type();
        if (oldType.equals(newType))
            return target;
        return MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
                                                 newType,false, target.type(), false, null);
    }

    /**
     * <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.
     * Duplication and omission is allowed.
     * The resulting method handle is guaranteed to confess a type
     * which is equal to the desired new type.
     * <p>
     * The given permutation string controls the reordering.
     * The characters of the string are treated as small integers.
     * All the characters must be in the range zero (i.e., '\0') to
     * the number of incoming arguments (the parameter count of the new type).
     * The length of the string must be equal to the number of outgoing
     * parameters (the parameter count of the original method handle's type).
     * If the n-th character of the string denotes the integer k,
     * then the n-th incoming argument becomes the k-th outgoing argument.
     * <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 permutation 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, String permutation) {
        MethodType oldType = target.type();
        return MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
                                                 newType,false, target.type(), false,
                                                 permutation);
    }

    /**
     * <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);
        if (spreadPos < 0 || numSpread < 0)
            throw newIllegalArgumentException("wrong number of arguments");
        return MethodHandleImpl.convertArguments(IMPL_TOKEN, target, newType, true, oldType, false, null);
    }

    /**
     * <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.
     * @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.convertArguments(IMPL_TOKEN, target, newType, false, oldType, true, null);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle which calls the original method handle,
     * after inserting the given argument as the first argument.
     * The type of the new method handle will drop the first argument
     * type from the original handle's type.
     * <p>
     * Equivalent to {@code insertArgument(target, value, 0)}.
     */
    public static
    MethodHandle insertArgument(MethodHandle target, Object value) {
        return insertArgument(target, value, 0);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Produce a method handle which calls the original method handle,
     * after appending the given argument as the final argument.
     * The type of the new method handle will drop the last argument
     * type from the original handle's type.
     * <p>
     * Equivalent to {@code insertArgument(target, value, N)},
     * where <i>N</i> is the number of arguments to <i>target</i>.
     */
    public static
    MethodHandle appendArgument(MethodHandle target, Object value) {
        return insertArgument(target, value, target.type().parameterCount());
    }

    /**
     * <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 value the argument to insert
     * @param pos where to insert the argument (zero for the first)
     * @return a new method handle which inserts an additional argument,
     *         before calling the original method handle
     */
    public static
    MethodHandle insertArgument(MethodHandle target, Object value, int pos) {
        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 choose(boolean z, MethodHandle t, MethodHandle f) {
                return z ? t : f;
            }
            MethodHandle choose = findVirtual(MethodHandles.class, "choose",
                    MethodType.make(boolean.class, MethodHandle.class, MethodHandle.class));
            choose = appendArgument(choose, target);
            choose = appendArgument(choose, fallback);
            choose = combineArguments(choose, test);
            MethodHandle invoke = findVirtual(MethodHandle.class, "invoke", target.type());
            return checkArguments(invoke, choose, 0);
        } */
        return MethodHandleImpl.makeGuardWithTest(IMPL_TOKEN, test, target, fallback);
    }

    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * Adapt a target method handle {@code target} by first checking its arguments,
     * and then calling the target.
     * The check is performed by a second method handle, the {@code checker}.
     * After this, control passes to the target method handle, with the same
     * arguments.
     * <p>
     * The return value of the checker is inserted into the argument list
     * for {@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 checker} must be identical.
     * <p>
     * The checker 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, B... b);
     * U checker(A..., B...);
     * T adapter(A... a, B... b) {
     *   V v = checker(a..., b...);
     *   return target(a..., v, b...);
     * }
     * </pre></blockquote>
     * @param target the method handle to invoke after arguments are checked
     * @param checker method handle to call initially on the incoming arguments
     * @param pos where the return value of {@code checker} is to
     *          be inserted as an argument to {@code target}
     * @return method handle which incorporates the specified dispatch logic
     * @throws IllegalArgumentException if {@code checker} 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 checkArguments(MethodHandle target, MethodHandle checker, int pos) {
        MethodType mhType = target.type();
        Class<?> checkType = checker.type().returnType();
        MethodType incomingArgs;
        if (pos < 0) {
            // No inserted argument; target & checker must have same argument types.
            incomingArgs = mhType;
            if (!incomingArgs.changeReturnType(checkType).equals(checker.type()))
                throw newIllegalArgumentException("target and checker types do not match");
        } else {
            // Inserted argument.
            if (pos >= mhType.parameterCount()
                || mhType.parameterType(pos) != checkType)
                throw newIllegalArgumentException("inserted checker argument does not match target");
            incomingArgs = mhType.deleteParameterType(pos);
        }
        if (!incomingArgs.changeReturnType(checkType).equals(checker.type())) {
            throw newIllegalArgumentException("target and checker types do not match");
        }
        return MethodHandleImpl.checkArguments(IMPL_TOKEN, target, checker, pos);
    }

    /// standard method handles

    /**
     * Identity function.
     * @param x an arbitrary reference value
     * @return the same value x
     */
    /*public*/ static <T>
    T identity(T x) {
        return x;
    }

    /**
     * A method handle for {@link #identity}
     */
    /*public*/ static
    MethodHandle identityMethod() {
        if (IDENTITY == null)
            IDENTITY = findStatic(MethodHandle.class, "identity", IDENTITY_TYPE);
        return IDENTITY;
    }
    private static
    MethodHandle IDENTITY;
    private static final
    MethodType IDENTITY_TYPE = MethodType.make(Object.class, Object.class);

    /**
     * Empty function.
     */
    /*public*/ static
    void empty() {
    }

    /**
     * A method handle for {@link #empty}
     */
    /*public*/ static
    MethodHandle emptyMethod() {
        if (EMPTY == null)
            EMPTY = findStatic(MethodHandle.class, "empty", EMPTY_TYPE);
        return EMPTY;
    }
    private static
    MethodHandle EMPTY;
    private static final
    MethodType EMPTY_TYPE = MethodType.make(void.class);
}
