changeset 5459:6e34c6d3479f

7023639: JSR 292 method handle invocation needs a fast path for compiled code 6984705: JSR 292 method handle creation should not go through JNI Summary: remove assembly code for JDK 7 chained method handles Reviewed-by: jrose, twisti, mhaupt, forax Contributed-by: John Rose <john.r.rose@oracle.com>, Christian Thalinger <christian.thalinger@oracle.com>, Michael Haupt <michael.haupt@oracle.com>
author twisti
date Tue, 24 Jul 2012 10:47:44 -0700
parents 0204531e8d85
children 7c14a2225485
files src/share/classes/java/lang/invoke/AdapterMethodHandle.java src/share/classes/java/lang/invoke/BoundMethodHandle.java src/share/classes/java/lang/invoke/CallSite.java src/share/classes/java/lang/invoke/CountingMethodHandle.java src/share/classes/java/lang/invoke/DirectMethodHandle.java src/share/classes/java/lang/invoke/DontInline.java src/share/classes/java/lang/invoke/ForceInline.java src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java src/share/classes/java/lang/invoke/Invokers.java src/share/classes/java/lang/invoke/LambdaForm.java src/share/classes/java/lang/invoke/MemberName.java src/share/classes/java/lang/invoke/MethodHandle.java src/share/classes/java/lang/invoke/MethodHandleImpl.java src/share/classes/java/lang/invoke/MethodHandleInfo.java src/share/classes/java/lang/invoke/MethodHandleNatives.java src/share/classes/java/lang/invoke/MethodHandleStatics.java src/share/classes/java/lang/invoke/MethodHandles.java src/share/classes/java/lang/invoke/MethodType.java src/share/classes/java/lang/invoke/MethodTypeForm.java src/share/classes/java/lang/invoke/SimpleMethodHandle.java src/share/classes/java/lang/invoke/package-info.java src/share/classes/sun/invoke/util/ValueConversions.java src/share/classes/sun/invoke/util/VerifyAccess.java src/share/classes/sun/invoke/util/VerifyType.java src/share/classes/sun/invoke/util/Wrapper.java src/share/classes/sun/misc/Unsafe.java test/java/lang/invoke/7157574/Test7157574.java test/java/lang/invoke/InvokeGenericTest.java test/java/lang/invoke/JavaDocExamplesTest.java test/java/lang/invoke/MaxTest.java test/java/lang/invoke/MethodHandlesTest.java test/java/lang/invoke/PrivateInvokeTest.java test/java/lang/invoke/ThrowExceptionsTest.java test/java/lang/invoke/remote/RemoteExample.java test/sun/invoke/util/ValueConversionsTest.java
diffstat 35 files changed, 7501 insertions(+), 3695 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/AdapterMethodHandle.java	Thu Jul 12 00:12:52 2012 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1204 +0,0 @@
-/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.lang.invoke;
-
-import sun.invoke.util.VerifyType;
-import sun.invoke.util.Wrapper;
-import sun.invoke.util.ValueConversions;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.Collections;
-import static java.lang.invoke.MethodHandleNatives.Constants.*;
-import static java.lang.invoke.MethodHandleStatics.*;
-
-/**
- * This method handle performs simple conversion or checking of a single argument.
- * @author jrose
- */
-class AdapterMethodHandle extends BoundMethodHandle {
-
-    //MethodHandle vmtarget;   // next AMH or BMH in chain or final DMH
-    //Object       argument;   // parameter to the conversion if needed
-    //int          vmargslot;  // which argument slot is affected
-    private final int conversion;  // the type of conversion: RETYPE_ONLY, etc.
-
-    // Constructors in this class *must* be package scoped or private.
-    private AdapterMethodHandle(MethodHandle target, MethodType newType,
-                long conv, Object convArg) {
-        super(newType, convArg, newType.parameterSlotDepth(1+convArgPos(conv)));
-        this.conversion = convCode(conv);
-        // JVM might update VM-specific bits of conversion (ignore)
-        MethodHandleNatives.init(this, target, convArgPos(conv));
-    }
-    AdapterMethodHandle(MethodHandle target, MethodType newType,
-                long conv) {
-        this(target, newType, conv, null);
-    }
-
-    int getConversion() { return conversion; }
-
-    // TO DO:  When adapting another MH with a null conversion, clone
-    // the target and change its type, instead of adding another layer.
-
-    /** Can a JVM-level adapter directly implement the proposed
-     *  argument conversions, as if by fixed-arity MethodHandle.asType?
-     */
-    static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) {
-        // same number of args, of course
-        int len = newType.parameterCount();
-        if (len != oldType.parameterCount())
-            return false;
-
-        // Check return type.
-        Class<?> exp = newType.returnType();
-        Class<?> ret = oldType.returnType();
-        if (!VerifyType.isNullConversion(ret, exp)) {
-            if (!convOpSupported(OP_COLLECT_ARGS))
-                return false;
-            if (!canConvertArgument(ret, exp, level))
-                return false;
-        }
-
-        // Check args pairwise.
-        for (int i = 0; i < len; i++) {
-            Class<?> src = newType.parameterType(i); // source type
-            Class<?> dst = oldType.parameterType(i); // destination type
-            if (!canConvertArgument(src, dst, level))
-                return false;
-        }
-
-        return true;
-    }
-
-    /** Can a JVM-level adapter directly implement the proposed
-     *  argument conversion, as if by fixed-arity MethodHandle.asType?
-     */
-    static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
-        // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
-        // so we don't need to repeat so much decision making.
-        if (VerifyType.isNullConversion(src, dst)) {
-            return true;
-        } else if (convOpSupported(OP_COLLECT_ARGS)) {
-            // If we can build filters, we can convert anything to anything.
-            return true;
-        } else if (src.isPrimitive()) {
-            if (dst.isPrimitive())
-                return canPrimCast(src, dst);
-            else
-                return canBoxArgument(src, dst);
-        } else {
-            if (dst.isPrimitive())
-                return canUnboxArgument(src, dst, level);
-            else
-                return true;  // any two refs can be interconverted
-        }
-    }
-
-    /**
-     * Create a JVM-level adapter method handle to conform the given method
-     * handle to the similar newType, using only pairwise argument conversions.
-     * For each argument, convert incoming argument to the exact type needed.
-     * The argument conversions allowed are casting, boxing and unboxing,
-     * integral widening or narrowing, and floating point widening or narrowing.
-     * @param newType required call type
-     * @param target original method handle
-     * @param level which strength of conversion is allowed
-     * @return an adapter to the original handle with the desired new type,
-     *          or the original target if the types are already identical
-     *          or null if the adaptation cannot be made
-     */
-    static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level) {
-        MethodType oldType = target.type();
-        if (newType == oldType)  return target;
-
-        if (!canPairwiseConvert(newType, oldType, level))
-            return null;
-        // (after this point, it is an assertion error to fail to convert)
-
-        // Find last non-trivial conversion (if any).
-        int lastConv = newType.parameterCount()-1;
-        while (lastConv >= 0) {
-            Class<?> src = newType.parameterType(lastConv); // source type
-            Class<?> dst = oldType.parameterType(lastConv); // destination type
-            if (isTrivialConversion(src, dst, level)) {
-                --lastConv;
-            } else {
-                break;
-            }
-        }
-
-        Class<?> needReturn = newType.returnType();
-        Class<?> haveReturn = oldType.returnType();
-        boolean retConv = !isTrivialConversion(haveReturn, needReturn, level);
-
-        // Now build a chain of one or more adapters.
-        MethodHandle adapter = target, adapter2;
-        MethodType midType = oldType;
-        for (int i = 0; i <= lastConv; i++) {
-            Class<?> src = newType.parameterType(i); // source type
-            Class<?> dst = midType.parameterType(i); // destination type
-            if (isTrivialConversion(src, dst, level)) {
-                // do nothing: difference is trivial
-                continue;
-            }
-            // Work the current type backward toward the desired caller type:
-            midType = midType.changeParameterType(i, src);
-            if (i == lastConv) {
-                // When doing the last (or only) real conversion,
-                // force all remaining null conversions to happen also.
-                MethodType lastMidType = newType;
-                if (retConv)  lastMidType = lastMidType.changeReturnType(haveReturn);
-                assert(VerifyType.isNullConversion(lastMidType, midType));
-                midType = lastMidType;
-            }
-
-            // Tricky case analysis follows.
-            // It parallels canConvertArgument() above.
-            if (src.isPrimitive()) {
-                if (dst.isPrimitive()) {
-                    adapter2 = makePrimCast(midType, adapter, i, dst);
-                } else {
-                    adapter2 = makeBoxArgument(midType, adapter, i, src);
-                }
-            } else {
-                if (dst.isPrimitive()) {
-                    // Caller has boxed a primitive.  Unbox it for the target.
-                    // The box type must correspond exactly to the primitive type.
-                    // This is simpler than the powerful set of widening
-                    // conversions supported by reflect.Method.invoke.
-                    // Those conversions require a big nest of if/then/else logic,
-                    // which we prefer to make a user responsibility.
-                    adapter2 = makeUnboxArgument(midType, adapter, i, dst, level);
-                } else {
-                    // Simple reference conversion.
-                    // Note:  Do not check for a class hierarchy relation
-                    // between src and dst.  In all cases a 'null' argument
-                    // will pass the cast conversion.
-                    adapter2 = makeCheckCast(midType, adapter, i, dst);
-                }
-            }
-            assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType);
-            assert(adapter2.type() == midType);
-            adapter = adapter2;
-        }
-        if (retConv) {
-            adapter2 = makeReturnConversion(adapter, haveReturn, needReturn);
-            assert(adapter2 != null);
-            adapter = adapter2;
-        }
-        if (adapter.type() != newType) {
-            // Only trivial conversions remain.
-            adapter2 = makeRetypeOnly(newType, adapter);
-            assert(adapter2 != null);
-            adapter = adapter2;
-            // Actually, that's because there were no non-trivial ones:
-            assert(lastConv == -1 || retConv);
-        }
-        assert(adapter.type() == newType);
-        return adapter;
-    }
-
-    private static boolean isTrivialConversion(Class<?> src, Class<?> dst, int level) {
-        if (src == dst || dst == void.class)  return true;
-        if (!VerifyType.isNullConversion(src, dst))  return false;
-        if (level > 1)  return true;  // explicitCastArguments
-        boolean sp = src.isPrimitive();
-        boolean dp = dst.isPrimitive();
-        if (sp != dp)  return false;
-        if (sp) {
-            // in addition to being a null conversion, forbid boolean->int etc.
-            return Wrapper.forPrimitiveType(dst)
-                    .isConvertibleFrom(Wrapper.forPrimitiveType(src));
-        } else {
-            return dst.isAssignableFrom(src);
-        }
-    }
-
-    private static MethodHandle makeReturnConversion(MethodHandle target, Class<?> haveReturn, Class<?> needReturn) {
-        MethodHandle adjustReturn;
-        if (haveReturn == void.class) {
-            // synthesize a zero value for the given void
-            Object zero = Wrapper.forBasicType(needReturn).zero();
-            adjustReturn = MethodHandles.constant(needReturn, zero);
-        } else {
-            MethodType needConversion = MethodType.methodType(needReturn, haveReturn);
-            adjustReturn = MethodHandles.identity(needReturn).asType(needConversion);
-        }
-        return makeCollectArguments(adjustReturn, target, 0, false);
-    }
-
-    /**
-     * Create a JVM-level adapter method handle to permute the arguments
-     * of the given method.
-     * @param newType required call type
-     * @param target original method handle
-     * @param argumentMap for each target argument, position of its source in newType
-     * @return an adapter to the original handle with the desired new type,
-     *          or the original target if the types are already identical
-     *          and the permutation is null
-     * @throws IllegalArgumentException if the adaptation cannot be made
-     *          directly by a JVM-level adapter, without help from Java code
-     */
-    static MethodHandle makePermutation(MethodType newType, MethodHandle target,
-                int[] argumentMap) {
-        MethodType oldType = target.type();
-        boolean nullPermutation = true;
-        for (int i = 0; i < argumentMap.length; i++) {
-            int pos = argumentMap[i];
-            if (pos != i)
-                nullPermutation = false;
-            if (pos < 0 || pos >= newType.parameterCount()) {
-                argumentMap = new int[0]; break;
-            }
-        }
-        if (argumentMap.length != oldType.parameterCount())
-            throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
-        if (nullPermutation) {
-            MethodHandle res = makePairwiseConvert(newType, target, 0);
-            // well, that was easy
-            if (res == null)
-                throw newIllegalArgumentException("cannot convert pairwise: "+newType);
-            return res;
-        }
-
-        // Check return type.  (Not much can be done with it.)
-        Class<?> exp = newType.returnType();
-        Class<?> ret = oldType.returnType();
-        if (!VerifyType.isNullConversion(ret, exp))
-            throw newIllegalArgumentException("bad return conversion for "+newType);
-
-        // See if the argument types match up.
-        for (int i = 0; i < argumentMap.length; i++) {
-            int j = argumentMap[i];
-            Class<?> src = newType.parameterType(j);
-            Class<?> dst = oldType.parameterType(i);
-            if (!VerifyType.isNullConversion(src, dst))
-                throw newIllegalArgumentException("bad argument #"+j+" conversion for "+newType);
-        }
-
-        // Now figure out a nice mix of SWAP, ROT, DUP, and DROP adapters.
-        // A workable greedy algorithm is as follows:
-        // Drop unused outgoing arguments (right to left: shallowest first).
-        // Duplicate doubly-used outgoing arguments (left to right: deepest first).
-        // Then the remaining problem is a true argument permutation.
-        // Marshal the outgoing arguments as required from left to right.
-        // That is, find the deepest outgoing stack position that does not yet
-        // have the correct argument value, and correct at least that position
-        // by swapping or rotating in the misplaced value (from a shallower place).
-        // If the misplaced value is followed by one or more consecutive values
-        // (also misplaced)  issue a rotation which brings as many as possible
-        // into position.  Otherwise make progress with either a swap or a
-        // rotation.  Prefer the swap as cheaper, but do not use it if it
-        // breaks a slot pair.  Prefer the rotation over the swap if it would
-        // preserve more consecutive values shallower than the target position.
-        // When more than one rotation will work (because the required value
-        // is already adjacent to the target position), then use a rotation
-        // which moves the old value in the target position adjacent to
-        // one of its consecutive values.  Also, prefer shorter rotation
-        // spans, since they use fewer memory cycles for shuffling.
-
-        throw new UnsupportedOperationException("NYI");
-    }
-
-    private static byte basicType(Class<?> type) {
-        if (type == null)  return T_VOID;
-        switch (Wrapper.forBasicType(type)) {
-            case BOOLEAN:  return T_BOOLEAN;
-            case CHAR:     return T_CHAR;
-            case FLOAT:    return T_FLOAT;
-            case DOUBLE:   return T_DOUBLE;
-            case BYTE:     return T_BYTE;
-            case SHORT:    return T_SHORT;
-            case INT:      return T_INT;
-            case LONG:     return T_LONG;
-            case OBJECT:   return T_OBJECT;
-            case VOID:     return T_VOID;
-        }
-        return 99; // T_ILLEGAL or some such
-    }
-
-    /** Number of stack slots for the given type.
-     *  Two for T_DOUBLE and T_FLOAT, one for the rest.
-     */
-    private static int type2size(int type) {
-        assert(type >= T_BOOLEAN && type <= T_OBJECT);
-        return (type == T_LONG || type == T_DOUBLE) ? 2 : 1;
-    }
-    private static int type2size(Class<?> type) {
-        return type2size(basicType(type));
-    }
-
-    /** The given stackMove is the number of slots pushed.
-     * It might be negative.  Scale it (multiply) by the
-     * VM's notion of how an address changes with a push,
-     * to get the raw SP change for stackMove.
-     * Then shift and mask it into the correct field.
-     */
-    private static long insertStackMove(int stackMove) {
-        // following variable must be long to avoid sign extension after '<<'
-        long spChange = stackMove * MethodHandleNatives.JVM_STACK_MOVE_UNIT;
-        return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT;
-    }
-
-    static int extractStackMove(int convOp) {
-        int spChange = convOp >> CONV_STACK_MOVE_SHIFT;
-        return spChange / MethodHandleNatives.JVM_STACK_MOVE_UNIT;
-    }
-
-    static int extractStackMove(MethodHandle target) {
-        if (target instanceof AdapterMethodHandle) {
-            AdapterMethodHandle amh = (AdapterMethodHandle) target;
-            return extractStackMove(amh.getConversion());
-        } else {
-            return 0;
-        }
-    }
-
-    /** Construct an adapter conversion descriptor for a single-argument conversion. */
-    @SuppressWarnings("cast")  // some (int) casts below provide clarity but trigger warnings
-    private static long makeConv(int convOp, int argnum, int src, int dest) {
-        assert(src  == (src  & CONV_TYPE_MASK));
-        assert(dest == (dest & CONV_TYPE_MASK));
-        assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS);
-        int stackMove = type2size(dest) - type2size(src);
-        return ((long) argnum << 32 |
-                (long) convOp << CONV_OP_SHIFT |
-                (int)  src    << CONV_SRC_TYPE_SHIFT |
-                (int)  dest   << CONV_DEST_TYPE_SHIFT |
-                insertStackMove(stackMove)
-                );
-    }
-    @SuppressWarnings("cast")  // some (int) casts below provide clarity but trigger warnings
-    private static long makeDupConv(int convOp, int argnum, int stackMove) {
-        // simple argument motion, requiring one slot to specify
-        assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS);
-        byte src = 0, dest = 0;
-        return ((long) argnum << 32 |
-                (long) convOp << CONV_OP_SHIFT |
-                (int)  src    << CONV_SRC_TYPE_SHIFT |
-                (int)  dest   << CONV_DEST_TYPE_SHIFT |
-                insertStackMove(stackMove)
-                );
-    }
-    @SuppressWarnings("cast")  // some (int) casts below provide clarity but trigger warnings
-    private static long makeSwapConv(int convOp, int srcArg, byte srcType, int destSlot, byte destType) {
-        // more complex argument motion, requiring two slots to specify
-        assert(convOp == OP_SWAP_ARGS || convOp == OP_ROT_ARGS);
-        return ((long) srcArg << 32 |
-                (long) convOp << CONV_OP_SHIFT |
-                (int)  srcType << CONV_SRC_TYPE_SHIFT |
-                (int)  destType << CONV_DEST_TYPE_SHIFT |
-                (int)  destSlot << CONV_VMINFO_SHIFT
-                );
-    }
-    @SuppressWarnings("cast")  // some (int) casts below provide clarity but trigger warnings
-    private static long makeSpreadConv(int convOp, int argnum, int src, int dest, int stackMove) {
-        // spreading or collecting, at a particular slot location
-        assert(convOp == OP_SPREAD_ARGS || convOp == OP_COLLECT_ARGS || convOp == OP_FOLD_ARGS);
-        // src  = spread ? T_OBJECT (for array)  : common type of collected args (else void)
-        // dest = spread ? element type of array : result type of collector (can be void)
-        return ((long) argnum << 32 |
-                (long) convOp << CONV_OP_SHIFT |
-                (int)  src    << CONV_SRC_TYPE_SHIFT |
-                (int)  dest   << CONV_DEST_TYPE_SHIFT |
-                insertStackMove(stackMove)
-                );
-    }
-    static long makeConv(int convOp) {
-        assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
-        return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT);   // stackMove, src, dst all zero
-    }
-    private static int convCode(long conv) {
-        return (int)conv;
-    }
-    private static int convArgPos(long conv) {
-        return (int)(conv >>> 32);
-    }
-    private static boolean convOpSupported(int convOp) {
-        assert(convOp >= 0 && convOp <= CONV_OP_LIMIT);
-        return ((1<<convOp) & MethodHandleNatives.CONV_OP_IMPLEMENTED_MASK) != 0;
-    }
-
-    /** One of OP_RETYPE_ONLY, etc. */
-    int conversionOp() { return (conversion & CONV_OP_MASK) >> CONV_OP_SHIFT; }
-
-    /* Return one plus the position of the first non-trivial difference
-     * between the given types.  This is not a symmetric operation;
-     * we are considering adapting the targetType to adapterType.
-     * Trivial differences are those which could be ignored by the JVM
-     * without subverting the verifier.  Otherwise, adaptable differences
-     * are ones for which we could create an adapter to make the type change.
-     * Return zero if there are no differences (other than trivial ones).
-     * Return 1+N if N is the only adaptable argument difference.
-     * Return the -2-N where N is the first of several adaptable
-     * argument differences.
-     * Return -1 if there there are differences which are not adaptable.
-     */
-    private static int diffTypes(MethodType adapterType,
-                                 MethodType targetType,
-                                 boolean raw) {
-        int diff;
-        diff = diffReturnTypes(adapterType, targetType, raw);
-        if (diff != 0)  return diff;
-        int nargs = adapterType.parameterCount();
-        if (nargs != targetType.parameterCount())
-            return -1;
-        diff = diffParamTypes(adapterType, 0, targetType, 0, nargs, raw);
-        //System.out.println("diff "+adapterType);
-        //System.out.println("  "+diff+" "+targetType);
-        return diff;
-    }
-    private static int diffReturnTypes(MethodType adapterType,
-                                       MethodType targetType,
-                                       boolean raw) {
-        Class<?> src = targetType.returnType();
-        Class<?> dst = adapterType.returnType();
-        if ((!raw
-             ? VerifyType.canPassUnchecked(src, dst)
-             : VerifyType.canPassRaw(src, dst)
-             ) > 0)
-            return 0;  // no significant difference
-        if (raw && !src.isPrimitive() && !dst.isPrimitive())
-            return 0;  // can force a reference return (very carefully!)
-        //if (false)  return 1;  // never adaptable!
-        return -1;  // some significant difference
-    }
-    private static int diffParamTypes(MethodType adapterType, int astart,
-                                      MethodType targetType, int tstart,
-                                      int nargs, boolean raw) {
-        assert(nargs >= 0);
-        int res = 0;
-        for (int i = 0; i < nargs; i++) {
-            Class<?> src  = adapterType.parameterType(astart+i);
-            Class<?> dest = targetType.parameterType(tstart+i);
-            if ((!raw
-                 ? VerifyType.canPassUnchecked(src, dest)
-                 : VerifyType.canPassRaw(src, dest)
-                ) <= 0) {
-                // found a difference; is it the only one so far?
-                if (res != 0)
-                    return -1-res; // return -2-i for prev. i
-                res = 1+i;
-            }
-        }
-        return res;
-    }
-
-    /** Can a retyping adapter (alone) validly convert the target to newType? */
-    static boolean canRetypeOnly(MethodType newType, MethodType targetType) {
-        return canRetype(newType, targetType, false);
-    }
-    /** Can a retyping adapter (alone) convert the target to newType?
-     *  It is allowed to widen subword types and void to int, to make bitwise
-     *  conversions between float/int and double/long, and to perform unchecked
-     *  reference conversions on return.  This last feature requires that the
-     *  caller be trusted, and perform explicit cast conversions on return values.
-     */
-    static boolean canRetypeRaw(MethodType newType, MethodType targetType) {
-        return canRetype(newType, targetType, true);
-    }
-    static boolean canRetype(MethodType newType, MethodType targetType, boolean raw) {
-        if (!convOpSupported(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY))  return false;
-        int diff = diffTypes(newType, targetType, raw);
-        // %%% This assert is too strong.  Factor diff into VerifyType and reconcile.
-        assert(raw || (diff == 0) == VerifyType.isNullConversion(newType, targetType));
-        return diff == 0;
-    }
-
-    /** Factory method:  Performs no conversions; simply retypes the adapter.
-     *  Allows unchecked argument conversions pairwise, if they are safe.
-     *  Returns null if not possible.
-     */
-    static MethodHandle makeRetypeOnly(MethodType newType, MethodHandle target) {
-        return makeRetype(newType, target, false);
-    }
-    static MethodHandle makeRetypeRaw(MethodType newType, MethodHandle target) {
-        return makeRetype(newType, target, true);
-    }
-    static MethodHandle makeRetype(MethodType newType, MethodHandle target, boolean raw) {
-        MethodType oldType = target.type();
-        if (oldType == newType)  return target;
-        if (!canRetype(newType, oldType, raw))
-            return null;
-        // TO DO:  clone the target guy, whatever he is, with new type.
-        return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY));
-    }
-
-    static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
-        MethodType type = target.type();
-        int last = type.parameterCount() - 1;
-        if (type.parameterType(last) != arrayType)
-            target = target.asType(type.changeParameterType(last, arrayType));
-        target = target.asFixedArity();  // make sure this attribute is turned off
-        return new AsVarargsCollector(target, arrayType);
-    }
-
-    static class AsVarargsCollector extends AdapterMethodHandle {
-        final MethodHandle target;
-        final Class<?> arrayType;
-        MethodHandle cache;
-
-        AsVarargsCollector(MethodHandle target, Class<?> arrayType) {
-            super(target, target.type(), makeConv(OP_RETYPE_ONLY));
-            this.target = target;
-            this.arrayType = arrayType;
-            this.cache = target.asCollector(arrayType, 0);
-        }
-
-        @Override
-        public boolean isVarargsCollector() {
-            return true;
-        }
-
-        @Override
-        public MethodHandle asFixedArity() {
-            return target;
-        }
-
-        @Override
-        public MethodHandle asType(MethodType newType) {
-            MethodType type = this.type();
-            int collectArg = type.parameterCount() - 1;
-            int newArity = newType.parameterCount();
-            if (newArity == collectArg+1 &&
-                type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
-                // if arity and trailing parameter are compatible, do normal thing
-                return super.asType(newType);
-            }
-            // check cache
-            if (cache.type().parameterCount() == newArity)
-                return cache.asType(newType);
-            // build and cache a collector
-            int arrayLength = newArity - collectArg;
-            MethodHandle collector;
-            try {
-                collector = target.asCollector(arrayType, arrayLength);
-            } catch (IllegalArgumentException ex) {
-                throw new WrongMethodTypeException("cannot build collector");
-            }
-            cache = collector;
-            return collector.asType(newType);
-        }
-    }
-
-    /** Can a checkcast adapter validly convert the target to newType?
-     *  The JVM supports all kind of reference casts, even silly ones.
-     */
-    static boolean canCheckCast(MethodType newType, MethodType targetType,
-                int arg, Class<?> castType) {
-        if (!convOpSupported(OP_CHECK_CAST))  return false;
-        Class<?> src = newType.parameterType(arg);
-        Class<?> dst = targetType.parameterType(arg);
-        if (!canCheckCast(src, castType)
-                || !VerifyType.isNullConversion(castType, dst))
-            return false;
-        int diff = diffTypes(newType, targetType, false);
-        return (diff == arg+1) || (diff == 0);  // arg is sole non-trivial diff
-    }
-    /** Can an primitive conversion adapter validly convert src to dst? */
-    static boolean canCheckCast(Class<?> src, Class<?> dst) {
-        return (!src.isPrimitive() && !dst.isPrimitive());
-    }
-
-    /** Factory method:  Forces a cast at the given argument.
-     *  The castType is the target of the cast, and can be any type
-     *  with a null conversion to the corresponding target parameter.
-     *  Return null if this cannot be done.
-     */
-    static MethodHandle makeCheckCast(MethodType newType, MethodHandle target,
-                int arg, Class<?> castType) {
-        if (!canCheckCast(newType, target.type(), arg, castType))
-            return null;
-        long conv = makeConv(OP_CHECK_CAST, arg, T_OBJECT, T_OBJECT);
-        return new AdapterMethodHandle(target, newType, conv, castType);
-    }
-
-    /** Can an primitive conversion adapter validly convert the target to newType?
-     *  The JVM currently supports all conversions except those between
-     *  floating and integral types.
-     */
-    static boolean canPrimCast(MethodType newType, MethodType targetType,
-                int arg, Class<?> convType) {
-        if (!convOpSupported(OP_PRIM_TO_PRIM))  return false;
-        Class<?> src = newType.parameterType(arg);
-        Class<?> dst = targetType.parameterType(arg);
-        if (!canPrimCast(src, convType)
-                || !VerifyType.isNullConversion(convType, dst))
-            return false;
-        int diff = diffTypes(newType, targetType, false);
-        return (diff == arg+1);  // arg is sole non-trivial diff
-    }
-    /** Can an primitive conversion adapter validly convert src to dst? */
-    static boolean canPrimCast(Class<?> src, Class<?> dst) {
-        if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
-            return false;
-        } else {
-            boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
-            boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
-            return !(sflt | dflt);  // no float support at present
-        }
-    }
-
-    /** Factory method:  Truncate the given argument with zero or sign extension,
-     *  and/or convert between single and doubleword versions of integer or float.
-     *  The convType is the target of the conversion, and can be any type
-     *  with a null conversion to the corresponding target parameter.
-     *  Return null if this cannot be done.
-     */
-    static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
-                int arg, Class<?> convType) {
-        Class<?> src = newType.parameterType(arg);
-        if (canPrimCast(src, convType))
-            return makePrimCastOnly(newType, target, arg, convType);
-        Class<?> dst = convType;
-        boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
-        boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
-        if (sflt | dflt) {
-            MethodHandle convMethod;
-            if (sflt)
-                convMethod = ((src == double.class)
-                        ? ValueConversions.convertFromDouble(dst)
-                        : ValueConversions.convertFromFloat(dst));
-            else
-                convMethod = ((dst == double.class)
-                        ? ValueConversions.convertToDouble(src)
-                        : ValueConversions.convertToFloat(src));
-            long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
-            return new AdapterMethodHandle(target, newType, conv, convMethod);
-        }
-        throw new InternalError("makePrimCast");
-    }
-    static MethodHandle makePrimCastOnly(MethodType newType, MethodHandle target,
-                int arg, Class<?> convType) {
-        MethodType oldType = target.type();
-        if (!canPrimCast(newType, oldType, arg, convType))
-            return null;
-        Class<?> src = newType.parameterType(arg);
-        long conv = makeConv(OP_PRIM_TO_PRIM, arg, basicType(src), basicType(convType));
-        return new AdapterMethodHandle(target, newType, conv);
-    }
-
-    /** Can an unboxing conversion validly convert src to dst?
-     *  The JVM currently supports all kinds of casting and unboxing.
-     *  The convType is the unboxed type; it can be either a primitive or wrapper.
-     */
-    static boolean canUnboxArgument(MethodType newType, MethodType targetType,
-                int arg, Class<?> convType, int level) {
-        if (!convOpSupported(OP_REF_TO_PRIM))  return false;
-        Class<?> src = newType.parameterType(arg);
-        Class<?> dst = targetType.parameterType(arg);
-        Class<?> boxType = Wrapper.asWrapperType(convType);
-        convType = Wrapper.asPrimitiveType(convType);
-        if (!canCheckCast(src, boxType)
-                || boxType == convType
-                || !VerifyType.isNullConversion(convType, dst))
-            return false;
-        int diff = diffTypes(newType, targetType, false);
-        return (diff == arg+1);  // arg is sole non-trivial diff
-    }
-    /** Can an primitive unboxing adapter validly convert src to dst? */
-    static boolean canUnboxArgument(Class<?> src, Class<?> dst, int level) {
-        assert(dst.isPrimitive());
-        // if we have JVM support for boxing, we can also do complex unboxing
-        if (convOpSupported(OP_PRIM_TO_REF))  return true;
-        Wrapper dw = Wrapper.forPrimitiveType(dst);
-        // Level 0 means cast and unbox.  This works on any reference.
-        if (level == 0)  return !src.isPrimitive();
-        assert(level >= 0 && level <= 2);
-        // Levels 1 and 2 allow widening and/or narrowing conversions.
-        // These are not supported directly by the JVM.
-        // But if the input reference is monomorphic, we can do it.
-        return dw.wrapperType() == src;
-    }
-
-    /** Factory method:  Unbox the given argument.
-     *  Return null if this cannot be done.
-     */
-    static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
-                int arg, Class<?> convType, int level) {
-        MethodType oldType = target.type();
-        Class<?> src = newType.parameterType(arg);
-        Class<?> dst = oldType.parameterType(arg);
-        Class<?> boxType = Wrapper.asWrapperType(convType);
-        Class<?> primType = Wrapper.asPrimitiveType(convType);
-        if (!canUnboxArgument(newType, oldType, arg, convType, level))
-            return null;
-        MethodType castDone = newType;
-        if (!VerifyType.isNullConversion(src, boxType)) {
-            // Examples:  Object->int, Number->int, Comparable->int; Byte->int, Character->int
-            if (level != 0) {
-                // must include additional conversions
-                if (src == Object.class || !Wrapper.isWrapperType(src)) {
-                    // src must be examined at runtime, to detect Byte, Character, etc.
-                    MethodHandle unboxMethod = (level == 1
-                                                ? ValueConversions.unbox(dst)
-                                                : ValueConversions.unboxCast(dst));
-                    long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
-                    return new AdapterMethodHandle(target, newType, conv, unboxMethod);
-                }
-                // Example: Byte->int
-                // Do this by reformulating the problem to Byte->byte.
-                Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
-                MethodType midType = newType.changeParameterType(arg, srcPrim);
-                MethodHandle fixPrim; // makePairwiseConvert(midType, target, 0);
-                if (canPrimCast(midType, oldType, arg, dst))
-                    fixPrim = makePrimCast(midType, target, arg, dst);
-                else
-                    fixPrim = target;
-                return makeUnboxArgument(newType, fixPrim, arg, srcPrim, 0);
-            }
-            castDone = newType.changeParameterType(arg, boxType);
-        }
-        long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType));
-        MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType);
-        if (castDone == newType)
-            return adapter;
-        return makeCheckCast(newType, adapter, arg, boxType);
-    }
-
-    /** Can a boxing conversion validly convert src to dst? */
-    static boolean canBoxArgument(MethodType newType, MethodType targetType,
-                int arg, Class<?> convType) {
-        if (!convOpSupported(OP_PRIM_TO_REF))  return false;
-        Class<?> src = newType.parameterType(arg);
-        Class<?> dst = targetType.parameterType(arg);
-        Class<?> boxType = Wrapper.asWrapperType(convType);
-        convType = Wrapper.asPrimitiveType(convType);
-        if (!canCheckCast(boxType, dst)
-                || boxType == convType
-                || !VerifyType.isNullConversion(src, convType))
-            return false;
-        int diff = diffTypes(newType, targetType, false);
-        return (diff == arg+1);  // arg is sole non-trivial diff
-    }
-
-    /** Can an primitive boxing adapter validly convert src to dst? */
-    static boolean canBoxArgument(Class<?> src, Class<?> dst) {
-        if (!convOpSupported(OP_PRIM_TO_REF))  return false;
-        return (src.isPrimitive() && !dst.isPrimitive());
-    }
-
-    /** Factory method:  Box the given argument.
-     *  Return null if this cannot be done.
-     */
-    static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
-                int arg, Class<?> convType) {
-        MethodType oldType = target.type();
-        Class<?> src = newType.parameterType(arg);
-        Class<?> dst = oldType.parameterType(arg);
-        Class<?> boxType = Wrapper.asWrapperType(convType);
-        Class<?> primType = Wrapper.asPrimitiveType(convType);
-        if (!canBoxArgument(newType, oldType, arg, convType)) {
-            return null;
-        }
-        if (!VerifyType.isNullConversion(boxType, dst))
-            target = makeCheckCast(oldType.changeParameterType(arg, boxType), target, arg, dst);
-        MethodHandle boxerMethod = ValueConversions.box(Wrapper.forPrimitiveType(primType));
-        long conv = makeConv(OP_PRIM_TO_REF, arg, basicType(primType), T_OBJECT);
-        return new AdapterMethodHandle(target, newType, conv, boxerMethod);
-    }
-
-    /** Can an adapter simply drop arguments to convert the target to newType? */
-    static boolean canDropArguments(MethodType newType, MethodType targetType,
-                int dropArgPos, int dropArgCount) {
-        if (dropArgCount == 0)
-            return canRetypeOnly(newType, targetType);
-        if (!convOpSupported(OP_DROP_ARGS))  return false;
-        if (diffReturnTypes(newType, targetType, false) != 0)
-            return false;
-        int nptypes = newType.parameterCount();
-        // parameter types must be the same up to the drop point
-        if (dropArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, dropArgPos, false) != 0)
-            return false;
-        int afterPos = dropArgPos + dropArgCount;
-        int afterCount = nptypes - afterPos;
-        if (dropArgPos < 0 || dropArgPos >= nptypes ||
-            dropArgCount < 1 || afterPos > nptypes ||
-            targetType.parameterCount() != nptypes - dropArgCount)
-            return false;
-        // parameter types after the drop point must also be the same
-        if (afterCount != 0 && diffParamTypes(newType, afterPos, targetType, dropArgPos, afterCount, false) != 0)
-            return false;
-        return true;
-    }
-
-    /** Factory method:  Drop selected arguments.
-     *  Allow unchecked retyping of remaining arguments, pairwise.
-     *  Return null if this is not possible.
-     */
-    static MethodHandle makeDropArguments(MethodType newType, MethodHandle target,
-                int dropArgPos, int dropArgCount) {
-        if (dropArgCount == 0)
-            return makeRetypeOnly(newType, target);
-        if (!canDropArguments(newType, target.type(), dropArgPos, dropArgCount))
-            return null;
-        // in  arglist: [0: ...keep1 | dpos: drop... | dpos+dcount: keep2... ]
-        // out arglist: [0: ...keep1 |                        dpos: keep2... ]
-        int keep2InPos  = dropArgPos + dropArgCount;
-        int dropSlot    = newType.parameterSlotDepth(keep2InPos);
-        int keep1InSlot = newType.parameterSlotDepth(dropArgPos);
-        int slotCount   = keep1InSlot - dropSlot;
-        assert(slotCount >= dropArgCount);
-        assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
-        long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
-        return new AdapterMethodHandle(target, newType, conv);
-    }
-
-    /** Can an adapter duplicate an argument to convert the target to newType? */
-    static boolean canDupArguments(MethodType newType, MethodType targetType,
-                int dupArgPos, int dupArgCount) {
-        if (!convOpSupported(OP_DUP_ARGS))  return false;
-        if (diffReturnTypes(newType, targetType, false) != 0)
-            return false;
-        int nptypes = newType.parameterCount();
-        if (dupArgCount < 0 || dupArgPos + dupArgCount > nptypes)
-            return false;
-        if (targetType.parameterCount() != nptypes + dupArgCount)
-            return false;
-        // parameter types must be the same up to the duplicated arguments
-        if (diffParamTypes(newType, 0, targetType, 0, nptypes, false) != 0)
-            return false;
-        // duplicated types must be, well, duplicates
-        if (diffParamTypes(newType, dupArgPos, targetType, nptypes, dupArgCount, false) != 0)
-            return false;
-        return true;
-    }
-
-    /** Factory method:  Duplicate the selected argument.
-     *  Return null if this is not possible.
-     */
-    static MethodHandle makeDupArguments(MethodType newType, MethodHandle target,
-                int dupArgPos, int dupArgCount) {
-        if (!canDupArguments(newType, target.type(), dupArgPos, dupArgCount))
-            return null;
-        if (dupArgCount == 0)
-            return target;
-        // in  arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... ]
-        // out arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... | dup... ]
-        int keep2InPos  = dupArgPos + dupArgCount;
-        int dupSlot     = newType.parameterSlotDepth(keep2InPos);
-        int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
-        int slotCount   = keep1InSlot - dupSlot;
-        assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
-        long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
-        return new AdapterMethodHandle(target, newType, conv);
-    }
-
-    /** Can an adapter swap two arguments to convert the target to newType? */
-    static boolean canSwapArguments(MethodType newType, MethodType targetType,
-                int swapArg1, int swapArg2) {
-        if (!convOpSupported(OP_SWAP_ARGS))  return false;
-        if (diffReturnTypes(newType, targetType, false) != 0)
-            return false;
-        if (swapArg1 >= swapArg2)  return false;  // caller resp
-        int nptypes = newType.parameterCount();
-        if (targetType.parameterCount() != nptypes)
-            return false;
-        if (swapArg1 < 0 || swapArg2 >= nptypes)
-            return false;
-        if (diffParamTypes(newType, 0, targetType, 0, swapArg1, false) != 0)
-            return false;
-        if (diffParamTypes(newType, swapArg1, targetType, swapArg2, 1, false) != 0)
-            return false;
-        if (diffParamTypes(newType, swapArg1+1, targetType, swapArg1+1, swapArg2-swapArg1-1, false) != 0)
-            return false;
-        if (diffParamTypes(newType, swapArg2, targetType, swapArg1, 1, false) != 0)
-            return false;
-        if (diffParamTypes(newType, swapArg2+1, targetType, swapArg2+1, nptypes-swapArg2-1, false) != 0)
-            return false;
-        return true;
-    }
-
-    /** Factory method:  Swap the selected arguments.
-     *  Return null if this is not possible.
-     */
-    static MethodHandle makeSwapArguments(MethodType newType, MethodHandle target,
-                int swapArg1, int swapArg2) {
-        if (swapArg1 == swapArg2)
-            return target;
-        if (swapArg1 > swapArg2) { int t = swapArg1; swapArg1 = swapArg2; swapArg2 = t; }
-        if (type2size(newType.parameterType(swapArg1)) !=
-            type2size(newType.parameterType(swapArg2))) {
-            // turn a swap into a pair of rotates:
-            // [x a b c y] rot2(-1,argc=5) => [a b c y x] rot1(+1,argc=4) => target[y a b c x]
-            int argc = swapArg2 - swapArg1 + 1;
-            final int ROT = 1;
-            ArrayList<Class<?>> rot1Params = new ArrayList<Class<?>>(target.type().parameterList());
-            Collections.rotate(rot1Params.subList(swapArg1, swapArg1 + argc), -ROT);
-            MethodType rot1Type = MethodType.methodType(target.type().returnType(), rot1Params);
-            MethodHandle rot1 = makeRotateArguments(rot1Type, target, swapArg1, argc, +ROT);
-            assert(rot1 != null);
-            if (argc == 2)  return rot1;
-            MethodHandle rot2 = makeRotateArguments(newType, rot1, swapArg1, argc-1, -ROT);
-            assert(rot2 != null);
-            return rot2;
-        }
-        if (!canSwapArguments(newType, target.type(), swapArg1, swapArg2))
-            return null;
-        Class<?> type1 = newType.parameterType(swapArg1);
-        Class<?> type2 = newType.parameterType(swapArg2);
-        // in  arglist: [0: ...keep1 | pos1: a1 | pos1+1: keep2... | pos2: a2 | pos2+1: keep3... ]
-        // out arglist: [0: ...keep1 | pos1: a2 | pos1+1: keep2... | pos2: a1 | pos2+1: keep3... ]
-        int swapSlot2  = newType.parameterSlotDepth(swapArg2 + 1);
-        long conv = makeSwapConv(OP_SWAP_ARGS, swapArg1, basicType(type1), swapSlot2, basicType(type2));
-        return new AdapterMethodHandle(target, newType, conv);
-    }
-
-    static int positiveRotation(int argCount, int rotateBy) {
-        assert(argCount > 0);
-        if (rotateBy >= 0) {
-            if (rotateBy < argCount)
-                return rotateBy;
-            return rotateBy % argCount;
-        } else if (rotateBy >= -argCount) {
-            return rotateBy + argCount;
-        } else {
-            return (-1-((-1-rotateBy) % argCount)) + argCount;
-        }
-    }
-
-    final static int MAX_ARG_ROTATION = 1;
-
-    /** Can an adapter rotate arguments to convert the target to newType? */
-    static boolean canRotateArguments(MethodType newType, MethodType targetType,
-                int firstArg, int argCount, int rotateBy) {
-        if (!convOpSupported(OP_ROT_ARGS))  return false;
-        rotateBy = positiveRotation(argCount, rotateBy);
-        if (rotateBy == 0)  return false;  // no rotation
-        if (rotateBy > MAX_ARG_ROTATION && rotateBy < argCount - MAX_ARG_ROTATION)
-            return false;  // too many argument positions
-        // Rotate incoming args right N to the out args, N in 1..(argCouunt-1).
-        if (diffReturnTypes(newType, targetType, false) != 0)
-            return false;
-        int nptypes = newType.parameterCount();
-        if (targetType.parameterCount() != nptypes)
-            return false;
-        if (firstArg < 0 || firstArg >= nptypes)  return false;
-        int argLimit = firstArg + argCount;
-        if (argLimit > nptypes)  return false;
-        if (diffParamTypes(newType, 0, targetType, 0, firstArg, false) != 0)
-            return false;
-        int newChunk1 = argCount - rotateBy, newChunk2 = rotateBy;
-        // swap new chunk1 with target chunk2
-        if (diffParamTypes(newType, firstArg, targetType, argLimit-newChunk1, newChunk1, false) != 0)
-            return false;
-        // swap new chunk2 with target chunk1
-        if (diffParamTypes(newType, firstArg+newChunk1, targetType, firstArg, newChunk2, false) != 0)
-            return false;
-        return true;
-    }
-
-    /** Factory method:  Rotate the selected argument range.
-     *  Return null if this is not possible.
-     */
-    static MethodHandle makeRotateArguments(MethodType newType, MethodHandle target,
-                int firstArg, int argCount, int rotateBy) {
-        rotateBy = positiveRotation(argCount, rotateBy);
-        if (!canRotateArguments(newType, target.type(), firstArg, argCount, rotateBy))
-            return null;
-        // Decide whether it should be done as a right or left rotation,
-        // on the JVM stack.  Return the number of stack slots to rotate by,
-        // positive if right, negative if left.
-        int limit = firstArg + argCount;
-        int depth0 = newType.parameterSlotDepth(firstArg);
-        int depth1 = newType.parameterSlotDepth(limit-rotateBy);
-        int depth2 = newType.parameterSlotDepth(limit);
-        int chunk1Slots = depth0 - depth1; assert(chunk1Slots > 0);
-        int chunk2Slots = depth1 - depth2; assert(chunk2Slots > 0);
-        // From here on out, it assumes a single-argument shift.
-        assert(MAX_ARG_ROTATION == 1);
-        int srcArg, dstArg;
-        int dstSlot;
-        int moveChunk;
-        if (rotateBy == 1) {
-            // Rotate right/down N (rotateBy = +N, N small, c2 small):
-            // in  arglist: [0: ...keep1 | arg1: c1...  | limit-N: c2 | limit: keep2... ]
-            // out arglist: [0: ...keep1 | arg1: c2 | arg1+N: c1...   | limit: keep2... ]
-            srcArg = limit-1;
-            dstArg = firstArg;
-            //dstSlot = depth0 - chunk2Slots;  //chunk2Slots is not relevant
-            dstSlot = depth0 + MethodHandleNatives.OP_ROT_ARGS_DOWN_LIMIT_BIAS;
-            moveChunk = chunk2Slots;
-        } else {
-            // Rotate left/up N (rotateBy = -N, N small, c1 small):
-            // in  arglist: [0: ...keep1 | arg1: c1 | arg1+N: c2...   | limit: keep2... ]
-            // out arglist: [0: ...keep1 | arg1: c2 ... | limit-N: c1 | limit: keep2... ]
-            srcArg = firstArg;
-            dstArg = limit-1;
-            dstSlot = depth2;
-            moveChunk = chunk1Slots;
-        }
-        byte srcType = basicType(newType.parameterType(srcArg));
-        byte dstType = basicType(newType.parameterType(dstArg));
-        assert(moveChunk == type2size(srcType));
-        long conv = makeSwapConv(OP_ROT_ARGS, srcArg, srcType, dstSlot, dstType);
-        return new AdapterMethodHandle(target, newType, conv);
-    }
-
-    /** Can an adapter spread an argument to convert the target to newType? */
-    static boolean canSpreadArguments(MethodType newType, MethodType targetType,
-                Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
-        if (!convOpSupported(OP_SPREAD_ARGS))  return false;
-        if (diffReturnTypes(newType, targetType, false) != 0)
-            return false;
-        int nptypes = newType.parameterCount();
-        // parameter types must be the same up to the spread point
-        if (spreadArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, spreadArgPos, false) != 0)
-            return false;
-        int afterPos = spreadArgPos + spreadArgCount;
-        int afterCount = nptypes - (spreadArgPos + 1);
-        if (spreadArgPos < 0 || spreadArgPos >= nptypes ||
-            spreadArgCount < 0 ||
-            targetType.parameterCount() != afterPos + afterCount)
-            return false;
-        // parameter types after the spread point must also be the same
-        if (afterCount != 0 && diffParamTypes(newType, spreadArgPos+1, targetType, afterPos, afterCount, false) != 0)
-            return false;
-        // match the array element type to the spread arg types
-        Class<?> rawSpreadArgType = newType.parameterType(spreadArgPos);
-        if (rawSpreadArgType != spreadArgType && !canCheckCast(rawSpreadArgType, spreadArgType))
-            return false;
-        for (int i = 0; i < spreadArgCount; i++) {
-            Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
-            Class<?> dst = targetType.parameterType(spreadArgPos + i);
-            if (src == null || !canConvertArgument(src, dst, 1))
-                return false;
-        }
-        return true;
-    }
-
-
-    /** Factory method:  Spread selected argument. */
-    static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
-                Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
-        // FIXME: Get rid of newType; derive new arguments from structure of spreadArgType
-        MethodType targetType = target.type();
-        assert(canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
-            : "[newType, targetType, spreadArgType, spreadArgPos, spreadArgCount] = "
-              + Arrays.asList(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount);
-        // dest is not significant; remove?
-        int dest = T_VOID;
-        for (int i = 0; i < spreadArgCount; i++) {
-            Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
-            if (arg == null)  arg = Object.class;
-            int dest2 = basicType(arg);
-            if      (dest == T_VOID)  dest = dest2;
-            else if (dest != dest2)   dest = T_VOID;
-            if (dest == T_VOID)  break;
-            targetType = targetType.changeParameterType(spreadArgPos + i, arg);
-        }
-        target = target.asType(targetType);
-        int arrayArgSize = 1;  // always a reference
-        // in  arglist: [0: ...keep1 | spos: spreadArg | spos+1:      keep2... ]
-        // out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
-        int keep2OutPos  = spreadArgPos + spreadArgCount;
-        int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos);   // leading edge of |spread...|
-        int spreadSlot   = targetType.parameterSlotDepth(keep2OutPos);    // trailing edge of |spread...|
-        assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize));
-        int slotCount    = keep1OutSlot - spreadSlot;                     // slots in |spread...|
-        assert(slotCount >= spreadArgCount);
-        int stackMove = - arrayArgSize + slotCount;  // pop array, push N slots
-        long conv = makeSpreadConv(OP_SPREAD_ARGS, spreadArgPos, T_OBJECT, dest, stackMove);
-        MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType);
-        assert(res.type().parameterType(spreadArgPos) == spreadArgType);
-        return res;
-    }
-
-    /** Can an adapter collect a series of arguments, replacing them by zero or one results? */
-    static boolean canCollectArguments(MethodType targetType,
-                MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) {
-        if (!convOpSupported(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS))  return false;
-        int collectArgCount = collectorType.parameterCount();
-        Class<?> rtype = collectorType.returnType();
-        assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype)
-                // [(Object)Object[], (Object[])Object[], 0, 1]
-                : Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount)
-                ;
-        return true;
-    }
-
-    /** Factory method:  Collect or filter selected argument(s). */
-    static MethodHandle makeCollectArguments(MethodHandle target,
-                MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
-        assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs));
-        MethodType targetType = target.type();
-        MethodType collectorType = collector.type();
-        int collectArgCount = collectorType.parameterCount();
-        Class<?> collectValType = collectorType.returnType();
-        int collectValCount = (collectValType == void.class ? 0 : 1);
-        int collectValSlots = collectorType.returnSlotCount();
-        MethodType newType = targetType
-                .dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
-        if (!retainOriginalArgs) {
-            newType = newType
-                .insertParameterTypes(collectArgPos, collectorType.parameterList());
-        } else {
-            // parameter types at the fold point must be the same
-            assert(diffParamTypes(newType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0)
-                : Arrays.asList(target, collector, collectArgPos, retainOriginalArgs);
-        }
-        // in  arglist: [0: ...keep1 | cpos: collect...  | cpos+cacount: keep2... ]
-        // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
-        // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
-        int keep2InPos   = collectArgPos + collectArgCount;
-        int keep1InSlot  = newType.parameterSlotDepth(collectArgPos);  // leading edge of |collect...|
-        int collectSlot  = newType.parameterSlotDepth(keep2InPos);     // trailing edge of |collect...|
-        int slotCount    = keep1InSlot - collectSlot;                  // slots in |collect...|
-        assert(slotCount >= collectArgCount);
-        assert(collectSlot == targetType.parameterSlotDepth(
-                collectArgPos + collectValCount + (retainOriginalArgs ? collectArgCount : 0) ));
-        int dest = basicType(collectValType);
-        int src = T_VOID;
-        // src is not significant; remove?
-        for (int i = 0; i < collectArgCount; i++) {
-            int src2 = basicType(collectorType.parameterType(i));
-            if      (src == T_VOID)  src = src2;
-            else if (src != src2)    src = T_VOID;
-            if (src == T_VOID)  break;
-        }
-        int stackMove = collectValSlots;  // push 0..2 results
-        if (!retainOriginalArgs)  stackMove -= slotCount; // pop N arguments
-        int lastCollectArg = keep2InPos-1;
-        long conv = makeSpreadConv(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS,
-                                   lastCollectArg, src, dest, stackMove);
-        MethodHandle res = new AdapterMethodHandle(target, newType, conv, collector);
-        assert(res.type().parameterList().subList(collectArgPos, collectArgPos+collectArgCount)
-                .equals(collector.type().parameterList()));
-        return res;
-    }
-
-    @Override
-    String debugString() {
-        return getNameString(nonAdapter((MethodHandle)vmtarget), this);
-    }
-
-    private static MethodHandle nonAdapter(MethodHandle mh) {
-        while (mh instanceof AdapterMethodHandle) {
-            mh = (MethodHandle) mh.vmtarget;
-        }
-        return mh;
-    }
-}
--- a/src/share/classes/java/lang/invoke/BoundMethodHandle.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/BoundMethodHandle.java	Tue Jul 24 10:47:44 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,164 +25,828 @@
 
 package java.lang.invoke;
 
-import sun.invoke.util.VerifyType;
+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.*;
+import static java.lang.invoke.LambdaForm.basicTypes;
+import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
+import static java.lang.invoke.MethodHandleStatics.*;
+
+import java.lang.invoke.LambdaForm.Name;
+import java.lang.invoke.LambdaForm.NamedFunction;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import sun.invoke.util.ValueConversions;
 import sun.invoke.util.Wrapper;
-import static java.lang.invoke.MethodHandleStatics.*;
+
+import com.sun.xml.internal.ws.org.objectweb.asm.ClassWriter;
+import com.sun.xml.internal.ws.org.objectweb.asm.MethodVisitor;
+import com.sun.xml.internal.ws.org.objectweb.asm.Type;
 
 /**
  * The flavor of method handle which emulates an invoke instruction
  * on a predetermined argument.  The JVM dispatches to the correct method
  * when the handle is created, not when it is invoked.
- * @author jrose
+ *
+ * All bound arguments are encapsulated in dedicated species.
  */
-class BoundMethodHandle extends MethodHandle {
-    //MethodHandle vmtarget;           // next BMH or final DMH or methodOop
-    private final Object argument;     // argument to insert
-    private final int    vmargslot;    // position at which it is inserted
+/* non-public */ abstract class BoundMethodHandle extends MethodHandle {
 
-    // Constructors in this class *must* be package scoped or private.
-
-    /** Bind a direct MH to its receiver (or first ref. argument).
-     *  The JVM will pre-dispatch the MH if it is not already static.
-     */
-    /*non-public*/ BoundMethodHandle(DirectMethodHandle mh, Object argument) {
-        super(mh.type().dropParameterTypes(0, 1));
-        // check the type now, once for all:
-        this.argument = checkReferenceArgument(argument, mh, 0);
-        this.vmargslot = this.type().parameterSlotCount();
-        initTarget(mh, 0);
+    /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
+        super(type, form);
     }
 
-    /** Insert an argument into an arbitrary method handle.
-     *  If argnum is zero, inserts the first argument, etc.
-     *  The argument type must be a reference.
-     */
-    /*non-public*/ BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
-        this(mh.type().dropParameterTypes(argnum, argnum+1),
-             mh, argument, argnum);
+    //
+    // BMH API and internals
+    //
+
+    static MethodHandle bindSingle(MethodType type, LambdaForm form, char xtype, Object x) {
+        // for some type signatures, there exist pre-defined concrete BMH classes
+        try {
+            switch (xtype) {
+            case 'L':
+                if (true)  return bindSingle(type, form, x);  // Use known fast path.
+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('L').constructor[0].invokeBasic(type, form, x);
+            case 'I':
+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('I').constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
+            case 'J':
+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('J').constructor[0].invokeBasic(type, form, (long) x);
+            case 'F':
+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('F').constructor[0].invokeBasic(type, form, (float) x);
+            case 'D':
+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('D').constructor[0].invokeBasic(type, form, (double) x);
+            default : throw new InternalError("unexpected xtype: " + xtype);
+            }
+        } catch (Throwable t) {
+            throw new InternalError(t);
+        }
     }
 
-    /** Insert an argument into an arbitrary method handle.
-     *  If argnum is zero, inserts the first argument, etc.
-     */
-    /*non-public*/ BoundMethodHandle(MethodType type, MethodHandle mh, Object argument, int argnum) {
-        super(type);
-        if (mh.type().parameterType(argnum).isPrimitive())
-            this.argument = bindPrimitiveArgument(argument, mh, argnum);
-        else {
-            this.argument = checkReferenceArgument(argument, mh, argnum);
-        }
-        this.vmargslot = type.parameterSlotDepth(argnum);
-        initTarget(mh, argnum);
+    static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
+            return new Species_L(type, form, x);
     }
 
-    private void initTarget(MethodHandle mh, int argnum) {
-        //this.vmtarget = mh;  // maybe updated by JVM
-        MethodHandleNatives.init(this, mh, argnum);
-    }
-
-    /** For the AdapterMethodHandle subclass.
-     */
-    /*non-public*/ BoundMethodHandle(MethodType type, Object argument, int vmargslot) {
-        super(type);
-        this.argument = argument;
-        this.vmargslot = vmargslot;
-        assert(this instanceof AdapterMethodHandle);
-    }
-
-    /** Initialize the current object as a self-bound method handle, binding it
-     *  as the first argument of the method handle {@code entryPoint}.
-     *  The invocation type of the resulting method handle will be the
-     *  same as {@code entryPoint},  except that the first argument
-     *  type will be dropped.
-     */
-    /*non-public*/ BoundMethodHandle(MethodHandle entryPoint) {
-        super(entryPoint.type().dropParameterTypes(0, 1));
-        this.argument = this; // kludge; get rid of
-        this.vmargslot = this.type().parameterSlotDepth(0);
-        initTarget(entryPoint, 0);
-    }
-
-    /** Make sure the given {@code argument} can be used as {@code argnum}-th
-     *  parameter of the given method handle {@code mh}, which must be a reference.
-     *  <p>
-     *  If this fails, throw a suitable {@code WrongMethodTypeException},
-     *  which will prevent the creation of an illegally typed bound
-     *  method handle.
-     */
-    final static Object checkReferenceArgument(Object argument, MethodHandle mh, int argnum) {
-        Class<?> ptype = mh.type().parameterType(argnum);
-        if (ptype.isPrimitive()) {
-            // fail
-        } else if (argument == null) {
-            return null;
-        } else if (VerifyType.isNullReferenceConversion(argument.getClass(), ptype)) {
-            return argument;
+    MethodHandle cloneExtend(MethodType type, LambdaForm form, char xtype, Object x) {
+        try {
+            switch (xtype) {
+            case 'L': return cloneExtendL(type, form, x);
+            case 'I': return cloneExtendI(type, form, ValueConversions.widenSubword(x));
+            case 'J': return cloneExtendJ(type, form, (long) x);
+            case 'F': return cloneExtendF(type, form, (float) x);
+            case 'D': return cloneExtendD(type, form, (double) x);
+            }
+        } catch (Throwable t) {
+            throw new InternalError(t);
         }
-        throw badBoundArgumentException(argument, mh, argnum);
-    }
-
-    /** Make sure the given {@code argument} can be used as {@code argnum}-th
-     *  parameter of the given method handle {@code mh}, which must be a primitive.
-     *  <p>
-     *  If this fails, throw a suitable {@code WrongMethodTypeException},
-     *  which will prevent the creation of an illegally typed bound
-     *  method handle.
-     */
-    final static Object bindPrimitiveArgument(Object argument, MethodHandle mh, int argnum) {
-        Class<?> ptype = mh.type().parameterType(argnum);
-        Wrapper  wrap = Wrapper.forPrimitiveType(ptype);
-        Object   zero  = wrap.zero();
-        if (zero == null) {
-            // fail
-        } else if (argument == null) {
-            if (ptype != int.class && wrap.isSubwordOrInt())
-                return Integer.valueOf(0);
-            else
-                return zero;
-        } else if (VerifyType.isNullReferenceConversion(argument.getClass(), zero.getClass())) {
-            if (ptype != int.class && wrap.isSubwordOrInt())
-                return Wrapper.INT.wrap(argument);
-            else
-                return argument;
-        }
-        throw badBoundArgumentException(argument, mh, argnum);
-    }
-
-    final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) {
-        String atype = (argument == null) ? "null" : argument.getClass().toString();
-        return new ClassCastException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
+        throw new InternalError("unexpected type: " + xtype);
     }
 
     @Override
-    String debugString() {
-        return addTypeString(baseName(), this);
+    MethodHandle bindArgument(int pos, char basicType, Object value) {
+        MethodType type = type().dropParameterTypes(pos, pos+1);
+        LambdaForm form = internalForm().bind(1+pos, speciesData());
+        return cloneExtend(type, form, basicType, value);
     }
 
-    /** Component of toString() before the type string. */
-    protected String baseName() {
-        MethodHandle mh = this;
-        while (mh instanceof BoundMethodHandle) {
-            Object info = MethodHandleNatives.getTargetInfo(mh);
-            if (info instanceof MethodHandle) {
-                mh = (MethodHandle) info;
-            } else {
-                String name = null;
-                if (info instanceof MemberName)
-                    name = ((MemberName)info).getName();
-                if (name != null)
-                    return name;
-                else
-                    return noParens(super.toString()); // "invoke", probably
-            }
-            assert(mh != this);
-        }
-        return noParens(mh.toString());
+    @Override
+    MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+        LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
+        try {
+             return clone(srcType, form);
+         } catch (Throwable t) {
+             throw new InternalError(t);
+         }
     }
 
-    private static String noParens(String str) {
-        int paren = str.indexOf('(');
-        if (paren >= 0) str = str.substring(0, paren);
-        return str;
+    @Override
+    MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+        try {
+             return clone(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
+         } catch (Throwable t) {
+             throw new InternalError(t);
+         }
     }
+
+    static final String EXTENSION_TYPES = "LIJFD";
+    static final byte INDEX_L = 0, INDEX_I = 1, INDEX_J = 2, INDEX_F = 3, INDEX_D = 4;
+    static byte extensionIndex(char type) {
+        int i = EXTENSION_TYPES.indexOf(type);
+        if (i < 0)  throw new InternalError();
+        return (byte) i;
+    }
+
+    /**
+     * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a
+     * static field containing this value, and they must accordingly implement this method.
+     */
+    protected abstract SpeciesData speciesData();
+
+    @Override
+    final Object internalValues() {
+        Object[] boundValues = new Object[speciesData().fieldCount()];
+        for (int i = 0; i < boundValues.length; ++i) {
+            boundValues[i] = arg(i);
+        }
+        return Arrays.asList(boundValues);
+    }
+
+    public final Object arg(int i) {
+        try {
+            switch (speciesData().fieldType(i)) {
+            case 'L': return argL(i);
+            case 'I': return argI(i);
+            case 'F': return argF(i);
+            case 'D': return argD(i);
+            case 'J': return argJ(i);
+            }
+        } catch (Throwable ex) {
+            throw new InternalError(ex);
+        }
+        throw new InternalError("unexpected type: " + speciesData().types+"."+i);
+    }
+    public final Object argL(int i) throws Throwable { return          speciesData().getters[i].invokeBasic(this); }
+    public final int    argI(int i) throws Throwable { return (int)    speciesData().getters[i].invokeBasic(this); }
+    public final float  argF(int i) throws Throwable { return (float)  speciesData().getters[i].invokeBasic(this); }
+    public final double argD(int i) throws Throwable { return (double) speciesData().getters[i].invokeBasic(this); }
+    public final long   argJ(int i) throws Throwable { return (long)   speciesData().getters[i].invokeBasic(this); }
+
+    //
+    // cloning API
+    //
+
+    public abstract BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable;
+    public abstract BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable;
+    public abstract BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int    narg) throws Throwable;
+    public abstract BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long   narg) throws Throwable;
+    public abstract BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float  narg) throws Throwable;
+    public abstract BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable;
+
+    // The following is a grossly irregular hack:
+    @Override MethodHandle reinvokerTarget() {
+        try {
+            return (MethodHandle) argL(0);
+        } catch (Throwable ex) {
+            throw new InternalError(ex);
+        }
+    }
+
+    //
+    // concrete BMH classes required to close bootstrap loops
+    //
+
+    private  // make it private to force users to access the enclosing class first
+    static final class Species_L extends BoundMethodHandle {
+        final Object argL0;
+        public Species_L(MethodType mt, LambdaForm lf, Object argL0) {
+            super(mt, lf);
+            this.argL0 = argL0;
+        }
+        // The following is a grossly irregular hack:
+        @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; }
+        @Override
+        public SpeciesData speciesData() {
+            return SPECIES_DATA;
+        }
+        public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class);
+        @Override
+        public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+            return new Species_L(mt, lf, argL0);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, narg);
+        }
+    }
+
+/*
+    static final class Species_LL extends BoundMethodHandle {
+        final Object argL0;
+        final Object argL1;
+        public Species_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
+            super(mt, lf);
+            this.argL0 = argL0;
+            this.argL1 = argL1;
+        }
+        @Override
+        public SpeciesData speciesData() {
+            return SPECIES_DATA;
+        }
+        public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LL", Species_LL.class);
+        @Override
+        public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+            return new Species_LL(mt, lf, argL0, argL1);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+        }
+    }
+
+    static final class Species_JL extends BoundMethodHandle {
+        final long argJ0;
+        final Object argL1;
+        public Species_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
+            super(mt, lf);
+            this.argJ0 = argJ0;
+            this.argL1 = argL1;
+        }
+        @Override
+        public SpeciesData speciesData() {
+            return SPECIES_DATA;
+        }
+        public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("JL", Species_JL.class);
+        @Override public final long   argJ0() { return argJ0; }
+        @Override public final Object argL1() { return argL1; }
+        @Override
+        public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+            return new Species_JL(mt, lf, argJ0, argL1);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+        }
+        @Override
+        public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+        }
+    }
+*/
+
+    //
+    // BMH species meta-data
+    //
+
+    /**
+     * Meta-data wrapper for concrete BMH classes.
+     */
+    static class SpeciesData {
+        final String                             types;
+        final Class<? extends BoundMethodHandle> clazz;
+        // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
+        // Therefore, we need a non-final link in the chain.  Use array elements.
+        final MethodHandle[]                     constructor;
+        final MethodHandle[]                     getters;
+        final SpeciesData[]                      extensions;
+
+        public int fieldCount() {
+            return types.length();
+        }
+        public char fieldType(int i) {
+            return types.charAt(i);
+        }
+
+        public String toString() {
+            return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]";
+        }
+
+        /**
+         * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
+         * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
+         * getter.
+         */
+        Name getterName(Name mhName, int i) {
+            MethodHandle mh = getters[i];
+            assert(mh != null) : this+"."+i;
+            return new Name(mh, mhName);
+        }
+
+        static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
+
+        private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
+            this.types = types;
+            this.clazz = clazz;
+            if (!INIT_DONE) {
+                this.constructor = new MethodHandle[1];
+                this.getters = new MethodHandle[types.length()];
+            } else {
+                this.constructor = Factory.makeCtors(clazz, types, null);
+                this.getters = Factory.makeGetters(clazz, types, null);
+            }
+            this.extensions = new SpeciesData[EXTENSION_TYPES.length()];
+        }
+
+        private void initForBootstrap() {
+            assert(!INIT_DONE);
+            if (constructor[0] == null) {
+                Factory.makeCtors(clazz, types, this.constructor);
+                Factory.makeGetters(clazz, types, this.getters);
+            }
+        }
+
+        private SpeciesData(String types) {
+            // Placeholder only.
+            this.types = types;
+            this.clazz = null;
+            this.constructor = null;
+            this.getters = null;
+            this.extensions = null;
+        }
+        private boolean isPlaceholder() { return clazz == null; }
+
+        private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
+        private static final boolean INIT_DONE;  // set after <clinit> finishes...
+
+        SpeciesData extendWithType(char type) {
+            int i = extensionIndex(type);
+            SpeciesData d = extensions[i];
+            if (d != null)  return d;
+            extensions[i] = d = get(types+type);
+            return d;
+        }
+
+        SpeciesData extendWithIndex(byte index) {
+            SpeciesData d = extensions[index];
+            if (d != null)  return d;
+            extensions[index] = d = get(types+EXTENSION_TYPES.charAt(index));
+            return d;
+        }
+
+        private static SpeciesData get(String types) {
+            // Acquire cache lock for query.
+            SpeciesData d = lookupCache(types);
+            if (!d.isPlaceholder())
+                return d;
+            Class<? extends BoundMethodHandle> cbmh;
+            synchronized (d) {
+                // Use synch. on the placeholder to prevent multiple instantiation of one species.
+                // Creating this class forces a recursive call to getForClass.
+                cbmh = Factory.generateConcreteBMHClass(types);
+            }
+            // Reacquire cache lock.
+            d = lookupCache(types);
+            // Class loading must have upgraded the cache.
+            assert(d != null && !d.isPlaceholder());
+            return d;
+        }
+        static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
+            // clazz is a new class which is initializing its SPECIES_DATA field
+            return updateCache(types, new SpeciesData(types, clazz));
+        }
+        private static synchronized SpeciesData lookupCache(String types) {
+            SpeciesData d = CACHE.get(types);
+            if (d != null)  return d;
+            d = new SpeciesData(types);
+            assert(d.isPlaceholder());
+            CACHE.put(types, d);
+            return d;
+        }
+        private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
+            SpeciesData d2;
+            assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
+            assert(!d.isPlaceholder());
+            CACHE.put(types, d);
+            return d;
+        }
+
+        static {
+            // pre-fill the BMH speciesdata cache with BMH's inner classes
+            final Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
+            SpeciesData d0 = BoundMethodHandle.SPECIES_DATA;  // trigger class init
+            assert(d0 == null || d0 == lookupCache("")) : d0;
+            try {
+                for (Class<?> c : rootCls.getDeclaredClasses()) {
+                    if (rootCls.isAssignableFrom(c)) {
+                        final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
+                        SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
+                        assert(d != null) : cbmh.getName();
+                        assert(d.clazz == cbmh);
+                        assert(d == lookupCache(d.types));
+                    }
+                }
+            } catch (Throwable e) {
+                throw new InternalError(e);
+            }
+
+            for (SpeciesData d : CACHE.values()) {
+                d.initForBootstrap();
+            }
+            // Note:  Do not simplify this, because INIT_DONE must not be
+            // a compile-time constant during bootstrapping.
+            INIT_DONE = Boolean.TRUE;
+        }
+    }
+
+    static SpeciesData getSpeciesData(String types) {
+        return SpeciesData.get(types);
+    }
+
+    /**
+     * Generation of concrete BMH classes.
+     *
+     * A concrete BMH species is fit for binding a number of values adhering to a
+     * given type pattern. Reference types are erased.
+     *
+     * BMH species are cached by type pattern.
+     *
+     * A BMH species has a number of fields with the concrete (possibly erased) types of
+     * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
+     * which can be included as names in lambda forms.
+     */
+    static class Factory {
+
+        static final String JLO_SIG  = "Ljava/lang/Object;";
+        static final String JLS_SIG  = "Ljava/lang/String;";
+        static final String JLC_SIG  = "Ljava/lang/Class;";
+        static final String MH       = "java/lang/invoke/MethodHandle";
+        static final String MH_SIG   = "L"+MH+";";
+        static final String BMH      = "java/lang/invoke/BoundMethodHandle";
+        static final String BMH_SIG  = "L"+BMH+";";
+        static final String SPECIES_DATA     = "java/lang/invoke/BoundMethodHandle$SpeciesData";
+        static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
+
+        static final String SPECIES_PREFIX_NAME = "Species_";
+        static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
+
+        static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
+        static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
+        static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
+        static final String VOID_SIG   = "()V";
+
+        static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
+
+        static final Class<?>[] TYPES = new Class<?>[] { Object.class, int.class, long.class, float.class, double.class };
+
+        static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
+
+        /**
+         * Generate a concrete subclass of BMH for a given combination of bound types.
+         *
+         * A concrete BMH species adheres to the following schema:
+         *
+         * <pre>
+         * class Species_<<types>> extends BoundMethodHandle {
+         *     <<fields>>
+         *     final SpeciesData speciesData() { return SpeciesData.get("<<types>>"); }
+         * }
+         * </pre>
+         *
+         * The {@code <<types>>} signature is precisely the string that is passed to this
+         * method.
+         *
+         * The {@code <<fields>>} section consists of one field definition per character in
+         * the type signature, adhering to the naming schema described in the definition of
+         * {@link #makeFieldName()}.
+         *
+         * For example, a concrete BMH species for two reference and one integral bound values
+         * would have the following shape:
+         *
+         * <pre>
+         * class BoundMethodHandle { ... private static
+         * final class Species_LLI extends BoundMethodHandle {
+         *     final Object argL0;
+         *     final Object argL1;
+         *     final int argI2;
+         *     public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
+         *         super(mt, lf);
+         *         this.argL0 = argL0;
+         *         this.argL1 = argL1;
+         *         this.argI2 = argI2;
+         *     }
+         *     public final SpeciesData speciesData() { return SPECIES_DATA; }
+         *     public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
+         *     public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) {
+         *         return SPECIES_DATA.constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2);
+         *     }
+         *     public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) {
+         *         return SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+         *     }
+         *     public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) {
+         *         return SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+         *     }
+         *     public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) {
+         *         return SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+         *     }
+         *     public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) {
+         *         return SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+         *     }
+         *     public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) {
+         *         return SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+         *     }
+         * }
+         * </pre>
+         *
+         * @param types the type signature, wherein reference types are erased to 'L'
+         * @return the generated concrete BMH class
+         */
+        static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
+            final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+
+            final String className  = SPECIES_PREFIX_PATH + types;
+            final String sourceFile = SPECIES_PREFIX_NAME + types;
+            cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
+            cw.visitSource(sourceFile, null);
+
+            // emit static types and SPECIES_DATA fields
+            cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd();
+
+            // emit bound argument fields
+            for (int i = 0; i < types.length(); ++i) {
+                final char t = types.charAt(i);
+                final String fieldName = makeFieldName(types, i);
+                final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
+                cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
+            }
+
+            MethodVisitor mv;
+
+            // emit constructor
+            mv = cw.visitMethod(ACC_PUBLIC, "<init>", makeSignature(types, true), null, null);
+            mv.visitCode();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitVarInsn(ALOAD, 2);
+
+            mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true));
+
+            for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
+                // i counts the arguments, j counts corresponding argument slots
+                char t = types.charAt(i);
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3
+                mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t));
+                if (t == 'J' || t == 'D') {
+                    ++j; // adjust argument register access
+                }
+            }
+
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+
+            // emit implementation of reinvokerTarget()
+            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null);
+            mv.visitCode();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG);
+            mv.visitTypeInsn(CHECKCAST, MH);
+            mv.visitInsn(ARETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+
+            // emit implementation of speciesData()
+            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
+            mv.visitCode();
+            mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+            mv.visitInsn(ARETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+
+            // emit clone()
+            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE);
+            mv.visitCode();
+            // return speciesData().constructor[0].invokeBasic(mt, lf, argL0, ...)
+            // obtain constructor
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+            mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
+            mv.visitInsn(ICONST_0);
+            mv.visitInsn(AALOAD);
+            // load mt, lf
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitVarInsn(ALOAD, 2);
+            // put fields on the stack
+            emitPushFields(types, className, mv);
+            // finally, invoke the constructor and return
+            mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types, false));
+            mv.visitInsn(ARETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+
+            // for each type, emit cloneExtendT()
+            for (Class<?> c : TYPES) {
+                char t = Wrapper.basicTypeChar(c);
+                mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "cloneExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE);
+                mv.visitCode();
+                // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
+                // obtain constructor
+                mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+                int iconstInsn = ICONST_0 + extensionIndex(t);
+                assert(iconstInsn <= ICONST_5);
+                mv.visitInsn(iconstInsn);
+                mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG);
+                mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
+                mv.visitInsn(ICONST_0);
+                mv.visitInsn(AALOAD);
+                // load mt, lf
+                mv.visitVarInsn(ALOAD, 1);
+                mv.visitVarInsn(ALOAD, 2);
+                // put fields on the stack
+                emitPushFields(types, className, mv);
+                // put narg on stack
+                mv.visitVarInsn(typeLoadOp(t), 3);
+                // finally, invoke the constructor and return
+                mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + t, false));
+                mv.visitInsn(ARETURN);
+                mv.visitMaxs(0, 0);
+                mv.visitEnd();
+            }
+
+            // emit class initializer
+            mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null);
+            mv.visitCode();
+            mv.visitLdcInsn(types);
+            mv.visitLdcInsn(Type.getObjectType(className));
+            mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG);
+            mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+
+            cw.visitEnd();
+
+            // load class
+            final byte[] classFile = cw.toByteArray();
+            InvokerBytecodeGenerator.maybeDump(className, classFile);
+            Class<? extends BoundMethodHandle> bmhClass =
+                //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
+                UNSAFE.defineClass(className, classFile, 0, classFile.length).asSubclass(BoundMethodHandle.class);
+            UNSAFE.ensureClassInitialized(bmhClass);
+
+            return bmhClass;
+        }
+
+        private static int typeLoadOp(char t) {
+            switch (t) {
+            case 'L': return ALOAD;
+            case 'I': return ILOAD;
+            case 'J': return LLOAD;
+            case 'F': return FLOAD;
+            case 'D': return DLOAD;
+            default : throw new InternalError("unrecognized type " + t);
+            }
+        }
+
+        private static void emitPushFields(String types, String className, MethodVisitor mv) {
+            for (int i = 0; i < types.length(); ++i) {
+                char tc = types.charAt(i);
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
+            }
+        }
+
+        static String typeSig(char t) {
+            return t == 'L' ? JLO_SIG : String.valueOf(t);
+        }
+
+        //
+        // Getter MH generation.
+        //
+
+        private static MethodHandle makeGetter(Class<?> cbmhClass, String types, int index) {
+            String fieldName = makeFieldName(types, index);
+            Class<?> fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType();
+            try {
+                return LOOKUP.findGetter(cbmhClass, fieldName, fieldType);
+            } catch (NoSuchFieldException | IllegalAccessException e) {
+                throw new InternalError(e);
+            }
+        }
+
+        static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
+            if (mhs == null)  mhs = new MethodHandle[types.length()];
+            for (int i = 0; i < mhs.length; ++i) {
+                mhs[i] = makeGetter(cbmhClass, types, i);
+                assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
+            }
+            return mhs;
+        }
+
+        static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
+            if (mhs == null)  mhs = new MethodHandle[1];
+            mhs[0] = makeCbmhCtor(cbmh, types);
+            return mhs;
+        }
+
+        //
+        // Auxiliary methods.
+        //
+
+        static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
+            try {
+                Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
+                return (SpeciesData) F_SPECIES_DATA.get(null);
+            } catch (ReflectiveOperationException ex) {
+                throw new InternalError(ex);
+            }
+        }
+
+        /**
+         * Field names in concrete BMHs adhere to this pattern:
+         * arg + type + index
+         * where type is a single character (L, I, J, F, D).
+         */
+        private static String makeFieldName(String types, int index) {
+            assert index >= 0 && index < types.length();
+            return "arg" + types.charAt(index) + index;
+        }
+
+        private static String makeSignature(String types, boolean ctor) {
+            StringBuilder buf = new StringBuilder(SIG_INCIPIT);
+            for (char c : types.toCharArray()) {
+                buf.append(typeSig(c));
+            }
+            return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
+        }
+
+        static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
+            try {
+                return linkConstructor(LOOKUP.findConstructor(cbmh, MethodType.fromMethodDescriptorString(makeSignature(types, true), null)));
+            } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
+                throw new InternalError(e);
+            }
+        }
+
+        /**
+         * Wrap a constructor call in a {@link LambdaForm}.
+         *
+         * If constructors ({@code <init>} methods) are called in LFs, problems might arise if the LFs
+         * are turned into bytecode, because the call to the allocator is routed through an MH, and the
+         * verifier cannot find a {@code NEW} instruction preceding the {@code INVOKESPECIAL} to
+         * {@code <init>}. To avoid this, we add an indirection by invoking {@code <init>} through
+         * {@link MethodHandle#linkToSpecial}.
+         *
+         * The last {@link LambdaForm#Name Name} in the argument's form is expected to be the {@code void}
+         * result of the {@code <init>} invocation. This entry is replaced.
+         */
+        private static MethodHandle linkConstructor(MethodHandle cmh) {
+            final LambdaForm lf = cmh.form;
+            final int initNameIndex = lf.names.length - 1;
+            final Name initName = lf.names[initNameIndex];
+            final MemberName ctorMN = initName.function.member;
+            final MethodType ctorMT = ctorMN.getInvocationType();
+
+            // obtain function member (call target)
+            // linker method type replaces initial parameter (BMH species) with BMH to avoid naming a species (anonymous class!)
+            final MethodType linkerMT = ctorMT.changeParameterType(0, BoundMethodHandle.class).appendParameterTypes(MemberName.class);
+            MemberName linkerMN = new MemberName(MethodHandle.class, "linkToSpecial", linkerMT, REF_invokeStatic);
+            try {
+                linkerMN = MemberName.getFactory().resolveOrFail(REF_invokeStatic, linkerMN, null, NoSuchMethodException.class);
+                assert(linkerMN.isStatic());
+            } catch (ReflectiveOperationException ex) {
+                throw new InternalError(ex);
+            }
+            // extend arguments array
+            Object[] newArgs = Arrays.copyOf(initName.arguments, initName.arguments.length + 1);
+            newArgs[newArgs.length - 1] = ctorMN;
+            // replace function
+            final NamedFunction nf = new NamedFunction(linkerMN);
+            final Name linkedCtor = new Name(nf, newArgs);
+            linkedCtor.initIndex(initNameIndex);
+            lf.names[initNameIndex] = linkedCtor;
+            return cmh;
+        }
+
+    }
+
+    private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
+
+    /**
+     * All subclasses must provide such a value describing their type signature.
+     */
+    static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
 }
--- a/src/share/classes/java/lang/invoke/CallSite.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/CallSite.java	Tue Jul 24 10:47:44 2012 -0700
@@ -26,7 +26,6 @@
 package java.lang.invoke;
 
 import sun.invoke.empty.Empty;
-import sun.misc.Unsafe;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 
@@ -87,13 +86,9 @@
 public class CallSite {
     static { MethodHandleImpl.initStatics(); }
 
-    // Fields used only by the JVM.  Do not use or change.
-    private MemberName vmmethod; // supplied by the JVM (ref. to calling method)
-    private int        vmindex;  // supplied by the JVM (BCI within calling method)
-
     // The actual payload of this call site:
     /*package-private*/
-    MethodHandle target;
+    MethodHandle target;    // Note: This field is known to the JVM.  Do not change.
 
     /**
      * Make a blank call site object with the given method type.
@@ -152,24 +147,6 @@
         return target.type();
     }
 
-    /** Called from JVM (or low-level Java code) after the BSM returns the newly created CallSite.
-     *  The parameters are JVM-specific.
-     */
-    void initializeFromJVM(String name,
-                           MethodType type,
-                           MemberName callerMethod,
-                           int        callerBCI) {
-        if (this.vmmethod != null) {
-            // FIXME
-            throw new BootstrapMethodError("call site has already been linked to an invokedynamic instruction");
-        }
-        if (!this.type().equals(type)) {
-            throw wrongTargetType(target, type);
-        }
-        this.vmindex  = callerBCI;
-        this.vmmethod = callerMethod;
-    }
-
     /**
      * Returns the target method of the call site, according to the
      * behavior defined by this call site's specific class.
@@ -234,7 +211,7 @@
     public abstract MethodHandle dynamicInvoker();
 
     /*non-public*/ MethodHandle makeDynamicInvoker() {
-        MethodHandle getTarget = MethodHandleImpl.bindReceiver(GET_TARGET, this);
+        MethodHandle getTarget = GET_TARGET.bindReceiver(this);
         MethodHandle invoker = MethodHandles.exactInvoker(this.type());
         return MethodHandles.foldArguments(invoker, getTarget);
     }
@@ -256,12 +233,10 @@
     }
 
     // unsafe stuff:
-    private static final Unsafe unsafe = Unsafe.getUnsafe();
     private static final long TARGET_OFFSET;
-
     static {
         try {
-            TARGET_OFFSET = unsafe.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+            TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
         } catch (Exception ex) { throw new Error(ex); }
     }
 
@@ -271,7 +246,7 @@
     }
     /*package-private*/
     MethodHandle getTargetVolatile() {
-        return (MethodHandle) unsafe.getObjectVolatile(this, TARGET_OFFSET);
+        return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
     }
     /*package-private*/
     void setTargetVolatile(MethodHandle newTarget) {
@@ -285,8 +260,7 @@
                              // Extra arguments for BSM, if any:
                              Object info,
                              // Caller information:
-                             MemberName callerMethod, int callerBCI) {
-        Class<?> callerClass = callerMethod.getDeclaringClass();
+                             Class<?> callerClass) {
         Object caller = IMPL_LOOKUP.in(callerClass);
         CallSite site;
         try {
--- a/src/share/classes/java/lang/invoke/CountingMethodHandle.java	Thu Jul 12 00:12:52 2012 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.lang.invoke;
-
-import static java.lang.invoke.MethodHandleNatives.Constants.*;
-
-/**
- * This method handle is used to optionally provide a count of how
- * many times it was invoked.
- *
- * @author never
- */
-class CountingMethodHandle extends AdapterMethodHandle {
-    private int vmcount;
-
-    private CountingMethodHandle(MethodHandle target) {
-        super(target, target.type(), AdapterMethodHandle.makeConv(OP_RETYPE_ONLY));
-    }
-
-    /** Wrap the incoming MethodHandle in a CountingMethodHandle if they are enabled */
-    static MethodHandle wrap(MethodHandle mh) {
-        if (MethodHandleNatives.COUNT_GWT) {
-            return new CountingMethodHandle(mh);
-        }
-        return mh;
-    }
-}
--- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java	Tue Jul 24 10:47:44 2012 -0700
@@ -25,29 +25,635 @@
 
 package java.lang.invoke;
 
+import sun.misc.Unsafe;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import sun.invoke.util.VerifyAccess;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.MethodTypeForm.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
+import sun.invoke.util.Wrapper;
 
 /**
- * The flavor of method handle which emulates invokespecial or invokestatic.
+ * The flavor of method handle which implements a constant reference
+ * to a class member.
  * @author jrose
  */
 class DirectMethodHandle extends MethodHandle {
-    //inherited oop    vmtarget;    // methodOop or virtual class/interface oop
-    private final int  vmindex;     // method index within class or interface
-    { vmindex = VM_INDEX_UNINITIALIZED; }  // JVM may change this
+    final MemberName member;
 
-    // Constructors in this class *must* be package scoped or private.
-    DirectMethodHandle(MethodType mtype, MemberName m, boolean doDispatch, Class<?> lookupClass) {
-        super(mtype);
-
-        assert(m.isMethod() || !doDispatch && m.isConstructor());
-        if (!m.isResolved())
-            throw new InternalError();
-
-        MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass);
+    // Constructors and factory methods in this class *must* be package scoped or private.
+    private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member) {
+        super(mtype, form);
+        if (!member.isResolved())  throw new InternalError();
+        this.member = member;
     }
 
-    boolean isValid() {
-        return (vmindex != VM_INDEX_UNINITIALIZED);
+    // Factory methods:
+
+    static DirectMethodHandle make(Class<?> receiver, MemberName member) {
+        MethodType mtype = member.getMethodOrFieldType();
+        if (!member.isStatic()) {
+            if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
+                throw new InternalError(member.toString());
+            mtype = mtype.insertParameterTypes(0, receiver);
+        }
+        if (!member.isField()) {
+            LambdaForm lform = preparedLambdaForm(member);
+            return new DirectMethodHandle(mtype, lform, member);
+        } else {
+            LambdaForm lform = preparedFieldLambdaForm(member);
+            if (member.isStatic()) {
+                long offset = MethodHandleNatives.staticFieldOffset(member);
+                Object base = MethodHandleNatives.staticFieldBase(member);
+                return new StaticAccessor(mtype, lform, member, base, offset);
+            } else {
+                long offset = MethodHandleNatives.objectFieldOffset(member);
+                assert(offset == (int)offset);
+                return new Accessor(mtype, lform, member, (int)offset);
+            }
+        }
+    }
+    static DirectMethodHandle make(MemberName member) {
+        if (member.isConstructor())
+            return makeAllocator(member);
+        return make(member.getDeclaringClass(), member);
+    }
+    static DirectMethodHandle make(Method method) {
+        return make(method.getDeclaringClass(), new MemberName(method));
+    }
+    static DirectMethodHandle make(Field field) {
+        return make(field.getDeclaringClass(), new MemberName(field));
+    }
+    private static DirectMethodHandle makeAllocator(MemberName ctor) {
+        assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
+        Class<?> instanceClass = ctor.getDeclaringClass();
+        ctor = ctor.asConstructor();
+        assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
+        MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
+        LambdaForm lform = preparedLambdaForm(ctor);
+        MemberName init = ctor.asSpecial();
+        assert(init.getMethodType().returnType() == void.class);
+        return new Constructor(mtype, lform, ctor, init, instanceClass);
+    }
+
+    @Override
+    MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+        return new DirectMethodHandle(mt, lf, member);
+    }
+
+    @Override
+    String debugString() {
+        return "DMH["+member.toString()+"]="+super.debugString();
+    }
+
+    //// Implementation methods.
+    @Override
+    @ForceInline
+    MemberName internalMemberName() {
+        return member;
+    }
+
+    @Override
+    MethodHandle bindArgument(int pos, char basicType, Object value) {
+        // If the member needs dispatching, do so.
+        if (pos == 0 && basicType == 'L') {
+            DirectMethodHandle concrete = maybeRebind(value);
+            if (concrete != null)
+                return concrete.bindReceiver(value);
+        }
+        return super.bindArgument(pos, basicType, value);
+    }
+
+    @Override
+    MethodHandle bindReceiver(Object receiver) {
+        // If the member needs dispatching, do so.
+        DirectMethodHandle concrete = maybeRebind(receiver);
+        if (concrete != null)
+            return concrete.bindReceiver(receiver);
+        return super.bindReceiver(receiver);
+    }
+
+    private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
+
+    private DirectMethodHandle maybeRebind(Object receiver) {
+        if (receiver != null) {
+            switch (member.getReferenceKind()) {
+            case REF_invokeInterface:
+            case REF_invokeVirtual:
+                // Pre-dispatch the member.
+                Class<?> concreteClass = receiver.getClass();
+                MemberName concrete = new MemberName(concreteClass, member.getName(), member.getMethodType(), REF_invokeSpecial);
+                concrete = IMPL_NAMES.resolveOrNull(REF_invokeSpecial, concrete, concreteClass);
+                if (concrete != null)
+                    return new DirectMethodHandle(type(), preparedLambdaForm(concrete), concrete);
+                break;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Create a LF which can invoke the given method.
+     * Cache and share this structure among all methods with
+     * the same basicType and refKind.
+     */
+    private static LambdaForm preparedLambdaForm(MemberName m) {
+        assert(m.isInvocable()) : m;  // call preparedFieldLambdaForm instead
+        MethodType mtype = m.getInvocationType().basicType();
+        assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
+        int which;
+        switch (m.getReferenceKind()) {
+        case REF_invokeVirtual:    which = LF_INVVIRTUAL;    break;
+        case REF_invokeStatic:     which = LF_INVSTATIC;     break;
+        case REF_invokeSpecial:    which = LF_INVSPECIAL;    break;
+        case REF_invokeInterface:  which = LF_INVINTERFACE;  break;
+        case REF_newInvokeSpecial: which = LF_NEWINVSPECIAL; break;
+        default:  throw new InternalError(m.toString());
+        }
+        if (which == LF_INVSTATIC && shouldBeInitialized(m)) {
+            // precompute the barrier-free version:
+            preparedLambdaForm(mtype, which);
+            which = LF_INVSTATIC_INIT;
+        }
+        LambdaForm lform = preparedLambdaForm(mtype, which);
+        maybeCompile(lform, m);
+        assert(lform.methodType().dropParameterTypes(0, 1)
+                .equals(m.getInvocationType().basicType()))
+                : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
+        return lform;
+    }
+
+    private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
+        LambdaForm lform = mtype.form().cachedLambdaForm(which);
+        if (lform != null)  return lform;
+        lform = makePreparedLambdaForm(mtype, which);
+        return mtype.form().setCachedLambdaForm(which, lform);
+    }
+
+    private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
+        boolean needsInit = (which == LF_INVSTATIC_INIT);
+        boolean doesAlloc = (which == LF_NEWINVSPECIAL);
+        String linkerName, lambdaName;
+        switch (which) {
+        case LF_INVVIRTUAL:    linkerName = "linkToVirtual";    lambdaName = "DMH.invokeVirtual";    break;
+        case LF_INVSTATIC:     linkerName = "linkToStatic";     lambdaName = "DMH.invokeStatic";     break;
+        case LF_INVSTATIC_INIT:linkerName = "linkToStatic";     lambdaName = "DMH.invokeStaticInit"; break;
+        case LF_INVSPECIAL:    linkerName = "linkToSpecial";    lambdaName = "DMH.invokeSpecial";    break;
+        case LF_INVINTERFACE:  linkerName = "linkToInterface";  lambdaName = "DMH.invokeInterface";  break;
+        case LF_NEWINVSPECIAL: linkerName = "linkToSpecial";    lambdaName = "DMH.newInvokeSpecial"; break;
+        default:  throw new InternalError("which="+which);
+        }
+        MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class);
+        if (doesAlloc)
+            mtypeWithArg = mtypeWithArg
+                    .insertParameterTypes(0, Object.class)  // insert newly allocated obj
+                    .changeReturnType(void.class);          // <init> returns void
+        MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
+        try {
+            linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex);
+        }
+        final int DMH_THIS    = 0;
+        final int ARG_BASE    = 1;
+        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
+        int nameCursor = ARG_LIMIT;
+        final int NEW_OBJ     = (doesAlloc ? nameCursor++ : -1);
+        final int GET_MEMBER  = nameCursor++;
+        final int LINKER_CALL = nameCursor++;
+        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+        assert(names.length == nameCursor);
+        if (doesAlloc) {
+            // names = { argx,y,z,... new C, init method }
+            names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
+            names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
+        } else if (needsInit) {
+            names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
+        } else {
+            names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
+        }
+        Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
+        assert(outArgs[outArgs.length-1] == names[GET_MEMBER]);  // look, shifted args!
+        int result = LambdaForm.LAST_RESULT;
+        if (doesAlloc) {
+            assert(outArgs[outArgs.length-2] == names[NEW_OBJ]);  // got to move this one
+            System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
+            outArgs[0] = names[NEW_OBJ];
+            result = NEW_OBJ;
+        }
+        names[LINKER_CALL] = new Name(linker, outArgs);
+        lambdaName += "_" + LambdaForm.basicTypeSignature(mtype);
+        LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
+        // This is a tricky bit of code.  Don't send it through the LF interpreter.
+        lform.compileToBytecode();
+        return lform;
+    }
+
+    private static void maybeCompile(LambdaForm lform, MemberName m) {
+        if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
+            // Help along bootstrapping...
+            lform.compileToBytecode();
+    }
+
+    /** Static wrapper for DirectMethodHandle.internalMemberName. */
+    @ForceInline
+    /*non-public*/ static Object internalMemberName(Object mh) {
+        return ((DirectMethodHandle)mh).member;
+    }
+
+    /** Static wrapper for DirectMethodHandle.internalMemberName.
+     * This one also forces initialization.
+     */
+    /*non-public*/ static Object internalMemberNameEnsureInit(Object mh) {
+        DirectMethodHandle dmh = (DirectMethodHandle)mh;
+        dmh.ensureInitialized();
+        return dmh.member;
+    }
+
+    /*non-public*/ static
+    boolean shouldBeInitialized(MemberName member) {
+        switch (member.getReferenceKind()) {
+        case REF_invokeStatic:
+        case REF_getStatic:
+        case REF_putStatic:
+        case REF_newInvokeSpecial:
+            break;
+        default:
+            // No need to initialize the class on this kind of member.
+            return false;
+        }
+        Class<?> cls = member.getDeclaringClass();
+        if (cls == ValueConversions.class ||
+            cls == MethodHandleImpl.class ||
+            cls == Invokers.class) {
+            // These guys have lots of <clinit> DMH creation but we know
+            // the MHs will not be used until the system is booted.
+            return false;
+        }
+        if (VerifyAccess.isSamePackage(MethodHandle.class, cls) ||
+            VerifyAccess.isSamePackage(ValueConversions.class, cls)) {
+            // It is a system class.  It is probably in the process of
+            // being initialized, but we will help it along just to be safe.
+            if (UNSAFE.shouldBeInitialized(cls)) {
+                UNSAFE.ensureClassInitialized(cls);
+            }
+            return false;
+        }
+        return UNSAFE.shouldBeInitialized(cls);
+    }
+
+    private static class EnsureInitialized extends ClassValue<WeakReference<Thread>> {
+        @Override
+        protected WeakReference<Thread> computeValue(Class<?> type) {
+            UNSAFE.ensureClassInitialized(type);
+            if (UNSAFE.shouldBeInitialized(type))
+                // If the previous call didn't block, this can happen.
+                // We are executing inside <clinit>.
+                return new WeakReference<>(Thread.currentThread());
+            return null;
+        }
+        static final EnsureInitialized INSTANCE = new EnsureInitialized();
+    }
+
+    private void ensureInitialized() {
+        if (checkInitialized(member)) {
+            // The coast is clear.  Delete the <clinit> barrier.
+            if (member.isField())
+                updateForm(preparedFieldLambdaForm(member));
+            else
+                updateForm(preparedLambdaForm(member));
+        }
+    }
+    private static boolean checkInitialized(MemberName member) {
+        Class<?> defc = member.getDeclaringClass();
+        WeakReference<Thread> ref = EnsureInitialized.INSTANCE.get(defc);
+        if (ref == null) {
+            return true;  // the final state
+        }
+        Thread clinitThread = ref.get();
+        // Somebody may still be running defc.<clinit>.
+        if (clinitThread == Thread.currentThread()) {
+            // If anybody is running defc.<clinit>, it is this thread.
+            if (UNSAFE.shouldBeInitialized(defc))
+                // Yes, we are running it; keep the barrier for now.
+                return false;
+        } else {
+            // We are in a random thread.  Block.
+            UNSAFE.ensureClassInitialized(defc);
+        }
+        assert(!UNSAFE.shouldBeInitialized(defc));
+        // put it into the final state
+        EnsureInitialized.INSTANCE.remove(defc);
+        return true;
+    }
+
+    /*non-public*/ static void ensureInitialized(Object mh) {
+        ((DirectMethodHandle)mh).ensureInitialized();
+    }
+
+    /** This subclass handles constructor references. */
+    static class Constructor extends DirectMethodHandle {
+        final MemberName initMethod;
+        final Class<?>   instanceClass;
+
+        private Constructor(MethodType mtype, LambdaForm form, MemberName constructor,
+                            MemberName initMethod, Class<?> instanceClass) {
+            super(mtype, form, constructor);
+            this.initMethod = initMethod;
+            this.instanceClass = instanceClass;
+            assert(initMethod.isResolved());
+        }
+    }
+
+    /*non-public*/ static Object constructorMethod(Object mh) {
+        Constructor dmh = (Constructor)mh;
+        return dmh.initMethod;
+    }
+
+    /*non-public*/ static Object allocateInstance(Object mh) throws InstantiationException {
+        Constructor dmh = (Constructor)mh;
+        return UNSAFE.allocateInstance(dmh.instanceClass);
+    }
+
+    /** This subclass handles non-static field references. */
+    static class Accessor extends DirectMethodHandle {
+        final Class<?> fieldType;
+        final int      fieldOffset;
+        private Accessor(MethodType mtype, LambdaForm form, MemberName member,
+                         int fieldOffset) {
+            super(mtype, form, member);
+            this.fieldType   = member.getFieldType();
+            this.fieldOffset = fieldOffset;
+        }
+
+        @Override Object checkCast(Object obj) {
+            return fieldType.cast(obj);
+        }
+    }
+
+    @ForceInline
+    /*non-public*/ static long fieldOffset(Object accessorObj) {
+        // Note: We return a long because that is what Unsafe.getObject likes.
+        // We store a plain int because it is more compact.
+        return ((Accessor)accessorObj).fieldOffset;
+    }
+
+    @ForceInline
+    /*non-public*/ static Object checkBase(Object obj) {
+        // Note that the object's class has already been verified,
+        // since the parameter type of the Accessor method handle
+        // is either member.getDeclaringClass or a subclass.
+        // This was verified in DirectMethodHandle.make.
+        // Therefore, the only remaining check is for null.
+        // Since this check is *not* guaranteed by Unsafe.getInt
+        // and its siblings, we need to make an explicit one here.
+        obj.getClass();  // maybe throw NPE
+        return obj;
+    }
+
+    /** This subclass handles static field references. */
+    static class StaticAccessor extends DirectMethodHandle {
+        final private Class<?> fieldType;
+        final private Object   staticBase;
+        final private long     staticOffset;
+
+        private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member,
+                               Object staticBase, long staticOffset) {
+            super(mtype, form, member);
+            this.fieldType    = member.getFieldType();
+            this.staticBase   = staticBase;
+            this.staticOffset = staticOffset;
+        }
+
+        @Override Object checkCast(Object obj) {
+            return fieldType.cast(obj);
+        }
+    }
+
+    @ForceInline
+    /*non-public*/ static Object nullCheck(Object obj) {
+        obj.getClass();
+        return obj;
+    }
+
+    @ForceInline
+    /*non-public*/ static Object staticBase(Object accessorObj) {
+        return ((StaticAccessor)accessorObj).staticBase;
+    }
+
+    @ForceInline
+    /*non-public*/ static long staticOffset(Object accessorObj) {
+        return ((StaticAccessor)accessorObj).staticOffset;
+    }
+
+    @ForceInline
+    /*non-public*/ static Object checkCast(Object mh, Object obj) {
+        return ((DirectMethodHandle) mh).checkCast(obj);
+    }
+
+    Object checkCast(Object obj) {
+        return member.getReturnType().cast(obj);
+    }
+
+    // Caching machinery for field accessors:
+    private static byte
+            AF_GETFIELD        = 0,
+            AF_PUTFIELD        = 1,
+            AF_GETSTATIC       = 2,
+            AF_PUTSTATIC       = 3,
+            AF_GETSTATIC_INIT  = 4,
+            AF_PUTSTATIC_INIT  = 5,
+            AF_LIMIT           = 6;
+    // Enumerate the different field kinds using Wrapper,
+    // with an extra case added for checked references.
+    private static int
+            FT_LAST_WRAPPER    = Wrapper.values().length-1,
+            FT_UNCHECKED_REF   = Wrapper.OBJECT.ordinal(),
+            FT_CHECKED_REF     = FT_LAST_WRAPPER+1,
+            FT_LIMIT           = FT_LAST_WRAPPER+2;
+    private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) {
+        return ((formOp * FT_LIMIT * 2)
+                + (isVolatile ? FT_LIMIT : 0)
+                + ftypeKind);
+    }
+    private static final LambdaForm[] ACCESSOR_FORMS
+            = new LambdaForm[afIndex(AF_LIMIT, false, 0)];
+    private static int ftypeKind(Class<?> ftype) {
+        if (ftype.isPrimitive())
+            return Wrapper.forPrimitiveType(ftype).ordinal();
+        else if (VerifyType.isNullReferenceConversion(Object.class, ftype))
+            return FT_UNCHECKED_REF;
+        else
+            return FT_CHECKED_REF;
+    }
+
+    /**
+     * Create a LF which can access the given field.
+     * Cache and share this structure among all fields with
+     * the same basicType and refKind.
+     */
+    private static LambdaForm preparedFieldLambdaForm(MemberName m) {
+        Class<?> ftype = m.getFieldType();
+        boolean isVolatile = m.isVolatile();
+        byte formOp;
+        switch (m.getReferenceKind()) {
+        case REF_getField:      formOp = AF_GETFIELD;    break;
+        case REF_putField:      formOp = AF_PUTFIELD;    break;
+        case REF_getStatic:     formOp = AF_GETSTATIC;   break;
+        case REF_putStatic:     formOp = AF_PUTSTATIC;   break;
+        default:  throw new InternalError(m.toString());
+        }
+        if (shouldBeInitialized(m)) {
+            // precompute the barrier-free version:
+            preparedFieldLambdaForm(formOp, isVolatile, ftype);
+            assert((AF_GETSTATIC_INIT - AF_GETSTATIC) ==
+                   (AF_PUTSTATIC_INIT - AF_PUTSTATIC));
+            formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC);
+        }
+        LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype);
+        maybeCompile(lform, m);
+        assert(lform.methodType().dropParameterTypes(0, 1)
+                .equals(m.getInvocationType().basicType()))
+                : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
+        return lform;
+    }
+    private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class<?> ftype) {
+        int afIndex = afIndex(formOp, isVolatile, ftypeKind(ftype));
+        LambdaForm lform = ACCESSOR_FORMS[afIndex];
+        if (lform != null)  return lform;
+        lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind(ftype));
+        ACCESSOR_FORMS[afIndex] = lform;  // don't bother with a CAS
+        return lform;
+    }
+
+    private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
+        boolean isGetter  = (formOp & 1) == (AF_GETFIELD & 1);
+        boolean isStatic  = (formOp >= AF_GETSTATIC);
+        boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
+        boolean needsCast = (ftypeKind == FT_CHECKED_REF);
+        Wrapper fw = (needsCast ? Wrapper.OBJECT : Wrapper.values()[ftypeKind]);
+        Class<?> ft = fw.primitiveType();
+        assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
+        String tname  = fw.primitiveSimpleName();
+        String ctname = Character.toUpperCase(tname.charAt(0)) + tname.substring(1);
+        if (isVolatile)  ctname += "Volatile";
+        String getOrPut = (isGetter ? "get" : "put");
+        String linkerName = (getOrPut + ctname);  // getObject, putIntVolatile, etc.
+        MethodType linkerType;
+        if (isGetter)
+            linkerType = MethodType.methodType(ft, Object.class, long.class);
+        else
+            linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
+        MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual);
+        try {
+            linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex);
+        }
+
+        // What is the external type of the lambda form?
+        MethodType mtype;
+        if (isGetter)
+            mtype = MethodType.methodType(ft);
+        else
+            mtype = MethodType.methodType(void.class, ft);
+        mtype = mtype.basicType();  // erase short to int, etc.
+        if (!isStatic)
+            mtype = mtype.insertParameterTypes(0, Object.class);
+        final int DMH_THIS  = 0;
+        final int ARG_BASE  = 1;
+        final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
+        // if this is for non-static access, the base pointer is stored at this index:
+        final int OBJ_BASE  = isStatic ? -1 : ARG_BASE;
+        // if this is for write access, the value to be written is stored at this index:
+        final int SET_VALUE  = isGetter ? -1 : ARG_LIMIT - 1;
+        int nameCursor = ARG_LIMIT;
+        final int F_HOLDER  = (isStatic ? nameCursor++ : -1);  // static base if any
+        final int F_OFFSET  = nameCursor++;  // Either static offset or field offset.
+        final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
+        final int INIT_BAR  = (needsInit ? nameCursor++ : -1);
+        final int PRE_CAST  = (needsCast && !isGetter ? nameCursor++ : -1);
+        final int LINKER_CALL = nameCursor++;
+        final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
+        final int RESULT    = nameCursor-1;  // either the call or the cast
+        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+        if (needsInit)
+            names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
+        if (needsCast && !isGetter)
+            names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
+        Object[] outArgs = new Object[1 + linkerType.parameterCount()];
+        assert(outArgs.length == (isGetter ? 3 : 4));
+        outArgs[0] = UNSAFE;
+        if (isStatic) {
+            outArgs[1] = names[F_HOLDER]  = new Name(NF_staticBase, names[DMH_THIS]);
+            outArgs[2] = names[F_OFFSET]  = new Name(NF_staticOffset, names[DMH_THIS]);
+        } else {
+            outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
+            outArgs[2] = names[F_OFFSET]  = new Name(NF_fieldOffset, names[DMH_THIS]);
+        }
+        if (!isGetter) {
+            outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
+        }
+        for (Object a : outArgs)  assert(a != null);
+        names[LINKER_CALL] = new Name(linker, outArgs);
+        if (needsCast && isGetter)
+            names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
+        for (Name n : names)  assert(n != null);
+        String fieldOrStatic = (isStatic ? "Static" : "Field");
+        String lambdaName = (linkerName + fieldOrStatic);  // significant only for debugging
+        if (needsCast)  lambdaName += "Cast";
+        if (needsInit)  lambdaName += "Init";
+        return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT);
+    }
+
+    private static final NamedFunction
+            NF_internalMemberName,
+            NF_internalMemberNameEnsureInit,
+            NF_ensureInitialized,
+            NF_fieldOffset,
+            NF_checkBase,
+            NF_staticBase,
+            NF_staticOffset,
+            NF_checkCast,
+            NF_allocateInstance,
+            NF_constructorMethod;
+    static {
+        try {
+            NamedFunction nfs[] = {
+                NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("internalMemberName", Object.class)),
+                NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
+                NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("ensureInitialized", Object.class)),
+                NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("fieldOffset", Object.class)),
+                NF_checkBase = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("checkBase", Object.class)),
+                NF_staticBase = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("staticBase", Object.class)),
+                NF_staticOffset = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("staticOffset", Object.class)),
+                NF_checkCast = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("checkCast", Object.class, Object.class)),
+                NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("allocateInstance", Object.class)),
+                NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
+                    .getDeclaredMethod("constructorMethod", Object.class))
+            };
+            for (NamedFunction nf : nfs) {
+                // Each nf must be statically invocable or we get tied up in our bootstraps.
+                assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
+                nf.resolve();
+            }
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex);
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/lang/invoke/DontInline.java	Tue Jul 24 10:47:44 2012 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import java.lang.annotation.*;
+
+/**
+ * Internal marker for some methods in the JSR 292 implementation.
+ */
+/*non-public*/
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+@interface DontInline {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/lang/invoke/ForceInline.java	Tue Jul 24 10:47:44 2012 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import java.lang.annotation.*;
+
+/**
+ * Internal marker for some methods in the JSR 292 implementation.
+ */
+/*non-public*/
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+@interface ForceInline {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Tue Jul 24 10:47:44 2012 -0700
@@ -0,0 +1,1065 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import sun.invoke.util.VerifyAccess;
+import java.lang.invoke.LambdaForm.Name;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import sun.invoke.util.Wrapper;
+
+import java.io.*;
+import java.util.*;
+
+import com.sun.xml.internal.ws.org.objectweb.asm.*;
+
+import java.lang.reflect.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
+
+/**
+ * Code generation backend for LambdaForm.
+ * <p>
+ * @author John Rose, JSR 292 EG
+ */
+class InvokerBytecodeGenerator {
+    /** Define class names for convenience. */
+    private static final String MH      = "java/lang/invoke/MethodHandle";
+    private static final String BMH     = "java/lang/invoke/BoundMethodHandle";
+    private static final String LF      = "java/lang/invoke/LambdaForm";
+    private static final String LFN     = "java/lang/invoke/LambdaForm$Name";
+    private static final String CLS     = "java/lang/Class";
+    private static final String OBJ     = "java/lang/Object";
+    private static final String OBJARY  = "[Ljava/lang/Object;";
+
+    private static final String LF_SIG  = "L" + LF + ";";
+    private static final String LFN_SIG = "L" + LFN + ";";
+    private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
+
+    /** Name of its super class*/
+    private static final String superName = LF;
+
+    /** Name of new class */
+    private final String className;
+
+    /** Name of the source file (for stack trace printing). */
+    private final String sourceFile;
+
+    private final LambdaForm lambdaForm;
+    private final String     invokerName;
+    private final MethodType invokerType;
+    private final int[] localsMap;
+
+    /** ASM bytecode generation. */
+    private ClassWriter cw;
+    private MethodVisitor mv;
+
+    private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
+    private static final Class<?> HOST_CLASS = LambdaForm.class;
+
+    private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
+                                     String className, String invokerName, MethodType invokerType) {
+        if (invokerName.contains(".")) {
+            int p = invokerName.indexOf(".");
+            className = invokerName.substring(0, p);
+            invokerName = invokerName.substring(p+1);
+        }
+        if (DUMP_CLASS_FILES) {
+            className = makeDumpableClassName(className);
+        }
+        this.className  = superName + "$" + className;
+        this.sourceFile = "LambdaForm$" + className;
+        this.lambdaForm = lambdaForm;
+        this.invokerName = invokerName;
+        this.invokerType = invokerType;
+        this.localsMap = new int[localsMapSize];
+    }
+
+    private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
+        this(null, invokerType.parameterCount(),
+             className, invokerName, invokerType);
+        // Create an array to map name indexes to locals indexes.
+        for (int i = 0; i < localsMap.length; i++) {
+            localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i);
+        }
+    }
+
+    private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
+        this(form, form.names.length,
+             className, form.debugName, invokerType);
+        // Create an array to map name indexes to locals indexes.
+        Name[] names = form.names;
+        for (int i = 0, index = 0; i < localsMap.length; i++) {
+            localsMap[i] = index;
+            index += Wrapper.forBasicType(names[i].type).stackSlots();
+        }
+    }
+
+
+    /** instance counters for dumped classes */
+    private final static HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
+    /** debugging flag for saving generated class files */
+    private final static File DUMP_CLASS_FILES_DIR;
+
+    static {
+        if (DUMP_CLASS_FILES) {
+            DUMP_CLASS_FILES_COUNTERS = new HashMap<>();
+            try {
+                File dumpDir = new File("DUMP_CLASS_FILES");
+                if (!dumpDir.exists()) {
+                    dumpDir.mkdirs();
+                }
+                DUMP_CLASS_FILES_DIR = dumpDir;
+                System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/...");
+            } catch (Exception e) {
+                throw new InternalError(e);
+            }
+        } else {
+            DUMP_CLASS_FILES_COUNTERS = null;
+            DUMP_CLASS_FILES_DIR = null;
+        }
+    }
+
+    static void maybeDump(final String className, final byte[] classFile) {
+        if (DUMP_CLASS_FILES) {
+            System.out.println("dump: " + className);
+            java.security.AccessController.doPrivileged(
+            new java.security.PrivilegedAction<Void>() {
+                public Void run() {
+                    try {
+                        String dumpName = className;
+                        //dumpName = dumpName.replace('/', '-');
+                        File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
+                        dumpFile.getParentFile().mkdirs();
+                        FileOutputStream file = new FileOutputStream(dumpFile);
+                        file.write(classFile);
+                        file.close();
+                        return null;
+                    } catch (IOException ex) {
+                        throw new InternalError(ex);
+                    }
+                }
+            });
+        }
+
+    }
+
+    private static String makeDumpableClassName(String className) {
+        Integer ctr;
+        synchronized (DUMP_CLASS_FILES_COUNTERS) {
+            ctr = DUMP_CLASS_FILES_COUNTERS.get(className);
+            if (ctr == null)  ctr = 0;
+            DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1);
+        }
+        String sfx = ctr.toString();
+        while (sfx.length() < 3)
+            sfx = "0"+sfx;
+        className += sfx;
+        return className;
+    }
+
+    class CpPatch {
+        int index;
+        Object value;
+        CpPatch(int index, Object value) {
+            this.index = index;
+            this.value = value;
+        }
+    }
+
+    Map<Object, CpPatch> cpPatches = new HashMap<>();
+
+    int cph = 0;  // for counting constant placeholders
+
+    String constantPlaceholder(Object arg) {
+        String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
+        if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>";  // debugging aid
+        if (cpPatches.containsKey(cpPlaceholder)) {
+            throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
+        }
+        // insert placeholder in CP and remember the patch
+        int index = cw.newConst((Object) cpPlaceholder);  // TODO check if aready in the constant pool
+        cpPatches.put(cpPlaceholder, new CpPatch(index, arg));
+        return cpPlaceholder;
+    }
+
+    Object[] cpPatches(byte[] classFile) {
+        int size = getConstantPoolSize(classFile);
+        Object[] res = new Object[size];
+        for (CpPatch p : cpPatches.values()) {
+                res[p.index] = p.value;
+        }
+        return res;
+    }
+
+    /**
+     * Extract the number of constant pool entries from a given class file.
+     *
+     * @param classFile the bytes of the class file in question.
+     * @return the number of entries in the constant pool.
+     */
+    private static int getConstantPoolSize(byte[] classFile) {
+        // The first few bytes:
+        // u4 magic;
+        // u2 minor_version;
+        // u2 major_version;
+        // u2 constant_pool_count;
+        return ((classFile[8] << 8) & 0xFF) | ( classFile[9] & 0xFF);
+    }
+
+    /**
+     * Extract the MemberName of a newly-defined method.
+     *
+     * @param classFile
+     * @return
+     */
+    private MemberName loadMethod(byte[] classFile) {
+        Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
+        return resolveInvokerMember(invokerClass, invokerName, invokerType);
+    }
+
+    /**
+     * Define a given class as anonymous class in the runtime system.
+     *
+     * @param classBytes
+     * @param patches
+     * @return
+     */
+    private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
+        Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
+        UNSAFE.ensureClassInitialized(invokerClass);  // Make sure the class is initialized; VM might complain.
+        return invokerClass;
+    }
+
+    /**
+     * TODO
+     *
+     * @param invokerClass
+     * @param name
+     * @param type
+     * @return
+     */
+    private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
+        MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
+        //System.out.println("resolveInvokerMember => "+member);
+        //for (Method m : invokerClass.getDeclaredMethods())  System.out.println("  "+m);
+        try {
+            member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
+        } catch (ReflectiveOperationException e) {
+            throw new InternalError(e);
+        }
+        //System.out.println("resolveInvokerMember => "+member);
+        return member;
+    }
+
+    /**
+     * Set up class file generation.
+     */
+    private void classFilePrologue() {
+        cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+        cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null);
+        cw.visitSource(sourceFile, null);
+
+        String invokerDesc = invokerType.toMethodDescriptorString();
+        mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null);
+
+        // Force inlining of this invoker method.
+        mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
+    }
+
+    /**
+     * Tear down class file generation.
+     */
+    private void classFileEpilogue() {
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+    }
+
+    /*
+     * Low-level emit helpers.
+     */
+    private void emitConst(Object con) {
+        if (con == null) {
+            mv.visitInsn(Opcodes.ACONST_NULL);
+            return;
+        }
+        if (con instanceof Integer) {
+            emitIconstInsn((int) con);
+            return;
+        }
+        if (con instanceof Long) {
+            long x = (long) con;
+            if (x == (short) x) {
+                emitIconstInsn((int) x);
+                mv.visitInsn(Opcodes.I2L);
+                return;
+            }
+        }
+        if (con instanceof Float) {
+            float x = (float) con;
+            if (x == (short) x) {
+                emitIconstInsn((int) x);
+                mv.visitInsn(Opcodes.I2F);
+                return;
+            }
+        }
+        if (con instanceof Double) {
+            double x = (double) con;
+            if (x == (short) x) {
+                emitIconstInsn((int) x);
+                mv.visitInsn(Opcodes.I2D);
+                return;
+            }
+        }
+        if (con instanceof Boolean) {
+            emitIconstInsn((boolean) con ? 1 : 0);
+            return;
+        }
+        // fall through:
+        mv.visitLdcInsn(con);
+    }
+
+    private void emitIconstInsn(int i) {
+        int opcode;
+        switch (i) {
+        case 0:  opcode = Opcodes.ICONST_0;  break;
+        case 1:  opcode = Opcodes.ICONST_1;  break;
+        case 2:  opcode = Opcodes.ICONST_2;  break;
+        case 3:  opcode = Opcodes.ICONST_3;  break;
+        case 4:  opcode = Opcodes.ICONST_4;  break;
+        case 5:  opcode = Opcodes.ICONST_5;  break;
+        default:
+            if (i == (byte) i) {
+                mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF);
+            } else if (i == (short) i) {
+                mv.visitIntInsn(Opcodes.SIPUSH, (char) i);
+            } else {
+                mv.visitLdcInsn(i);
+            }
+            return;
+        }
+        mv.visitInsn(opcode);
+    }
+
+    /*
+     * NOTE: These load/store methods use the localsMap to find the correct index!
+     */
+    private void emitLoadInsn(char type, int index) {
+        int opcode;
+        switch (type) {
+        case 'I':  opcode = Opcodes.ILOAD;  break;
+        case 'J':  opcode = Opcodes.LLOAD;  break;
+        case 'F':  opcode = Opcodes.FLOAD;  break;
+        case 'D':  opcode = Opcodes.DLOAD;  break;
+        case 'L':  opcode = Opcodes.ALOAD;  break;
+        default:
+            throw new InternalError("unknown type: " + type);
+        }
+        mv.visitVarInsn(opcode, localsMap[index]);
+    }
+    private void emitAloadInsn(int index) {
+        emitLoadInsn('L', index);
+    }
+
+    private void emitStoreInsn(char type, int index) {
+        int opcode;
+        switch (type) {
+        case 'I':  opcode = Opcodes.ISTORE;  break;
+        case 'J':  opcode = Opcodes.LSTORE;  break;
+        case 'F':  opcode = Opcodes.FSTORE;  break;
+        case 'D':  opcode = Opcodes.DSTORE;  break;
+        case 'L':  opcode = Opcodes.ASTORE;  break;
+        default:
+            throw new InternalError("unknown type: " + type);
+        }
+        mv.visitVarInsn(opcode, localsMap[index]);
+    }
+    private void emitAstoreInsn(int index) {
+        emitStoreInsn('L', index);
+    }
+
+    /**
+     * Emit a boxing call.
+     *
+     * @param type primitive type class to box.
+     */
+    private void emitBoxing(Class<?> type) {
+        Wrapper wrapper = Wrapper.forPrimitiveType(type);
+        String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+        String name  = "valueOf";
+        String desc  = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc);
+    }
+
+    /**
+     * Emit an unboxing call (plus preceding checkcast).
+     *
+     * @param type wrapper type class to unbox.
+     */
+    private void emitUnboxing(Class<?> type) {
+        Wrapper wrapper = Wrapper.forWrapperType(type);
+        String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+        String name  = wrapper.primitiveSimpleName() + "Value";
+        String desc  = "()" + wrapper.basicTypeChar();
+        mv.visitTypeInsn(Opcodes.CHECKCAST, owner);
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc);
+    }
+
+    /**
+     * Emit an implicit conversion.
+     *
+     * @param ptype type of value present on stack
+     * @param pclass type of value required on stack
+     */
+    private void emitImplicitConversion(char ptype, Class<?> pclass) {
+        switch (ptype) {
+        case 'L':
+            if (VerifyType.isNullConversion(Object.class, pclass))
+                return;
+            if (isStaticallyNameable(pclass)) {
+                mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
+            } else {
+                mv.visitLdcInsn(constantPlaceholder(pclass));
+                mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
+                mv.visitInsn(Opcodes.SWAP);
+                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
+                if (pclass.isArray())
+                    mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
+            }
+            return;
+        case 'I':
+            if (!VerifyType.isNullConversion(int.class, pclass))
+                emitPrimCast(ptype, Wrapper.basicTypeChar(pclass));
+            return;
+        case 'J':
+            assert(pclass == long.class);
+            return;
+        case 'F':
+            assert(pclass == float.class);
+            return;
+        case 'D':
+            assert(pclass == double.class);
+            return;
+        }
+        throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass);
+    }
+
+    /**
+     * Emits an actual return instruction conforming to the given return type.
+     */
+    private void emitReturnInsn(Class<?> type) {
+        int opcode;
+        switch (Wrapper.basicTypeChar(type)) {
+        case 'I':  opcode = Opcodes.IRETURN;  break;
+        case 'J':  opcode = Opcodes.LRETURN;  break;
+        case 'F':  opcode = Opcodes.FRETURN;  break;
+        case 'D':  opcode = Opcodes.DRETURN;  break;
+        case 'L':  opcode = Opcodes.ARETURN;  break;
+        case 'V':  opcode = Opcodes.RETURN;   break;
+        default:
+            throw new InternalError("unknown return type: " + type);
+        }
+        mv.visitInsn(opcode);
+    }
+
+    private static String getInternalName(Class<?> c) {
+        assert(VerifyAccess.isTypeVisible(c, Object.class));
+        return c.getName().replace('.', '/');
+    }
+
+    /**
+     * Generate customized bytecode for a given LambdaForm.
+     *
+     * @param form
+     * @param invokerType
+     * @return
+     */
+    static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
+        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
+        return g.loadMethod(g.generateCustomizedCodeBytes());
+    }
+
+    /**
+     * Generate an invoker method for the passed {@link LambdaForm}.
+     */
+    private byte[] generateCustomizedCodeBytes() {
+        classFilePrologue();
+
+        // Suppress this method in backtraces displayed to the user.
+        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+        // Mark this method as a compiled LambdaForm
+        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
+
+        // iterate over the form's names, generating bytecode instructions for each
+        // start iterating at the first name following the arguments
+        for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
+            Name name = lambdaForm.names[i];
+            MemberName member = name.function.member();
+
+            if (isSelectAlternative(member)) {
+                // selectAlternative idiom
+                // FIXME: make sure this idiom is really present!
+                emitSelectAlternative(name, lambdaForm.names[i + 1]);
+                i++;  // skip MH.invokeBasic of the selectAlternative result
+            } else if (isStaticallyInvocable(member)) {
+                emitStaticInvoke(member, name);
+            } else {
+                emitInvoke(name);
+            }
+
+            // store the result from evaluating to the target name in a local if required
+            // (if this is the last value, i.e., the one that is going to be returned,
+            // avoid store/load/return and just return)
+            if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
+                // return value - do nothing
+            } else if (name.type != 'V') {
+                // non-void: actually assign
+                emitStoreInsn(name.type, name.index());
+            }
+        }
+
+        // return statement
+        emitReturn();
+
+        classFileEpilogue();
+        bogusMethod(lambdaForm);
+
+        final byte[] classFile = cw.toByteArray();
+        maybeDump(className, classFile);
+        return classFile;
+    }
+
+    /**
+     * Emit an invoke for the given name.
+     *
+     * @param name
+     */
+    void emitInvoke(Name name) {
+        if (true) {
+            // push receiver
+            MethodHandle target = name.function.resolvedHandle;
+            assert(target != null) : name.exprString();
+            mv.visitLdcInsn(constantPlaceholder(target));
+            mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+        } else {
+            // load receiver
+            emitAloadInsn(0);
+            mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+            mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
+            mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
+            // TODO more to come
+        }
+
+        // push arguments
+        for (int i = 0; i < name.arguments.length; i++) {
+            emitPushArgument(name, i);
+        }
+
+        // invocation
+        MethodType type = name.function.methodType();
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString());
+    }
+
+    static private Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
+        // Sample classes from each package we are willing to bind to statically:
+        java.lang.Object.class,
+        java.util.Arrays.class,
+        sun.misc.Unsafe.class
+        //MethodHandle.class already covered
+    };
+
+    static boolean isStaticallyInvocable(MemberName member) {
+        if (member == null)  return false;
+        if (member.isConstructor())  return false;
+        Class<?> cls = member.getDeclaringClass();
+        if (cls.isArray() || cls.isPrimitive())
+            return false;  // FIXME
+        if (cls.isAnonymousClass() || cls.isLocalClass())
+            return false;  // inner class of some sort
+        if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
+            return false;  // not on BCP
+        if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
+            return true;   // in java.lang.invoke package
+        if (member.isPublic() && isStaticallyNameable(cls))
+            return true;
+        return false;
+    }
+
+    static boolean isStaticallyNameable(Class<?> cls) {
+        while (cls.isArray())
+            cls = cls.getComponentType();
+        if (cls.isPrimitive())
+            return true;  // int[].class, for example
+        if (cls.getClassLoader() != Object.class.getClassLoader())
+            return false;
+        if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
+            return true;
+        if (!Modifier.isPublic(cls.getModifiers()))
+            return false;
+        for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
+            if (VerifyAccess.isSamePackage(pkgcls, cls))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Emit an invoke for the given name, using the MemberName directly.
+     *
+     * @param name
+     */
+    void emitStaticInvoke(MemberName member, Name name) {
+        assert(member.equals(name.function.member()));
+        String cname = getInternalName(member.getDeclaringClass());
+        String mname = member.getName();
+        String mtype;
+        byte refKind = member.getReferenceKind();
+        if (refKind == REF_invokeSpecial) {
+            // in order to pass the verifier, we need to convert this to invokevirtual in all cases
+            assert(member.canBeStaticallyBound()) : member;
+            refKind = REF_invokeVirtual;
+        }
+
+        // push arguments
+        for (int i = 0; i < name.arguments.length; i++) {
+            emitPushArgument(name, i);
+        }
+
+        // invocation
+        if (member.isMethod()) {
+            mtype = member.getMethodType().toMethodDescriptorString();
+            mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype);
+        } else {
+            mtype = MethodType.toFieldDescriptorString(member.getFieldType());
+            mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
+        }
+    }
+    int refKindOpcode(byte refKind) {
+        switch (refKind) {
+        case REF_invokeVirtual:      return Opcodes.INVOKEVIRTUAL;
+        case REF_invokeStatic:       return Opcodes.INVOKESTATIC;
+        case REF_invokeSpecial:      return Opcodes.INVOKESPECIAL;
+        case REF_invokeInterface:    return Opcodes.INVOKEINTERFACE;
+        case REF_getField:           return Opcodes.GETFIELD;
+        case REF_putField:           return Opcodes.PUTFIELD;
+        case REF_getStatic:          return Opcodes.GETSTATIC;
+        case REF_putStatic:          return Opcodes.PUTSTATIC;
+        }
+        throw new InternalError("refKind="+refKind);
+    }
+
+    /**
+     * Check if MemberName is a call to MethodHandleImpl.selectAlternative.
+     *
+     * @param member
+     * @return true if member is a call to MethodHandleImpl.selectAlternative
+     */
+    private boolean isSelectAlternative(MemberName member) {
+        return member != null &&
+               member.getDeclaringClass() == MethodHandleImpl.class &&
+               member.getName().equals("selectAlternative");
+    }
+
+    /**
+     * Emit bytecode for the selectAlternative idiom.
+     *
+     * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
+     *
+     *   Lambda(a0:L,a1:I)=>{
+     *     t2:I=foo.test(a1:I);
+     *     t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
+     *     t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
+     *
+     * @param selectAlternativeName
+     * @param invokeBasicName
+     */
+    private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
+        MethodType type = selectAlternativeName.function.methodType();
+
+        Name receiver = (Name) invokeBasicName.arguments[0];
+
+        Label L_fallback = new Label();
+        Label L_done     = new Label();
+
+        // load test result
+        emitPushArgument(selectAlternativeName, 0);
+        mv.visitInsn(Opcodes.ICONST_1);
+
+        // if_icmpne L_fallback
+        mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
+
+        // invoke selectAlternativeName.arguments[1]
+        MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
+        emitPushArgument(selectAlternativeName, 1);  // get 2nd argument of selectAlternative
+        emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
+        emitInvoke(invokeBasicName);
+
+        // goto L_done
+        mv.visitJumpInsn(Opcodes.GOTO, L_done);
+
+        // L_fallback:
+        mv.visitLabel(L_fallback);
+
+        // invoke selectAlternativeName.arguments[2]
+        MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
+        emitPushArgument(selectAlternativeName, 2);  // get 3rd argument of selectAlternative
+        emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
+        emitInvoke(invokeBasicName);
+
+        // L_done:
+        mv.visitLabel(L_done);
+    }
+
+    /**
+     *
+     * @param name
+     * @param paramIndex
+     */
+    private void emitPushArgument(Name name, int paramIndex) {
+        Object arg = name.arguments[paramIndex];
+        char ptype = name.function.parameterType(paramIndex);
+        MethodType mtype = name.function.methodType();
+        if (arg instanceof Name) {
+            Name n = (Name) arg;
+            emitLoadInsn(n.type, n.index());
+            emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
+        } else if ((arg == null || arg instanceof String) && ptype == 'L') {
+            emitConst(arg);
+        } else {
+            if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
+                emitConst(arg);
+            } else {
+                mv.visitLdcInsn(constantPlaceholder(arg));
+                emitImplicitConversion('L', mtype.parameterType(paramIndex));
+            }
+        }
+    }
+
+    /**
+     * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
+     */
+    private void emitReturn() {
+        // return statement
+        if (lambdaForm.result == -1) {
+            // void
+            mv.visitInsn(Opcodes.RETURN);
+        } else {
+            LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
+            char rtype = Wrapper.basicTypeChar(invokerType.returnType());
+
+            // put return value on the stack if it is not already there
+            if (lambdaForm.result != lambdaForm.names.length - 1) {
+                emitLoadInsn(rn.type, lambdaForm.result);
+            }
+
+            // potentially generate cast
+            // rtype is the return type of the invoker - generated code must conform to this
+            // rn.type is the type of the result Name in the LF
+            if (rtype != rn.type) {
+                // need cast
+                if (rtype == 'L') {
+                    // possibly cast the primitive to the correct type for boxing
+                    char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar();
+                    if (boxedType != rn.type) {
+                        emitPrimCast(rn.type, boxedType);
+                    }
+                    // cast primitive to reference ("boxing")
+                    emitBoxing(invokerType.returnType());
+                } else {
+                    // to-primitive cast
+                    if (rn.type != 'L') {
+                        // prim-to-prim cast
+                        emitPrimCast(rn.type, rtype);
+                    } else {
+                        // ref-to-prim cast ("unboxing")
+                        throw new InternalError("no ref-to-prim (unboxing) casts supported right now");
+                    }
+                }
+            }
+
+            // generate actual return statement
+            emitReturnInsn(invokerType.returnType());
+        }
+    }
+
+    /**
+     * Emit a type conversion bytecode casting from "from" to "to".
+     */
+    private void emitPrimCast(char from, char to) {
+        // Here's how.
+        // -   indicates forbidden
+        // <-> indicates implicit
+        //      to ----> boolean  byte     short    char     int      long     float    double
+        // from boolean    <->        -        -        -        -        -        -        -
+        //      byte        -       <->       i2s      i2c      <->      i2l      i2f      i2d
+        //      short       -       i2b       <->      i2c      <->      i2l      i2f      i2d
+        //      char        -       i2b       i2s      <->      <->      i2l      i2f      i2d
+        //      int         -       i2b       i2s      i2c      <->      i2l      i2f      i2d
+        //      long        -     l2i,i2b   l2i,i2s  l2i,i2c    l2i      <->      l2f      l2d
+        //      float       -     f2i,i2b   f2i,i2s  f2i,i2c    f2i      f2l      <->      f2d
+        //      double      -     d2i,i2b   d2i,i2s  d2i,i2c    d2i      d2l      d2f      <->
+        if (from == to) {
+            // no cast required, should be dead code anyway
+            return;
+        }
+        Wrapper wfrom = Wrapper.forBasicType(from);
+        Wrapper wto   = Wrapper.forBasicType(to);
+        if (wfrom.isSubwordOrInt()) {
+            // cast from {byte,short,char,int} to anything
+            emitI2X(to);
+        } else {
+            // cast from {long,float,double} to anything
+            if (wto.isSubwordOrInt()) {
+                // cast to {byte,short,char,int}
+                emitX2I(from);
+                if (wto.bitWidth() < 32) {
+                    // targets other than int require another conversion
+                    emitI2X(to);
+                }
+            } else {
+                // cast to {long,float,double} - this is verbose
+                boolean error = false;
+                switch (from) {
+                case 'J':
+                         if (to == 'F') { mv.visitInsn(Opcodes.L2F); }
+                    else if (to == 'D') { mv.visitInsn(Opcodes.L2D); }
+                    else error = true;
+                    break;
+                case 'F':
+                         if (to == 'J') { mv.visitInsn(Opcodes.F2L); }
+                    else if (to == 'D') { mv.visitInsn(Opcodes.F2D); }
+                    else error = true;
+                    break;
+                case 'D':
+                         if (to == 'J') { mv.visitInsn(Opcodes.D2L); }
+                    else if (to == 'F') { mv.visitInsn(Opcodes.D2F); }
+                    else error = true;
+                    break;
+                default:
+                    error = true;
+                    break;
+                }
+                if (error) {
+                    throw new IllegalStateException("unhandled prim cast: " + from + "2" + to);
+                }
+            }
+        }
+    }
+
+    private void emitI2X(char type) {
+        switch (type) {
+        case 'B':  mv.visitInsn(Opcodes.I2B);  break;
+        case 'S':  mv.visitInsn(Opcodes.I2S);  break;
+        case 'C':  mv.visitInsn(Opcodes.I2C);  break;
+        case 'I':  /* naught */                break;
+        case 'J':  mv.visitInsn(Opcodes.I2L);  break;
+        case 'F':  mv.visitInsn(Opcodes.I2F);  break;
+        case 'D':  mv.visitInsn(Opcodes.I2D);  break;
+        case 'Z':
+            // For compatibility with ValueConversions and explicitCastArguments:
+            mv.visitInsn(Opcodes.ICONST_1);
+            mv.visitInsn(Opcodes.IAND);
+            break;
+        default:   throw new InternalError("unknown type: " + type);
+        }
+    }
+
+    private void emitX2I(char type) {
+        switch (type) {
+        case 'J':  mv.visitInsn(Opcodes.L2I);  break;
+        case 'F':  mv.visitInsn(Opcodes.F2I);  break;
+        case 'D':  mv.visitInsn(Opcodes.D2I);  break;
+        default:   throw new InternalError("unknown type: " + type);
+        }
+    }
+
+    private static String basicTypeCharSignature(String prefix, MethodType type) {
+        StringBuilder buf = new StringBuilder(prefix);
+        for (Class<?> ptype : type.parameterList())
+            buf.append(Wrapper.forBasicType(ptype).basicTypeChar());
+        buf.append('_').append(Wrapper.forBasicType(type.returnType()).basicTypeChar());
+        return buf.toString();
+    }
+
+    /**
+     * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments.
+     *
+     * @param sig
+     * @return
+     */
+    static MemberName generateLambdaFormInterpreterEntryPoint(String sig) {
+        assert(LambdaForm.isValidSignature(sig));
+        //System.out.println("generateExactInvoker "+sig);
+        // compute method type
+        // first parameter and return type
+        char tret = LambdaForm.signatureReturn(sig);
+        MethodType type = MethodType.methodType(LambdaForm.typeClass(tret), MethodHandle.class);
+        // other parameter types
+        int arity = LambdaForm.signatureArity(sig);
+        for (int i = 1; i < arity; i++) {
+            type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i)));
+        }
+        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type);
+        return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
+    }
+
+    private byte[] generateLambdaFormInterpreterEntryPointBytes() {
+        classFilePrologue();
+
+        // Suppress this method in backtraces displayed to the user.
+        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+        // create parameter array
+        emitIconstInsn(invokerType.parameterCount());
+        mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
+
+        // fill parameter array
+        for (int i = 0; i < invokerType.parameterCount(); i++) {
+            Class<?> ptype = invokerType.parameterType(i);
+            mv.visitInsn(Opcodes.DUP);
+            emitIconstInsn(i);
+            emitLoadInsn(Wrapper.basicTypeChar(ptype), i);
+            // box if primitive type
+            if (ptype.isPrimitive()) {
+                emitBoxing(ptype);
+            }
+            mv.visitInsn(Opcodes.AASTORE);
+        }
+        // invoke
+        emitAloadInsn(0);
+        mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
+        mv.visitInsn(Opcodes.SWAP);  // swap form and array; avoid local variable
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;");
+
+        // maybe unbox
+        Class<?> rtype = invokerType.returnType();
+        if (rtype.isPrimitive() && rtype != void.class) {
+            emitUnboxing(Wrapper.asWrapperType(rtype));
+        }
+
+        // return statement
+        emitReturnInsn(rtype);
+
+        classFileEpilogue();
+        bogusMethod(invokerType);
+
+        final byte[] classFile = cw.toByteArray();
+        maybeDump(className, classFile);
+        return classFile;
+    }
+
+    /**
+     * Generate bytecode for a NamedFunction invoker.
+     *
+     * @param srcType
+     * @param dstType
+     * @return
+     */
+    static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
+        MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE;
+        String invokerName = basicTypeCharSignature("invoke_", typeForm.erasedType());
+        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
+        return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
+    }
+
+    static int nfi = 0;
+
+    private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
+        MethodType dstType = typeForm.erasedType();
+        classFilePrologue();
+
+        // Suppress this method in backtraces displayed to the user.
+        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+        // Load receiver
+        emitAloadInsn(0);
+
+        // Load arguments from array
+        for (int i = 0; i < dstType.parameterCount(); i++) {
+            emitAloadInsn(1);
+            emitIconstInsn(i);
+            mv.visitInsn(Opcodes.AALOAD);
+
+            // Maybe unbox
+            Class<?> dptype = dstType.parameterType(i);
+            if (dptype.isPrimitive()) {
+                Class<?> sptype = dstType.basicType().wrap().parameterType(i);
+                Wrapper dstWrapper = Wrapper.forBasicType(dptype);
+                Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper;  // narrow subword from int
+                emitUnboxing(srcWrapper.wrapperType());
+                emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
+            }
+        }
+
+        // Invoke
+        String targetDesc = dstType.basicType().toMethodDescriptorString();
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc);
+
+        // Box primitive types
+        Class<?> rtype = dstType.returnType();
+        if (rtype != void.class && rtype.isPrimitive()) {
+            Wrapper srcWrapper = Wrapper.forBasicType(rtype);
+            Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper;  // widen subword to int
+            // boolean casts not allowed
+            emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
+            emitBoxing(dstWrapper.primitiveType());
+        }
+
+        // If the return type is void we return a null reference.
+        if (rtype == void.class) {
+            mv.visitInsn(Opcodes.ACONST_NULL);
+        }
+        emitReturnInsn(Object.class);  // NOTE: NamedFunction invokers always return a reference value.
+
+        classFileEpilogue();
+        bogusMethod(dstType);
+
+        final byte[] classFile = cw.toByteArray();
+        maybeDump(className, classFile);
+        return classFile;
+    }
+
+    /**
+     * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
+     * for debugging purposes.
+     */
+    private void bogusMethod(Object... os) {
+        if (DUMP_CLASS_FILES) {
+            mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
+            for (Object o : os) {
+                mv.visitLdcInsn(o.toString());
+                mv.visitInsn(Opcodes.POP);
+            }
+            mv.visitInsn(Opcodes.RETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+        }
+    }
+}
--- a/src/share/classes/java/lang/invoke/Invokers.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/Invokers.java	Tue Jul 24 10:47:44 2012 -0700
@@ -25,8 +25,11 @@
 
 package java.lang.invoke;
 
+import java.util.Arrays;
 import sun.invoke.empty.Empty;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static java.lang.invoke.LambdaForm.*;
 
 /**
  * Construction and caching of often-used invokers.
@@ -36,11 +39,15 @@
     // exact type (sans leading taget MH) for the outgoing call
     private final MethodType targetType;
 
+    // FIXME: Get rid of the invokers that are not useful.
+
     // exact invoker for the outgoing call
     private /*lazy*/ MethodHandle exactInvoker;
 
     // erased (partially untyped but with primitives) invoker for the outgoing call
+    // FIXME: get rid of
     private /*lazy*/ MethodHandle erasedInvoker;
+    // FIXME: get rid of
     /*lazy*/ MethodHandle erasedInvokerWithDrops;  // for InvokeGeneric
 
     // general invoker for the outgoing call
@@ -63,14 +70,13 @@
         this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
     }
 
-    /*non-public*/ static MethodType invokerType(MethodType targetType) {
-        return targetType.insertParameterTypes(0, MethodHandle.class);
-    }
-
     /*non-public*/ MethodHandle exactInvoker() {
         MethodHandle invoker = exactInvoker;
         if (invoker != null)  return invoker;
-        invoker = lookupInvoker("invokeExact");
+        MethodType mtype = targetType;
+        LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_INVOKER);
+        invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
+        assert(checkInvoker(invoker));
         exactInvoker = invoker;
         return invoker;
     }
@@ -78,29 +84,50 @@
     /*non-public*/ MethodHandle generalInvoker() {
         MethodHandle invoker = generalInvoker;
         if (invoker != null)  return invoker;
-        invoker = lookupInvoker("invoke");
+        MethodType mtype = targetType;
+        prepareForGenericCall(mtype);
+        LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_INVOKER);
+        invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
+        assert(checkInvoker(invoker));
         generalInvoker = invoker;
         return invoker;
     }
 
-    private MethodHandle lookupInvoker(String name) {
-        MethodHandle invoker;
-        try {
-            invoker = IMPL_LOOKUP.findVirtual(MethodHandle.class, name, targetType);
-        } catch (ReflectiveOperationException ex) {
-            throw new InternalError("JVM cannot find invoker for "+targetType);
-        }
-        assert(invokerType(targetType) == invoker.type());
-        assert(!invoker.isVarargsCollector());
+    /*non-public*/ MethodHandle makeBasicInvoker() {
+        MethodHandle invoker = DirectMethodHandle.make(invokeBasicMethod(targetType));
+        assert(targetType == targetType.basicType());
+        // Note:  This is not cached here.  It is cached by the calling MethodTypeForm.
+        assert(checkInvoker(invoker));
         return invoker;
     }
 
+    static MemberName invokeBasicMethod(MethodType type) {
+        String name = "invokeBasic";
+        try {
+            //Lookup.findVirtual(MethodHandle.class, name, type);
+            return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, name, type);
+
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError("JVM cannot find invoker for "+type);
+        }
+    }
+
+    private boolean checkInvoker(MethodHandle invoker) {
+        assert(targetType.invokerType().equals(invoker.type()))
+                : java.util.Arrays.asList(targetType, targetType.invokerType(), invoker);
+        assert(invoker.internalMemberName() == null ||
+               invoker.internalMemberName().getMethodType().equals(targetType));
+        assert(!invoker.isVarargsCollector());
+        return true;
+    }
+
+    // FIXME: get rid of
     /*non-public*/ MethodHandle erasedInvoker() {
         MethodHandle xinvoker = exactInvoker();
         MethodHandle invoker = erasedInvoker;
         if (invoker != null)  return invoker;
         MethodType erasedType = targetType.erase();
-        invoker = xinvoker.asType(invokerType(erasedType));
+        invoker = xinvoker.asType(erasedType.invokerType());
         erasedInvoker = invoker;
         return invoker;
     }
@@ -118,7 +145,7 @@
     /*non-public*/ MethodHandle varargsInvoker() {
         MethodHandle vaInvoker = varargsInvoker;
         if (vaInvoker != null)  return vaInvoker;
-        vaInvoker = spreadInvoker(0).asType(invokerType(MethodType.genericMethodType(0, true)));
+        vaInvoker = spreadInvoker(0).asType(MethodType.genericMethodType(0, true).invokerType());
         varargsInvoker = vaInvoker;
         return vaInvoker;
     }
@@ -137,16 +164,18 @@
             uninitializedCallSite = invoker;
             return invoker;
         }
-        if (THROW_UCS == null) {
+        invoker = THROW_UCS;
+        if (invoker == null) {
             try {
-                THROW_UCS = IMPL_LOOKUP
+                THROW_UCS = invoker = IMPL_LOOKUP
                     .findStatic(CallSite.class, "uninitializedCallSite",
                                 MethodType.methodType(Empty.class));
             } catch (ReflectiveOperationException ex) {
                 throw new RuntimeException(ex);
             }
         }
-        invoker = AdapterMethodHandle.makeRetypeRaw(targetType, THROW_UCS);
+        invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType()));
+        invoker = invoker.dropArguments(targetType, 0, targetType.parameterCount());
         assert(invoker.type().equals(targetType));
         uninitializedCallSite = invoker;
         return invoker;
@@ -155,4 +184,208 @@
     public String toString() {
         return "Invokers"+targetType;
     }
+
+    private static MethodType fixMethodType(Class<?> callerClass, Object type) {
+        if (type instanceof MethodType)
+            return (MethodType) type;
+        else
+            return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader());
+    }
+
+    static MemberName exactInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
+        MethodType mtype = fixMethodType(callerClass, type);
+        LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_LINKER);
+        appendixResult[0] = mtype;
+        return lform.vmentry;
+    }
+
+    static MemberName genericInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
+        MethodType mtype = fixMethodType(callerClass, type);
+        LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_LINKER);
+        prepareForGenericCall(mtype);
+        appendixResult[0] = mtype;
+        return lform.vmentry;
+    }
+
+    private static LambdaForm invokeForm(MethodType mtype, int which) {
+        mtype = mtype.basicType();  // normalize Z to I, String to Object, etc.
+        boolean isLinker, isGeneric;
+        String debugName;
+        switch (which) {
+        case MethodTypeForm.LF_EX_LINKER:   isLinker = true;  isGeneric = false; debugName = "invokeExact_MT"; break;
+        case MethodTypeForm.LF_EX_INVOKER:  isLinker = false; isGeneric = false; debugName = "exactInvoker"; break;
+        case MethodTypeForm.LF_GEN_LINKER:  isLinker = true;  isGeneric = true;  debugName = "invoke_MT"; break;
+        case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true;  debugName = "invoker"; break;
+        default: throw new InternalError();
+        }
+        LambdaForm lform = mtype.form().cachedLambdaForm(which);
+        if (lform != null)  return lform;
+        // exactInvokerForm (Object,Object)Object
+        //   link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
+        final int THIS_MH      = 0;
+        final int CALL_MH      = THIS_MH + (isLinker ? 0 : 1);
+        final int ARG_BASE     = CALL_MH + 1;
+        final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
+        final int INARG_LIMIT  = OUTARG_LIMIT + (isLinker ? 1 : 0);
+        int nameCursor = OUTARG_LIMIT;
+        final int MTYPE_ARG    = nameCursor++;  // might be last in-argument
+        final int CHECK_TYPE   = nameCursor++;
+        final int LINKER_CALL  = nameCursor++;
+        MethodType invokerFormType = mtype.invokerType();
+        if (isLinker) {
+            invokerFormType = invokerFormType.appendParameterTypes(MemberName.class);
+        } else {
+            invokerFormType = invokerFormType.invokerType();
+        }
+        Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
+        assert(names.length == nameCursor);
+        if (MTYPE_ARG >= INARG_LIMIT) {
+            assert(names[MTYPE_ARG] == null);
+            names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0);
+            // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM)
+        }
+
+        // Make the final call.  If isGeneric, then prepend the result of type checking.
+        MethodType outCallType;
+        Object[] outArgs;
+        if (!isGeneric) {
+            names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], names[MTYPE_ARG]);
+            // mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*)
+            outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class);
+            outCallType = mtype;
+        } else {
+            names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], names[MTYPE_ARG]);
+            // mh.invokeGeneric(a*):R =>
+            //  let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt);
+            //    gamh.invokeBasic(mt, mh, a*)
+            final int PREPEND_GAMH = 0, PREPEND_MT = 1, PREPEND_COUNT = 2;
+            outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
+            // prepend arguments:
+            System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
+            outArgs[PREPEND_GAMH] = names[CHECK_TYPE];
+            outArgs[PREPEND_MT] = names[MTYPE_ARG];
+            outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class);
+        }
+        names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs);
+        lform = new LambdaForm(debugName, INARG_LIMIT, names);
+        if (isLinker)
+            lform.compileToBytecode();  // JVM needs a real methodOop
+        lform = mtype.form().setCachedLambdaForm(which, lform);
+        return lform;
+    }
+
+    /*non-public*/ static
+    WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) {
+        // FIXME: merge with JVM logic for throwing WMTE
+        return new WrongMethodTypeException("expected "+expected+" but found "+actual);
+    }
+
+    /** Static definition of MethodHandle.invokeExact checking code. */
+    /*non-public*/ static
+    @ForceInline
+    void checkExactType(Object mhObj, Object expectedObj) {
+        MethodHandle mh = (MethodHandle) mhObj;
+        MethodType expected = (MethodType) expectedObj;
+        MethodType actual = mh.type();
+        if (actual != expected)
+            throw newWrongMethodTypeException(expected, actual);
+    }
+
+    /** Static definition of MethodHandle.invokeGeneric checking code. */
+    /*non-public*/ static
+    @ForceInline
+    Object checkGenericType(Object mhObj, Object expectedObj) {
+        MethodHandle mh = (MethodHandle) mhObj;
+        MethodType expected = (MethodType) expectedObj;
+        //MethodType actual = mh.type();
+        MethodHandle gamh = expected.form().genericInvoker;
+        if (gamh != null)  return gamh;
+        return prepareForGenericCall(expected);
+    }
+
+    /**
+     * Returns an adapter GA for invoking a MH with type adjustments.
+     * The MethodType of the generic invocation site is prepended to MH
+     * and its arguments as follows:
+     * {@code (R)MH.invoke(A*) => GA.invokeBasic(TYPEOF<A*,R>, MH, A*)}
+     */
+    /*non-public*/ static MethodHandle prepareForGenericCall(MethodType mtype) {
+        // force any needed adapters to be preconstructed
+        MethodTypeForm form = mtype.form();
+        MethodHandle gamh = form.genericInvoker;
+        if (gamh != null)  return gamh;
+        try {
+            // Trigger adapter creation.
+            gamh = InvokeGeneric.generalInvokerOf(form.erasedType);
+            form.genericInvoker = gamh;
+            return gamh;
+        } catch (Exception ex) {
+            throw new InternalError("Exception while resolving inexact invoke", ex);
+        }
+    }
+
+    static MemberName linkToCallSiteMethod(MethodType mtype) {
+        LambdaForm lform = callSiteForm(mtype);
+        return lform.vmentry;
+    }
+
+    private static LambdaForm callSiteForm(MethodType mtype) {
+        mtype = mtype.basicType();  // normalize Z to I, String to Object, etc.
+        LambdaForm lform = mtype.form().cachedLambdaForm(MethodTypeForm.LF_CS_LINKER);
+        if (lform != null)  return lform;
+        // exactInvokerForm (Object,Object)Object
+        //   link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
+        final int ARG_BASE     = 0;
+        final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
+        final int INARG_LIMIT  = OUTARG_LIMIT + 1;
+        int nameCursor = OUTARG_LIMIT;
+        final int CSITE_ARG    = nameCursor++;  // the last in-argument
+        final int CALL_MH      = nameCursor++;  // result of getTarget
+        final int LINKER_CALL  = nameCursor++;
+        MethodType invokerFormType = mtype.appendParameterTypes(CallSite.class);
+        Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
+        assert(names.length == nameCursor);
+        assert(names[CSITE_ARG] != null);
+        names[CALL_MH] = new Name(NF_getCallSiteTarget, names[CSITE_ARG]);
+        // (site.)invokedynamic(a*):R => mh = site.getTarget(); mh.invokeBasic(a*)
+        final int PREPEND_MH = 0, PREPEND_COUNT = 1;
+        Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
+        // prepend MH argument:
+        System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
+        outArgs[PREPEND_MH] = names[CALL_MH];
+        names[LINKER_CALL] = new Name(invokeBasicMethod(mtype), outArgs);
+        lform = new LambdaForm("linkToCallSite", INARG_LIMIT, names);
+        lform.compileToBytecode();  // JVM needs a real methodOop
+        lform = mtype.form().setCachedLambdaForm(MethodTypeForm.LF_CS_LINKER, lform);
+        return lform;
+    }
+
+    /** Static definition of MethodHandle.invokeGeneric checking code. */
+    /*non-public*/ static
+    @ForceInline
+    Object getCallSiteTarget(Object site) {
+        return ((CallSite)site).getTarget();
+    }
+
+    // Local constant functions:
+    private static final NamedFunction NF_checkExactType;
+    private static final NamedFunction NF_checkGenericType;
+    private static final NamedFunction NF_getCallSiteTarget;
+    static {
+        try {
+            NF_checkExactType = new NamedFunction(Invokers.class
+                    .getDeclaredMethod("checkExactType", Object.class, Object.class));
+            NF_checkGenericType = new NamedFunction(Invokers.class
+                    .getDeclaredMethod("checkGenericType", Object.class, Object.class));
+            NF_getCallSiteTarget = new NamedFunction(Invokers.class
+                    .getDeclaredMethod("getCallSiteTarget", Object.class));
+            NF_checkExactType.resolve();
+            NF_checkGenericType.resolve();
+            NF_getCallSiteTarget.resolve();
+            // bound
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex);
+        }
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/lang/invoke/LambdaForm.java	Tue Jul 24 10:47:44 2012 -0700
@@ -0,0 +1,1620 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import java.lang.annotation.*;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import sun.invoke.util.Wrapper;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+/**
+ * The symbolic, non-executable form of a method handle's invocation semantics.
+ * It consists of a series of names.
+ * The first N (N=arity) names are parameters,
+ * while any remaining names are temporary values.
+ * Each temporary specifies the application of a function to some arguments.
+ * The functions are method handles, while the arguments are mixes of
+ * constant values and local names.
+ * The result of the lambda is defined as one of the names, often the last one.
+ * <p>
+ * Here is an approximate grammar:
+ * <pre>
+ * LambdaForm = "(" ArgName* ")=>{" TempName* Result "}"
+ * ArgName = "a" N ":" T
+ * TempName = "t" N ":" T "=" Function "(" Argument* ");"
+ * Function = ConstantValue
+ * Argument = NameRef | ConstantValue
+ * Result = NameRef | "void"
+ * NameRef = "a" N | "t" N
+ * N = (any whole number)
+ * T = "L" | "I" | "J" | "F" | "D" | "V"
+ * </pre>
+ * Names are numbered consecutively from left to right starting at zero.
+ * (The letters are merely a taste of syntax sugar.)
+ * Thus, the first temporary (if any) is always numbered N (where N=arity).
+ * Every occurrence of a name reference in an argument list must refer to
+ * a name previously defined within the same lambda.
+ * A lambda has a void result if and only if its result index is -1.
+ * If a temporary has the type "V", it cannot be the subject of a NameRef,
+ * even though possesses a number.
+ * Note that all reference types are erased to "L", which stands for {@code Object).
+ * All subword types (boolean, byte, short, char) are erased to "I" which is {@code int}.
+ * The other types stand for the usual primitive types.
+ * <p>
+ * Function invocation closely follows the static rules of the Java verifier.
+ * Arguments and return values must exactly match when their "Name" types are
+ * considered.
+ * Conversions are allowed only if they do not change the erased type.
+ * <ul>
+ * <li>L = Object: casts are used freely to convert into and out of reference types
+ * <li>I = int: subword types are forcibly narrowed when passed as arguments (see {@code explicitCastArguments})
+ * <li>J = long: no implicit conversions
+ * <li>F = float: no implicit conversions
+ * <li>D = double: no implicit conversions
+ * <li>V = void: a function result may be void if and only if its Name is of type "V"
+ * </ul>
+ * Although implicit conversions are not allowed, explicit ones can easily be
+ * encoded by using temporary expressions which call type-transformed identity functions.
+ * <p>
+ * Examples:
+ * <pre>
+ * (a0:J)=>{ a0 }
+ *     == identity(long)
+ * (a0:I)=>{ t1:V = System.out#println(a0); void }
+ *     == System.out#println(int)
+ * (a0:L)=>{ t1:V = System.out#println(a0); a0 }
+ *     == identity, with printing side-effect
+ * (a0:L, a1:L)=>{ t2:L = BoundMethodHandle#argument(a0);
+ *                 t3:L = BoundMethodHandle#target(a0);
+ *                 t4:L = MethodHandle#invoke(t3, t2, a1); t4 }
+ *     == general invoker for unary insertArgument combination
+ * (a0:L, a1:L)=>{ t2:L = FilterMethodHandle#filter(a0);
+ *                 t3:L = MethodHandle#invoke(t2, a1);
+ *                 t4:L = FilterMethodHandle#target(a0);
+ *                 t5:L = MethodHandle#invoke(t4, t3); t5 }
+ *     == general invoker for unary filterArgument combination
+ * (a0:L, a1:L)=>{ ...(same as previous example)...
+ *                 t5:L = MethodHandle#invoke(t4, t3, a1); t5 }
+ *     == general invoker for unary/unary foldArgument combination
+ * (a0:L, a1:I)=>{ t2:I = identity(long).asType((int)->long)(a1); t2 }
+ *     == invoker for identity method handle which performs i2l
+ * (a0:L, a1:L)=>{ t2:L = BoundMethodHandle#argument(a0);
+ *                 t3:L = Class#cast(t2,a1); t3 }
+ *     == invoker for identity method handle which performs cast
+ * </pre>
+ * <p>
+ * @author John Rose, JSR 292 EG
+ */
+class LambdaForm {
+    final int arity;
+    final int result;
+    final Name[] names;
+    final String debugName;
+    MemberName vmentry;   // low-level behavior, or null if not yet prepared
+    private boolean isCompiled;
+
+    // Caches for common structural transforms:
+    LambdaForm[] bindCache;
+
+    public static final int VOID_RESULT = -1, LAST_RESULT = -2;
+
+    LambdaForm(String debugName,
+               int arity, Name[] names, int result) {
+        assert(namesOK(arity, names));
+        this.arity = arity;
+        this.result = fixResult(result, names);
+        this.names = names.clone();
+        this.debugName = debugName;
+        normalize();
+    }
+
+    LambdaForm(String debugName,
+               int arity, Name[] names) {
+        this(debugName,
+             arity, names, LAST_RESULT);
+    }
+
+    LambdaForm(String debugName,
+               Name[] formals, Name[] temps, Name result) {
+        this(debugName,
+             formals.length, buildNames(formals, temps, result), LAST_RESULT);
+    }
+
+    private static Name[] buildNames(Name[] formals, Name[] temps, Name result) {
+        int arity = formals.length;
+        int length = arity + temps.length + (result == null ? 0 : 1);
+        Name[] names = Arrays.copyOf(formals, length);
+        System.arraycopy(temps, 0, names, arity, temps.length);
+        if (result != null)
+            names[length - 1] = result;
+        return names;
+    }
+
+    private LambdaForm(String sig) {
+        // Make a blank lambda form, which returns a constant zero or null.
+        // It is used as a template for managing the invocation of similar forms that are non-empty.
+        // Called only from getPreparedForm.
+        assert(isValidSignature(sig));
+        this.arity = signatureArity(sig);
+        this.result = (signatureReturn(sig) == 'V' ? -1 : arity);
+        this.names = buildEmptyNames(arity, sig);
+        this.debugName = "LF.zero";
+        assert(nameRefsAreLegal());
+        assert(isEmpty());
+        assert(sig.equals(basicTypeSignature()));
+    }
+
+    private static Name[] buildEmptyNames(int arity, String basicTypeSignature) {
+        assert(isValidSignature(basicTypeSignature));
+        int resultPos = arity + 1;  // skip '_'
+        if (arity < 0 || basicTypeSignature.length() != resultPos+1)
+            throw new IllegalArgumentException("bad arity for "+basicTypeSignature);
+        int numRes = (basicTypeSignature.charAt(resultPos) == 'V' ? 0 : 1);
+        Name[] names = arguments(numRes, basicTypeSignature.substring(0, arity));
+        for (int i = 0; i < numRes; i++) {
+            names[arity + i] = constantZero(arity + i, basicTypeSignature.charAt(resultPos + i));
+        }
+        return names;
+    }
+
+    private static int fixResult(int result, Name[] names) {
+        if (result >= 0) {
+            if (names[result].type == 'V')
+                return -1;
+        } else if (result == LAST_RESULT) {
+            return names.length - 1;
+        }
+        return result;
+    }
+
+    private static boolean namesOK(int arity, Name[] names) {
+        for (int i = 0; i < names.length; i++) {
+            Name n = names[i];
+            assert(n != null) : "n is null";
+            if (i < arity)
+                assert( n.isParam()) : n + " is not param at " + i;
+            else
+                assert(!n.isParam()) : n + " is param at " + i;
+        }
+        return true;
+    }
+
+    /** Renumber and/or replace params so that they are interned and canonically numbered. */
+    private void normalize() {
+        Name[] oldNames = null;
+        int changesStart = 0;
+        for (int i = 0; i < names.length; i++) {
+            Name n = names[i];
+            if (!n.initIndex(i)) {
+                if (oldNames == null) {
+                    oldNames = names.clone();
+                    changesStart = i;
+                }
+                names[i] = n.cloneWithIndex(i);
+            }
+        }
+        if (oldNames != null) {
+            int startFixing = arity;
+            if (startFixing <= changesStart)
+                startFixing = changesStart+1;
+            for (int i = startFixing; i < names.length; i++) {
+                Name fixed = names[i].replaceNames(oldNames, names, changesStart, i);
+                names[i] = fixed.newIndex(i);
+            }
+        }
+        assert(nameRefsAreLegal());
+        int maxInterned = Math.min(arity, INTERNED_ARGUMENT_LIMIT);
+        boolean needIntern = false;
+        for (int i = 0; i < maxInterned; i++) {
+            Name n = names[i], n2 = internArgument(n);
+            if (n != n2) {
+                names[i] = n2;
+                needIntern = true;
+            }
+        }
+        if (needIntern) {
+            for (int i = arity; i < names.length; i++) {
+                names[i].internArguments();
+            }
+            assert(nameRefsAreLegal());
+        }
+    }
+
+    /**
+     * Check that all embedded Name references are localizable to this lambda,
+     * and are properly ordered after their corresponding definitions.
+     * <p>
+     * Note that a Name can be local to multiple lambdas, as long as
+     * it possesses the same index in each use site.
+     * This allows Name references to be freely reused to construct
+     * fresh lambdas, without confusion.
+     */
+    private boolean nameRefsAreLegal() {
+        assert(arity >= 0 && arity <= names.length);
+        assert(result >= -1 && result < names.length);
+        // Do all names possess an index consistent with their local definition order?
+        for (int i = 0; i < arity; i++) {
+            Name n = names[i];
+            assert(n.index() == i) : Arrays.asList(n.index(), i);
+            assert(n.isParam());
+        }
+        // Also, do all local name references
+        for (int i = arity; i < names.length; i++) {
+            Name n = names[i];
+            assert(n.index() == i);
+            for (Object arg : n.arguments) {
+                if (arg instanceof Name) {
+                    Name n2 = (Name) arg;
+                    int i2 = n2.index;
+                    assert(0 <= i2 && i2 < names.length) : n.debugString() + ": 0 <= i2 && i2 < names.length: 0 <= " + i2 + " < " + names.length;
+                    assert(names[i2] == n2) : Arrays.asList("-1-", i, "-2-", n.debugString(), "-3-", i2, "-4-", n2.debugString(), "-5-", names[i2].debugString(), "-6-", this);
+                    assert(i2 < i);  // ref must come after def!
+                }
+            }
+        }
+        return true;
+    }
+
+    /** Invoke this form on the given arguments. */
+    // final Object invoke(Object... args) throws Throwable {
+    //     // NYI: fit this into the fast path?
+    //     return interpretWithArguments(args);
+    // }
+
+    /** Report the return type. */
+    char returnType() {
+        if (result < 0)  return 'V';
+        Name n = names[result];
+        return n.type;
+    }
+
+    /** Report the N-th argument type. */
+    char parameterType(int n) {
+        assert(n < arity);
+        return names[n].type;
+    }
+
+    /** Report the arity. */
+    int arity() {
+        return arity;
+    }
+
+    /** Return the method type corresponding to my basic type signature. */
+    MethodType methodType() {
+        return signatureType(basicTypeSignature());
+    }
+    /** Return ABC_Z, where the ABC are parameter type characters, and Z is the return type character. */
+    final String basicTypeSignature() {
+        StringBuilder buf = new StringBuilder(arity() + 3);
+        for (int i = 0, a = arity(); i < a; i++)
+            buf.append(parameterType(i));
+        return buf.append('_').append(returnType()).toString();
+    }
+    static int signatureArity(String sig) {
+        assert(isValidSignature(sig));
+        return sig.indexOf('_');
+    }
+    static char signatureReturn(String sig) {
+        return sig.charAt(signatureArity(sig)+1);
+    }
+    static boolean isValidSignature(String sig) {
+        int arity = sig.indexOf('_');
+        if (arity < 0)  return false;  // must be of the form *_*
+        int siglen = sig.length();
+        if (siglen != arity + 2)  return false;  // *_X
+        for (int i = 0; i < siglen; i++) {
+            if (i == arity)  continue;  // skip '_'
+            char c = sig.charAt(i);
+            if (c == 'V')
+                return (i == siglen - 1 && arity == siglen - 2);
+            if (ALL_TYPES.indexOf(c) < 0)  return false; // must be [LIJFD]
+        }
+        return true;  // [LIJFD]*_[LIJFDV]
+    }
+    static Class<?> typeClass(char t) {
+        switch (t) {
+        case 'I': return int.class;
+        case 'J': return long.class;
+        case 'F': return float.class;
+        case 'D': return double.class;
+        case 'L': return Object.class;
+        case 'V': return void.class;
+        default: assert false;
+        }
+        return null;
+    }
+    static MethodType signatureType(String sig) {
+        Class<?>[] ptypes = new Class<?>[signatureArity(sig)];
+        for (int i = 0; i < ptypes.length; i++)
+            ptypes[i] = typeClass(sig.charAt(i));
+        Class<?> rtype = typeClass(signatureReturn(sig));
+        return MethodType.methodType(rtype, ptypes);
+    }
+
+    /*
+     * Code generation issues:
+     *
+     * Compiled LFs should be reusable in general.
+     * The biggest issue is how to decide when to pull a name into
+     * the bytecode, versus loading a reified form from the MH data.
+     *
+     * For example, an asType wrapper may require execution of a cast
+     * after a call to a MH.  The target type of the cast can be placed
+     * as a constant in the LF itself.  This will force the cast type
+     * to be compiled into the bytecodes and native code for the MH.
+     * Or, the target type of the cast can be erased in the LF, and
+     * loaded from the MH data.  (Later on, if the MH as a whole is
+     * inlined, the data will flow into the inlined instance of the LF,
+     * as a constant, and the end result will be an optimal cast.)
+     *
+     * This erasure of cast types can be done with any use of
+     * reference types.  It can also be done with whole method
+     * handles.  Erasing a method handle might leave behind
+     * LF code that executes correctly for any MH of a given
+     * type, and load the required MH from the enclosing MH's data.
+     * Or, the erasure might even erase the expected MT.
+     *
+     * Also, for direct MHs, the MemberName of the target
+     * could be erased, and loaded from the containing direct MH.
+     * As a simple case, a LF for all int-valued non-static
+     * field getters would perform a cast on its input argument
+     * (to non-constant base type derived from the MemberName)
+     * and load an integer value from the input object
+     * (at a non-constant offset also derived from the MemberName).
+     * Such MN-erased LFs would be inlinable back to optimized
+     * code, whenever a constant enclosing DMH is available
+     * to supply a constant MN from its data.
+     *
+     * The main problem here is to keep LFs reasonably generic,
+     * while ensuring that hot spots will inline good instances.
+     * "Reasonably generic" means that we don't end up with
+     * repeated versions of bytecode or machine code that do
+     * not differ in their optimized form.  Repeated versions
+     * of machine would have the undesirable overheads of
+     * (a) redundant compilation work and (b) extra I$ pressure.
+     * To control repeated versions, we need to be ready to
+     * erase details from LFs and move them into MH data,
+     * whevener those details are not relevant to significant
+     * optimization.  "Significant" means optimization of
+     * code that is actually hot.
+     *
+     * Achieving this may require dynamic splitting of MHs, by replacing
+     * a generic LF with a more specialized one, on the same MH,
+     * if (a) the MH is frequently executed and (b) the MH cannot
+     * be inlined into a containing caller, such as an invokedynamic.
+     *
+     * Compiled LFs that are no longer used should be GC-able.
+     * If they contain non-BCP references, they should be properly
+     * interlinked with the class loader(s) that their embedded types
+     * depend on.  This probably means that reusable compiled LFs
+     * will be tabulated (indexed) on relevant class loaders,
+     * or else that the tables that cache them will have weak links.
+     */
+
+    /**
+     * Make this LF directly executable, as part of a MethodHandle.
+     * Invariant:  Every MH which is invoked must prepare its LF
+     * before invocation.
+     * (In principle, the JVM could do this very lazily,
+     * as a sort of pre-invocation linkage step.)
+     */
+    public void prepare() {
+        if (COMPILE_THRESHOLD == 0) {
+            compileToBytecode();
+        }
+        if (this.vmentry != null) {
+            // already prepared (e.g., a primitive DMH invoker form)
+            return;
+        }
+        LambdaForm prep = getPreparedForm(basicTypeSignature());
+        this.vmentry = prep.vmentry;
+        // TO DO: Maybe add invokeGeneric, invokeWithArguments
+    }
+
+    /** Generate optimizable bytecode for this form. */
+    MemberName compileToBytecode() {
+        MethodType invokerType = methodType();
+        assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
+        if (vmentry != null && isCompiled) {
+            return vmentry;  // already compiled somehow
+        }
+        try {
+            vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
+            if (TRACE_INTERPRETER)
+                traceInterpreter("compileToBytecode", this);
+            isCompiled = true;
+            return vmentry;
+        } catch (Error | Exception ex) {
+            throw new InternalError(this.toString(), ex);
+        }
+    }
+
+    private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
+    static {
+        int   capacity   = 512;    // expect many distinct signatures over time
+        float loadFactor = 0.75f;  // normal default
+        int   writers    = 1;
+        PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers);
+    }
+
+    private static Map<String,LambdaForm> computeInitialPreparedForms() {
+        // Find all predefined invokers and associate them with canonical empty lambda forms.
+        HashMap<String,LambdaForm> forms = new HashMap<>();
+        for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) {
+            if (!m.isStatic() || !m.isPackage())  continue;
+            MethodType mt = m.getMethodType();
+            if (mt.parameterCount() > 0 &&
+                mt.parameterType(0) == MethodHandle.class &&
+                m.getName().startsWith("interpret_")) {
+                String sig = basicTypeSignature(mt);
+                assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_'))));
+                LambdaForm form = new LambdaForm(sig);
+                form.vmentry = m;
+                mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form);
+                // FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only
+                forms.put(sig, form);
+            }
+        }
+        //System.out.println("computeInitialPreparedForms => "+forms);
+        return forms;
+    }
+
+    // Set this false to disable use of the interpret_L methods defined in this file.
+    private static final boolean USE_PREDEFINED_INTERPRET_METHODS = true;
+
+    // The following are predefined exact invokers.  The system must build
+    // a separate invoker for each distinct signature.
+    static Object interpret_L(MethodHandle mh) throws Throwable {
+        Object[] av = {mh};
+        String sig = null;
+        assert(argumentTypesMatch(sig = "L_L", av));
+        Object res = mh.form.interpretWithArguments(av);
+        assert(returnTypesMatch(sig, av, res));
+        return res;
+    }
+    static Object interpret_L(MethodHandle mh, Object x1) throws Throwable {
+        Object[] av = {mh, x1};
+        String sig = null;
+        assert(argumentTypesMatch(sig = "LL_L", av));
+        Object res = mh.form.interpretWithArguments(av);
+        assert(returnTypesMatch(sig, av, res));
+        return res;
+    }
+    static Object interpret_L(MethodHandle mh, Object x1, Object x2) throws Throwable {
+        Object[] av = {mh, x1, x2};
+        String sig = null;
+        assert(argumentTypesMatch(sig = "LLL_L", av));
+        Object res = mh.form.interpretWithArguments(av);
+        assert(returnTypesMatch(sig, av, res));
+        return res;
+    }
+    private static LambdaForm getPreparedForm(String sig) {
+        MethodType mtype = signatureType(sig);
+        //LambdaForm prep = PREPARED_FORMS.get(sig);
+        LambdaForm prep =  mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET);
+        if (prep != null)  return prep;
+        assert(isValidSignature(sig));
+        prep = new LambdaForm(sig);
+        prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig);
+        //LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep);
+        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep);
+    }
+
+    // The next few routines are called only from assert expressions
+    // They verify that the built-in invokers process the correct raw data types.
+    private static boolean argumentTypesMatch(String sig, Object[] av) {
+        int arity = signatureArity(sig);
+        assert(av.length == arity) : "av.length == arity: av.length=" + av.length + ", arity=" + arity;
+        assert(av[0] instanceof MethodHandle) : "av[0] not instace of MethodHandle: " + av[0];
+        MethodHandle mh = (MethodHandle) av[0];
+        MethodType mt = mh.type();
+        assert(mt.parameterCount() == arity-1);
+        for (int i = 0; i < av.length; i++) {
+            Class<?> pt = (i == 0 ? MethodHandle.class : mt.parameterType(i-1));
+            assert(valueMatches(sig.charAt(i), pt, av[i]));
+        }
+        return true;
+    }
+    private static boolean valueMatches(char tc, Class<?> type, Object x) {
+        // The following line is needed because (...)void method handles can use non-void invokers
+        if (type == void.class)  tc = 'V';   // can drop any kind of value
+        assert tc == basicType(type) : tc + " == basicType(" + type + ")=" + basicType(type);
+        switch (tc) {
+        case 'I': assert checkInt(type, x)   : "checkInt(" + type + "," + x +")";   break;
+        case 'J': assert x instanceof Long   : "instanceof Long: " + x;             break;
+        case 'F': assert x instanceof Float  : "instanceof Float: " + x;            break;
+        case 'D': assert x instanceof Double : "instanceof Double: " + x;           break;
+        case 'L': assert checkRef(type, x)   : "checkRef(" + type + "," + x + ")";  break;
+        case 'V': break;  // allow anything here; will be dropped
+        default:  assert(false);
+        }
+        return true;
+    }
+    private static boolean returnTypesMatch(String sig, Object[] av, Object res) {
+        MethodHandle mh = (MethodHandle) av[0];
+        return valueMatches(signatureReturn(sig), mh.type().returnType(), res);
+    }
+    private static boolean checkInt(Class<?> type, Object x) {
+        assert(x instanceof Integer);
+        if (type == int.class)  return true;
+        Wrapper w = Wrapper.forBasicType(type);
+        assert(w.isSubwordOrInt());
+        Object x1 = Wrapper.INT.wrap(w.wrap(x));
+        return x.equals(x1);
+    }
+    private static boolean checkRef(Class<?> type, Object x) {
+        assert(!type.isPrimitive());
+        if (x == null)  return true;
+        if (type.isInterface())  return true;
+        return type.isInstance(x);
+    }
+
+    /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
+    private static final int COMPILE_THRESHOLD;
+    static {
+        if (MethodHandleStatics.COMPILE_THRESHOLD != null)
+            COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD;
+        else
+            COMPILE_THRESHOLD = 30;  // default value
+    }
+    private int invocationCounter = 0;
+
+    @Hidden
+    /** Interpretively invoke this form on the given arguments. */
+    Object interpretWithArguments(Object... argumentValues) throws Throwable {
+        if (TRACE_INTERPRETER)
+            return interpretWithArgumentsTracing(argumentValues);
+        if (COMPILE_THRESHOLD != 0 &&
+            invocationCounter < COMPILE_THRESHOLD) {
+            invocationCounter++;  // benign race
+            if (invocationCounter >= COMPILE_THRESHOLD) {
+                // Replace vmentry with a bytecode version of this LF.
+                compileToBytecode();
+            }
+        }
+        assert(arityCheck(argumentValues));
+        Object[] values = Arrays.copyOf(argumentValues, names.length);
+        for (int i = argumentValues.length; i < values.length; i++) {
+            values[i] = interpretName(names[i], values);
+        }
+        return (result < 0) ? null : values[result];
+    }
+
+    @Hidden
+    /** Evaluate a single Name within this form, applying its function to its arguments. */
+    Object interpretName(Name name, Object[] values) throws Throwable {
+        if (TRACE_INTERPRETER)
+            traceInterpreter("| interpretName", name.debugString(), (Object[]) null);
+        Object[] arguments = Arrays.copyOf(name.arguments, name.arguments.length, Object[].class);
+        for (int i = 0; i < arguments.length; i++) {
+            Object a = arguments[i];
+            if (a instanceof Name) {
+                int i2 = ((Name)a).index();
+                assert(names[i2] == a);
+                a = values[i2];
+                arguments[i] = a;
+            }
+        }
+        return name.function.invokeWithArguments(arguments);
+    }
+
+    Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
+        traceInterpreter("[ interpretWithArguments", this, argumentValues);
+        if (invocationCounter < COMPILE_THRESHOLD) {
+            int ctr = invocationCounter++;  // benign race
+            traceInterpreter("| invocationCounter", ctr);
+            if (invocationCounter >= COMPILE_THRESHOLD) {
+                compileToBytecode();
+            }
+        }
+        Object rval;
+        try {
+            assert(arityCheck(argumentValues));
+            Object[] values = Arrays.copyOf(argumentValues, names.length);
+            for (int i = argumentValues.length; i < values.length; i++) {
+                values[i] = interpretName(names[i], values);
+            }
+            rval = (result < 0) ? null : values[result];
+        } catch (Throwable ex) {
+            traceInterpreter("] throw =>", ex);
+            throw ex;
+        }
+        traceInterpreter("] return =>", rval);
+        return rval;
+    }
+
+    //** This transform is applied (statically) to every name.function. */
+    /*
+    private static MethodHandle eraseSubwordTypes(MethodHandle mh) {
+        MethodType mt = mh.type();
+        if (mt.hasPrimitives()) {
+            mt = mt.changeReturnType(eraseSubwordType(mt.returnType()));
+            for (int i = 0; i < mt.parameterCount(); i++) {
+                mt = mt.changeParameterType(i, eraseSubwordType(mt.parameterType(i)));
+            }
+            mh = MethodHandles.explicitCastArguments(mh, mt);
+        }
+        return mh;
+    }
+    private static Class<?> eraseSubwordType(Class<?> type) {
+        if (!type.isPrimitive())  return type;
+        if (type == int.class)  return type;
+        Wrapper w = Wrapper.forPrimitiveType(type);
+        if (w.isSubwordOrInt())  return int.class;
+        return type;
+    }
+    */
+
+    static void traceInterpreter(String event, Object obj, Object... args) {
+        if (!TRACE_INTERPRETER)  return;
+        System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : ""));
+    }
+    static void traceInterpreter(String event, Object obj) {
+        traceInterpreter(event, obj, (Object[])null);
+    }
+    private boolean arityCheck(Object[] argumentValues) {
+        assert(argumentValues.length == arity) : arity+"!="+Arrays.asList(argumentValues)+".length";
+        // also check that the leading (receiver) argument is somehow bound to this LF:
+        assert(argumentValues[0] instanceof MethodHandle) : "not MH: " + argumentValues[0];
+        assert(((MethodHandle)argumentValues[0]).internalForm() == this);
+        // note:  argument #0 could also be an interface wrapper, in the future
+        return true;
+    }
+
+    private boolean isEmpty() {
+        if (result < 0)
+            return (names.length == arity);
+        else if (result == arity && names.length == arity + 1)
+            return names[arity].isConstantZero();
+        else
+            return false;
+    }
+
+    public String toString() {
+        StringBuilder buf = new StringBuilder("Lambda(");
+        for (int i = 0; i < names.length; i++) {
+            if (i == arity)  buf.append(")=>{");
+            Name n = names[i];
+            if (i >= arity)  buf.append("\n    ");
+            buf.append(n);
+            if (i < arity) {
+                if (i+1 < arity)  buf.append(",");
+                continue;
+            }
+            buf.append("=").append(n.exprString());
+            buf.append(";");
+        }
+        buf.append(result < 0 ? "void" : names[result]).append("}");
+        if (TRACE_INTERPRETER) {
+            // Extra verbosity:
+            buf.append(":").append(basicTypeSignature());
+            buf.append("/").append(vmentry);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Apply immediate binding for a Name in this form indicated by its position relative to the form.
+     * The first parameter to a LambdaForm, a0:L, always represents the form's method handle, so 0 is not
+     * accepted as valid.
+     */
+    LambdaForm bindImmediate(int pos, char basicType, Object value) {
+        // must be an argument, and the types must match
+        assert pos > 0 && pos < arity && names[pos].type == basicType && Name.typesMatch(basicType, value);
+
+        int arity2 = arity - 1;
+        Name[] names2 = new Name[names.length - 1];
+        for (int r = 0, w = 0; r < names.length; ++r, ++w) { // (r)ead from names, (w)rite to names2
+            Name n = names[r];
+            if (n.isParam()) {
+                if (n.index == pos) {
+                    // do not copy over the argument that is to be replaced with a literal,
+                    // but adjust the write index
+                    --w;
+                } else {
+                    names2[w] = new Name(w, n.type);
+                }
+            } else {
+                Object[] arguments2 = new Object[n.arguments.length];
+                for (int i = 0; i < n.arguments.length; ++i) {
+                    Object arg = n.arguments[i];
+                    if (arg instanceof Name) {
+                        int ni = ((Name) arg).index;
+                        if (ni == pos) {
+                            arguments2[i] = value;
+                        } else if (ni < pos) {
+                            // replacement position not yet passed
+                            arguments2[i] = names2[ni];
+                        } else {
+                            // replacement position passed
+                            arguments2[i] = names2[ni - 1];
+                        }
+                    } else {
+                        arguments2[i] = arg;
+                    }
+                }
+                names2[w] = new Name(n.function, arguments2);
+                names2[w].initIndex(w);
+            }
+        }
+
+        int result2 = result == -1 ? -1 : result - 1;
+        return new LambdaForm(debugName, arity2, names2, result2);
+    }
+
+    LambdaForm bind(int namePos, BoundMethodHandle.SpeciesData oldData) {
+        Name name = names[namePos];
+        BoundMethodHandle.SpeciesData newData = oldData.extendWithType(name.type);
+        return bind(name, newData.getterName(names[0], oldData.fieldCount()), oldData, newData);
+    }
+    LambdaForm bind(Name name, Name binding,
+                    BoundMethodHandle.SpeciesData oldData,
+                    BoundMethodHandle.SpeciesData newData) {
+        int pos = name.index;
+        assert(name.isParam());
+        assert(!binding.isParam());
+        assert(name.type == binding.type);
+        assert(0 <= pos && pos < arity && names[pos] == name);
+        assert(binding.function.memberDeclaringClassOrNull() == newData.clazz);
+        assert(oldData.getters.length == newData.getters.length-1);
+        if (bindCache != null) {
+            LambdaForm form = bindCache[pos];
+            if (form != null) {
+                assert(form.contains(binding)) : "form << " + form + " >> does not contain binding << " + binding + " >>";
+                return form;
+            }
+        } else {
+            bindCache = new LambdaForm[arity];
+        }
+        assert(nameRefsAreLegal());
+        int arity2 = arity-1;
+        Name[] names2 = names.clone();
+        names2[pos] = binding;  // we might move this in a moment
+
+        // The newly created LF will run with a different BMH.
+        // Switch over any pre-existing BMH field references to the new BMH class.
+        int firstOldRef = -1;
+        for (int i = 0; i < names2.length; i++) {
+            Name n = names[i];
+            if (n.function != null &&
+                n.function.memberDeclaringClassOrNull() == oldData.clazz) {
+                MethodHandle oldGetter = n.function.resolvedHandle;
+                MethodHandle newGetter = null;
+                for (int j = 0; j < oldData.getters.length; j++) {
+                    if (oldGetter == oldData.getters[j])
+                        newGetter =  newData.getters[j];
+                }
+                if (newGetter != null) {
+                    if (firstOldRef < 0)  firstOldRef = i;
+                    Name n2 = new Name(newGetter, n.arguments);
+                    names2[i] = n2;
+                }
+            }
+        }
+
+        // Walk over the new list of names once, in forward order.
+        // Replace references to 'name' with 'binding'.
+        // Replace data structure references to the old BMH species with the new.
+        // This might cause a ripple effect, but it will settle in one pass.
+        assert(firstOldRef < 0 || firstOldRef > pos);
+        for (int i = pos+1; i < names2.length; i++) {
+            if (i <= arity2)  continue;
+            names2[i] = names2[i].replaceNames(names, names2, pos, i);
+        }
+
+        //  (a0, a1, name=a2, a3, a4)  =>  (a0, a1, a3, a4, binding)
+        int insPos = pos;
+        for (; insPos+1 < names2.length; insPos++) {
+            Name n = names2[insPos+1];
+            if (n.isSiblingBindingBefore(binding)) {
+                names2[insPos] = n;
+            } else {
+                break;
+            }
+        }
+        names2[insPos] = binding;
+
+        // Since we moved some stuff, maybe update the result reference:
+        int result2 = result;
+        if (result2 == pos)
+            result2 = insPos;
+        else if (result2 > pos && result2 <= insPos)
+            result2 -= 1;
+
+        return bindCache[pos] = new LambdaForm(debugName, arity2, names2, result2);
+    }
+
+    boolean contains(Name name) {
+        int pos = name.index();
+        if (pos >= 0) {
+            return pos < names.length && name.equals(names[pos]);
+        }
+        for (int i = arity; i < names.length; i++) {
+            if (name.equals(names[i]))
+                return true;
+        }
+        return false;
+    }
+
+    LambdaForm addArguments(int pos, char... types) {
+        assert(pos <= arity);
+        int length = names.length;
+        int inTypes = types.length;
+        Name[] names2 = Arrays.copyOf(names, length + inTypes);
+        int arity2 = arity + inTypes;
+        int result2 = result;
+        if (result2 >= arity)
+            result2 += inTypes;
+        // names array has MH in slot 0; skip it.
+        int argpos = pos + 1;
+        // Note:  The LF constructor will rename names2[argpos...].
+        // Make space for new arguments (shift temporaries).
+        System.arraycopy(names, argpos, names2, argpos + inTypes, length - argpos);
+        for (int i = 0; i < inTypes; i++) {
+            names2[argpos + i] = new Name(types[i]);
+        }
+        return new LambdaForm(debugName, arity2, names2, result2);
+    }
+
+    LambdaForm addArguments(int pos, List<Class<?>> types) {
+        char[] basicTypes = new char[types.size()];
+        for (int i = 0; i < basicTypes.length; i++)
+            basicTypes[i] = basicType(types.get(i));
+        return addArguments(pos, basicTypes);
+    }
+
+    LambdaForm permuteArguments(int skip, int[] reorder, char[] types) {
+        // Note:  When inArg = reorder[outArg], outArg is fed by a copy of inArg.
+        // The types are the types of the new (incoming) arguments.
+        int length = names.length;
+        int inTypes = types.length;
+        int outArgs = reorder.length;
+        assert(skip+outArgs == arity);
+        assert(permutedTypesMatch(reorder, types, names, skip));
+        int pos = 0;
+        // skip trivial first part of reordering:
+        while (pos < outArgs && reorder[pos] == pos)  pos += 1;
+        Name[] names2 = new Name[length - outArgs + inTypes];
+        System.arraycopy(names, 0, names2, 0, skip+pos);
+        // copy the body:
+        int bodyLength = length - arity;
+        System.arraycopy(names, skip+outArgs, names2, skip+inTypes, bodyLength);
+        int arity2 = names2.length - bodyLength;
+        int result2 = result;
+        if (result2 >= 0) {
+            if (result2 < skip+outArgs) {
+                // return the corresponding inArg
+                result2 = reorder[result2-skip];
+            } else {
+                result2 = result2 - outArgs + inTypes;
+            }
+        }
+        // rework names in the body:
+        for (int j = pos; j < outArgs; j++) {
+            Name n = names[skip+j];
+            int i = reorder[j];
+            // replace names[skip+j] by names2[skip+i]
+            Name n2 = names2[skip+i];
+            if (n2 == null)
+                names2[skip+i] = n2 = new Name(types[i]);
+            else
+                assert(n2.type == types[i]);
+            for (int k = arity2; k < names2.length; k++) {
+                names2[k] = names2[k].replaceName(n, n2);
+            }
+        }
+        // some names are unused, but must be filled in
+        for (int i = skip+pos; i < arity2; i++) {
+            if (names2[i] == null)
+                names2[i] = argument(i, types[i - skip]);
+        }
+        for (int j = arity; j < names.length; j++) {
+            int i = j - arity + arity2;
+            // replace names2[i] by names[j]
+            Name n = names[j];
+            Name n2 = names2[i];
+            if (n != n2) {
+                for (int k = i+1; k < names2.length; k++) {
+                    names2[k] = names2[k].replaceName(n, n2);
+                }
+            }
+        }
+        return new LambdaForm(debugName, arity2, names2, result2);
+    }
+
+    static boolean permutedTypesMatch(int[] reorder, char[] types, Name[] names, int skip) {
+        int inTypes = types.length;
+        int outArgs = reorder.length;
+        for (int i = 0; i < outArgs; i++) {
+            assert(names[skip+i].isParam());
+            assert(names[skip+i].type == types[reorder[i]]);
+        }
+        return true;
+    }
+
+    static class NamedFunction {
+        final MemberName member;
+        MethodHandle resolvedHandle;
+        MethodHandle invoker;
+
+        NamedFunction(MethodHandle resolvedHandle) {
+            this(resolvedHandle.internalMemberName(), resolvedHandle);
+        }
+        NamedFunction(MemberName member, MethodHandle resolvedHandle) {
+            this.member = member;
+            //resolvedHandle = eraseSubwordTypes(resolvedHandle);
+            this.resolvedHandle = resolvedHandle;
+        }
+
+        // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc.
+        // Any LambdaForm containing such a member is not interpretable.
+        // This is OK, since all such LFs are prepared with special primitive vmentry points.
+        // And even without the resolvedHandle, the name can still be compiled and optimized.
+        NamedFunction(Method method) {
+            this(new MemberName(method));
+        }
+        NamedFunction(Field field) {
+            this(new MemberName(field));
+        }
+        NamedFunction(MemberName member) {
+            this.member = member;
+            this.resolvedHandle = null;
+        }
+
+        MethodHandle resolvedHandle() {
+            if (resolvedHandle == null)  resolve();
+            return resolvedHandle;
+        }
+
+        void resolve() {
+            resolvedHandle = DirectMethodHandle.make(member);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) return true;
+            if (other == null) return false;
+            if (!(other instanceof NamedFunction)) return false;
+            NamedFunction that = (NamedFunction) other;
+            return this.member != null && this.member.equals(that.member);
+        }
+
+        @Override
+        public int hashCode() {
+            if (member != null)
+                return member.hashCode();
+            return super.hashCode();
+        }
+
+        // Put the predefined NamedFunction invokers into the table.
+        static void initializeInvokers() {
+            for (MemberName m : MemberName.getFactory().getMethods(NamedFunction.class, false, null, null, null)) {
+                if (!m.isStatic() || !m.isPackage())  continue;
+                MethodType type = m.getMethodType();
+                if (type.equals(INVOKER_METHOD_TYPE) &&
+                    m.getName().startsWith("invoke_")) {
+                    String sig = m.getName().substring("invoke_".length());
+                    int arity = LambdaForm.signatureArity(sig);
+                    MethodType srcType = MethodType.genericMethodType(arity);
+                    if (LambdaForm.signatureReturn(sig) == 'V')
+                        srcType = srcType.changeReturnType(void.class);
+                    MethodTypeForm typeForm = srcType.form();
+                    typeForm.namedFunctionInvoker = DirectMethodHandle.make(m);
+                }
+            }
+        }
+
+        // The following are predefined NamedFunction invokers.  The system must build
+        // a separate invoker for each distinct signature.
+        /** void return type invokers. */
+        @Hidden
+        static Object invoke__V(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 0);
+            mh.invokeBasic();
+            return null;
+        }
+        @Hidden
+        static Object invoke_L_V(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 1);
+            mh.invokeBasic(a[0]);
+            return null;
+        }
+        @Hidden
+        static Object invoke_LL_V(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 2);
+            mh.invokeBasic(a[0], a[1]);
+            return null;
+        }
+        @Hidden
+        static Object invoke_LLL_V(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 3);
+            mh.invokeBasic(a[0], a[1], a[2]);
+            return null;
+        }
+        @Hidden
+        static Object invoke_LLLL_V(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 4);
+            mh.invokeBasic(a[0], a[1], a[2], a[3]);
+            return null;
+        }
+        @Hidden
+        static Object invoke_LLLLL_V(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 5);
+            mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
+            return null;
+        }
+        /** Object return type invokers. */
+        @Hidden
+        static Object invoke__L(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 0);
+            return mh.invokeBasic();
+        }
+        @Hidden
+        static Object invoke_L_L(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 1);
+            return mh.invokeBasic(a[0]);
+        }
+        @Hidden
+        static Object invoke_LL_L(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 2);
+            return mh.invokeBasic(a[0], a[1]);
+        }
+        @Hidden
+        static Object invoke_LLL_L(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 3);
+            return mh.invokeBasic(a[0], a[1], a[2]);
+        }
+        @Hidden
+        static Object invoke_LLLL_L(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 4);
+            return mh.invokeBasic(a[0], a[1], a[2], a[3]);
+        }
+        @Hidden
+        static Object invoke_LLLLL_L(MethodHandle mh, Object[] a) throws Throwable {
+            assert(a.length == 5);
+            return mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
+        }
+
+        static final MethodType INVOKER_METHOD_TYPE =
+            MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
+
+        private static MethodHandle computeInvoker(MethodTypeForm typeForm) {
+            MethodHandle mh = typeForm.namedFunctionInvoker;
+            if (mh != null)  return mh;
+            MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm);  // this could take a while
+            mh = DirectMethodHandle.make(invoker);
+            MethodHandle mh2 = typeForm.namedFunctionInvoker;
+            if (mh2 != null)  return mh2;  // benign race
+            if (!mh.type().equals(INVOKER_METHOD_TYPE))
+                throw new InternalError(mh.debugString());
+            return typeForm.namedFunctionInvoker = mh;
+        }
+
+        @Hidden
+        Object invokeWithArguments(Object... arguments) throws Throwable {
+            // If we have a cached invoker, call it right away.
+            // NOTE: The invoker always returns a reference value.
+            if (TRACE_INTERPRETER)  return invokeWithArgumentsTracing(arguments);
+            assert(checkArgumentTypes(arguments, methodType()));
+            return invoker().invokeBasic(resolvedHandle(), arguments);
+        }
+
+        @Hidden
+        Object invokeWithArgumentsTracing(Object[] arguments) throws Throwable {
+            Object rval;
+            try {
+                traceInterpreter("[ call", this, arguments);
+                if (invoker == null) {
+                    traceInterpreter("| getInvoker", this);
+                    invoker();
+                }
+                if (resolvedHandle == null) {
+                    traceInterpreter("| resolve", this);
+                    resolvedHandle();
+                }
+                assert(checkArgumentTypes(arguments, methodType()));
+                rval = invoker().invokeBasic(resolvedHandle(), arguments);
+            } catch (Throwable ex) {
+                traceInterpreter("] throw =>", ex);
+                throw ex;
+            }
+            traceInterpreter("] return =>", rval);
+            return rval;
+        }
+
+        private MethodHandle invoker() {
+            if (invoker != null)  return invoker;
+            // Get an invoker and cache it.
+            return invoker = computeInvoker(methodType().form());
+        }
+
+        private static boolean checkArgumentTypes(Object[] arguments, MethodType methodType) {
+            if (true)  return true;  // FIXME
+            MethodType dstType = methodType.form().erasedType();
+            MethodType srcType = dstType.basicType().wrap();
+            Class<?>[] ptypes = new Class<?>[arguments.length];
+            for (int i = 0; i < arguments.length; i++) {
+                Object arg = arguments[i];
+                Class<?> ptype = arg == null ? Object.class : arg.getClass();
+                // If the dest. type is a primitive we keep the
+                // argument type.
+                ptypes[i] = dstType.parameterType(i).isPrimitive() ? ptype : Object.class;
+            }
+            MethodType argType = MethodType.methodType(srcType.returnType(), ptypes).wrap();
+            assert(argType.isConvertibleTo(srcType)) : "wrong argument types: cannot convert " + argType + " to " + srcType;
+            return true;
+        }
+
+        String basicTypeSignature() {
+            //return LambdaForm.basicTypeSignature(resolvedHandle.type());
+            return LambdaForm.basicTypeSignature(methodType());
+        }
+
+        MethodType methodType() {
+            if (resolvedHandle != null)
+                return resolvedHandle.type();
+            else
+                // only for certain internal LFs during bootstrapping
+                return member.getInvocationType();
+        }
+
+        MemberName member() {
+            assert(assertMemberIsConsistent());
+            return member;
+        }
+
+        // Called only from assert.
+        private boolean assertMemberIsConsistent() {
+            if (resolvedHandle instanceof DirectMethodHandle) {
+                MemberName m = resolvedHandle.internalMemberName();
+                assert(m.equals(member));
+            }
+            return true;
+        }
+
+        Class<?> memberDeclaringClassOrNull() {
+            return (member == null) ? null : member.getDeclaringClass();
+        }
+
+        char returnType() {
+            return basicType(methodType().returnType());
+        }
+
+        char parameterType(int n) {
+            return basicType(methodType().parameterType(n));
+        }
+
+        int arity() {
+            //int siglen = member.getMethodType().parameterCount();
+            //if (!member.isStatic())  siglen += 1;
+            //return siglen;
+            return methodType().parameterCount();
+        }
+
+        public String toString() {
+            if (member == null)  return resolvedHandle.toString();
+            return member.getDeclaringClass().getSimpleName()+"."+member.getName();
+        }
+    }
+
+    void resolve() {
+        for (Name n : names) n.resolve();
+    }
+
+    public static char basicType(Class<?> type) {
+        char c = Wrapper.basicTypeChar(type);
+        if ("ZBSC".indexOf(c) >= 0)  c = 'I';
+        assert("LIJFDV".indexOf(c) >= 0);
+        return c;
+    }
+    public static char[] basicTypes(List<Class<?>> types) {
+        char[] btypes = new char[types.size()];
+        for (int i = 0; i < btypes.length; i++) {
+            btypes[i] = basicType(types.get(i));
+        }
+        return btypes;
+    }
+    public static String basicTypeSignature(MethodType type) {
+        char[] sig = new char[type.parameterCount() + 2];
+        int sigp = 0;
+        for (Class<?> pt : type.parameterList()) {
+            sig[sigp++] = basicType(pt);
+        }
+        sig[sigp++] = '_';
+        sig[sigp++] = basicType(type.returnType());
+        assert(sigp == sig.length);
+        return String.valueOf(sig);
+    }
+
+    static final class Name {
+        final char type;
+        private short index;
+        final NamedFunction function;
+        final Object[] arguments;
+
+        private Name(int index, char type, NamedFunction function, Object[] arguments) {
+            this.index = (short)index;
+            this.type = type;
+            this.function = function;
+            this.arguments = arguments;
+            assert(this.index == index);
+        }
+        Name(MethodHandle function, Object... arguments) {
+            this(new NamedFunction(function), arguments);
+        }
+        Name(MemberName function, Object... arguments) {
+            this(new NamedFunction(function), arguments);
+        }
+        Name(NamedFunction function, Object... arguments) {
+            this(-1, function.returnType(), function, arguments = arguments.clone());
+            assert(arguments.length == function.arity()) : "arity mismatch: arguments.length=" + arguments.length + " == function.arity()=" + function.arity() + " in " + debugString();
+            for (int i = 0; i < arguments.length; i++)
+                assert(typesMatch(function.parameterType(i), arguments[i])) : "types don't match: function.parameterType(" + i + ")=" + function.parameterType(i) + ", arguments[" + i + "]=" + arguments[i] + " in " + debugString();
+        }
+        Name(int index, char type) {
+            this(index, type, null, null);
+        }
+        Name(char type) {
+            this(-1, type);
+        }
+
+        char type() { return type; }
+        int index() { return index; }
+        boolean initIndex(int i) {
+            if (index != i) {
+                if (index != -1)  return false;
+                index = (short)i;
+            }
+            return true;
+        }
+
+
+        void resolve() {
+            if (function != null)
+                function.resolve();
+        }
+
+        Name newIndex(int i) {
+            if (initIndex(i))  return this;
+            return cloneWithIndex(i);
+        }
+        Name cloneWithIndex(int i) {
+            Object[] newArguments = (arguments == null) ? null : arguments.clone();
+            return new Name(i, type, function, newArguments);
+        }
+        Name replaceName(Name oldName, Name newName) {  // FIXME: use replaceNames uniformly
+            if (oldName == newName)  return this;
+            @SuppressWarnings("LocalVariableHidesMemberVariable")
+            Object[] arguments = this.arguments;
+            if (arguments == null)  return this;
+            boolean replaced = false;
+            for (int j = 0; j < arguments.length; j++) {
+                if (arguments[j] == oldName) {
+                    if (!replaced) {
+                        replaced = true;
+                        arguments = arguments.clone();
+                    }
+                    arguments[j] = newName;
+                }
+            }
+            if (!replaced)  return this;
+            return new Name(function, arguments);
+        }
+        Name replaceNames(Name[] oldNames, Name[] newNames, int start, int end) {
+            @SuppressWarnings("LocalVariableHidesMemberVariable")
+            Object[] arguments = this.arguments;
+            boolean replaced = false;
+        eachArg:
+            for (int j = 0; j < arguments.length; j++) {
+                if (arguments[j] instanceof Name) {
+                    Name n = (Name) arguments[j];
+                    int check = n.index;
+                    // harmless check to see if the thing is already in newNames:
+                    if (check >= 0 && check < newNames.length && n == newNames[check])
+                        continue eachArg;
+                    // n might not have the correct index: n != oldNames[n.index].
+                    for (int i = start; i < end; i++) {
+                        if (n == oldNames[i]) {
+                            if (n == newNames[i])
+                                continue eachArg;
+                            if (!replaced) {
+                                replaced = true;
+                                arguments = arguments.clone();
+                            }
+                            arguments[j] = newNames[i];
+                            continue eachArg;
+                        }
+                    }
+                }
+            }
+            if (!replaced)  return this;
+            return new Name(function, arguments);
+        }
+        void internArguments() {
+            @SuppressWarnings("LocalVariableHidesMemberVariable")
+            Object[] arguments = this.arguments;
+            for (int j = 0; j < arguments.length; j++) {
+                if (arguments[j] instanceof Name) {
+                    Name n = (Name) arguments[j];
+                    if (n.isParam() && n.index < INTERNED_ARGUMENT_LIMIT)
+                        arguments[j] = internArgument(n);
+                }
+            }
+        }
+        boolean isParam() {
+            return function == null;
+        }
+        boolean isConstantZero() {
+            return !isParam() && arguments.length == 0 && function.equals(constantZero(0, type).function);
+        }
+
+        public String toString() {
+            return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+type;
+        }
+        public String debugString() {
+            String s = toString();
+            return (function == null) ? s : s + "=" + exprString();
+        }
+        public String exprString() {
+            if (function == null)  return "null";
+            StringBuilder buf = new StringBuilder(function.toString());
+            buf.append("(");
+            String cma = "";
+            for (Object a : arguments) {
+                buf.append(cma); cma = ",";
+                if (a instanceof Name || a instanceof Integer)
+                    buf.append(a);
+                else
+                    buf.append("(").append(a).append(")");
+            }
+            buf.append(")");
+            return buf.toString();
+        }
+
+        private static boolean typesMatch(char parameterType, Object object) {
+            if (object instanceof Name) {
+                return ((Name)object).type == parameterType;
+            }
+            switch (parameterType) {
+                case 'I':  return object instanceof Integer;
+                case 'J':  return object instanceof Long;
+                case 'F':  return object instanceof Float;
+                case 'D':  return object instanceof Double;
+            }
+            assert(parameterType == 'L');
+            return true;
+        }
+
+        /**
+         * Does this Name precede the given binding node in some canonical order?
+         * This predicate is used to order data bindings (via insertion sort)
+         * with some stability.
+         * @param binding
+         * @return
+         */
+        boolean isSiblingBindingBefore(Name binding) {
+            assert(!binding.isParam());
+            if (isParam())  return true;
+            if (function.equals(binding.function) &&
+                arguments.length == binding.arguments.length) {
+                boolean sawInt = false;
+                for (int i = 0; i < arguments.length; i++) {
+                    Object a1 = arguments[i];
+                    Object a2 = binding.arguments[i];
+                    if (!a1.equals(a2)) {
+                        if (a1 instanceof Integer && a2 instanceof Integer) {
+                            if (sawInt)  continue;
+                            sawInt = true;
+                            if ((int)a1 < (int)a2)  continue;  // still might be true
+                        }
+                        return false;
+                    }
+                }
+                return sawInt;
+            }
+            return false;
+        }
+
+        public boolean equals(Name that) {
+            if (this == that)  return true;
+            if (isParam())
+                // each parameter is a unique atom
+                return false;  // this != that
+            return
+                //this.index == that.index &&
+                this.type == that.type &&
+                this.function.equals(that.function) &&
+                Arrays.equals(this.arguments, that.arguments);
+        }
+        @Override
+        public boolean equals(Object x) {
+            return x instanceof Name && equals((Name)x);
+        }
+        @Override
+        public int hashCode() {
+            if (isParam())
+                return index | (type << 8);
+            return function.hashCode() ^ Arrays.hashCode(arguments);
+        }
+    }
+
+    static Name argument(int which, char type) {
+        int tn = ALL_TYPES.indexOf(type);
+        if (tn < 0 || which >= INTERNED_ARGUMENT_LIMIT)
+            return new Name(which, type);
+        return INTERNED_ARGUMENTS[tn][which];
+    }
+    static Name internArgument(Name n) {
+        assert(n.isParam()) : "not param: " + n;
+        assert(n.index < INTERNED_ARGUMENT_LIMIT);
+        return argument(n.index, n.type);
+    }
+    static Name[] arguments(int extra, String types) {
+        int length = types.length();
+        Name[] names = new Name[length + extra];
+        for (int i = 0; i < length; i++)
+            names[i] = argument(i, types.charAt(i));
+        return names;
+    }
+    static Name[] arguments(int extra, char... types) {
+        int length = types.length;
+        Name[] names = new Name[length + extra];
+        for (int i = 0; i < length; i++)
+            names[i] = argument(i, types[i]);
+        return names;
+    }
+    static Name[] arguments(int extra, List<Class<?>> types) {
+        int length = types.size();
+        Name[] names = new Name[length + extra];
+        for (int i = 0; i < length; i++)
+            names[i] = argument(i, basicType(types.get(i)));
+        return names;
+    }
+    static Name[] arguments(int extra, Class<?>... types) {
+        int length = types.length;
+        Name[] names = new Name[length + extra];
+        for (int i = 0; i < length; i++)
+            names[i] = argument(i, basicType(types[i]));
+        return names;
+    }
+    static Name[] arguments(int extra, MethodType types) {
+        int length = types.parameterCount();
+        Name[] names = new Name[length + extra];
+        for (int i = 0; i < length; i++)
+            names[i] = argument(i, basicType(types.parameterType(i)));
+        return names;
+    }
+    static final String ALL_TYPES = "LIJFD";  // omit V, not an argument type
+    static final int INTERNED_ARGUMENT_LIMIT = 10;
+    private static final Name[][] INTERNED_ARGUMENTS
+            = new Name[ALL_TYPES.length()][INTERNED_ARGUMENT_LIMIT];
+    static {
+        for (int tn = 0; tn < ALL_TYPES.length(); tn++) {
+            for (int i = 0; i < INTERNED_ARGUMENTS[tn].length; i++) {
+                char type = ALL_TYPES.charAt(tn);
+                INTERNED_ARGUMENTS[tn][i] = new Name(i, type);
+            }
+        }
+    }
+
+    private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
+
+    static Name constantZero(int which, char type) {
+        return CONSTANT_ZERO[ALL_TYPES.indexOf(type)].newIndex(which);
+    }
+    private static final Name[] CONSTANT_ZERO
+            = new Name[ALL_TYPES.length()];
+    static {
+        for (int tn = 0; tn < ALL_TYPES.length(); tn++) {
+            char bt = ALL_TYPES.charAt(tn);
+            Wrapper wrap = Wrapper.forBasicType(bt);
+            MemberName zmem = new MemberName(LambdaForm.class, "zero"+bt, MethodType.methodType(wrap.primitiveType()), REF_invokeStatic);
+            try {
+                zmem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zmem, null, NoSuchMethodException.class);
+            } catch (IllegalAccessException|NoSuchMethodException ex) {
+                throw new InternalError(ex);
+            }
+            NamedFunction zcon = new NamedFunction(zmem);
+            Name n = new Name(zcon).newIndex(0);
+            assert(n.type == ALL_TYPES.charAt(tn));
+            CONSTANT_ZERO[tn] = n;
+            assert(n.isConstantZero());
+        }
+    }
+
+    // Avoid appealing to ValueConversions at bootstrap time:
+    private static int zeroI() { return 0; }
+    private static long zeroJ() { return 0; }
+    private static float zeroF() { return 0; }
+    private static double zeroD() { return 0; }
+    private static Object zeroL() { return null; }
+
+    // Put this last, so that previous static inits can run before.
+    static {
+        if (USE_PREDEFINED_INTERPRET_METHODS)
+            PREPARED_FORMS.putAll(computeInitialPreparedForms());
+    }
+
+    /**
+     * Internal marker for byte-compiled LambdaForms.
+     */
+    /*non-public*/
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.RUNTIME)
+    @interface Compiled {
+    }
+
+    /**
+     * Internal marker for LambdaForm interpreter frames.
+     */
+    /*non-public*/
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.RUNTIME)
+    @interface Hidden {
+    }
+
+
+/*
+    // Smoke-test for the invokers used in this file.
+    static void testMethodHandleLinkers() throws Throwable {
+        MemberName.Factory lookup = MemberName.getFactory();
+        MemberName asList_MN = new MemberName(Arrays.class, "asList",
+                                              MethodType.methodType(List.class, Object[].class),
+                                              REF_invokeStatic);
+        //MethodHandleNatives.resolve(asList_MN, null);
+        asList_MN = lookup.resolveOrFail(asList_MN, REF_invokeStatic, null, NoSuchMethodException.class);
+        System.out.println("about to call "+asList_MN);
+        Object[] abc = { "a", "bc" };
+        List<?> lst = (List<?>) MethodHandle.linkToStatic(abc, asList_MN);
+        System.out.println("lst="+lst);
+        MemberName toString_MN = new MemberName(Object.class.getMethod("toString"));
+        String s1 = (String) MethodHandle.linkToVirtual(lst, toString_MN);
+        toString_MN = new MemberName(Object.class.getMethod("toString"), true);
+        String s2 = (String) MethodHandle.linkToSpecial(lst, toString_MN);
+        System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString()));
+        MemberName toArray_MN = new MemberName(List.class.getMethod("toArray"));
+        Object[] arr = (Object[]) MethodHandle.linkToInterface(lst, toArray_MN);
+        System.out.println("toArray="+Arrays.toString(arr));
+    }
+    static { try { testMethodHandleLinkers(); } catch (Throwable ex) { throw new RuntimeException(ex); } }
+    // Requires these definitions in MethodHandle:
+    static final native Object linkToStatic(Object x1, MemberName mn) throws Throwable;
+    static final native Object linkToVirtual(Object x1, MemberName mn) throws Throwable;
+    static final native Object linkToSpecial(Object x1, MemberName mn) throws Throwable;
+    static final native Object linkToInterface(Object x1, MemberName mn) throws Throwable;
+ */
+
+    static { NamedFunction.initializeInvokers(); }
+}
--- a/src/share/classes/java/lang/invoke/MemberName.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/MemberName.java	Tue Jul 24 10:47:44 2012 -0700
@@ -26,6 +26,8 @@
 package java.lang.invoke;
 
 import sun.invoke.util.BytecodeDescriptor;
+import sun.invoke.util.VerifyAccess;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -38,6 +40,7 @@
 import java.util.List;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandleStatics.*;
+import java.util.Objects;
 
 /**
  * A {@code MemberName} is a compact symbolic datum which fully characterizes
@@ -71,19 +74,14 @@
     private String     name;        // may be null if not yet materialized
     private Object     type;        // may be null if not yet materialized
     private int        flags;       // modifier bits; see reflect.Modifier
-
-    private Object     vmtarget;    // VM-specific target value
-    private int        vmindex;     // method index within class or interface
-
-    { vmindex = VM_INDEX_UNINITIALIZED; }
+    //@Injected JVM_Method* vmtarget;
+    //@Injected int         vmindex;
+    private Object     resolution;  // if null, this guy is resolved
 
     /** Return the declaring class of this member.
      *  In the case of a bare name and type, the declaring class will be null.
      */
     public Class<?> getDeclaringClass() {
-        if (clazz == null && isResolved()) {
-            expandFromVM();
-        }
         return clazz;
     }
 
@@ -105,6 +103,16 @@
         return name;
     }
 
+    public MethodType getMethodOrFieldType() {
+        if (isInvocable())
+            return getMethodType();
+        if (isGetter())
+            return MethodType.methodType(getFieldType());
+        if (isSetter())
+            return MethodType.methodType(void.class, getFieldType());
+        throw new InternalError("not a method or field: "+this);
+    }
+
     /** Return the declared type of this member, which
      *  must be a method or constructor.
      */
@@ -140,9 +148,11 @@
      *  a reference to declaring class.  For static methods, it is the same as the declared type.
      */
     public MethodType getInvocationType() {
-        MethodType itype = getMethodType();
+        MethodType itype = getMethodOrFieldType();
+        if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
+            return itype.changeReturnType(clazz);
         if (!isStatic())
-            itype = itype.insertParameterTypes(0, clazz);
+            return itype.insertParameterTypes(0, clazz);
         return itype;
     }
 
@@ -208,9 +218,98 @@
         return (flags & RECOGNIZED_MODIFIERS);
     }
 
+    /** Return the reference kind of this member, or zero if none.
+     */
+    public byte getReferenceKind() {
+        return (byte) ((flags >>> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK);
+    }
+    private boolean referenceKindIsConsistent() {
+        byte refKind = getReferenceKind();
+        if (refKind == REF_NONE)  return isType();
+        if (isField()) {
+            assert(staticIsConsistent());
+            assert(MethodHandleNatives.refKindIsField(refKind));
+        } else if (isConstructor()) {
+            assert(refKind == REF_newInvokeSpecial || refKind == REF_invokeSpecial);
+        } else if (isMethod()) {
+            assert(staticIsConsistent());
+            assert(MethodHandleNatives.refKindIsMethod(refKind));
+            if (clazz.isInterface())
+                assert(refKind == REF_invokeInterface ||
+                       refKind == REF_invokeVirtual && isObjectPublicMethod());
+        } else {
+            assert(false);
+        }
+        return true;
+    }
+    private boolean isObjectPublicMethod() {
+        if (clazz == Object.class)  return true;
+        MethodType mtype = getMethodType();
+        if (name.equals("toString") && mtype.returnType() == String.class && mtype.parameterCount() == 0)
+            return true;
+        if (name.equals("hashCode") && mtype.returnType() == int.class && mtype.parameterCount() == 0)
+            return true;
+        if (name.equals("equals") && mtype.returnType() == boolean.class && mtype.parameterCount() == 1 && mtype.parameterType(0) == Object.class)
+            return true;
+        return false;
+    }
+    /*non-public*/ boolean referenceKindIsConsistentWith(int originalRefKind) {
+        int refKind = getReferenceKind();
+        if (refKind == originalRefKind)  return true;
+        switch (originalRefKind) {
+        case REF_invokeInterface:
+            // Looking up an interface method, can get (e.g.) Object.hashCode
+            assert(refKind == REF_invokeVirtual ||
+                   refKind == REF_invokeSpecial) : this;
+            return true;
+        case REF_invokeVirtual:
+        case REF_newInvokeSpecial:
+            // Looked up a virtual, can get (e.g.) final String.hashCode.
+            assert(refKind == REF_invokeSpecial) : this;
+            return true;
+        }
+        assert(false) : this;
+        return true;
+    }
+    private boolean staticIsConsistent() {
+        byte refKind = getReferenceKind();
+        return MethodHandleNatives.refKindIsStatic(refKind) == isStatic() || getModifiers() == 0;
+    }
+    private boolean vminfoIsConsistent() {
+        byte refKind = getReferenceKind();
+        assert(isResolved());  // else don't call
+        Object vminfo = MethodHandleNatives.getMemberVMInfo(this);
+        assert(vminfo instanceof Object[]);
+        long vmindex = (Long) ((Object[])vminfo)[0];
+        Object vmtarget = ((Object[])vminfo)[1];
+        if (MethodHandleNatives.refKindIsField(refKind)) {
+            assert(vmindex >= 0) : vmindex + ":" + this;
+            assert(vmtarget instanceof Class);
+        } else {
+            if (MethodHandleNatives.refKindDoesDispatch(refKind))
+                assert(vmindex >= 0) : vmindex + ":" + this;
+            else
+                assert(vmindex < 0) : vmindex;
+            assert(vmtarget instanceof MemberName) : vmtarget + " in " + this;
+        }
+        return true;
+    }
+
+    private MemberName changeReferenceKind(byte refKind, byte oldKind) {
+        assert(getReferenceKind() == oldKind);
+        assert(MethodHandleNatives.refKindIsValid(refKind));
+        flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
+//        if (isConstructor() && refKind != REF_newInvokeSpecial)
+//            flags += (IS_METHOD - IS_CONSTRUCTOR);
+//        else if (refKind == REF_newInvokeSpecial && isMethod())
+//            flags += (IS_CONSTRUCTOR - IS_METHOD);
+        return this;
+    }
+
     private void setFlags(int flags) {
         this.flags = flags;
         assert(testAnyFlags(ALL_KINDS));
+        assert(referenceKindIsConsistent());
     }
 
     private boolean testFlags(int mask, int value) {
@@ -223,6 +322,17 @@
         return !testFlags(mask, 0);
     }
 
+    /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
+    public boolean isMethodHandleInvoke() {
+        final int bits = Modifier.NATIVE | Modifier.FINAL;
+        final int negs = Modifier.STATIC;
+        if (testFlags(bits | negs, bits) &&
+            clazz == MethodHandle.class) {
+            return name.equals("invoke") || name.equals("invokeExact");
+        }
+        return false;
+    }
+
     /** Utility method to query the modifier flags of this member. */
     public boolean isStatic() {
         return Modifier.isStatic(flags);
@@ -243,10 +353,22 @@
     public boolean isFinal() {
         return Modifier.isFinal(flags);
     }
+    /** Utility method to query whether this member or its defining class is final. */
+    public boolean canBeStaticallyBound() {
+        return Modifier.isFinal(flags | clazz.getModifiers());
+    }
+    /** Utility method to query the modifier flags of this member. */
+    public boolean isVolatile() {
+        return Modifier.isVolatile(flags);
+    }
     /** Utility method to query the modifier flags of this member. */
     public boolean isAbstract() {
         return Modifier.isAbstract(flags);
     }
+    /** Utility method to query the modifier flags of this member. */
+    public boolean isNative() {
+        return Modifier.isNative(flags);
+    }
     // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
 
     // unofficial modifier flags, used by HotSpot:
@@ -279,15 +401,12 @@
             IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
             IS_FIELD       = MN_IS_FIELD,       // field
             IS_TYPE        = MN_IS_TYPE;        // nested type
-    static final int  // for MethodHandleNatives.getMembers
-            SEARCH_SUPERCLASSES = MN_SEARCH_SUPERCLASSES,
-            SEARCH_INTERFACES   = MN_SEARCH_INTERFACES;
 
     static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
     static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
     static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
     static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD;
-    static final int SEARCH_ALL_SUPERS = SEARCH_SUPERCLASSES | SEARCH_INTERFACES;
+    static final int SEARCH_ALL_SUPERS = MN_SEARCH_SUPERCLASSES | MN_SEARCH_INTERFACES;
 
     /** Utility method to query whether this member is a method or constructor. */
     public boolean isInvocable() {
@@ -318,6 +437,12 @@
         return !testAnyFlags(ALL_ACCESS);
     }
 
+    /** Utility method to query whether this member is accessible from a given lookup class. */
+    public boolean isAccessibleFrom(Class<?> lookupClass) {
+        return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
+                                               lookupClass, ALL_ACCESS|MethodHandles.Lookup.PACKAGE);
+    }
+
     /** Initialize a query.   It is not resolved. */
     private void init(Class<?> defClass, String name, Object type, int flags) {
         // defining class is allowed to be null (for a naked name/type pair)
@@ -328,7 +453,7 @@
         this.name = name;
         this.type = type;
         setFlags(flags);
-        assert(!isResolved());
+        assert(this.resolution == null);  // nobody should have touched this yet
     }
 
     private void expandFromVM() {
@@ -339,39 +464,91 @@
     }
 
     // Capturing information from the Core Reflection API:
-    private static int flagsMods(int flags, int mods) {
+    private static int flagsMods(int flags, int mods, byte refKind) {
         assert((flags & RECOGNIZED_MODIFIERS) == 0);
         assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
-        return flags | mods;
+        assert((refKind & ~MN_REFERENCE_KIND_MASK) == 0);
+        return flags | mods | (refKind << MN_REFERENCE_KIND_SHIFT);
     }
     /** Create a name for the given reflected method.  The resulting name will be in a resolved state. */
     public MemberName(Method m) {
-        Object[] typeInfo = { m.getReturnType(), m.getParameterTypes() };
-        init(m.getDeclaringClass(), m.getName(), typeInfo, flagsMods(IS_METHOD, m.getModifiers()));
+        this(m, false);
+    }
+    @SuppressWarnings("LeakingThisInConstructor")
+    public MemberName(Method m, boolean wantSpecial) {
         // fill in vmtarget, vmindex while we have m in hand:
         MethodHandleNatives.init(this, m);
-        assert(isResolved());
+        assert(isResolved() && this.clazz != null);
+        this.name = m.getName();
+        if (this.type == null)
+            this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
+        if (wantSpecial) {
+            if (getReferenceKind() == REF_invokeVirtual)
+                changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
+        }
+    }
+    public MemberName asSpecial() {
+        switch (getReferenceKind()) {
+        case REF_invokeSpecial:     return this;
+        case REF_invokeVirtual:     return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
+        case REF_newInvokeSpecial:  return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
+        }
+        throw new IllegalArgumentException(this.toString());
+    }
+    public MemberName asConstructor() {
+        switch (getReferenceKind()) {
+        case REF_invokeSpecial:     return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial);
+        case REF_newInvokeSpecial:  return this;
+        }
+        throw new IllegalArgumentException(this.toString());
     }
     /** Create a name for the given reflected constructor.  The resulting name will be in a resolved state. */
+    @SuppressWarnings("LeakingThisInConstructor")
     public MemberName(Constructor<?> ctor) {
-        Object[] typeInfo = { void.class, ctor.getParameterTypes() };
-        init(ctor.getDeclaringClass(), CONSTRUCTOR_NAME, typeInfo, flagsMods(IS_CONSTRUCTOR, ctor.getModifiers()));
         // fill in vmtarget, vmindex while we have ctor in hand:
         MethodHandleNatives.init(this, ctor);
-        assert(isResolved());
+        assert(isResolved() && this.clazz != null);
+        this.name = CONSTRUCTOR_NAME;
+        if (this.type == null)
+            this.type = new Object[] { void.class, ctor.getParameterTypes() };
     }
-    /** Create a name for the given reflected field.  The resulting name will be in a resolved state. */
+    /** Create a name for the given reflected field.  The resulting name will be in a resolved state.
+     */
     public MemberName(Field fld) {
-        init(fld.getDeclaringClass(), fld.getName(), fld.getType(), flagsMods(IS_FIELD, fld.getModifiers()));
+        this(fld, false);
+    }
+    @SuppressWarnings("LeakingThisInConstructor")
+    public MemberName(Field fld, boolean makeSetter) {
         // fill in vmtarget, vmindex while we have fld in hand:
         MethodHandleNatives.init(this, fld);
-        assert(isResolved());
+        assert(isResolved() && this.clazz != null);
+        this.name = fld.getName();
+        this.type = fld.getType();
+        assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+        byte refKind = this.getReferenceKind();
+        assert(refKind == (isStatic() ? REF_getStatic : REF_getField));
+        if (makeSetter) {
+            changeReferenceKind((byte)(refKind + (REF_putStatic - REF_getStatic)), refKind);
+        }
+    }
+    public boolean isGetter() {
+        return MethodHandleNatives.refKindIsGetter(getReferenceKind());
+    }
+    public boolean isSetter() {
+        return MethodHandleNatives.refKindIsSetter(getReferenceKind());
+    }
+    public MemberName asSetter() {
+        byte refKind = getReferenceKind();
+        assert(MethodHandleNatives.refKindIsGetter(refKind));
+        assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+        byte setterRefKind = (byte)(refKind + (REF_putField - REF_getField));
+        return clone().changeReferenceKind(setterRefKind, refKind);
     }
     /** Create a name for the given class.  The resulting name will be in a resolved state. */
     public MemberName(Class<?> type) {
-        init(type.getDeclaringClass(), type.getSimpleName(), type, flagsMods(IS_TYPE, type.getModifiers()));
-        vmindex = 0;  // isResolved
-        assert(isResolved());
+        init(type.getDeclaringClass(), type.getSimpleName(), type,
+                flagsMods(IS_TYPE, type.getModifiers(), REF_NONE));
+        initResolved(true);
     }
 
     // bare-bones constructor; the JVM will fill it in
@@ -386,41 +563,89 @@
         }
      }
 
-    // %%% define equals/hashcode?
+    /** Get the definition of this member name.
+     *  This may be in a super-class of the declaring class of this member.
+     */
+    public MemberName getDefinition() {
+        if (!isResolved())  throw new IllegalStateException("must be resolved: "+this);
+        if (isType())  return this;
+        MemberName res = this.clone();
+        res.clazz = null;
+        res.type = null;
+        res.name = null;
+        res.resolution = res;
+        res.expandFromVM();
+        assert(res.getName().equals(this.getName()));
+        return res;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(clazz, flags, name, getType());
+    }
+    @Override
+    public boolean equals(Object that) {
+        return (that instanceof MemberName && this.equals((MemberName)that));
+    }
+
+    /** Decide if two member names have exactly the same symbolic content.
+     *  Does not take into account any actual class members, so even if
+     *  two member names resolve to the same actual member, they may
+     *  be distinct references.
+     */
+    public boolean equals(MemberName that) {
+        if (this == that)  return true;
+        if (that == null)  return false;
+        return this.clazz == that.clazz
+                && this.flags == that.flags
+                && Objects.equals(this.name, that.name)
+                && Objects.equals(this.getType(), that.getType());
+    }
 
     // Construction from symbolic parts, for queries:
-    /** Create a field or type name from the given components:  Declaring class, name, type, modifiers.
+    /** Create a field or type name from the given components:  Declaring class, name, type, reference kind.
      *  The declaring class may be supplied as null if this is to be a bare name and type.
      *  The resulting name will in an unresolved state.
      */
-    public MemberName(Class<?> defClass, String name, Class<?> type, int modifiers) {
-        init(defClass, name, type, IS_FIELD | (modifiers & RECOGNIZED_MODIFIERS));
+    public MemberName(Class<?> defClass, String name, Class<?> type, byte refKind) {
+        init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
+        initResolved(false);
     }
     /** Create a field or type name from the given components:  Declaring class, name, type.
      *  The declaring class may be supplied as null if this is to be a bare name and type.
      *  The modifier flags default to zero.
      *  The resulting name will in an unresolved state.
      */
-    public MemberName(Class<?> defClass, String name, Class<?> type) {
-        this(defClass, name, type, 0);
+    public MemberName(Class<?> defClass, String name, Class<?> type, Void unused) {
+        this(defClass, name, type, REF_NONE);
+        initResolved(false);
     }
     /** Create a method or constructor name from the given components:  Declaring class, name, type, modifiers.
      *  It will be a constructor if and only if the name is {@code "&lt;init&gt;"}.
      *  The declaring class may be supplied as null if this is to be a bare name and type.
+     *  The last argument is optional, a boolean which requests REF_invokeSpecial.
      *  The resulting name will in an unresolved state.
      */
-    public MemberName(Class<?> defClass, String name, MethodType type, int modifiers) {
-        int flagBit = (name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
-        init(defClass, name, type, flagBit | (modifiers & RECOGNIZED_MODIFIERS));
+    public MemberName(Class<?> defClass, String name, MethodType type, byte refKind) {
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        int flags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
+        init(defClass, name, type, flagsMods(flags, 0, refKind));
+        initResolved(false);
     }
-    /** Create a method or constructor name from the given components:  Declaring class, name, type, modifiers.
-     *  It will be a constructor if and only if the name is {@code "&lt;init&gt;"}.
-     *  The declaring class may be supplied as null if this is to be a bare name and type.
-     *  The modifier flags default to zero.
-     *  The resulting name will in an unresolved state.
+//    /** Create a method or constructor name from the given components:  Declaring class, name, type, modifiers.
+//     *  It will be a constructor if and only if the name is {@code "&lt;init&gt;"}.
+//     *  The declaring class may be supplied as null if this is to be a bare name and type.
+//     *  The modifier flags default to zero.
+//     *  The resulting name will in an unresolved state.
+//     */
+//    public MemberName(Class<?> defClass, String name, MethodType type, Void unused) {
+//        this(defClass, name, type, REF_NONE);
+//    }
+
+    /** Query whether this member name is resolved to a non-static, non-final method.
      */
-    public MemberName(Class<?> defClass, String name, MethodType type) {
-        this(defClass, name, type, 0);
+    public boolean hasReceiverTypeDispatch() {
+        return MethodHandleNatives.refKindDoesDispatch(getReferenceKind());
     }
 
     /** Query whether this member name is resolved.
@@ -429,15 +654,38 @@
      *  (Document?)
      */
     public boolean isResolved() {
-        return (vmindex != VM_INDEX_UNINITIALIZED);
+        return resolution == null;
     }
 
-    /** Query whether this member name is resolved to a non-static, non-final method.
-     */
-    public boolean hasReceiverTypeDispatch() {
-        return (isMethod() && getVMIndex() >= 0);
+    private void initResolved(boolean isResolved) {
+        assert(this.resolution == null);  // not initialized yet!
+        if (!isResolved)
+            this.resolution = this;
+        assert(isResolved() == isResolved);
     }
 
+    void checkForTypeAlias() {
+        if (isInvocable()) {
+            MethodType type;
+            if (this.type instanceof MethodType)
+                type = (MethodType) this.type;
+            else
+                this.type = type = getMethodType();
+            if (type.erase() == type)  return;
+            if (VerifyAccess.isTypeVisible(type, clazz))  return;
+            throw new LinkageError("bad method type alias: "+type+" not visible from "+clazz);
+        } else {
+            Class<?> type;
+            if (this.type instanceof Class<?>)
+                type = (Class<?>) this.type;
+            else
+                this.type = type = getFieldType();
+            if (VerifyAccess.isTypeVisible(type, clazz))  return;
+            throw new LinkageError("bad field type alias: "+type+" not visible from "+clazz);
+        }
+    }
+
+
     /** Produce a string form of this member name.
      *  For types, it is simply the type's own string (as reported by {@code toString}).
      *  For fields, it is {@code "DeclaringClass.name/type"}.
@@ -445,6 +693,7 @@
      *  If the declaring class is null, the prefix {@code "DeclaringClass."} is omitted.
      *  If the member is unresolved, a prefix {@code "*."} is prepended.
      */
+    @SuppressWarnings("LocalVariableHidesMemberVariable")
     @Override
     public String toString() {
         if (isType())
@@ -464,22 +713,12 @@
         } else {
             buf.append(type == null ? "(*)*" : getName(type));
         }
-        /*
-        buf.append('/');
-        // key: Public, private, pRotected, sTatic, Final, sYnchronized,
-        // transient/Varargs, native, (interface), abstract, sTrict, sYnthetic,
-        // (annotation), Enum, (unused)
-        final String FIELD_MOD_CHARS  = "PprTF?vt????Y?E?";
-        final String METHOD_MOD_CHARS = "PprTFybVn?atY???";
-        String modChars = (isInvocable() ? METHOD_MOD_CHARS : FIELD_MOD_CHARS);
-        for (int i = 0; i < modChars.length(); i++) {
-            if ((flags & (1 << i)) != 0) {
-                char mc = modChars.charAt(i);
-                if (mc != '?')
-                    buf.append(mc);
-            }
+        byte refKind = getReferenceKind();
+        if (refKind != REF_NONE) {
+            buf.append('/');
+            buf.append(MethodHandleNatives.refKindName(refKind));
         }
-         */
+        //buf.append("#").append(System.identityHashCode(this));
         return buf.toString();
     }
     private static String getName(Object obj) {
@@ -488,19 +727,6 @@
         return String.valueOf(obj);
     }
 
-    // Queries to the JVM:
-    /** Document? */
-    /*non-public*/ int getVMIndex() {
-        if (!isResolved())
-            throw newIllegalStateException("not resolved", this);
-        return vmindex;
-    }
-//    /*non-public*/ Object getVMTarget() {
-//        if (!isResolved())
-//            throw newIllegalStateException("not resolved", this);
-//        return vmtarget;
-//    }
-
     public IllegalAccessException makeAccessException(String message, Object from) {
         message = message + ": "+ toString();
         if (from != null)  message += ", from " + from;
@@ -518,14 +744,19 @@
     }
     public ReflectiveOperationException makeAccessException() {
         String message = message() + ": "+ toString();
-        if (isResolved())
-            return new IllegalAccessException(message);
+        ReflectiveOperationException ex;
+        if (isResolved() || !(resolution instanceof NoSuchMethodError ||
+                              resolution instanceof NoSuchFieldError))
+            ex = new IllegalAccessException(message);
         else if (isConstructor())
-            return new NoSuchMethodException(message);
+            ex = new NoSuchMethodException(message);
         else if (isMethod())
-            return new NoSuchMethodException(message);
+            ex = new NoSuchMethodException(message);
         else
-            return new NoSuchFieldException(message);
+            ex = new NoSuchFieldException(message);
+        if (resolution instanceof Throwable)
+            ex.initCause((Throwable) resolution);
+        return ex;
     }
 
     /** Actually making a query requires an access check. */
@@ -539,7 +770,7 @@
         private Factory() { } // singleton pattern
         static Factory INSTANCE = new Factory();
 
-        private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS;
+        private static int ALLOWED_FLAGS = ALL_KINDS;
 
         /// Queries
         List<MemberName> getMembers(Class<?> defc,
@@ -573,14 +804,14 @@
                 // JVM returned to us with an intentional overflow!
                 totalCount += buf.length;
                 int excess = bufCount - buf.length;
-                if (bufs == null)  bufs = new ArrayList<MemberName[]>(1);
+                if (bufs == null)  bufs = new ArrayList<>(1);
                 bufs.add(buf);
                 int len2 = buf.length;
                 len2 = Math.max(len2, excess);
                 len2 = Math.max(len2, totalCount / 4);
                 buf = newMemberBuffer(Math.min(BUF_MAX, len2));
             }
-            ArrayList<MemberName> result = new ArrayList<MemberName>(totalCount);
+            ArrayList<MemberName> result = new ArrayList<>(totalCount);
             if (bufs != null) {
                 for (MemberName[] buf0 : bufs) {
                     Collections.addAll(result, buf0);
@@ -599,47 +830,29 @@
             }
             return result;
         }
-        boolean resolveInPlace(MemberName m, boolean searchSupers, Class<?> lookupClass) {
-            if (m.name == null || m.type == null) {  // find unique non-overloaded name
-                Class<?> defc = m.getDeclaringClass();
-                List<MemberName> choices = null;
-                if (m.isMethod())
-                    choices = getMethods(defc, searchSupers, m.name, (MethodType) m.type, lookupClass);
-                else if (m.isConstructor())
-                    choices = getConstructors(defc, lookupClass);
-                else if (m.isField())
-                    choices = getFields(defc, searchSupers, m.name, (Class<?>) m.type, lookupClass);
-                //System.out.println("resolving "+m+" to "+choices);
-                if (choices == null || choices.size() != 1)
-                    return false;
-                if (m.name == null)  m.name = choices.get(0).name;
-                if (m.type == null)  m.type = choices.get(0).type;
-            }
-            MethodHandleNatives.resolve(m, lookupClass);
-            if (m.isResolved())  return true;
-            int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0);
-            String matchSig = m.getSignature();
-            MemberName[] buf = { m };
-            int n = MethodHandleNatives.getMembers(m.getDeclaringClass(),
-                    m.getName(), matchSig, matchFlags, lookupClass, 0, buf);
-            if (n == 0 || !m.isResolved())
-                return false;  // no result
-            else if (n == 1 || m.clazz.isInterface())
-                return true;   // unique result, or multiple inheritance is OK
-            else
-                return false;  // ambiguous result (can this happen?)
-        }
         /** Produce a resolved version of the given member.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
          *  Access checking is performed on behalf of the given {@code lookupClass}.
          *  If lookup fails or access is not permitted, null is returned.
          *  Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
          */
-        public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class<?> lookupClass) {
-            MemberName result = m.clone();
-            if (resolveInPlace(result, searchSupers, lookupClass))
-                return result;
-            return null;
+        private MemberName resolve(byte refKind, MemberName ref, Class<?> lookupClass) {
+            MemberName m = ref.clone();  // JVM will side-effect the ref
+            assert(refKind == m.getReferenceKind());
+            try {
+                m = MethodHandleNatives.resolve(m, lookupClass);
+                m.checkForTypeAlias();
+                m.resolution = null;
+            } catch (LinkageError ex) {
+                // JVM reports that the "bytecode behavior" would get an error
+                assert(!m.isResolved());
+                m.resolution = ex;
+                return m;
+            }
+            assert(m.referenceKindIsConsistent());
+            m.initResolved(true);
+            assert(m.vminfoIsConsistent());
+            return m;
         }
         /** Produce a resolved version of the given member.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
@@ -649,16 +862,29 @@
          */
         public
         <NoSuchMemberException extends ReflectiveOperationException>
-        MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass,
+        MemberName resolveOrFail(byte refKind, MemberName m, Class<?> lookupClass,
                                  Class<NoSuchMemberException> nsmClass)
                 throws IllegalAccessException, NoSuchMemberException {
-            MemberName result = resolveOrNull(m, searchSupers, lookupClass);
-            if (result != null)
+            MemberName result = resolve(refKind, m, lookupClass);
+            if (result.isResolved())
                 return result;
-            ReflectiveOperationException ex = m.makeAccessException();
+            ReflectiveOperationException ex = result.makeAccessException();
             if (ex instanceof IllegalAccessException)  throw (IllegalAccessException) ex;
             throw nsmClass.cast(ex);
         }
+        /** Produce a resolved version of the given member.
+         *  Super types are searched (for inherited members) if {@code searchSupers} is true.
+         *  Access checking is performed on behalf of the given {@code lookupClass}.
+         *  If lookup fails or access is not permitted, return null.
+         *  Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
+         */
+        public
+        MemberName resolveOrNull(byte refKind, MemberName m, Class<?> lookupClass) {
+            MemberName result = resolve(refKind, m, lookupClass);
+            if (result.isResolved())
+                return result;
+            return null;
+        }
         /** Return a list of all methods defined by the given class.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
          *  Access checking is performed on behalf of the given {@code lookupClass}.
--- a/src/share/classes/java/lang/invoke/MethodHandle.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/MethodHandle.java	Tue Jul 24 10:47:44 2012 -0700
@@ -26,9 +26,13 @@
 package java.lang.invoke;
 
 
-import java.util.ArrayList;
-import sun.invoke.util.ValueConversions;
+import java.util.*;
+import sun.invoke.util.*;
+import sun.misc.Unsafe;
+
 import static java.lang.invoke.MethodHandleStatics.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * A method handle is a typed, directly executable reference to an underlying method,
@@ -208,8 +212,8 @@
  * refers directly to an associated {@code CONSTANT_Methodref},
  * {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
  * constant pool entry.
- * (For more details on method handle constants,
- * see the <a href="package-summary.html#mhcon">package summary</a>.)
+ * (For full details on method handle constants,
+ * see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.)
  * <p>
  * Method handles produced by lookups or constant loads from methods or
  * constructors with the variable arity modifier bit ({@code 0x0080})
@@ -224,6 +228,19 @@
  * (E.g., if a non-static method handle is obtained via {@code ldc},
  * the type of the receiver is the class named in the constant pool entry.)
  * <p>
+ * Method handle constants are subject to the same link-time access checks
+ * their corresponding bytecode instructions, and the {@code ldc} instruction
+ * will throw corresponding linkage errors if the bytecode behaviors would
+ * throw such errors.
+ * <p>
+ * As a corollary of this, access to protected members is restricted
+ * to receivers only of the accessing class, or one of its subclasses,
+ * and the accessing class must in turn be a subclass (or package sibling)
+ * of the protected member's defining class.
+ * If a method reference refers to a protected non-static method or field
+ * of a class outside the current package, the receiver argument will
+ * be narrowed to the type of the accessing class.
+ * <p>
  * When a method handle to a virtual method is invoked, the method is
  * always looked up in the receiver (that is, the first argument).
  * <p>
@@ -390,39 +407,8 @@
  * @author John Rose, JSR 292 EG
  */
 public abstract class MethodHandle {
-    // { JVM internals:
-
-    private byte       vmentry;    // adapter stub or method entry point
-    //private int      vmslots;    // optionally, hoist type.form.vmslots
-    /*non-public*/ Object vmtarget;   // VM-specific, class-specific target value
-
-    // TO DO:  vmtarget should be invisible to Java, since the JVM puts internal
-    // managed pointers into it.  Making it visible exposes it to debuggers,
-    // which can cause errors when they treat the pointer as an Object.
-
-    // These two dummy fields are present to force 'I' and 'J' signatures
-    // into this class's constant pool, so they can be transferred
-    // to vmentry when this class is loaded.
-    static final int  INT_FIELD = 0;
-    static final long LONG_FIELD = 0;
-
-    // vmentry (a void* field) is used *only* by the JVM.
-    // The JVM adjusts its type to int or long depending on system wordsize.
-    // Since it is statically typed as neither int nor long, it is impossible
-    // to use this field from Java bytecode.  (Please don't try to, either.)
-
-    // The vmentry is an assembly-language stub which is jumped to
-    // immediately after the method type is verified.
-    // For a direct MH, this stub loads the vmtarget's entry point
-    // and jumps to it.
-
-    // } End of JVM internals.
-
     static { MethodHandleImpl.initStatics(); }
 
-    // interface MethodHandle<R throws X extends Exception,A...>
-    // { MethodType<R throws X,A...> type(); public R invokeExact(A...) throws X; }
-
     /**
      * Internal marker interface which distinguishes (to the Java compiler)
      * those methods which are <a href="MethodHandle.html#sigpoly">signature polymorphic</a>.
@@ -431,7 +417,9 @@
     @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
     @interface PolymorphicSignature { }
 
-    private MethodType type;
+    private final MethodType type;
+    /*private*/ final LambdaForm form;
+    // form is not private so that invokers can easily fetch it
 
     /**
      * Reports the type of this method handle.
@@ -448,9 +436,13 @@
      * the {@code java.lang.invoke} package.
      */
     // @param type type (permanently assigned) of the new method handle
-    /*non-public*/ MethodHandle(MethodType type) {
-        type.getClass();  // elicit NPE
+    /*non-public*/ MethodHandle(MethodType type, LambdaForm form) {
+        type.getClass();  // explicit NPE
+        form.getClass();  // explicit NPE
         this.type = type;
+        this.form = form;
+
+        form.prepare();  // TO DO:  Try to delay this step until just before invocation.
     }
 
     /**
@@ -506,6 +498,46 @@
     public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
 
     /**
+     * Private method for trusted invocation of a method handle respecting simplified signatures.
+     * Type mismatches will not throw {@code WrongMethodTypeException}, but could crash the JVM.
+     * <p>
+     * The caller signature is restricted to the following basic types:
+     * Object, int, long, float, double, and void return.
+     * <p>
+     * The caller is responsible for maintaining type correctness by ensuring
+     * that the each outgoing argument value is a member of the range of the corresponding
+     * callee argument type.
+     * (The caller should therefore issue appropriate casts and integer narrowing
+     * operations on outgoing argument values.)
+     * The caller can assume that the incoming result value is part of the range
+     * of the callee's return type.
+     */
+    /*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args) throws Throwable;
+
+    /*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args) throws Throwable;
+
+    /**
+     * Private method for trusted invocation of a MemberName of kind {@code REF_invokeStatic}.
+     * The caller signature is restricted to basic types as with {@code invokeBasic}.
+     * The trailing (not leading) argument must be a MemberName.
+     */
+    /*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args) throws Throwable;
+
+    /**
+     * Private method for trusted invocation of a MemberName of kind {@code REF_invokeSpecial}.
+     * The caller signature is restricted to basic types as with {@code invokeBasic}.
+     * The trailing (not leading) argument must be a MemberName.
+     */
+    /*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args) throws Throwable;
+
+    /**
+     * Private method for trusted invocation of a MemberName of kind {@code REF_invokeInterface}.
+     * The caller signature is restricted to basic types as with {@code invokeBasic}.
+     * The trailing (not leading) argument must be a MemberName.
+     */
+    /*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable;
+
+    /**
      * Performs a variable arity invocation, passing the arguments in the given array
      * to the method handle, as if via an inexact {@link #invoke invoke} from a call site
      * which mentions only the type {@code Object}, and whose arity is the length
@@ -557,6 +589,7 @@
      */
     public Object invokeWithArguments(Object... arguments) throws Throwable {
         int argc = arguments == null ? 0 : arguments.length;
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
         MethodType type = type();
         if (type.parameterCount() != argc || isVarargsCollector()) {
             // simulate invoke
@@ -690,7 +723,7 @@
         if (!type.isConvertibleTo(newType)) {
             throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
         }
-        return MethodHandleImpl.convertArguments(this, newType, 1);
+        return convertArguments(newType);
     }
 
     /**
@@ -772,7 +805,8 @@
      */
     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
         asSpreaderChecks(arrayType, arrayLength);
-        return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
+        int spreadArgPos = type.parameterCount() - arrayLength;
+        return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
     }
 
     private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
@@ -790,7 +824,7 @@
                 }
             }
             if (sawProblem) {
-                ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>(type().parameterList());
+                ArrayList<Class<?>> ptypes = new ArrayList<>(type().parameterList());
                 for (int i = nargs - arrayLength; i < nargs; i++) {
                     ptypes.set(i, arrayElement);
                 }
@@ -885,8 +919,12 @@
      */
     public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
         asCollectorChecks(arrayType, arrayLength);
+        int collectArgPos = type().parameterCount()-1;
+        MethodHandle target = this;
+        if (arrayType != type().parameterType(collectArgPos))
+            target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
         MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
-        return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
+        return MethodHandleImpl.makeCollectArguments(target, collector, collectArgPos, false);
     }
 
     // private API: return true if last param exactly matches arrayType
@@ -1056,7 +1094,7 @@
         boolean lastMatch = asCollectorChecks(arrayType, 0);
         if (isVarargsCollector() && lastMatch)
             return this;
-        return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
+        return MethodHandleImpl.makeVarargsCollector(this, arrayType);
     }
 
     /**
@@ -1155,14 +1193,13 @@
      */
     public MethodHandle bindTo(Object x) {
         Class<?> ptype;
-        if (type().parameterCount() == 0 ||
-            (ptype = type().parameterType(0)).isPrimitive())
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        MethodType type = type();
+        if (type.parameterCount() == 0 ||
+            (ptype = type.parameterType(0)).isPrimitive())
             throw newIllegalArgumentException("no leading reference parameter", x);
-        x = MethodHandles.checkValue(ptype, x);
-        // Cf. MethodHandles.insertArguments for the following logic:
-        MethodHandle bmh = MethodHandleImpl.bindReceiver(this, x);
-        if (bmh != null)  return bmh;
-        return MethodHandleImpl.bindArgument(this, 0, x);
+        x = ptype.cast(x);  // throw CCE if needed
+        return bindReceiver(x);
     }
 
     /**
@@ -1183,11 +1220,178 @@
     @Override
     public String toString() {
         if (DEBUG_METHOD_HANDLE_NAMES)  return debugString();
+        return standardString();
+    }
+    String standardString() {
         return "MethodHandle"+type;
     }
+    String debugString() {
+        return standardString()+"="+internalForm()+internalValues();
+    }
+
+    //// Implementation methods.
+    //// Sub-classes can override these default implementations.
+    //// All these methods assume arguments are already validated.
+
+    // Other transforms to do:  convert, explicitCast, permute, drop, filter, fold, GWT, catch
+
+    /*non-public*/
+    MethodHandle setVarargs(MemberName member) throws IllegalAccessException {
+        if (!member.isVarargs())  return this;
+        int argc = type().parameterCount();
+        if (argc != 0) {
+            Class<?> arrayType = type().parameterType(argc-1);
+            if (arrayType.isArray()) {
+                return MethodHandleImpl.makeVarargsCollector(this, arrayType);
+            }
+        }
+        throw member.makeAccessException("cannot make variable arity", null);
+    }
+    /*non-public*/
+    MethodHandle viewAsType(MethodType newType) {
+        // No actual conversions, just a new view of the same method.
+        if (!type.isViewableAs(newType))
+            throw new InternalError();
+        return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
+    }
+
+    // Decoding
+
+    /*non-public*/
+    LambdaForm internalForm() {
+        return form;
+    }
 
     /*non-public*/
-    String debugString() {
-        return getNameString(this);
+    MemberName internalMemberName() {
+        return null;  // DMH returns DMH.member
+    }
+
+    /*non-public*/
+    Object internalValues() {
+        return "";
+    }
+
+    //// Method handle implementation methods.
+    //// Sub-classes can override these default implementations.
+    //// All these methods assume arguments are already validated.
+
+    /*non-public*/ MethodHandle convertArguments(MethodType newType) {
+        // Override this if it can be improved.
+        return MethodHandleImpl.makePairwiseConvert(this, newType, 1);
+    }
+
+    /*non-public*/
+    MethodHandle bindArgument(int pos, char basicType, Object value) {
+        // Override this if it can be improved.
+        return rebind().bindArgument(pos, basicType, value);
+    }
+
+    /*non-public*/
+    MethodHandle bindReceiver(Object receiver) {
+        // Override this if it can be improved.
+        return bindArgument(0, 'L', receiver);
+    }
+
+    /*non-public*/
+    MethodHandle bindImmediate(int pos, char basicType, Object value) {
+        // Bind an immediate value to a position in the arguments.
+        // This means, elide the respective argument,
+        // and replace all references to it in NamedFunction args with the specified value.
+
+        // CURRENT RESTRICTIONS
+        // * only for pos 0 and UNSAFE (position is adjusted in MHImpl to make API usable for others)
+        assert pos == 0 && basicType == 'L' && value instanceof Unsafe;
+        MethodType type2 = type.dropParameterTypes(pos, pos + 1); // adjustment: ignore receiver!
+        LambdaForm form2 = form.bindImmediate(pos + 1, basicType, value); // adjust pos to form-relative pos
+        return copyWith(type2, form2);
+    }
+
+    /*non-public*/
+    MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+        throw new InternalError("copyWith: " + this.getClass());
+    }
+
+    /*non-public*/
+    MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+        // Override this if it can be improved.
+        return rebind().dropArguments(srcType, pos, drops);
+    }
+
+    /*non-public*/
+    MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+        // Override this if it can be improved.
+        return rebind().permuteArguments(newType, reorder);
+    }
+
+    /*non-public*/
+    MethodHandle rebind() {
+        // Bind 'this' into a new invoker, of the known class BMH.
+        MethodType type2 = type();
+        LambdaForm form2 = reinvokerForm(type2.basicType());
+        // form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
+        return BoundMethodHandle.bindSingle(type2, form2, this);
+    }
+
+    /*non-public*/
+    MethodHandle reinvokerTarget() {
+        throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this);
+    }
+
+    /** Create a LF which simply reinvokes a target of the given basic type.
+     *  The target MH must override {@link #reinvokerTarget} to provide the target.
+     */
+    static LambdaForm reinvokerForm(MethodType mtype) {
+        mtype = mtype.basicType();
+        LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
+        if (reinvoker != null)  return reinvoker;
+        MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
+        final int THIS_BMH    = 0;
+        final int ARG_BASE    = 1;
+        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
+        int nameCursor = ARG_LIMIT;
+        final int NEXT_MH     = nameCursor++;
+        final int REINVOKE    = nameCursor++;
+        LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+        names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
+        Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
+        targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
+        names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
+        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
+    }
+
+    private static final LambdaForm.NamedFunction NF_reinvokerTarget;
+    static {
+        try {
+            NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class
+                .getDeclaredMethod("reinvokerTarget"));
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex);
+        }
+    }
+
+    /**
+     * Replace the old lambda form of this method handle with a new one.
+     * The new one must be functionally equivalent to the old one.
+     * Threads may continue running the old form indefinitely,
+     * but it is likely that the new one will be preferred for new executions.
+     * Use with discretion.
+     * @param newForm
+     */
+    /*non-public*/
+    void updateForm(LambdaForm newForm) {
+        if (form == newForm)  return;
+        // ISSUE: Should we have a memory fence here?
+        UNSAFE.putObject(this, FORM_OFFSET, newForm);
+        this.form.prepare();  // as in MethodHandle.<init>
+    }
+
+    private static final long FORM_OFFSET;
+    static {
+        try {
+            FORM_OFFSET = UNSAFE.objectFieldOffset(MethodHandle.class.getDeclaredField("form"));
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex);
+        }
     }
 }
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Tue Jul 24 10:47:44 2012 -0700
@@ -29,14 +29,13 @@
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
 import sun.invoke.empty.Empty;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
 import sun.misc.Unsafe;
+import static java.lang.invoke.LambdaForm.*;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 
@@ -47,673 +46,471 @@
 /*non-public*/ abstract class MethodHandleImpl {
     /// Factory methods to create method handles:
 
-    private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
-
     static void initStatics() {
-        // Trigger preceding sequence.
+        // Trigger selected static initializations.
+        MemberName.Factory.INSTANCE.getClass();
     }
 
-    /** Look up a given method.
-     * Callable only from sun.invoke and related packages.
-     * <p>
-     * The resulting method handle type will be of the given type,
-     * with a receiver type {@code rcvc} prepended if the member is not static.
-     * <p>
-     * Access checks are made as of the given lookup class.
-     * In particular, if the method is protected and {@code defc} is in a
-     * different package from the lookup class, then {@code rcvc} must be
-     * the lookup class or a subclass.
-     * @param token Proof that the lookup class has access to this package.
-     * @param member Resolved method or constructor to call.
-     * @param name Name of the desired method.
-     * @param rcvc Receiver type of desired non-static method (else null)
-     * @param doDispatch whether the method handle will test the receiver type
-     * @param lookupClass access-check relative to this class
-     * @return a direct handle to the matching method
-     * @throws IllegalAccessException if the given method cannot be accessed by the lookup class
-     */
-    static
-    MethodHandle findMethod(MemberName method,
-                            boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException {
-        MethodType mtype = method.getMethodType();
-        if (!method.isStatic()) {
-            // adjust the advertised receiver type to be exactly the one requested
-            // (in the case of invokespecial, this will be the calling class)
-            Class<?> recvType = method.getDeclaringClass();
-            mtype = mtype.insertParameterTypes(0, recvType);
+    static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
+        if (!arrayClass.isArray())
+            throw newIllegalArgumentException("not an array: "+arrayClass);
+        MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter);
+        MethodType srcType = accessor.type().erase();
+        MethodType lambdaType = srcType.invokerType();
+        Name[] names = arguments(1, lambdaType);
+        Name[] args  = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
+        names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
+        LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
+        MethodHandle mh = new SimpleMethodHandle(srcType, form);
+        if (ArrayAccessor.needCast(arrayClass)) {
+            mh = mh.bindTo(arrayClass);
         }
-        DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
-        if (!mh.isValid())
-            throw method.makeAccessException("no direct method handle", lookupClass);
-        assert(mh.type() == mtype);
-        if (!method.isVarargs())
-            return mh;
-        int argc = mtype.parameterCount();
-        if (argc != 0) {
-            Class<?> arrayType = mtype.parameterType(argc-1);
-            if (arrayType.isArray())
-                return AdapterMethodHandle.makeVarargsCollector(mh, arrayType);
-        }
-        throw method.makeAccessException("cannot make variable arity", null);
+        mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter));
+        return mh;
     }
 
-    static
-    MethodHandle makeAllocator(MethodHandle rawConstructor) {
-        MethodType rawConType = rawConstructor.type();
-        Class<?> allocateClass = rawConType.parameterType(0);
-        // Wrap the raw (unsafe) constructor with the allocation of a suitable object.
-        assert(AdapterMethodHandle.canCollectArguments(rawConType, MethodType.methodType(allocateClass), 0, true));
-        // allocator(arg...)
-        // [fold]=> cookedConstructor(obj=allocate(C), arg...)
-        // [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...))
-        MethodHandle returner = MethodHandles.identity(allocateClass);
-        MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass);
-        MethodHandle  cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false);
-        assert(cookedConstructor.type().equals(ctype));
-        ctype = ctype.dropParameterTypes(0, 1);
-        cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true);
-        AllocateObject allocator = new AllocateObject(allocateClass);
-        // allocate() => new C(void)
-        assert(allocator.type().equals(MethodType.methodType(allocateClass)));
-        ctype = ctype.dropParameterTypes(0, 1);
-        MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator);
-        return fold;
-    }
+    static final class ArrayAccessor {
+        /// Support for array element access
+        static final HashMap<Class<?>, MethodHandle> GETTER_CACHE = new HashMap<>();  // TODO use it
+        static final HashMap<Class<?>, MethodHandle> SETTER_CACHE = new HashMap<>();  // TODO use it
 
-    static final class AllocateObject /*<C>*/ extends BoundMethodHandle {
-        private static final Unsafe unsafe = Unsafe.getUnsafe();
+        static int     getElementI(int[]     a, int i)            { return              a[i]; }
+        static long    getElementJ(long[]    a, int i)            { return              a[i]; }
+        static float   getElementF(float[]   a, int i)            { return              a[i]; }
+        static double  getElementD(double[]  a, int i)            { return              a[i]; }
+        static boolean getElementZ(boolean[] a, int i)            { return              a[i]; }
+        static byte    getElementB(byte[]    a, int i)            { return              a[i]; }
+        static short   getElementS(short[]   a, int i)            { return              a[i]; }
+        static char    getElementC(char[]    a, int i)            { return              a[i]; }
+        static Object  getElementL(Object[]  a, int i)            { return              a[i]; }
 
-        private final Class<?> /*<C>*/ allocateClass;
+        static void    setElementI(int[]     a, int i, int     x) {              a[i] = x; }
+        static void    setElementJ(long[]    a, int i, long    x) {              a[i] = x; }
+        static void    setElementF(float[]   a, int i, float   x) {              a[i] = x; }
+        static void    setElementD(double[]  a, int i, double  x) {              a[i] = x; }
+        static void    setElementZ(boolean[] a, int i, boolean x) {              a[i] = x; }
+        static void    setElementB(byte[]    a, int i, byte    x) {              a[i] = x; }
+        static void    setElementS(short[]   a, int i, short   x) {              a[i] = x; }
+        static void    setElementC(char[]    a, int i, char    x) {              a[i] = x; }
+        static void    setElementL(Object[]  a, int i, Object  x) {              a[i] = x; }
 
-        // for allocation only:
-        private AllocateObject(Class<?> /*<C>*/ allocateClass) {
-            super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class)));
-            this.allocateClass = allocateClass;
+        static Object  getElementL(Class<?> arrayClass, Object[] a, int i)           { arrayClass.cast(a); return a[i]; }
+        static void    setElementL(Class<?> arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; }
+
+        // Weakly typed wrappers of Object[] accessors:
+        static Object  getElementL(Object    a, int i)            { return getElementL((Object[])a, i); }
+        static void    setElementL(Object    a, int i, Object  x) {        setElementL((Object[]) a, i, x); }
+        static Object  getElementL(Object   arrayClass, Object a, int i)             { return getElementL((Class<?>) arrayClass, (Object[])a, i); }
+        static void    setElementL(Object   arrayClass, Object a, int i, Object x)   {        setElementL((Class<?>) arrayClass, (Object[])a, i, x); }
+
+        static boolean needCast(Class<?> arrayClass) {
+            Class<?> elemClass = arrayClass.getComponentType();
+            return !elemClass.isPrimitive() && elemClass != Object.class;
         }
-        @SuppressWarnings("unchecked")
-        private Object /*C*/ allocate() throws InstantiationException {
-            return unsafe.allocateInstance(allocateClass);
+        static String name(Class<?> arrayClass, boolean isSetter) {
+            Class<?> elemClass = arrayClass.getComponentType();
+            if (elemClass == null)  throw new IllegalArgumentException();
+            return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
         }
-        static final MethodHandle ALLOCATE;
-        static {
+        static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false;  // FIXME: decide
+        static MethodType type(Class<?> arrayClass, boolean isSetter) {
+            Class<?> elemClass = arrayClass.getComponentType();
+            Class<?> arrayArgClass = arrayClass;
+            if (!elemClass.isPrimitive()) {
+                arrayArgClass = Object[].class;
+                if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
+                    arrayArgClass = Object.class;
+            }
+            if (!needCast(arrayClass)) {
+                return !isSetter ?
+                    MethodType.methodType(elemClass,  arrayArgClass, int.class) :
+                    MethodType.methodType(void.class, arrayArgClass, int.class, elemClass);
+            } else {
+                Class<?> classArgClass = Class.class;
+                if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
+                    classArgClass = Object.class;
+                return !isSetter ?
+                    MethodType.methodType(Object.class, classArgClass, arrayArgClass, int.class) :
+                    MethodType.methodType(void.class,   classArgClass, arrayArgClass, int.class, Object.class);
+            }
+        }
+        static MethodType correctType(Class<?> arrayClass, boolean isSetter) {
+            Class<?> elemClass = arrayClass.getComponentType();
+            return !isSetter ?
+                    MethodType.methodType(elemClass,  arrayClass, int.class) :
+                    MethodType.methodType(void.class, arrayClass, int.class, elemClass);
+        }
+        static MethodHandle getAccessor(Class<?> arrayClass, boolean isSetter) {
+            String     name = name(arrayClass, isSetter);
+            MethodType type = type(arrayClass, isSetter);
             try {
-                ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0));
+                return IMPL_LOOKUP.findStatic(ArrayAccessor.class, name, type);
             } catch (ReflectiveOperationException ex) {
                 throw uncaughtException(ex);
             }
         }
     }
 
-    static
-    MethodHandle accessField(MemberName member, boolean isSetter,
-                             Class<?> lookupClass) {
-        // Use sun. misc.Unsafe to dig up the dirt on the field.
-        FieldAccessor accessor = new FieldAccessor(member, isSetter);
-        return accessor;
+    /**
+     * Create a JVM-level adapter method handle to conform the given method
+     * handle to the similar newType, using only pairwise argument conversions.
+     * For each argument, convert incoming argument to the exact type needed.
+     * The argument conversions allowed are casting, boxing and unboxing,
+     * integral widening or narrowing, and floating point widening or narrowing.
+     * @param srcType required call type
+     * @param target original method handle
+     * @param level which strength of conversion is allowed
+     * @return an adapter to the original handle with the desired new type,
+     *          or the original target if the types are already identical
+     *          or null if the adaptation cannot be made
+     */
+    static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) {
+        assert(level >= 0 && level <= 2);
+        MethodType dstType = target.type();
+        assert(dstType.parameterCount() == target.type().parameterCount());
+        if (srcType == dstType)
+            return target;
+
+        // Calculate extra arguments (temporaries) required in the names array.
+        // FIXME: Use an ArrayList<Name>.  Some arguments require more than one conversion step.
+        int extra = 0;
+        for (int i = 0; i < srcType.parameterCount(); i++) {
+            Class<?> src = srcType.parameterType(i);
+            Class<?> dst = dstType.parameterType(i);
+            if (!VerifyType.isNullConversion(src, dst)) {
+                extra++;
+            }
+        }
+
+        Class<?> needReturn = srcType.returnType();
+        Class<?> haveReturn = dstType.returnType();
+        boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
+
+        // Now build a LambdaForm.
+        MethodType lambdaType = srcType.invokerType();
+        Name[] names = arguments(extra + 1, lambdaType);
+        int[] indexes = new int[lambdaType.parameterCount()];
+
+        MethodType midType = dstType;
+        for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) {
+            Class<?> src = srcType.parameterType(i);
+            Class<?> dst = midType.parameterType(i);
+
+            if (VerifyType.isNullConversion(src, dst)) {
+                // do nothing: difference is trivial
+                indexes[i] = argIndex;
+                continue;
+            }
+
+            // Work the current type backward toward the desired caller type:
+            midType = midType.changeParameterType(i, src);
+
+            // Tricky case analysis follows.
+            MethodHandle fn = null;
+            if (src.isPrimitive()) {
+                if (dst.isPrimitive()) {
+                    fn = ValueConversions.convertPrimitive(src, dst);
+                } else {
+                    Wrapper w = Wrapper.forPrimitiveType(src);
+                    MethodHandle boxMethod = ValueConversions.box(w);
+                    if (dst == w.wrapperType())
+                        fn = boxMethod;
+                    else
+                        fn = boxMethod.asType(MethodType.methodType(dst, src));
+                }
+            } else {
+                if (dst.isPrimitive()) {
+                    // Caller has boxed a primitive.  Unbox it for the target.
+                    Wrapper w = Wrapper.forPrimitiveType(dst);
+                    if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType())) {
+                        fn = ValueConversions.unbox(dst);
+                    } else if (src == Object.class || !Wrapper.isWrapperType(src)) {
+                        // Examples:  Object->int, Number->int, Comparable->int; Byte->int, Character->int
+                        // must include additional conversions
+                        // src must be examined at runtime, to detect Byte, Character, etc.
+                        MethodHandle unboxMethod = (level == 1
+                                                    ? ValueConversions.unbox(dst)
+                                                    : ValueConversions.unboxCast(dst));
+                        fn = unboxMethod;
+                    } else {
+                        // Example: Byte->int
+                        // Do this by reformulating the problem to Byte->byte.
+                        Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
+                        MethodHandle unbox = ValueConversions.unbox(srcPrim);
+                        // Compose the two conversions.  FIXME:  should make two Names for this job
+                        fn = unbox.asType(MethodType.methodType(dst, src));
+                    }
+                } else {
+                    // Simple reference conversion.
+                    // Note:  Do not check for a class hierarchy relation
+                    // between src and dst.  In all cases a 'null' argument
+                    // will pass the cast conversion.
+                    fn = ValueConversions.cast(dst);
+                }
+            }
+            names[tmpIndex] = new Name(fn, names[argIndex]);
+            indexes[i] = tmpIndex;
+            tmpIndex++;
+        }
+        if (retConv) {
+            MethodHandle adjustReturn;
+            if (haveReturn == void.class) {
+                // synthesize a zero value for the given void
+                Object zero = Wrapper.forBasicType(needReturn).zero();
+                adjustReturn = MethodHandles.constant(needReturn, zero);
+            } else {
+                MethodHandle identity = MethodHandles.identity(needReturn);
+                MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
+                adjustReturn = makePairwiseConvert(identity, needConversion, level);
+            }
+            target = makeCollectArguments(adjustReturn, target, 0, false);
+        }
+
+        // Build argument array for the call.
+        Name[] targetArgs = new Name[dstType.parameterCount()];
+        for (int i = 0; i < dstType.parameterCount(); i++) {
+            int idx = indexes[i];
+            targetArgs[i] = names[idx];
+        }
+        names[names.length - 1] = new Name(target, (Object[]) targetArgs);
+        LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
+        return new SimpleMethodHandle(srcType, form);
     }
 
-    static
-    MethodHandle accessArrayElement(Class<?> arrayClass, boolean isSetter) {
-        if (!arrayClass.isArray())
-            throw newIllegalArgumentException("not an array: "+arrayClass);
-        Class<?> elemClass = arrayClass.getComponentType();
-        MethodHandle[] mhs = FieldAccessor.ARRAY_CACHE.get(elemClass);
-        if (mhs == null) {
-            if (!FieldAccessor.doCache(elemClass))
-                return FieldAccessor.ahandle(arrayClass, isSetter);
-            mhs = new MethodHandle[] {
-                FieldAccessor.ahandle(arrayClass, false),
-                FieldAccessor.ahandle(arrayClass, true)
-            };
-            if (mhs[0].type().parameterType(0) == Class.class) {
-                mhs[0] = mhs[0].bindTo(elemClass);
-                mhs[1] = mhs[1].bindTo(elemClass);
-            }
-            synchronized (FieldAccessor.ARRAY_CACHE) {}  // memory barrier
-            FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
-        }
-        return mhs[isSetter ? 1 : 0];
+    static MethodHandle makeReferenceIdentity(Class<?> refType) {
+        MethodType lambdaType = MethodType.genericMethodType(1).invokerType();
+        Name[] names = arguments(1, lambdaType);
+        names[names.length - 1] = new Name(ValueConversions.identity(), names[1]);
+        LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names);
+        return new SimpleMethodHandle(MethodType.methodType(refType, refType), form);
     }
 
-    static final class FieldAccessor /*<C,V>*/ extends BoundMethodHandle {
-        private static final Unsafe unsafe = Unsafe.getUnsafe();
-        final Object base;  // for static refs only
-        final long offset;
-        final String name;
+    static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
+        MethodType type = target.type();
+        int last = type.parameterCount() - 1;
+        if (type.parameterType(last) != arrayType)
+            target = target.asType(type.changeParameterType(last, arrayType));
+        target = target.asFixedArity();  // make sure this attribute is turned off
+        return new AsVarargsCollector(target, target.type(), arrayType);
+    }
 
-        FieldAccessor(MemberName field, boolean isSetter) {
-            super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic()));
-            this.offset = (long) field.getVMIndex();
-            this.name = field.getName();
-            this.base = staticBase(field);
-        }
-        @Override
-        String debugString() { return addTypeString(name, this); }
+    static class AsVarargsCollector extends MethodHandle {
+        MethodHandle target;
+        final Class<?> arrayType;
+        MethodHandle cache;
 
-        private static Object nullCheck(Object obj) {
-            obj.getClass();  // NPE
-            return obj;
+        AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
+            super(type, reinvokerForm(type));
+            this.target = target;
+            this.arrayType = arrayType;
+            this.cache = target.asCollector(arrayType, 0);
         }
 
-        int getFieldI(Object /*C*/ obj) { return unsafe.getInt(nullCheck(obj), offset); }
-        void setFieldI(Object /*C*/ obj, int x) { unsafe.putInt(nullCheck(obj), offset, x); }
-        long getFieldJ(Object /*C*/ obj) { return unsafe.getLong(nullCheck(obj), offset); }
-        void setFieldJ(Object /*C*/ obj, long x) { unsafe.putLong(nullCheck(obj), offset, x); }
-        float getFieldF(Object /*C*/ obj) { return unsafe.getFloat(nullCheck(obj), offset); }
-        void setFieldF(Object /*C*/ obj, float x) { unsafe.putFloat(nullCheck(obj), offset, x); }
-        double getFieldD(Object /*C*/ obj) { return unsafe.getDouble(nullCheck(obj), offset); }
-        void setFieldD(Object /*C*/ obj, double x) { unsafe.putDouble(nullCheck(obj), offset, x); }
-        boolean getFieldZ(Object /*C*/ obj) { return unsafe.getBoolean(nullCheck(obj), offset); }
-        void setFieldZ(Object /*C*/ obj, boolean x) { unsafe.putBoolean(nullCheck(obj), offset, x); }
-        byte getFieldB(Object /*C*/ obj) { return unsafe.getByte(nullCheck(obj), offset); }
-        void setFieldB(Object /*C*/ obj, byte x) { unsafe.putByte(nullCheck(obj), offset, x); }
-        short getFieldS(Object /*C*/ obj) { return unsafe.getShort(nullCheck(obj), offset); }
-        void setFieldS(Object /*C*/ obj, short x) { unsafe.putShort(nullCheck(obj), offset, x); }
-        char getFieldC(Object /*C*/ obj) { return unsafe.getChar(nullCheck(obj), offset); }
-        void setFieldC(Object /*C*/ obj, char x) { unsafe.putChar(nullCheck(obj), offset, x); }
-        Object /*V*/ getFieldL(Object /*C*/ obj) { return unsafe.getObject(nullCheck(obj), offset); }
-        void setFieldL(Object /*C*/ obj, Object /*V*/ x) { unsafe.putObject(nullCheck(obj), offset, x); }
+        @Override MethodHandle reinvokerTarget() { return target; }
 
-        static Object staticBase(final MemberName field) {
-            if (!field.isStatic())  return null;
-            return AccessController.doPrivileged(new PrivilegedAction<Object>() {
-                    public Object run() {
-                        try {
-                            Class<?> c = field.getDeclaringClass();
-                            // FIXME:  Should not have to create 'f' to get this value.
-                            java.lang.reflect.Field f = c.getDeclaredField(field.getName());
-                            return unsafe.staticFieldBase(f);
-                        } catch (NoSuchFieldException ee) {
-                            throw uncaughtException(ee);
-                        }
-                    }
-                });
+        @Override
+        public boolean isVarargsCollector() {
+            return true;
         }
 
-        int getStaticI() { return unsafe.getInt(base, offset); }
-        void setStaticI(int x) { unsafe.putInt(base, offset, x); }
-        long getStaticJ() { return unsafe.getLong(base, offset); }
-        void setStaticJ(long x) { unsafe.putLong(base, offset, x); }
-        float getStaticF() { return unsafe.getFloat(base, offset); }
-        void setStaticF(float x) { unsafe.putFloat(base, offset, x); }
-        double getStaticD() { return unsafe.getDouble(base, offset); }
-        void setStaticD(double x) { unsafe.putDouble(base, offset, x); }
-        boolean getStaticZ() { return unsafe.getBoolean(base, offset); }
-        void setStaticZ(boolean x) { unsafe.putBoolean(base, offset, x); }
-        byte getStaticB() { return unsafe.getByte(base, offset); }
-        void setStaticB(byte x) { unsafe.putByte(base, offset, x); }
-        short getStaticS() { return unsafe.getShort(base, offset); }
-        void setStaticS(short x) { unsafe.putShort(base, offset, x); }
-        char getStaticC() { return unsafe.getChar(base, offset); }
-        void setStaticC(char x) { unsafe.putChar(base, offset, x); }
-        Object /*V*/ getStaticL() { return unsafe.getObject(base, offset); }
-        void setStaticL(Object /*V*/ x) { unsafe.putObject(base, offset, x); }
-
-        static String fname(Class<?> vclass, boolean isSetter, boolean isStatic) {
-            String stem;
-            if (!isStatic)
-                stem = (!isSetter ? "getField" : "setField");
-            else
-                stem = (!isSetter ? "getStatic" : "setStatic");
-            return stem + Wrapper.basicTypeChar(vclass);
-        }
-        static MethodType ftype(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
-            MethodType type;
-            if (!isStatic) {
-                if (!isSetter)
-                    return MethodType.methodType(vclass, cclass);
-                else
-                    return MethodType.methodType(void.class, cclass, vclass);
-            } else {
-                if (!isSetter)
-                    return MethodType.methodType(vclass);
-                else
-                    return MethodType.methodType(void.class, vclass);
-            }
-        }
-        static MethodHandle fhandle(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
-            String name = FieldAccessor.fname(vclass, isSetter, isStatic);
-            if (cclass.isPrimitive())  throw newIllegalArgumentException("primitive "+cclass);
-            Class<?> ecclass = Object.class;  //erase this type
-            Class<?> evclass = vclass;
-            if (!evclass.isPrimitive())  evclass = Object.class;
-            MethodType type = FieldAccessor.ftype(ecclass, evclass, isSetter, isStatic);
-            MethodHandle mh;
-            try {
-                mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
-            } catch (ReflectiveOperationException ex) {
-                throw uncaughtException(ex);
-            }
-            if (evclass != vclass || (!isStatic && ecclass != cclass)) {
-                MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
-                strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
-                mh = convertArguments(mh, strongType, 0);
-            }
-            return mh;
+        @Override
+        public MethodHandle asFixedArity() {
+            return target;
         }
 
-        /// Support for array element access
-        static final HashMap<Class<?>, MethodHandle[]> ARRAY_CACHE =
-                new HashMap<Class<?>, MethodHandle[]>();
-        // FIXME: Cache on the classes themselves, not here.
-        static boolean doCache(Class<?> elemClass) {
-            if (elemClass.isPrimitive())  return true;
-            ClassLoader cl = elemClass.getClassLoader();
-            return cl == null || cl == ClassLoader.getSystemClassLoader();
+        @Override
+        public MethodHandle asType(MethodType newType) {
+            MethodType type = this.type();
+            int collectArg = type.parameterCount() - 1;
+            int newArity = newType.parameterCount();
+            if (newArity == collectArg+1 &&
+                type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
+                // if arity and trailing parameter are compatible, do normal thing
+                return asFixedArity().asType(newType);
+            }
+            // check cache
+            if (cache.type().parameterCount() == newArity)
+                return cache.asType(newType);
+            // build and cache a collector
+            int arrayLength = newArity - collectArg;
+            MethodHandle collector;
+            try {
+                collector = asFixedArity().asCollector(arrayType, arrayLength);
+            } catch (IllegalArgumentException ex) {
+                throw new WrongMethodTypeException("cannot build collector");
+            }
+            cache = collector;
+            return collector.asType(newType);
         }
-        static int getElementI(int[] a, int i) { return a[i]; }
-        static void setElementI(int[] a, int i, int x) { a[i] = x; }
-        static long getElementJ(long[] a, int i) { return a[i]; }
-        static void setElementJ(long[] a, int i, long x) { a[i] = x; }
-        static float getElementF(float[] a, int i) { return a[i]; }
-        static void setElementF(float[] a, int i, float x) { a[i] = x; }
-        static double getElementD(double[] a, int i) { return a[i]; }
-        static void setElementD(double[] a, int i, double x) { a[i] = x; }
-        static boolean getElementZ(boolean[] a, int i) { return a[i]; }
-        static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; }
-        static byte getElementB(byte[] a, int i) { return a[i]; }
-        static void setElementB(byte[] a, int i, byte x) { a[i] = x; }
-        static short getElementS(short[] a, int i) { return a[i]; }
-        static void setElementS(short[] a, int i, short x) { a[i] = x; }
-        static char getElementC(char[] a, int i) { return a[i]; }
-        static void setElementC(char[] a, int i, char x) { a[i] = x; }
-        static Object getElementL(Object[] a, int i) { return a[i]; }
-        static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
-        static <V> V getElementL(Class<V[]> aclass, V[] a, int i) { return aclass.cast(a)[i]; }
-        static <V> void setElementL(Class<V[]> aclass, V[] a, int i, V x) { aclass.cast(a)[i] = x; }
 
-        static String aname(Class<?> aclass, boolean isSetter) {
-            Class<?> vclass = aclass.getComponentType();
-            if (vclass == null)  throw new IllegalArgumentException();
-            return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(vclass);
+        @Override
+        MethodHandle setVarargs(MemberName member) {
+            if (member.isVarargs())  return this;
+            return asFixedArity();
         }
-        static MethodType atype(Class<?> aclass, boolean isSetter) {
-            Class<?> vclass = aclass.getComponentType();
-            if (!isSetter)
-                return MethodType.methodType(vclass, aclass, int.class);
-            else
-                return MethodType.methodType(void.class, aclass, int.class, vclass);
+
+        @Override
+        MethodHandle viewAsType(MethodType newType) {
+            MethodHandle mh = super.viewAsType(newType);
+            // put back the varargs bit:
+            MethodType type = mh.type();
+            int arity = type.parameterCount();
+            return mh.asVarargsCollector(type.parameterType(arity-1));
         }
-        static MethodHandle ahandle(Class<?> aclass, boolean isSetter) {
-            Class<?> vclass = aclass.getComponentType();
-            String name = FieldAccessor.aname(aclass, isSetter);
-            Class<?> caclass = null;
-            if (!vclass.isPrimitive() && vclass != Object.class) {
-                caclass = aclass;
-                aclass = Object[].class;
-                vclass = Object.class;
-            }
-            MethodType type = FieldAccessor.atype(aclass, isSetter);
-            if (caclass != null)
-                type = type.insertParameterTypes(0, Class.class);
-            MethodHandle mh;
-            try {
-                mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
-            } catch (ReflectiveOperationException ex) {
-                throw uncaughtException(ex);
-            }
-            if (caclass != null) {
-                MethodType strongType = FieldAccessor.atype(caclass, isSetter);
-                mh = mh.bindTo(caclass);
-                mh = convertArguments(mh, strongType, 0);
-            }
-            return mh;
+
+        @Override
+        MemberName internalMemberName() {
+            return asFixedArity().internalMemberName();
+        }
+
+
+        @Override
+        MethodHandle bindArgument(int pos, char basicType, Object value) {
+            return asFixedArity().bindArgument(pos, basicType, value);
+        }
+
+        @Override
+        MethodHandle bindReceiver(Object receiver) {
+            return asFixedArity().bindReceiver(receiver);
+        }
+
+        @Override
+        MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+            return asFixedArity().dropArguments(srcType, pos, drops);
+        }
+
+        @Override
+        MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+            return asFixedArity().permuteArguments(newType, reorder);
         }
     }
 
-    /** Bind a predetermined first argument to the given direct method handle.
-     * Callable only from MethodHandles.
-     * @param token Proof that the caller has access to this package.
-     * @param target Any direct method handle.
-     * @param receiver Receiver (or first static method argument) to pre-bind.
-     * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
-     */
-    static
-    MethodHandle bindReceiver(MethodHandle target, Object receiver) {
-        if (receiver == null)  return null;
-        if (target instanceof AdapterMethodHandle &&
-            ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY
-            ) {
-            Object info = MethodHandleNatives.getTargetInfo(target);
-            if (info instanceof DirectMethodHandle) {
-                DirectMethodHandle dmh = (DirectMethodHandle) info;
-                if (dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
-                    MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
-                    MethodType newType = target.type().dropParameterTypes(0, 1);
-                    return convertArguments(bmh, newType, bmh.type(), 0);
+    /** Factory method:  Spread selected argument. */
+    static MethodHandle makeSpreadArguments(MethodHandle target,
+                                            Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
+        MethodType targetType = target.type();
+
+        for (int i = 0; i < spreadArgCount; i++) {
+            Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
+            if (arg == null)  arg = Object.class;
+            targetType = targetType.changeParameterType(spreadArgPos + i, arg);
+        }
+        target = target.asType(targetType);
+
+        MethodType srcType = targetType
+                .replaceParameterTypes(spreadArgPos, spreadArgPos + spreadArgCount, spreadArgType);
+        // Now build a LambdaForm.
+        MethodType lambdaType = srcType.invokerType();
+        Name[] names = arguments(spreadArgCount + 2, lambdaType);
+        int nameCursor = lambdaType.parameterCount();
+        int[] indexes = new int[targetType.parameterCount()];
+
+        for (int i = 0, argIndex = 1; i < targetType.parameterCount() + 1; i++, argIndex++) {
+            Class<?> src = lambdaType.parameterType(i);
+            if (i == spreadArgPos) {
+                // Spread the array.
+                MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
+                Name array = names[argIndex];
+                names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
+                for (int j = 0; j < spreadArgCount; i++, j++) {
+                    indexes[i] = nameCursor;
+                    names[nameCursor++] = new Name(aload, array, j);
                 }
+            } else if (i < indexes.length) {
+                indexes[i] = argIndex;
             }
         }
-        if (target instanceof DirectMethodHandle)
-            return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
-        return null;   // let caller try something else
+        assert(nameCursor == names.length-1);  // leave room for the final call
+
+        // Build argument array for the call.
+        Name[] targetArgs = new Name[targetType.parameterCount()];
+        for (int i = 0; i < targetType.parameterCount(); i++) {
+            int idx = indexes[i];
+            targetArgs[i] = names[idx];
+        }
+        names[names.length - 1] = new Name(target, (Object[]) targetArgs);
+
+        LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names);
+        return new SimpleMethodHandle(srcType, form);
     }
 
-    /** Bind a predetermined argument to the given arbitrary method handle.
-     * Callable only from MethodHandles.
-     * @param token Proof that the caller has access to this package.
-     * @param target Any method handle.
-     * @param receiver Argument (which can be a boxed primitive) to pre-bind.
-     * @return a suitable BoundMethodHandle
-     */
-    static
-    MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) {
-        return new BoundMethodHandle(target, receiver, argnum);
+    static void checkSpreadArgument(Object av, int n) {
+        if (av == null) {
+            if (n == 0)  return;
+        } else if (av instanceof Object[]) {
+            int len = ((Object[])av).length;
+            if (len == n)  return;
+        } else {
+            int len = java.lang.reflect.Array.getLength(av);
+            if (len == n)  return;
+        }
+        // fall through to error:
+        throw newIllegalArgumentException("Array is not of length "+n);
     }
 
-    static MethodHandle permuteArguments(MethodHandle target,
-                                                MethodType newType,
-                                                MethodType oldType,
-                                                int[] permutationOrNull) {
-        assert(oldType.parameterCount() == target.type().parameterCount());
-        int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
-        if (permutationOrNull.length != outargs)
-            throw newIllegalArgumentException("wrong number of arguments in permutation");
-        // Make the individual outgoing argument types match up first.
-        Class<?>[] callTypeArgs = new Class<?>[outargs];
-        for (int i = 0; i < outargs; i++)
-            callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
-        MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
-        target = convertArguments(target, callType, oldType, 0);
-        assert(target != null);
-        oldType = target.type();
-        List<Integer> goal = new ArrayList<Integer>();  // i*TOKEN
-        List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
-        List<Integer> drops = new ArrayList<Integer>(); // not tokens
-        List<Integer> dups = new ArrayList<Integer>();  // not tokens
-        final int TOKEN = 10; // to mark items which are symbolic only
-        // state represents the argument values coming into target
-        for (int i = 0; i < outargs; i++) {
-            state.add(permutationOrNull[i] * TOKEN);
+    private static final NamedFunction NF_checkSpreadArgument;
+    static {
+        try {
+            NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
+                    .getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
+            NF_checkSpreadArgument.resolve();
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex);
         }
-        // goal represents the desired state
-        for (int i = 0; i < inargs; i++) {
-            if (state.contains(i * TOKEN)) {
-                goal.add(i * TOKEN);
-            } else {
-                // adapter must initially drop all unused arguments
-                drops.add(i);
-            }
-        }
-        // detect duplications
-        while (state.size() > goal.size()) {
-            for (int i2 = 0; i2 < state.size(); i2++) {
-                int arg1 = state.get(i2);
-                int i1 = state.indexOf(arg1);
-                if (i1 != i2) {
-                    // found duplicate occurrence at i2
-                    int arg2 = (inargs++) * TOKEN;
-                    state.set(i2, arg2);
-                    dups.add(goal.indexOf(arg1));
-                    goal.add(arg2);
-                }
-            }
-        }
-        assert(state.size() == goal.size());
-        int size = goal.size();
-        while (!state.equals(goal)) {
-            // Look for a maximal sequence of adjacent misplaced arguments,
-            // and try to rotate them into place.
-            int bestRotArg = -10 * TOKEN, bestRotLen = 0;
-            int thisRotArg = -10 * TOKEN, thisRotLen = 0;
-            for (int i = 0; i < size; i++) {
-                int arg = state.get(i);
-                // Does this argument match the current run?
-                if (arg == thisRotArg + TOKEN) {
-                    thisRotArg = arg;
-                    thisRotLen += 1;
-                    if (bestRotLen < thisRotLen) {
-                        bestRotLen = thisRotLen;
-                        bestRotArg = thisRotArg;
-                    }
-                } else {
-                    // The old sequence (if any) stops here.
-                    thisRotLen = 0;
-                    thisRotArg = -10 * TOKEN;
-                    // But maybe a new one starts here also.
-                    int wantArg = goal.get(i);
-                    final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
-                    if (arg != wantArg &&
-                        arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
-                        arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
-                        thisRotArg = arg;
-                        thisRotLen = 1;
-                    }
-                }
-            }
-            if (bestRotLen >= 2) {
-                // Do a rotation if it can improve argument positioning
-                // by at least 2 arguments.  This is not always optimal,
-                // but it seems to catch common cases.
-                int dstEnd = state.indexOf(bestRotArg);
-                int srcEnd = goal.indexOf(bestRotArg);
-                int rotBy = dstEnd - srcEnd;
-                int dstBeg = dstEnd - (bestRotLen - 1);
-                int srcBeg = srcEnd - (bestRotLen - 1);
-                assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
-                // Make a span which covers both source and destination.
-                int rotBeg = Math.min(dstBeg, srcBeg);
-                int rotEnd = Math.max(dstEnd, srcEnd);
-                int score = 0;
-                for (int i = rotBeg; i <= rotEnd; i++) {
-                    if ((int)state.get(i) != (int)goal.get(i))
-                        score += 1;
-                }
-                List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
-                Collections.rotate(rotSpan, -rotBy);  // reverse direction
-                for (int i = rotBeg; i <= rotEnd; i++) {
-                    if ((int)state.get(i) != (int)goal.get(i))
-                        score -= 1;
-                }
-                if (score >= 2) {
-                    // Improved at least two argument positions.  Do it.
-                    List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
-                    Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
-                    MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
-                    MethodHandle nextTarget
-                            = AdapterMethodHandle.makeRotateArguments(rotType, target,
-                                    rotBeg, rotSpan.size(), rotBy);
-                    if (nextTarget != null) {
-                        //System.out.println("Rot: "+rotSpan+" by "+rotBy);
-                        target = nextTarget;
-                        oldType = rotType;
-                        continue;
-                    }
-                }
-                // Else de-rotate, and drop through to the swap-fest.
-                Collections.rotate(rotSpan, rotBy);
-            }
-
-            // Now swap like the wind!
-            List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
-            for (int i = 0; i < size; i++) {
-                // What argument do I want here?
-                int arg = goal.get(i);
-                if (arg != state.get(i)) {
-                    // Where is it now?
-                    int j = state.indexOf(arg);
-                    Collections.swap(ptypes, i, j);
-                    MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
-                    target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
-                    if (target == null)  throw newIllegalArgumentException("cannot swap");
-                    assert(target.type() == swapType);
-                    oldType = swapType;
-                    Collections.swap(state, i, j);
-                }
-            }
-            // One pass of swapping must finish the job.
-            assert(state.equals(goal));
-        }
-        while (!dups.isEmpty()) {
-            // Grab a contiguous trailing sequence of dups.
-            int grab = dups.size() - 1;
-            int dupArgPos = dups.get(grab), dupArgCount = 1;
-            while (grab - 1 >= 0) {
-                int dup0 = dups.get(grab - 1);
-                if (dup0 != dupArgPos - 1)  break;
-                dupArgPos -= 1;
-                dupArgCount += 1;
-                grab -= 1;
-            }
-            //if (dupArgCount > 1)  System.out.println("Dup: "+dups.subList(grab, dups.size()));
-            dups.subList(grab, dups.size()).clear();
-            // In the new target type drop that many args from the tail:
-            List<Class<?>> ptypes = oldType.parameterList();
-            ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
-            MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
-            target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
-            if (target == null)
-                throw newIllegalArgumentException("cannot dup");
-            oldType = target.type();
-        }
-        while (!drops.isEmpty()) {
-            // Grab a contiguous initial sequence of drops.
-            int dropArgPos = drops.get(0), dropArgCount = 1;
-            while (dropArgCount < drops.size()) {
-                int drop1 = drops.get(dropArgCount);
-                if (drop1 != dropArgPos + dropArgCount)  break;
-                dropArgCount += 1;
-            }
-            //if (dropArgCount > 1)  System.out.println("Drop: "+drops.subList(0, dropArgCount));
-            drops.subList(0, dropArgCount).clear();
-            List<Class<?>> dropTypes = newType.parameterList()
-                    .subList(dropArgPos, dropArgPos + dropArgCount);
-            MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
-            target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
-            if (target == null)  throw newIllegalArgumentException("cannot drop");
-            oldType = target.type();
-        }
-        target = convertArguments(target, newType, oldType, 0);
-        assert(target != null);
-        return target;
     }
 
-    /*non-public*/ static
-    MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) {
-        MethodType oldType = target.type();
-        if (oldType.equals(newType))
-            return target;
-        assert(level > 1 || oldType.isConvertibleTo(newType));
-        MethodHandle retFilter = null;
-        Class<?> oldRT = oldType.returnType();
-        Class<?> newRT = newType.returnType();
-        if (!VerifyType.isNullConversion(oldRT, newRT)) {
-            if (oldRT == void.class) {
-                Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
-                retFilter = ValueConversions.zeroConstantFunction(wrap);
-            } else {
-                retFilter = MethodHandles.identity(newRT);
-                retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
-            }
-            newType = newType.changeReturnType(oldRT);
+    /** Factory method:  Collect or filter selected argument(s). */
+    static MethodHandle makeCollectArguments(MethodHandle target,
+                MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
+        MethodType targetType = target.type();          // (a..., c, [b...])=>r
+        MethodType collectorType = collector.type();    // (b...)=>c
+        int collectArgCount = collectorType.parameterCount();
+        Class<?> collectValType = collectorType.returnType();
+        int collectValCount = (collectValType == void.class ? 0 : 1);
+        MethodType srcType = targetType                 // (a..., [b...])=>r
+                .dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
+        if (!retainOriginalArgs) {                      // (a..., b...)=>r
+            srcType = srcType.insertParameterTypes(collectArgPos, collectorType.parameterList());
         }
-        MethodHandle res = null;
-        Exception ex = null;
-        try {
-            res = convertArguments(target, newType, oldType, level);
-        } catch (IllegalArgumentException ex1) {
-            ex = ex1;
+        // in  arglist: [0: ...keep1 | cpos: collect...  | cpos+cacount: keep2... ]
+        // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
+        // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
+
+        // Now build a LambdaForm.
+        MethodType lambdaType = srcType.invokerType();
+        Name[] names = arguments(2, lambdaType);
+        final int collectNamePos = names.length - 2;
+        final int targetNamePos  = names.length - 1;
+
+        Name[] collectorArgs = Arrays.copyOfRange(names, 1 + collectArgPos, 1 + collectArgPos + collectArgCount);
+        names[collectNamePos] = new Name(collector, (Object[]) collectorArgs);
+
+        // Build argument array for the target.
+        // Incoming LF args to copy are: [ (mh) headArgs collectArgs tailArgs ].
+        // Output argument array is [ headArgs (collectVal)? (collectArgs)? tailArgs ].
+        Name[] targetArgs = new Name[targetType.parameterCount()];
+        int inputArgPos  = 1;  // incoming LF args to copy to target
+        int targetArgPos = 0;  // fill pointer for targetArgs
+        int chunk = collectArgPos;  // |headArgs|
+        System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+        inputArgPos  += chunk;
+        targetArgPos += chunk;
+        if (collectValType != void.class) {
+            targetArgs[targetArgPos++] = names[collectNamePos];
         }
-        if (res == null) {
-            WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target);
-            wmt.initCause(ex);
-            throw wmt;
+        chunk = collectArgCount;
+        if (retainOriginalArgs) {
+            System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+            targetArgPos += chunk;   // optionally pass on the collected chunk
         }
-        if (retFilter != null)
-            res = MethodHandles.filterReturnValue(res, retFilter);
-        return res;
-    }
+        inputArgPos += chunk;
+        chunk = targetArgs.length - targetArgPos;  // all the rest
+        System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+        assert(inputArgPos + chunk == collectNamePos);  // use of rest of input args also
+        names[targetNamePos] = new Name(target, (Object[]) targetArgs);
 
-    static MethodHandle convertArguments(MethodHandle target,
-                                                MethodType newType,
-                                                MethodType oldType,
-                                                int level) {
-        assert(oldType.parameterCount() == target.type().parameterCount());
-        if (newType == oldType)
-            return target;
-        if (oldType.parameterCount() != newType.parameterCount())
-            throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
-        return AdapterMethodHandle.makePairwiseConvert(newType, target, level);
-    }
-
-    static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) {
-        MethodType oldType = target.type();
-        int nargs = oldType.parameterCount();
-        int keepPosArgs = nargs - arrayLength;
-        MethodType newType = oldType
-                .dropParameterTypes(keepPosArgs, nargs)
-                .insertParameterTypes(keepPosArgs, arrayType);
-        return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength);
-    }
-    // called internally only
-    static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) {
-        int arrayLength = target.type().parameterCount() - spreadArgPos;
-        return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength);
-    }
-    static MethodHandle spreadArguments(MethodHandle target,
-                                               MethodType newType,
-                                               int spreadArgPos,
-                                               Class<?> arrayType,
-                                               int arrayLength) {
-        // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
-        MethodType oldType = target.type();
-        // spread the last argument of newType to oldType
-        assert(arrayLength == oldType.parameterCount() - spreadArgPos);
-        assert(newType.parameterType(spreadArgPos) == arrayType);
-        return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
-    }
-
-    static MethodHandle collectArguments(MethodHandle target,
-                                                int collectArg,
-                                                MethodHandle collector) {
-        MethodType type = target.type();
-        Class<?> collectType = collector.type().returnType();
-        assert(collectType != void.class);  // else use foldArguments
-        if (collectType != type.parameterType(collectArg))
-            target = target.asType(type.changeParameterType(collectArg, collectType));
-        MethodType newType = type
-                .dropParameterTypes(collectArg, collectArg+1)
-                .insertParameterTypes(collectArg, collector.type().parameterArray());
-        return collectArguments(target, newType, collectArg, collector);
-    }
-    static MethodHandle collectArguments(MethodHandle target,
-                                                MethodType newType,
-                                                int collectArg,
-                                                MethodHandle collector) {
-        MethodType oldType = target.type();     // (a...,c)=>r
-        //         newType                      // (a..., b...)=>r
-        MethodType colType = collector.type();  // (b...)=>c
-        //         oldType                      // (a..., b...)=>r
-        assert(newType.parameterCount() == collectArg + colType.parameterCount());
-        assert(oldType.parameterCount() == collectArg + 1);
-        assert(AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false));
-        return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
-    }
-
-    static MethodHandle filterArgument(MethodHandle target,
-                                       int pos,
-                                       MethodHandle filter) {
-        MethodType ttype = target.type();
-        MethodType ftype = filter.type();
-        assert(ftype.parameterCount() == 1);
-        return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
-    }
-
-    static MethodHandle foldArguments(MethodHandle target,
-                                      MethodType newType,
-                                      int foldPos,
-                                      MethodHandle combiner) {
-        MethodType oldType = target.type();
-        MethodType ctype = combiner.type();
-        assert(AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true));
-        return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
-    }
-
-    static
-    MethodHandle dropArguments(MethodHandle target,
-                               MethodType newType, int argnum) {
-        int drops = newType.parameterCount() - target.type().parameterCount();
-        return AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops);
+        LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names);
+        return new SimpleMethodHandle(srcType, form);
     }
 
     static
@@ -738,47 +535,42 @@
     MethodHandle makeGuardWithTest(MethodHandle test,
                                    MethodHandle target,
                                    MethodHandle fallback) {
-        // gwt(arg...)
-        // [fold]=> continueAfterTest(z=test(arg...), arg...)
-        // [filter]=> (tf=select(z))(arg...)
-        //    where select(z) = select(z, t, f).bindTo(t, f) => z ? t f
-        // [tailcall]=> tf(arg...)
-        assert(test.type().returnType() == boolean.class);
-        MethodType targetType = target.type();
-        MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class);
-        assert(AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true));
-        // working backwards, as usual:
-        assert(target.type().equals(fallback.type()));
-        MethodHandle tailcall = MethodHandles.exactInvoker(target.type());
-        MethodHandle select = selectAlternative();
-        select = bindArgument(select, 2, fallback);
-        select = bindArgument(select, 1, target);
-        // select(z: boolean) => (z ? target : fallback)
-        MethodHandle filter = filterArgument(tailcall, 0, select);
-        assert(filter.type().parameterType(0) == boolean.class);
-        MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test);
-        return fold;
+        MethodType basicType = target.type().basicType();
+        MethodHandle invokeBasic = MethodHandles.basicInvoker(basicType);
+        int arity = basicType.parameterCount();
+        int extraNames = 3;
+        MethodType lambdaType = basicType.invokerType();
+        Name[] names = arguments(extraNames, lambdaType);
+
+        Object[] testArgs   = Arrays.copyOfRange(names, 1, 1 + arity, Object[].class);
+        Object[] targetArgs = Arrays.copyOfRange(names, 0, 1 + arity, Object[].class);
+
+        // call test
+        names[arity + 1] = new Name(test, testArgs);
+
+        // call selectAlternative
+        Object[] selectArgs = { names[arity + 1], target, fallback };
+        names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs);
+        targetArgs[0] = names[arity + 2];
+
+        // call target or fallback
+        names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs);
+
+        LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names);
+        return new SimpleMethodHandle(target.type(), form);
     }
 
-    private static class GuardWithCatch extends BoundMethodHandle {
+    private static class GuardWithCatch {
         private final MethodHandle target;
         private final Class<? extends Throwable> exType;
         private final MethodHandle catcher;
+        // FIXME: Build the control flow out of foldArguments.
         GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
-            this(INVOKES[target.type().parameterCount()], target, exType, catcher);
-        }
-        // FIXME: Build the control flow out of foldArguments.
-        GuardWithCatch(MethodHandle invoker,
-                       MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
-            super(invoker);
             this.target = target;
             this.exType = exType;
             this.catcher = catcher;
         }
-        @Override
-        String debugString() {
-            return addTypeString(target, this);
-        }
+        @LambdaForm.Hidden
         private Object invoke_V(Object... av) throws Throwable {
             try {
                 return target.invokeExact(av);
@@ -787,6 +579,7 @@
                 return catcher.invokeExact(t, av);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L0() throws Throwable {
             try {
                 return target.invokeExact();
@@ -795,6 +588,7 @@
                 return catcher.invokeExact(t);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L1(Object a0) throws Throwable {
             try {
                 return target.invokeExact(a0);
@@ -803,6 +597,7 @@
                 return catcher.invokeExact(t, a0);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L2(Object a0, Object a1) throws Throwable {
             try {
                 return target.invokeExact(a0, a1);
@@ -811,6 +606,7 @@
                 return catcher.invokeExact(t, a0, a1);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
             try {
                 return target.invokeExact(a0, a1, a2);
@@ -819,6 +615,7 @@
                 return catcher.invokeExact(t, a0, a1, a2);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
             try {
                 return target.invokeExact(a0, a1, a2, a3);
@@ -827,6 +624,7 @@
                 return catcher.invokeExact(t, a0, a1, a2, a3);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
             try {
                 return target.invokeExact(a0, a1, a2, a3, a4);
@@ -835,6 +633,7 @@
                 return catcher.invokeExact(t, a0, a1, a2, a3, a4);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
             try {
                 return target.invokeExact(a0, a1, a2, a3, a4, a5);
@@ -843,6 +642,7 @@
                 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
             try {
                 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
@@ -851,6 +651,7 @@
                 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6);
             }
         }
+        @LambdaForm.Hidden
         private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
             try {
                 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
@@ -860,7 +661,7 @@
             }
         }
         static MethodHandle[] makeInvokes() {
-            ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>();
+            ArrayList<MethodHandle> invokes = new ArrayList<>();
             MethodHandles.Lookup lookup = IMPL_LOOKUP;
             for (;;) {
                 int nargs = invokes.size();
@@ -901,42 +702,62 @@
             MethodType gtype = type.generic();
             MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
             // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0)
-            MethodHandle gtarget = convertArguments(target, gtype, type, 2);
-            MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 2);
-            MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
-            if (gtarget == null || gcatcher == null || gguard == null)  return null;
-            return convertArguments(gguard, type, gtype, 2);
+            MethodHandle gtarget = makePairwiseConvert(target, gtype, 2);
+            MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2);
+            GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
+            if (gtarget == null || gcatcher == null)  throw new InternalError();
+            MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard);
+            return makePairwiseConvert(ginvoker, type, 2);
         } else {
-            MethodType gtype = MethodType.genericMethodType(0, true);
-            MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
-            MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0);
+            MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs);
             catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class));
-            MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1);
-            MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher);
-            if (gtarget == null || gcatcher == null || gguard == null)  return null;
-            return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type);
+            MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs);
+            GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
+            if (gtarget == null || gcatcher == null)  throw new InternalError();
+            MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard);
+            return makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
         }
     }
 
     static
     MethodHandle throwException(MethodType type) {
-        return AdapterMethodHandle.makeRetypeRaw(type, throwException());
+        assert(Throwable.class.isAssignableFrom(type.parameterType(0)));
+        int arity = type.parameterCount();
+        if (arity > 1) {
+            return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1);
+        }
+        return makePairwiseConvert(throwException(), type, 2);
     }
 
     static MethodHandle THROW_EXCEPTION;
     static MethodHandle throwException() {
-        if (THROW_EXCEPTION != null)  return THROW_EXCEPTION;
+        MethodHandle mh = THROW_EXCEPTION;
+        if (mh != null)  return mh;
         try {
-            THROW_EXCEPTION
+            mh
             = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
                     MethodType.methodType(Empty.class, Throwable.class));
         } catch (ReflectiveOperationException ex) {
             throw new RuntimeException(ex);
         }
-        return THROW_EXCEPTION;
+        THROW_EXCEPTION = mh;
+        return mh;
     }
     static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
 
+    static MethodHandle FAKE_METHOD_HANDLE_INVOKE;
+    static
+    MethodHandle fakeMethodHandleInvoke(MemberName method) {
+        MethodType type = method.getInvocationType();
+        assert(type.equals(MethodType.methodType(Object.class, Object[].class)));
+        MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE;
+        if (mh != null)  return mh;
+        mh = throwException(type.insertParameterTypes(0, UnsupportedOperationException.class));
+        mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle"));
+        FAKE_METHOD_HANDLE_INVOKE = mh;
+        return mh;
+    }
+
     /**
      * Create an alias for the method handle which, when called,
      * appears to be called from the same class loader and protection domain
@@ -1035,7 +856,9 @@
             MethodType mt = mh.type();
             int arity = mt.parameterCount();
             MethodHandle vamh = mh.asType(mt.generic());
+            vamh.internalForm().compileToBytecode();  // eliminate LFI stack frames
             vamh = vamh.asSpreader(Object[].class, arity);
+            vamh.internalForm().compileToBytecode();  // eliminate LFI stack frames
             return vamh;
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java	Tue Jul 24 10:47:44 2012 -0700
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+import java.lang.invoke.MethodHandleNatives.Constants;
+
+//Not yet public: public
+class MethodHandleInfo {
+   public static final int
+       REF_NONE                    = Constants.REF_NONE,
+       REF_getField                = Constants.REF_getField,
+       REF_getStatic               = Constants.REF_getStatic,
+       REF_putField                = Constants.REF_putField,
+       REF_putStatic               = Constants.REF_putStatic,
+       REF_invokeVirtual           = Constants.REF_invokeVirtual,
+       REF_invokeStatic            = Constants.REF_invokeStatic,
+       REF_invokeSpecial           = Constants.REF_invokeSpecial,
+       REF_newInvokeSpecial        = Constants.REF_newInvokeSpecial,
+       REF_invokeInterface         = Constants.REF_invokeInterface;
+
+   private final Class<?> declaringClass;
+   private final String name;
+   private final MethodType methodType;
+   private final int referenceKind;
+
+   public MethodHandleInfo(MethodHandle mh) throws ReflectiveOperationException {
+       MemberName mn = mh.internalMemberName();
+       this.declaringClass = mn.getDeclaringClass();
+       this.name = mn.getName();
+       this.methodType = mn.getMethodType();
+       this.referenceKind = mn.getReferenceKind();
+   }
+
+   public Class<?> getDeclaringClass() {
+       return declaringClass;
+   }
+
+   public String getName() {
+       return name;
+   }
+
+   public MethodType getMethodType() {
+       return methodType;
+   }
+
+   public int getReferenceKind() {
+       return referenceKind;
+   }
+}
--- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Tue Jul 24 10:47:44 2012 -0700
@@ -29,6 +29,7 @@
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 
 /**
@@ -41,76 +42,28 @@
 
     private MethodHandleNatives() { } // static only
 
-    /// MethodName support
+    /// MemberName support
 
     static native void init(MemberName self, Object ref);
     static native void expand(MemberName self);
-    static native void resolve(MemberName self, Class<?> caller);
+    static native MemberName resolve(MemberName self, Class<?> caller) throws LinkageError;
     static native int getMembers(Class<?> defc, String matchName, String matchSig,
             int matchFlags, Class<?> caller, int skip, MemberName[] results);
 
+    /// Field layout queries parallel to sun.misc.Unsafe:
+    static native long objectFieldOffset(MemberName self);  // e.g., returns vmindex
+    static native long staticFieldOffset(MemberName self);  // e.g., returns vmindex
+    static native Object staticFieldBase(MemberName self);  // e.g., returns clazz
+    static native Object getMemberVMInfo(MemberName self);  // returns {vmindex,vmtarget}
+
     /// MethodHandle support
 
-    /** Initialize the method handle to adapt the call. */
-    static native void init(AdapterMethodHandle self, MethodHandle target, int argnum);
-    /** Initialize the method handle to call the correct method, directly. */
-    static native void init(BoundMethodHandle self, Object target, int argnum);
-    /** Initialize the method handle to call as if by an invoke* instruction. */
-    static native void init(DirectMethodHandle self, Object ref, boolean doDispatch, Class<?> caller);
-
-    /** Initialize a method type, once per form. */
-    static native void init(MethodType self);
-
-    /** Fetch the vmtarget field.
-     *  It will be sanitized as necessary to avoid exposing non-Java references.
-     *  This routine is for debugging and reflection.
-     */
-    static native Object getTarget(MethodHandle self, int format);
-
-    /** Fetch the name of the handled method, if available.
-     *  This routine is for debugging and reflection.
-     */
-    static MemberName getMethodName(MethodHandle self) {
-        return (MemberName) getTarget(self, ETF_METHOD_NAME);
-    }
-
-    /** Fetch the reflective version of the handled method, if available.
-     */
-    static AccessibleObject getTargetMethod(MethodHandle self) {
-        return (AccessibleObject) getTarget(self, ETF_REFLECT_METHOD);
-    }
-
-    /** Fetch the target of this method handle.
-     *  If it directly targets a method, return a MemberName for the method.
-     *  If it is chained to another method handle, return that handle.
-     */
-    static Object getTargetInfo(MethodHandle self) {
-        return getTarget(self, ETF_HANDLE_OR_METHOD_NAME);
-    }
-
-    static Object[] makeTarget(Class<?> defc, String name, String sig, int mods, Class<?> refc) {
-        return new Object[] { defc, name, sig, mods, refc };
-    }
-
     /** Fetch MH-related JVM parameter.
      *  which=0 retrieves MethodHandlePushLimit
      *  which=1 retrieves stack slot push size (in address units)
      */
     static native int getConstant(int which);
 
-    /** Java copy of MethodHandlePushLimit in range 2..255. */
-    static final int JVM_PUSH_LIMIT;
-    /** JVM stack motion (in words) after one slot is pushed, usually -1.
-     */
-    static final int JVM_STACK_MOVE_UNIT;
-
-    /** Which conv-ops are implemented by the JVM? */
-    static final int CONV_OP_IMPLEMENTED_MASK;
-    /** Derived mode flag.  Only false on some old JVM implementations. */
-    static final boolean HAVE_RICOCHET_FRAMES;
-
-    static final int OP_ROT_ARGS_DOWN_LIMIT_BIAS;
-
     static final boolean COUNT_GWT;
 
     /// CallSite support
@@ -122,17 +75,11 @@
     private static native void registerNatives();
     static {
         registerNatives();
-        int k;
-        JVM_PUSH_LIMIT              = getConstant(Constants.GC_JVM_PUSH_LIMIT);
-        JVM_STACK_MOVE_UNIT         = getConstant(Constants.GC_JVM_STACK_MOVE_UNIT);
-        k                           = getConstant(Constants.GC_CONV_OP_IMPLEMENTED_MASK);
-        CONV_OP_IMPLEMENTED_MASK    = (k != 0) ? k : DEFAULT_CONV_OP_IMPLEMENTED_MASK;
-        k                           = getConstant(Constants.GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS);
-        OP_ROT_ARGS_DOWN_LIMIT_BIAS = (k != 0) ? (byte)k : -1;
-        HAVE_RICOCHET_FRAMES        = (CONV_OP_IMPLEMENTED_MASK & (1<<OP_COLLECT_ARGS)) != 0;
         COUNT_GWT                   = getConstant(Constants.GC_COUNT_GWT) != 0;
-        //sun.reflect.Reflection.registerMethodsToFilter(MethodHandleImpl.class, "init");
-    }
+
+        // The JVM calls MethodHandleNatives.<clinit>.  Cascade the <clinit> calls as needed:
+        MethodHandleImpl.initStatics();
+}
 
     // All compile-time constants go here.
     // There is an opportunity to check them against the JVM's idea of them.
@@ -140,16 +87,8 @@
         Constants() { } // static only
         // MethodHandleImpl
         static final int // for getConstant
-                GC_JVM_PUSH_LIMIT = 0,
-                GC_JVM_STACK_MOVE_UNIT = 1,
-                GC_CONV_OP_IMPLEMENTED_MASK = 2,
-                GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS = 3,
-                GC_COUNT_GWT = 4;
-        static final int
-                ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
-                ETF_DIRECT_HANDLE         = 1, // ultimate method handle (will be a DMH, may be self)
-                ETF_METHOD_NAME           = 2, // ultimate method as MemberName
-                ETF_REFLECT_METHOD        = 3; // ultimate method as java.lang.reflect object (sans refClass)
+                GC_COUNT_GWT = 4,
+                GC_LAMBDA_SUPPORT = 5;
 
         // MemberName
         // The JVM uses values of -2 and above for vtable indexes.
@@ -162,65 +101,11 @@
                 MN_IS_CONSTRUCTOR      = 0x00020000, // constructor
                 MN_IS_FIELD            = 0x00040000, // field
                 MN_IS_TYPE             = 0x00080000, // nested type
-                MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers
-                MN_SEARCH_INTERFACES   = 0x00200000, // for MHN.getMembers
-                VM_INDEX_UNINITIALIZED = -99;
-
-        // BoundMethodHandle
-        /** Constants for decoding the vmargslot field, which contains 2 values. */
-        static final int
-            ARG_SLOT_PUSH_SHIFT = 16,
-            ARG_SLOT_MASK = (1<<ARG_SLOT_PUSH_SHIFT)-1;
-
-        // AdapterMethodHandle
-        /** Conversions recognized by the JVM.
-         *  They must align with the constants in java.lang.invoke.AdapterMethodHandle,
-         *  in the JVM file hotspot/src/share/vm/classfile/javaClasses.hpp.
-         */
-        static final int
-            OP_RETYPE_ONLY   = 0x0, // no argument changes; straight retype
-            OP_RETYPE_RAW    = 0x1, // straight retype, trusted (void->int, Object->T)
-            OP_CHECK_CAST    = 0x2, // ref-to-ref conversion; requires a Class argument
-            OP_PRIM_TO_PRIM  = 0x3, // converts from one primitive to another
-            OP_REF_TO_PRIM   = 0x4, // unboxes a wrapper to produce a primitive
-            OP_PRIM_TO_REF   = 0x5, // boxes a primitive into a wrapper
-            OP_SWAP_ARGS     = 0x6, // swap arguments (vminfo is 2nd arg)
-            OP_ROT_ARGS      = 0x7, // rotate arguments (vminfo is displaced arg)
-            OP_DUP_ARGS      = 0x8, // duplicates one or more arguments (at TOS)
-            OP_DROP_ARGS     = 0x9, // remove one or more argument slots
-            OP_COLLECT_ARGS  = 0xA, // combine arguments using an auxiliary function
-            OP_SPREAD_ARGS   = 0xB, // expand in place a varargs array (of known size)
-            OP_FOLD_ARGS     = 0xC, // combine but do not remove arguments; prepend result
-            //OP_UNUSED_13   = 0xD, // unused code, perhaps for reified argument lists
-            CONV_OP_LIMIT    = 0xE; // limit of CONV_OP enumeration
-        /** Shift and mask values for decoding the AMH.conversion field.
-         *  These numbers are shared with the JVM for creating AMHs.
-         */
-        static final int
-            CONV_OP_MASK     = 0xF00, // this nybble contains the conversion op field
-            CONV_TYPE_MASK   = 0x0F,  // fits T_ADDRESS and below
-            CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
-            CONV_VMINFO_SHIFT     =  0, // position of bits in CONV_VMINFO_MASK
-            CONV_OP_SHIFT         =  8, // position of bits in CONV_OP_MASK
-            CONV_DEST_TYPE_SHIFT  = 12, // byte 2 has the adapter BasicType (if needed)
-            CONV_SRC_TYPE_SHIFT   = 16, // byte 2 has the source BasicType (if needed)
-            CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change
-            CONV_STACK_MOVE_MASK  = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1;
-
-        /** Which conv-ops are implemented by the JVM? */
-        static final int DEFAULT_CONV_OP_IMPLEMENTED_MASK =
-                // Value to use if the corresponding JVM query fails.
-                ((1<<OP_RETYPE_ONLY)
-                |(1<<OP_RETYPE_RAW)
-                |(1<<OP_CHECK_CAST)
-                |(1<<OP_PRIM_TO_PRIM)
-                |(1<<OP_REF_TO_PRIM)
-                |(1<<OP_SWAP_ARGS)
-                |(1<<OP_ROT_ARGS)
-                |(1<<OP_DUP_ARGS)
-                |(1<<OP_DROP_ARGS)
-                //|(1<<OP_SPREAD_ARGS)
-                );
+                MN_REFERENCE_KIND_SHIFT = 24, // refKind
+                MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
+                // The SEARCH_* bits are not for MN.flags but for the matchFlags argument of MHN.getMembers:
+                MN_SEARCH_SUPERCLASSES = 0x00100000,
+                MN_SEARCH_INTERFACES   = 0x00200000;
 
         /**
          * Basic types as encoded in the JVM.  These code values are not
@@ -243,9 +128,54 @@
             T_ILLEGAL  = 99;
 
         /**
+         * Constant pool entry types.
+         */
+        static final byte
+            CONSTANT_Utf8                = 1,
+            CONSTANT_Integer             = 3,
+            CONSTANT_Float               = 4,
+            CONSTANT_Long                = 5,
+            CONSTANT_Double              = 6,
+            CONSTANT_Class               = 7,
+            CONSTANT_String              = 8,
+            CONSTANT_Fieldref            = 9,
+            CONSTANT_Methodref           = 10,
+            CONSTANT_InterfaceMethodref  = 11,
+            CONSTANT_NameAndType         = 12,
+            CONSTANT_MethodHandle        = 15,  // JSR 292
+            CONSTANT_MethodType          = 16,  // JSR 292
+            CONSTANT_InvokeDynamic       = 18,
+            CONSTANT_LIMIT               = 19;   // Limit to tags found in classfiles
+
+        /**
+         * Access modifier flags.
+         */
+        static final char
+            ACC_PUBLIC                 = 0x0001,
+            ACC_PRIVATE                = 0x0002,
+            ACC_PROTECTED              = 0x0004,
+            ACC_STATIC                 = 0x0008,
+            ACC_FINAL                  = 0x0010,
+            ACC_SYNCHRONIZED           = 0x0020,
+            ACC_VOLATILE               = 0x0040,
+            ACC_TRANSIENT              = 0x0080,
+            ACC_NATIVE                 = 0x0100,
+            ACC_INTERFACE              = 0x0200,
+            ACC_ABSTRACT               = 0x0400,
+            ACC_STRICT                 = 0x0800,
+            ACC_SYNTHETIC              = 0x1000,
+            ACC_ANNOTATION             = 0x2000,
+            ACC_ENUM                   = 0x4000,
+            // aliases:
+            ACC_SUPER                  = ACC_SYNCHRONIZED,
+            ACC_BRIDGE                 = ACC_VOLATILE,
+            ACC_VARARGS                = ACC_TRANSIENT;
+
+        /**
          * Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
          */
-        static final int
+        static final byte
+            REF_NONE                    = 0,  // null value
             REF_getField                = 1,
             REF_getStatic               = 2,
             REF_putField                = 3,
@@ -254,9 +184,67 @@
             REF_invokeStatic            = 6,
             REF_invokeSpecial           = 7,
             REF_newInvokeSpecial        = 8,
-            REF_invokeInterface         = 9;
+            REF_invokeInterface         = 9,
+            REF_LIMIT                  = 10;
     }
 
+    static boolean refKindIsValid(int refKind) {
+        return (refKind > REF_NONE && refKind < REF_LIMIT);
+    }
+    static boolean refKindIsField(byte refKind) {
+        assert(refKindIsValid(refKind));
+        return (refKind <= REF_putStatic);
+    }
+    static boolean refKindIsGetter(byte refKind) {
+        assert(refKindIsValid(refKind));
+        return (refKind <= REF_getStatic);
+    }
+    static boolean refKindIsSetter(byte refKind) {
+        return refKindIsField(refKind) && !refKindIsGetter(refKind);
+    }
+    static boolean refKindIsMethod(byte refKind) {
+        return !refKindIsField(refKind) && (refKind != REF_newInvokeSpecial);
+    }
+    static boolean refKindHasReceiver(byte refKind) {
+        assert(refKindIsValid(refKind));
+        return (refKind & 1) != 0;
+    }
+    static boolean refKindIsStatic(byte refKind) {
+        return !refKindHasReceiver(refKind) && (refKind != REF_newInvokeSpecial);
+    }
+    static boolean refKindDoesDispatch(byte refKind) {
+        assert(refKindIsValid(refKind));
+        return (refKind == REF_invokeVirtual ||
+                refKind == REF_invokeInterface);
+    }
+    static {
+        final int HR_MASK = ((1 << REF_getField) |
+                             (1 << REF_putField) |
+                             (1 << REF_invokeVirtual) |
+                             (1 << REF_invokeSpecial) |
+                             (1 << REF_invokeInterface)
+                            );
+        for (byte refKind = REF_NONE+1; refKind < REF_LIMIT; refKind++) {
+            assert(refKindHasReceiver(refKind) == (((1<<refKind) & HR_MASK) != 0)) : refKind;
+        }
+    }
+    static String refKindName(byte refKind) {
+        assert(refKindIsValid(refKind));
+        return REFERENCE_KIND_NAME[refKind];
+    }
+    private static String[] REFERENCE_KIND_NAME = {
+            null,
+            "getField",
+            "getStatic",
+            "putField",
+            "putStatic",
+            "invokeVirtual",
+            "invokeStatic",
+            "invokeSpecial",
+            "newInvokeSpecial",
+            "invokeInterface"
+    };
+
     private static native int getNamedCon(int which, Object[] name);
     static boolean verifyConstants() {
         Object[] box = { null };
@@ -275,16 +263,11 @@
                     continue;
                 }
                 throw new InternalError(err);
-            } catch (Exception ex) {
-                if (ex instanceof NoSuchFieldException) {
-                    String err = (name+": JVM has "+vmval+" which Java does not define");
-                    // ignore exotic ops the JVM cares about; we just wont issue them
-                    if (name.startsWith("OP_") || name.startsWith("GC_")) {
-                        System.err.println("warning: "+err);
-                        continue;
-                    }
-                }
-                throw new InternalError(name+": access failed, got "+ex);
+            } catch (NoSuchFieldException | IllegalAccessException ex) {
+                String err = (name+": JVM has "+vmval+" which Java does not define");
+                // ignore exotic ops the JVM cares about; we just wont issue them
+                //System.err.println("warning: "+err);
+                continue;
             }
         }
         return true;
@@ -299,18 +282,21 @@
     /**
      * The JVM is linking an invokedynamic instruction.  Create a reified call site for it.
      */
-    static CallSite makeDynamicCallSite(MethodHandle bootstrapMethod,
-                                        String name, MethodType type,
-                                        Object info,
-                                        MemberName callerMethod, int callerBCI) {
-        return CallSite.makeSite(bootstrapMethod, name, type, info, callerMethod, callerBCI);
-    }
-
-    /**
-     * Called by the JVM to check the length of a spread array.
-     */
-    static void checkSpreadArgument(Object av, int n) {
-        MethodHandleStatics.checkSpreadArgument(av, n);
+    static MemberName linkCallSite(Object callerObj,
+                                   Object bootstrapMethodObj,
+                                   Object nameObj, Object typeObj,
+                                   Object staticArguments,
+                                   Object[] appendixResult) {
+        MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
+        Class<?> caller = (Class<?>)callerObj;
+        String name = nameObj.toString().intern();
+        MethodType type = (MethodType)typeObj;
+        appendixResult[0] = CallSite.makeSite(bootstrapMethod,
+                                              name,
+                                              type,
+                                              staticArguments,
+                                              caller);
+        return Invokers.linkToCallSiteMethod(type);
     }
 
     /**
@@ -321,71 +307,64 @@
     }
 
     /**
-     * The JVM wants to use a MethodType with inexact invoke.  Give the runtime fair warning.
+     * The JVM wants to link a call site that requires a dynamic type check.
+     * Name is a type-checking invoker, invokeExact or invoke.
+     * Return a JVM method (MemberName) to handle the invoking.
+     * The method assumes the following arguments on the stack:
+     * 0: the method handle being invoked
+     * 1-N: the arguments to the method handle invocation
+     * N+1: an implicitly added type argument (the given MethodType)
      */
-    static void notifyGenericMethodType(MethodType type) {
-        type.form().notifyGenericMethodType();
+    static MemberName linkMethod(Class<?> callerClass, int refKind,
+                                 Class<?> defc, String name, Object type,
+                                 Object[] appendixResult) {
+        if (!TRACE_METHOD_LINKAGE)
+            return linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
+        return linkMethodTracing(callerClass, refKind, defc, name, type, appendixResult);
     }
-
-    /**
-     * The JVM wants to raise an exception.  Here's the path.
-     */
-    static void raiseException(int code, Object actual, Object required) {
-        String message = null;
-        switch (code) {
-        case 190: // arraylength
-            try {
-                String reqLength = "";
-                if (required instanceof AdapterMethodHandle) {
-                    int conv = ((AdapterMethodHandle)required).getConversion();
-                    int spChange = AdapterMethodHandle.extractStackMove(conv);
-                    reqLength = " of length "+(spChange+1);
-                }
-                int actualLength = actual == null ? 0 : java.lang.reflect.Array.getLength(actual);
-                message = "required array"+reqLength+", but encountered wrong length "+actualLength;
-                break;
-            } catch (IllegalArgumentException ex) {
-            }
-            required = Object[].class;  // should have been an array
-            code = 192; // checkcast
-            break;
-        case 191: // athrow
-            // JVM is asking us to wrap an exception which happened during resolving
-            if (required == BootstrapMethodError.class) {
-                throw new BootstrapMethodError((Throwable) actual);
-            }
-            break;
+    static MemberName linkMethodImpl(Class<?> callerClass, int refKind,
+                                     Class<?> defc, String name, Object type,
+                                     Object[] appendixResult) {
+        if (defc != MethodHandle.class || refKind != REF_invokeVirtual)
+            throw new LinkageError("no such method "+defc.getName()+"."+name+type);
+        switch (name) {
+        case "invoke":
+            return Invokers.genericInvokerMethod(callerClass, type, appendixResult);
+        case "invokeExact":
+            return Invokers.exactInvokerMethod(callerClass, type, appendixResult);
         }
-        // disregard the identity of the actual object, if it is not a class:
-        if (message == null) {
-            if (!(actual instanceof Class) && !(actual instanceof MethodType))
-                actual = actual.getClass();
-           if (actual != null)
-               message = "required "+required+" but encountered "+actual;
-           else
-               message = "required "+required;
-        }
-        switch (code) {
-        case 190: // arraylength
-            throw new ArrayIndexOutOfBoundsException(message);
-        case 50: //_aaload
-            throw new ClassCastException(message);
-        case 192: // checkcast
-            throw new ClassCastException(message);
-        default:
-            throw new InternalError("unexpected code "+code+": "+message);
+        throw new UnsupportedOperationException("linkMethod "+name);
+    }
+    // Tracing logic:
+    static MemberName linkMethodTracing(Class<?> callerClass, int refKind,
+                                        Class<?> defc, String name, Object type,
+                                        Object[] appendixResult) {
+        System.out.println("linkMethod "+defc.getName()+"."+
+                           name+type+"/"+Integer.toHexString(refKind));
+        try {
+            MemberName res = linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
+            System.out.println("linkMethod => "+res+" + "+appendixResult[0]);
+            return res;
+        } catch (Throwable ex) {
+            System.out.println("linkMethod => throw "+ex);
+            throw ex;
         }
     }
 
     /**
      * The JVM is resolving a CONSTANT_MethodHandle CP entry.  And it wants our help.
      * It will make an up-call to this method.  (Do not change the name or signature.)
+     * The type argument is a Class for field requests and a MethodType for non-fields.
+     * <p>
+     * Recent versions of the JVM may also pass a resolved MemberName for the type.
+     * In that case, the name is ignored and may be null.
      */
     static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
                                                  Class<?> defc, String name, Object type) {
         try {
             Lookup lookup = IMPL_LOOKUP.in(callerClass);
-            return lookup.linkMethodHandleConstant(refKind, defc, name, type);
+            assert(refKindIsValid(refKind));
+            return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);
         } catch (ReflectiveOperationException ex) {
             Error err = new IncompatibleClassChangeError();
             err.initCause(ex);
--- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java	Tue Jul 24 10:47:44 2012 -0700
@@ -27,6 +27,7 @@
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import sun.misc.Unsafe;
 
 /**
  * This class consists exclusively of static names internal to the
@@ -38,16 +39,30 @@
 
     private MethodHandleStatics() { }  // do not instantiate
 
+    static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
     static final boolean DEBUG_METHOD_HANDLE_NAMES;
+    static final boolean DUMP_CLASS_FILES;
+    static final boolean TRACE_INTERPRETER;
+    static final boolean TRACE_METHOD_LINKAGE;
+    static final Integer COMPILE_THRESHOLD;
     static {
-        final Object[] values = { false };
+        final Object[] values = { false, false, false, false, null };
         AccessController.doPrivileged(new PrivilegedAction<Void>() {
                 public Void run() {
                     values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+                    values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES");
+                    values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
+                    values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
+                    values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
                     return null;
                 }
             });
         DEBUG_METHOD_HANDLE_NAMES = (Boolean) values[0];
+        DUMP_CLASS_FILES          = (Boolean) values[1];
+        TRACE_INTERPRETER         = (Boolean) values[2];
+        TRACE_METHOD_LINKAGE      = (Boolean) values[3];
+        COMPILE_THRESHOLD         = (Integer) values[4];
     }
 
     /*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
@@ -55,7 +70,7 @@
             type = target.type();
         MemberName name = null;
         if (target != null)
-            name = MethodHandleNatives.getMethodName(target);
+            name = target.internalMemberName();
         if (name == null)
             return "invoke" + type;
         return name.getName() + type;
@@ -77,20 +92,6 @@
         return str + target.type();
     }
 
-    static void checkSpreadArgument(Object av, int n) {
-        if (av == null) {
-            if (n == 0)  return;
-        } else if (av instanceof Object[]) {
-            int len = ((Object[])av).length;
-            if (len == n)  return;
-        } else {
-            int len = java.lang.reflect.Array.g