changeset 455:393962c8f84e

meth-count: debugging counters; meth-lfc: lambda form caching; relabel/reorder series file
author jrose
date Sun, 15 Sep 2013 17:15:12 -0700
parents 27753ae4c64a
children cf0407968713
files meth-counts.patch meth-lfc.patch series
diffstat 3 files changed, 8384 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/meth-counts.patch	Sun Sep 15 17:15:12 2013 -0700
@@ -0,0 +1,1005 @@
+Event counters for measuring JSR-292 internals.
+
+diff --git a/src/share/classes/java/lang/invoke/BoundMethodHandle.java b/src/share/classes/java/lang/invoke/BoundMethodHandle.java
+--- a/src/share/classes/java/lang/invoke/BoundMethodHandle.java
++++ b/src/share/classes/java/lang/invoke/BoundMethodHandle.java
+@@ -55,8 +55,24 @@
+ 
+     /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
+         super(type, form);
++        if (COUNT_EVENTS)  countBMH();
+     }
+ 
++    private void countBMH() {
++        int fc = fieldCount();
++        assert(fc >= 0);
++        switch (fc) {
++        case 0:  bump(EC_methodHandle_bound_arg0);  break;
++        case 1:  bump(EC_methodHandle_bound_arg1);  break;
++        case 2:  bump(EC_methodHandle_bound_arg2);  break;
++        case 3:  bump(EC_methodHandle_bound_arg3);  break;
++        case 4:  bump(EC_methodHandle_bound_arg4);  break;
++        default: bump(EC_methodHandle_bound_arg5up);
++        }
++    }
++
++    private int fieldCount() { return speciesData().fieldCount(); }
++
+     //
+     // BMH API and internals
+     //
+@@ -860,4 +876,13 @@
+      * All subclasses must provide such a value describing their type signature.
+      */
+     static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
++
++    // Event counters
++    private static final EventCounter EC_methodHandle_bound = eventCounter(EC_methodHandle, "bound", true);  // autoSum=true
++    private static final EventCounter EC_methodHandle_bound_arg0 = eventCounter(EC_methodHandle_bound, "arg0");
++    private static final EventCounter EC_methodHandle_bound_arg1 = eventCounter(EC_methodHandle_bound, "arg1");
++    private static final EventCounter EC_methodHandle_bound_arg2 = eventCounter(EC_methodHandle_bound, "arg2");
++    private static final EventCounter EC_methodHandle_bound_arg3 = eventCounter(EC_methodHandle_bound, "arg3");
++    private static final EventCounter EC_methodHandle_bound_arg4 = eventCounter(EC_methodHandle_bound, "arg4");
++    private static final EventCounter EC_methodHandle_bound_arg5up = eventCounter(EC_methodHandle_bound, "arg5up");
+ }
+diff --git a/src/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+--- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java
++++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+@@ -63,6 +63,7 @@
+         }
+ 
+         this.member = member;
++        bump(EC_methodHandle_direct);
+     }
+ 
+     // Factory methods:
+@@ -665,6 +666,9 @@
+         return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT);
+     }
+ 
++    // Event counters
++    private static final EventCounter EC_methodHandle_direct = eventCounter(EC_methodHandle, "direct");
++
+     private static final NamedFunction
+             NF_internalMemberName,
+             NF_internalMemberNameEnsureInit,
+diff --git a/src/share/classes/java/lang/invoke/Invokers.java b/src/share/classes/java/lang/invoke/Invokers.java
+--- a/src/share/classes/java/lang/invoke/Invokers.java
++++ b/src/share/classes/java/lang/invoke/Invokers.java
+@@ -358,6 +358,11 @@
+     Object checkGenericType(Object mhObj, Object expectedObj) {
+         MethodHandle mh = (MethodHandle) mhObj;
+         MethodType expected = (MethodType) expectedObj;
++        if (COUNT_EVENTS) {
++            bump(EC_checkGenericType);
++            if (mh.type() == expected)  bump(EC_checkGenericType_exact);
++            else  bump(EC_checkGenericType_miss);
++        }
+         if (mh.type() == expected)  return mh;
+         MethodHandle atc = mh.asTypeCache;
+         if (atc != null && atc.type() == expected)  return atc;
+@@ -390,6 +395,10 @@
+         LambdaForm lform = callSiteForm(mtype, true);
+         return lform.vmentry;
+     }
++    private static final EventCounter EC_checkGenericType = eventCounter("checkGenericType");
++    private static final EventCounter EC_checkGenericType_exact = eventCounter(EC_checkGenericType, "exact");
++    private static final EventCounter EC_checkGenericType_hit = eventCounter(EC_checkGenericType, "hit");
++    private static final EventCounter EC_checkGenericType_miss = eventCounter(EC_checkGenericType, "miss");
+ 
+     // skipCallSite is true if we are optimizing a ConstantCallSite
+     private static LambdaForm callSiteForm(MethodType mtype, boolean skipCallSite) {
+diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java
+--- a/src/share/classes/java/lang/invoke/LambdaForm.java
++++ b/src/share/classes/java/lang/invoke/LambdaForm.java
+@@ -138,6 +138,7 @@
+         this.names = names.clone();
+         this.debugName = debugName;
+         normalize();
++        bump(EC_lambdaForm);
+     }
+ 
+     LambdaForm(String debugName,
+@@ -174,6 +175,8 @@
+         assert(nameRefsAreLegal());
+         assert(isEmpty());
+         assert(sig.equals(basicTypeSignature()));
++        bump(EC_lambdaForm);
++        bump(EC_lambdaForm_blank);
+     }
+ 
+     private static Name[] buildEmptyNames(int arity, String basicTypeSignature) {
+@@ -441,6 +444,10 @@
+         LambdaForm prep = getPreparedForm(basicTypeSignature());
+         this.vmentry = prep.vmentry;
+         // TO DO: Maybe add invokeGeneric, invokeWithArguments
++        bump(EC_lambdaForm_prep);
++        if (false && COUNT_EVENTS)//@@
++        if ((EC_lambdaForm_prep.count() & (0x2000-1)) == 0)  Thread.dumpStack();//@@
++
+     }
+ 
+     /** Generate optimizable bytecode for this form. */
+@@ -455,6 +462,7 @@
+             if (TRACE_INTERPRETER && INIT_DONE)
+                 traceInterpreter("compileToBytecode", this);
+             isCompiled = true;
++            bump(EC_lambdaForm_comp);
+             return vmentry;
+         } catch (Error | Exception ex) {
+             throw newInternalError("compileToBytecode", ex);
+@@ -595,6 +603,7 @@
+     @DontInline
+     /** Interpretively invoke this form on the given arguments. */
+     Object interpretWithArguments(Object... argumentValues) throws Throwable {
++        bump(EC_lambdaForm_interp);
+         if (TRACE_INTERPRETER && INIT_DONE)
+             return interpretWithArgumentsTracing(argumentValues);
+         checkInvocationCounter();
+@@ -981,7 +990,9 @@
+             this.member = member;
+             //resolvedHandle = eraseSubwordTypes(resolvedHandle);
+             this.resolvedHandle = resolvedHandle;
++            bump(EC_namedFunction);
+         }
++        private static final EventCounter EC_namedFunction = eventCounter("namedFunction");
+         NamedFunction(MethodType basicInvokerType) {
+             assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
+             if (basicInvokerType.parameterSlotCount() < MethodType.MAX_MH_INVOKER_ARITY) {
+@@ -1134,6 +1145,7 @@
+             if (mh2 != null)  return mh2;  // benign race
+             if (!mh.type().equals(INVOKER_METHOD_TYPE))
+                 throw new InternalError(mh.debugString());
++            bump(EC_lambdaForm_invoker);
+             return typeForm.namedFunctionInvoker = mh;
+         }
+ 
+@@ -1141,6 +1153,7 @@
+         Object invokeWithArguments(Object... arguments) throws Throwable {
+             // If we have a cached invoker, call it right away.
+             // NOTE: The invoker always returns a reference value.
++            bump(EC_lambdaForm_interp_name);
+             if (TRACE_INTERPRETER && INIT_DONE)  return invokeWithArgumentsTracing(arguments);
+             assert(checkArgumentTypes(arguments, methodType()));
+             return invoker().invokeBasic(resolvedHandle(), arguments);
+@@ -1580,12 +1593,6 @@
+     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.
+      */
+@@ -1635,6 +1642,19 @@
+     static final native Object linkToInterface(Object x1, MemberName mn) throws Throwable;
+  */
+ 
+-    static { NamedFunction.initializeInvokers(); }
++    // Event counters
++    private static final EventCounter EC_lambdaForm_blank = eventCounter(EC_lambdaForm, "blank");
++    private static final EventCounter EC_lambdaForm_prep = eventCounter(EC_lambdaForm, "prep");
++    private static final EventCounter EC_lambdaForm_comp = eventCounter(EC_lambdaForm, "comp");
++    private static final EventCounter EC_lambdaForm_interp = eventCounter(EC_lambdaForm, "interp");
++    private static final EventCounter EC_lambdaForm_interp_name = eventCounter(EC_lambdaForm_interp, "name");
++    private static final EventCounter EC_lambdaForm_invoker = eventCounter(EC_lambdaForm, "invoker");
++
++    // Put this last, so that previous static inits can run before.
++    static {
++        if (USE_PREDEFINED_INTERPRET_METHODS)
++            PREPARED_FORMS.putAll(computeInitialPreparedForms());
++        NamedFunction.initializeInvokers();
++    }
+     static final boolean INIT_DONE = Boolean.TRUE.booleanValue();
+ }
+diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java
+--- a/src/share/classes/java/lang/invoke/MemberName.java
++++ b/src/share/classes/java/lang/invoke/MemberName.java
+@@ -448,6 +448,15 @@
+                                                lookupClass, ALL_ACCESS|MethodHandles.Lookup.PACKAGE);
+     }
+ 
++    // Event counters
++    private static final EventCounter EC_natives = eventCounter("natives");
++    private static final EventCounter EC_natives_expand = eventCounter(EC_natives, "expand");
++    private static final EventCounter EC_natives_initMethod = eventCounter(EC_natives, "initMethod");
++    private static final EventCounter EC_natives_initConstructor = eventCounter(EC_natives, "initConstructor");
++    private static final EventCounter EC_natives_initField = eventCounter(EC_natives, "initField");
++    private static final EventCounter EC_natives_getMembers = eventCounter(EC_natives, "getMembers");
++    private static final EventCounter EC_natives_resolve = eventCounter(EC_natives, "resolve");
++
+     /** 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)
+@@ -467,6 +476,7 @@
+         if (!isResolved())  return;
+         if (type instanceof Object[])
+             type = null;  // don't saddle JVM w/ typeInfo
++        bump(EC_natives_expand);
+         MethodHandleNatives.expand(this);
+     }
+ 
+@@ -485,6 +495,7 @@
+     public MemberName(Method m, boolean wantSpecial) {
+         m.getClass();  // NPE check
+         // fill in vmtarget, vmindex while we have m in hand:
++        bump(EC_natives_initMethod);
+         MethodHandleNatives.init(this, m);
+         if (clazz == null) {  // MHN.init failed
+             if (m.getDeclaringClass() == MethodHandle.class &&
+@@ -553,6 +564,7 @@
+     public MemberName(Constructor<?> ctor) {
+         ctor.getClass();  // NPE check
+         // fill in vmtarget, vmindex while we have ctor in hand:
++        bump(EC_natives_initConstructor);
+         MethodHandleNatives.init(this, ctor);
+         assert(isResolved() && this.clazz != null);
+         this.name = CONSTRUCTOR_NAME;
+@@ -568,6 +580,7 @@
+     public MemberName(Field fld, boolean makeSetter) {
+         fld.getClass();  // NPE check
+         // fill in vmtarget, vmindex while we have fld in hand:
++        bump(EC_natives_initField);
+         MethodHandleNatives.init(this, fld);
+         assert(isResolved() && this.clazz != null);
+         this.name = fld.getName();
+@@ -872,6 +885,7 @@
+             ArrayList<MemberName[]> bufs = null;
+             int bufCount = 0;
+             for (;;) {
++                bump(EC_natives_getMembers);
+                 bufCount = MethodHandleNatives.getMembers(defc,
+                         matchName, matchSig, matchFlags,
+                         lookupClass,
+@@ -920,6 +934,7 @@
+             MemberName m = ref.clone();  // JVM will side-effect the ref
+             assert(refKind == m.getReferenceKind());
+             try {
++                bump(EC_natives_resolve);
+                 m = MethodHandleNatives.resolve(m, lookupClass);
+                 m.checkForTypeAlias();
+                 m.resolution = null;
+diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java
+--- a/src/share/classes/java/lang/invoke/MethodHandle.java
++++ b/src/share/classes/java/lang/invoke/MethodHandle.java
+@@ -31,8 +31,6 @@
+ 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,
+@@ -445,6 +443,7 @@
+         this.form = form;
+ 
+         form.prepare();  // TO DO:  Try to delay this step until just before invocation.
++        bump(EC_methodHandle);
+     }
+ 
+     /**
+@@ -715,7 +714,7 @@
+      * <li>If the return type <em>T0</em> is void and <em>T1</em> a primitive,
+      *     a zero value is introduced.
+      * </ul>
+-    * (<em>Note:</em> Both <em>T0</em> and <em>T1</em> may be regarded as static types,
++     * (<em>Note:</em> Both <em>T0</em> and <em>T1</em> may be regarded as static types,
+      * because neither corresponds specifically to the <em>dynamic type</em> of any
+      * actual argument or return value.)
+      * <p>
+@@ -741,6 +740,7 @@
+      * @see MethodHandles#explicitCastArguments
+      */
+     public MethodHandle asType(MethodType newType) {
++        bump(EC_transform_asType);
+         // Fast path alternative to a heavyweight {@code asType} call.
+         // Return 'this' if the conversion will be a no-op.
+         if (newType == type) {
+@@ -839,6 +839,7 @@
+      * @see #asCollector
+      */
+     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
++        bump(EC_transform_asSpreader);
+         asSpreaderChecks(arrayType, arrayLength);
+         int spreadArgPos = type.parameterCount() - arrayLength;
+         return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
+@@ -953,6 +954,7 @@
+      * @see #asVarargsCollector
+      */
+     public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
++        bump(EC_transform_asCollector);
+         asCollectorChecks(arrayType, arrayLength);
+         int collectArgPos = type().parameterCount()-1;
+         MethodHandle target = this;
+@@ -1125,6 +1127,7 @@
+      * @see #asFixedArity
+      */
+     public MethodHandle asVarargsCollector(Class<?> arrayType) {
++        bump(EC_transform_asVarargsCollector);
+         Class<?> arrayElement = arrayType.getComponentType();
+         boolean lastMatch = asCollectorChecks(arrayType, 0);
+         if (isVarargsCollector() && lastMatch)
+@@ -1227,6 +1230,7 @@
+      * @see MethodHandles#insertArguments
+      */
+     public MethodHandle bindTo(Object x) {
++        bump(EC_transform_bindTo);
+         Class<?> ptype;
+         @SuppressWarnings("LocalVariableHidesMemberVariable")
+         MethodType type = type();
+@@ -1468,4 +1472,11 @@
+             throw newInternalError(ex);
+         }
+     }
++
++    // Event counters
++    private static final EventCounter EC_transform_asType = eventCounter(EC_transform, "asType");
++    private static final EventCounter EC_transform_asSpreader = eventCounter(EC_transform, "asSpreader");
++    private static final EventCounter EC_transform_asCollector = eventCounter(EC_transform, "asCollector");
++    private static final EventCounter EC_transform_asVarargsCollector = eventCounter(EC_transform, "asVarargsCollector");
++    private static final EventCounter EC_transform_bindTo = eventCounter(EC_transform, "bindTo");
+ }
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+@@ -171,6 +171,7 @@
+         assert(dstType.parameterCount() == target.type().parameterCount());
+         if (srcType == dstType)
+             return target;
++        bump(EC_makePairwiseConvert);
+ 
+         // Calculate extra arguments (temporaries) required in the names array.
+         // FIXME: Use an ArrayList<Name>.  Some arguments require more than one conversion step.
+@@ -184,6 +185,7 @@
+                 level <= 1 && dst.isInterface() && !dst.isAssignableFrom(src)) {
+                 needConv[i] = true;
+                 conversions++;
++                bump(EC_makePairwiseConvert_arg);
+             }
+         }
+         boolean retConv = needConv[INARG_COUNT];
+@@ -321,6 +323,7 @@
+             this.target = target;
+             this.arrayType = arrayType;
+             this.asCollectorCache = target.asCollector(arrayType, 0);
++            bump(EC_methodHandle_varargsCollector);
+         }
+ 
+         @Override MethodHandle reinvokerTarget() { return target; }
+@@ -965,6 +968,7 @@
+             super(type, reinvokerForm(target));
+             this.target = target;
+             this.member = member;
++            bump(EC_methodHandle_wrappedMember);
+         }
+ 
+         @Override
+@@ -997,4 +1001,9 @@
+         return new WrappedMember(target, target.type(), member);
+     }
+ 
++    // Event counters
++    private static final EventCounter EC_makePairwiseConvert = eventCounter("makePairwiseConvert");
++    private static final EventCounter EC_makePairwiseConvert_arg = eventCounter(EC_makePairwiseConvert, "arg");
++    private static final EventCounter EC_methodHandle_varargsCollector = eventCounter(EC_methodHandle, "varargsCollector");
++    private static final EventCounter EC_methodHandle_wrappedMember = eventCounter(EC_methodHandle, "wrappedMember");
+ }
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/share/classes/java/lang/invoke/MethodHandleNatives.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java
+@@ -78,7 +78,7 @@
+ 
+         // 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.
+@@ -301,6 +301,25 @@
+                                    Object nameObj, Object typeObj,
+                                    Object staticArguments,
+                                    Object[] appendixResult) {
++        if (COUNT_EVENTS) {
++            EC_link_callSite.bump();
++            if (staticArguments != null) {
++                EC_link_callSite_arg0.bump();
++            } else {
++                if (!(staticArguments instanceof Object[])) {
++                    EC_link_callSite_arg1.bump();
++                } else {
++                    switch (((Object[])staticArguments).length) {
++                    case 0: break;
++                    case 1: EC_link_callSite_arg1.bump(); break;
++                    case 2: EC_link_callSite_arg2.bump(); break;
++                    case 3: EC_link_callSite_arg3.bump(); break;
++                    case 4: EC_link_callSite_arg4.bump(); break;
++                    default: EC_link_callSite_arg5up.bump(); break;
++                    }
++                }
++            }
++        }
+         MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
+         Class<?> caller = (Class<?>)callerObj;
+         String name = nameObj.toString().intern();
+@@ -311,6 +330,7 @@
+                                               staticArguments,
+                                               caller);
+         if (callSite instanceof ConstantCallSite) {
++            bump(EC_link_callSite_constant);
+             appendixResult[0] = callSite.dynamicInvoker();
+             return Invokers.linkToTargetMethod(type);
+         } else {
+@@ -323,6 +343,7 @@
+      * The JVM wants a pointer to a MethodType.  Oblige it by finding or creating one.
+      */
+     static MethodType findMethodHandleType(Class<?> rtype, Class<?>[] ptypes) {
++        bump(EC_link_methodType);
+         return MethodType.makeImpl(rtype, ptypes, true);
+     }
+ 
+@@ -407,8 +428,10 @@
+             if (defc == MethodHandle.class && refKind == REF_invokeVirtual) {
+                 switch (name) {
+                 case "invoke":
++                    bump(EC_link_invokeGeneric);
+                     return Invokers.genericInvokeLinkerMethod(fixMethodType(callerClass, type), appendixResult);
+                 case "invokeExact":
++                    bump(EC_link_invokeExact);
+                     return Invokers.exactInvokeLinkerMethod(fixMethodType(callerClass, type), appendixResult);
+                 }
+             }
+@@ -453,6 +476,7 @@
+      */
+     static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
+                                                  Class<?> defc, String name, Object type) {
++        bump(EC_link_methodHandle);
+         try {
+             Lookup lookup = IMPL_LOOKUP.in(callerClass);
+             assert(refKindIsValid(refKind));
+@@ -494,4 +518,19 @@
+         return (definingClass.isAssignableFrom(symbolicRefClass) ||  // Msym overrides Mdef
+                 symbolicRefClass.isInterface());                     // Mdef implements Msym
+     }
++
++    // Event counters
++    static final EventCounter EC_link = eventCounter("link");
++    private static final EventCounter EC_link_callSite = eventCounter(EC_link, "callSite");
++    private static final EventCounter EC_link_callSite_constant = eventCounter(EC_link_callSite, "constant");
++    private static final EventCounter EC_link_callSite_arg0 = eventCounter(EC_link_callSite, "arg0");
++    private static final EventCounter EC_link_callSite_arg1 = eventCounter(EC_link_callSite, "arg1");
++    private static final EventCounter EC_link_callSite_arg2 = eventCounter(EC_link_callSite, "arg2");
++    private static final EventCounter EC_link_callSite_arg3 = eventCounter(EC_link_callSite, "arg3");
++    private static final EventCounter EC_link_callSite_arg4 = eventCounter(EC_link_callSite, "arg4");
++    private static final EventCounter EC_link_callSite_arg5up = eventCounter(EC_link_callSite, "arg5up");
++    private static final EventCounter EC_link_methodType = eventCounter(EC_link, "methodType");
++    private static final EventCounter EC_link_invokeGeneric = eventCounter(EC_link, "invokeGeneric");
++    private static final EventCounter EC_link_invokeExact = eventCounter(EC_link, "invokeExact");
++    private static final EventCounter EC_link_methodHandle = eventCounter(EC_link, "methodHandle");
+ }
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleStatics.java b/src/share/classes/java/lang/invoke/MethodHandleStatics.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java
+@@ -28,6 +28,8 @@
+ import java.security.AccessController;
+ import java.security.PrivilegedAction;
+ import sun.misc.Unsafe;
++import java.io.PrintStream;
++import java.util.ArrayList;
+ 
+ /**
+  * This class consists exclusively of static names internal to the
+@@ -45,16 +47,18 @@
+     static final boolean DUMP_CLASS_FILES;
+     static final boolean TRACE_INTERPRETER;
+     static final boolean TRACE_METHOD_LINKAGE;
++    static final boolean COUNT_EVENTS;
+     static final Integer COMPILE_THRESHOLD;
+     static {
+-        final Object[] values = { false, false, false, false, null };
++        final Object[] values = { false, 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");
++                    values[4] = Boolean.getBoolean("java.lang.invoke.MethodHandle.COUNT_EVENTS");
++                    values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
+                     return null;
+                 }
+             });
+@@ -62,7 +66,8 @@
+         DUMP_CLASS_FILES          = (Boolean) values[1];
+         TRACE_INTERPRETER         = (Boolean) values[2];
+         TRACE_METHOD_LINKAGE      = (Boolean) values[3];
+-        COMPILE_THRESHOLD         = (Integer) values[4];
++        COUNT_EVENTS              = (Boolean) values[4];
++        COMPILE_THRESHOLD         = (Integer) values[5];
+     }
+ 
+     /*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
+@@ -128,4 +133,136 @@
+         if (obj != null || obj2 != null)  message = message + ": " + obj + ", " + obj2;
+         return message;
+     }
++    static EventCounter eventCounter(final String name) {
++        return eventCounter(name, false);
++    }
++    static EventCounter eventCounter(final String name, final boolean autoSum) {
++        if (!COUNT_EVENTS)  return null;
++        return AccessController.doPrivileged(new PrivilegedAction<EventCounter>() {
++                public EventCounter run() {
++                    return new EventCounter(null, name, autoSum);
++                }
++            });
++    }
++    static EventCounter eventCounter(final EventCounter sup, final String name) {
++        return eventCounter(sup, name, false);
++    }
++    static EventCounter eventCounter(final EventCounter sup, final String name, final boolean autoSum) {
++        if (!COUNT_EVENTS)  return null;
++        return AccessController.doPrivileged(new PrivilegedAction<EventCounter>() {
++                public EventCounter run() {
++                    return new EventCounter(sup, sup.name+"."+name, autoSum);
++                }
++            });
++    }
++    static long count(EventCounter ec) {
++        if (!COUNT_EVENTS)  return -1;
++        return ec.count();
++    }
++    static void bump(EventCounter ec) {
++        if (!COUNT_EVENTS)  return;
++        ec.bump();
++    }
++    static class EventCounter {
++        final String name;
++        final boolean autoSum;
++        final EventCounter sup;  // used for toString
++        final ArrayList<EventCounter> subs = new ArrayList<>();
++        long count;
++        EventCounter(EventCounter sup, String name, boolean autoSum) {
++            this.name = name.toString();
++            this.autoSum = autoSum;
++            this.sup = sup;
++            ArrayList<EventCounter> ecs = (sup == null ? COUNTERS : sup.subs);
++            for (EventCounter ec : ecs) {
++                if (name.equals(ec.name))  throw new IllegalArgumentException("duplicate: "+ec);
++            }
++            ecs.add(this);
++        }
++        public String toString() {
++            return (sup == null ? "EventCounter:"+name : sup+"."+name);
++        }
++        long count() {
++            if (autoSum && count == 0) {
++                for (EventCounter ec : subs) {
++                    count += ec.count();
++                }
++            }
++            return count;
++        }
++        void bump() {
++            if (autoSum)  throw new IllegalArgumentException("do not bump directly");
++            ++count;
++            if (BLURB_COUNT != 0 && (count & (BLURB_COUNT-1)) == 0)
++                blurb();
++        }
++        static final int BLURB_COUNT = 0;  // must be power of 2 or zero
++        void blurb() {
++            dump(System.err, false, 0);
++        }
++        void dump(PrintStream str) {
++            if (!COUNT_EVENTS)  return;
++            dump(str, true, 0);
++        }
++        void dump(PrintStream str, boolean recursive, long total) {
++            if (isEmpty())  return;
++            if (autoSum)  count();  // compute the sum
++            StringBuilder sb = new StringBuilder();
++            sb.append("java.lang.invoke.MethodHandle.COUNT_EVENTS: ");
++            sb.append(name).append(": ").append(count);
++            if (total != 0)
++                sb.append(String.format("  (%.2f%% of %d)", (100.0 * count) / total, total));
++            if (!recursive || total != 0 || count != 0) {
++                str.println(sb);
++            }
++            if (recursive) {
++                for (EventCounter ec : subs) {
++                    ec.dump(str, recursive, count);
++                }
++            }
++            if (autoSum)  count = 0;  // reset the sum
++
++        }
++        boolean isEmpty() {
++            if (count != 0)  return false;
++            for (EventCounter ec : subs) {
++                if (!ec.isEmpty())  return false;
++            }
++            return true;
++        }
++
++        static ArrayList<EventCounter> COUNTERS = new ArrayList<>();
++        static void dumpAll(PrintStream str) {
++            if (!COUNT_EVENTS)  return;
++            for (EventCounter ec : COUNTERS) {
++                ec.dump(str);
++            }
++        }
++
++        static final Thread SHUT_DOWN_HOOK = new Thread(new Runnable() {
++                public void run() { shutDownHook(); }
++            });
++        static void shutDownHook() {
++            PrintStream str = System.err;
++            try {
++                dumpAll(str);
++            } catch (Throwable ex) {
++                ex.printStackTrace(str);
++            }
++        }
++        static {
++            assert(COUNT_EVENTS);
++            try {
++                Runtime.getRuntime().addShutdownHook(SHUT_DOWN_HOOK);
++            } catch (IllegalStateException e) {
++                // VM already shutting down.
++            }
++        }
++    }
++
++    // Shared event counters
++    static final EventCounter EC_lambdaForm = eventCounter("lambdaForm");
++    static final EventCounter EC_methodHandle = eventCounter("methodHandle");
++    static final EventCounter EC_lookup = eventCounter("lookup", true);  // autoSum=true
++    static final EventCounter EC_transform = eventCounter("transform", true);  // autoSum=true
+ }
+diff --git a/src/share/classes/java/lang/invoke/MethodHandles.java b/src/share/classes/java/lang/invoke/MethodHandles.java
+--- a/src/share/classes/java/lang/invoke/MethodHandles.java
++++ b/src/share/classes/java/lang/invoke/MethodHandles.java
+@@ -75,6 +75,7 @@
+      */
+     @CallerSensitive
+     public static Lookup lookup() {
++        bump(EC_lookup_lookup);
+         return new Lookup(Reflection.getCallerClass());
+     }
+ 
+@@ -93,6 +94,7 @@
+      * @return a lookup object which is trusted minimally
+      */
+     public static Lookup publicLookup() {
++        bump(EC_lookup_publicLookup);
+         return Lookup.PUBLIC_LOOKUP;
+     }
+ 
+@@ -119,6 +121,7 @@
+      */
+     public static <T extends Member> T
+     reflectAs(Class<T> expected, MethodHandle target) {
++        bump(EC_lookup_reflectAs);
+         SecurityManager smgr = System.getSecurityManager();
+         if (smgr != null)  smgr.checkPermission(ACCESS_PERMISSION);
+         Lookup lookup = Lookup.IMPL_LOOKUP;  // use maximally privileged lookup
+@@ -493,6 +496,7 @@
+          * @throws NullPointerException if the argument is null
+          */
+         public Lookup in(Class<?> requestedLookupClass) {
++            bump(EC_lookup_in);
+             requestedLookupClass.getClass();  // null check
+             if (allowedModes == TRUSTED)  // IMPL_LOOKUP can make any lookup at all
+                 return new Lookup(requestedLookupClass, ALL_MODES);
+@@ -615,6 +619,7 @@
+          */
+         public
+         MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
++            bump(EC_lookup_findStatic);
+             MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type);
+             checkSecurityManager(refc, method);
+             return getDirectMethod(REF_invokeStatic, refc, method, findBoundCallerClass(method));
+@@ -664,6 +669,7 @@
+          * @throws NullPointerException if any argument is null
+          */
+         public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
++            bump(EC_lookup_findVirtual);
+             if (refc == MethodHandle.class) {
+                 MethodHandle mh = findVirtualForMH(name, type);
+                 if (mh != null)  return mh;
+@@ -710,6 +716,7 @@
+          * @throws NullPointerException if any argument is null
+          */
+         public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
++            bump(EC_lookup_findConstructor);
+             String name = "<init>";
+             MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type);
+             checkSecurityManager(refc, ctor);
+@@ -752,6 +759,7 @@
+          */
+         public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
+                                         Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
++            bump(EC_lookup_findSpecial);
+             checkSpecialCaller(specialCaller);
+             Lookup specialLookup = this.in(specialCaller);
+             MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
+@@ -777,6 +785,7 @@
+          * @throws NullPointerException if any argument is null
+          */
+         public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
++            bump(EC_lookup_findGetter);
+             MemberName field = resolveOrFail(REF_getField, refc, name, type);
+             checkSecurityManager(refc, field);
+             return getDirectField(REF_getField, refc, field);
+@@ -800,6 +809,7 @@
+          * @throws NullPointerException if any argument is null
+          */
+         public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
++            bump(EC_lookup_findSetter);
+             MemberName field = resolveOrFail(REF_putField, refc, name, type);
+             checkSecurityManager(refc, field);
+             return getDirectField(REF_putField, refc, field);
+@@ -822,6 +832,7 @@
+          * @throws NullPointerException if any argument is null
+          */
+         public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
++            bump(EC_lookup_findStaticGetter);
+             MemberName field = resolveOrFail(REF_getStatic, refc, name, type);
+             checkSecurityManager(refc, field);
+             return getDirectField(REF_getStatic, refc, field);
+@@ -844,6 +855,7 @@
+          * @throws NullPointerException if any argument is null
+          */
+         public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
++            bump(EC_lookup_findStaticSetter);
+             MemberName field = resolveOrFail(REF_putStatic, refc, name, type);
+             checkSecurityManager(refc, field);
+             return getDirectField(REF_putStatic, refc, field);
+@@ -896,6 +908,7 @@
+          * @throws NullPointerException if any argument is null
+          */
+         public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
++            bump(EC_lookup_bind);
+             Class<? extends Object> refc = receiver.getClass(); // may get NPE
+             MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
+             checkSecurityManager(refc, method);
+@@ -925,6 +938,7 @@
+          * @throws NullPointerException if the argument is null
+          */
+         public MethodHandle unreflect(Method m) throws IllegalAccessException {
++            bump(EC_lookup_unreflect);
+             if (m.getDeclaringClass() == MethodHandle.class) {
+                 MethodHandle mh = unreflectForMH(m);
+                 if (mh != null)  return mh;
+@@ -966,6 +980,7 @@
+          * @throws NullPointerException if any argument is null
+          */
+         public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
++            bump(EC_lookup_unreflectSpecial);
+             checkSpecialCaller(specialCaller);
+             Lookup specialLookup = this.in(specialCaller);
+             MemberName method = new MemberName(m, true);
+@@ -997,6 +1012,7 @@
+          */
+         @SuppressWarnings("rawtypes")  // Will be Constructor<?> after JSR 292 MR
+         public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException {
++            bump(EC_lookup_unreflectConstructor);
+             MemberName ctor = new MemberName(c);
+             assert(ctor.isConstructor());
+             Lookup lookup = c.isAccessible() ? IMPL_LOOKUP : this;
+@@ -1018,6 +1034,7 @@
+          * @throws NullPointerException if the argument is null
+          */
+         public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
++            bump(EC_lookup_unreflectGetter);
+             return unreflectField(f, false);
+         }
+         private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
+@@ -1044,6 +1061,7 @@
+          * @throws NullPointerException if the argument is null
+          */
+         public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
++            bump(EC_lookup_unreflectSetter);
+             return unreflectField(f, true);
+         }
+ 
+@@ -1062,6 +1080,7 @@
+          * @since 1.8
+          */
+         public MethodHandleInfo revealDirect(MethodHandle target) {
++            bump(EC_lookup_revealDirect);
+             MemberName member = target.internalMemberName();
+             if (member == null || (!member.isResolved() && !member.isMethodHandleInvoke()))
+                 throw newIllegalArgumentException("not a direct method handle");
+@@ -1446,6 +1465,7 @@
+      */
+     public static
+     MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
++        bump(EC_lookup_arrayElementGetter);
+         return MethodHandleImpl.makeArrayElementAccessor(arrayClass, false);
+     }
+ 
+@@ -1461,6 +1481,7 @@
+      */
+     public static
+     MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
++        bump(EC_lookup_arrayElementSetter);
+         return MethodHandleImpl.makeArrayElementAccessor(arrayClass, true);
+     }
+ 
+@@ -1641,6 +1662,7 @@
+      */
+     public static
+     MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
++        bump(EC_transform_explicitCastArguments);
+         if (!target.type().isCastableTo(newType)) {
+             throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType);
+         }
+@@ -1708,6 +1730,7 @@
+      */
+     public static
+     MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
++        bump(EC_transform_permuteArguments);
+         checkReorder(reorder, newType, target.type());
+         return target.permuteArguments(newType, reorder);
+     }
+@@ -1752,6 +1775,7 @@
+      */
+     public static
+     MethodHandle constant(Class<?> type, Object value) {
++        bump(EC_lookup_constant);
+         if (type.isPrimitive()) {
+             if (type == void.class)
+                 throw newIllegalArgumentException("void type");
+@@ -1771,6 +1795,7 @@
+      */
+     public static
+     MethodHandle identity(Class<?> type) {
++        bump(EC_lookup_identity);
+         if (type == void.class)
+             throw newIllegalArgumentException("void type");
+         else if (type == Object.class)
+@@ -1813,6 +1838,7 @@
+      */
+     public static
+     MethodHandle insertArguments(MethodHandle target, int pos, Object... values) {
++        bump(EC_transform_insertArguments);
+         int insCount = values.length;
+         MethodType oldType = target.type();
+         int outargs = oldType.parameterCount();
+@@ -1892,6 +1918,7 @@
+      */
+     public static
+     MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
++        bump(EC_transform_dropArguments);
+         MethodType oldType = target.type();  // get NPE
+         int dropped = valueTypes.size();
+         MethodType.checkSlotCount(dropped);
+@@ -2022,6 +2049,7 @@
+      */
+     public static
+     MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
++        bump(EC_transform_filterArguments);
+         MethodType targetType = target.type();
+         MethodHandle adapter = target;
+         MethodType adapterType = null;
+@@ -2053,6 +2081,7 @@
+     // FIXME: Make this public in M1.
+     /*non-public*/ static
+     MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle collector) {
++        bump(EC_transform_collectArguments);
+         MethodType targetType = target.type();
+         MethodType filterType = collector.type();
+         if (filterType.returnType() != void.class &&
+@@ -2120,6 +2149,7 @@
+      */
+     public static
+     MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
++        bump(EC_transform_filterReturnValue);
+         MethodType targetType = target.type();
+         MethodType filterType = filter.type();
+         Class<?> rtype = targetType.returnType();
+@@ -2211,6 +2241,7 @@
+      */
+     public static
+     MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
++        bump(EC_transform_foldArguments);
+         int pos = 0;
+         MethodType targetType = target.type();
+         MethodType combinerType = combiner.type();
+@@ -2267,6 +2298,7 @@
+     MethodHandle guardWithTest(MethodHandle test,
+                                MethodHandle target,
+                                MethodHandle fallback) {
++        bump(EC_transform_guardWithTest);
+         MethodType gtype = test.type();
+         MethodType ttype = target.type();
+         MethodType ftype = fallback.type();
+@@ -2337,6 +2369,7 @@
+     MethodHandle catchException(MethodHandle target,
+                                 Class<? extends Throwable> exType,
+                                 MethodHandle handler) {
++        bump(EC_transform_catchException);
+         MethodType ttype = target.type();
+         MethodType htype = handler.type();
+         if (htype.parameterCount() < 1 ||
+@@ -2371,8 +2404,45 @@
+      */
+     public static
+     MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
++        bump(EC_transform_throwException);
+         if (!Throwable.class.isAssignableFrom(exType))
+             throw new ClassCastException(exType.getName());
+         return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType));
+     }
++
++    // Event counters
++    private static final EventCounter EC_lookup_lookup = eventCounter(EC_lookup, "lookup");
++    private static final EventCounter EC_lookup_publicLookup = eventCounter(EC_lookup, "publicLookup");
++    private static final EventCounter EC_lookup_reflectAs = eventCounter(EC_lookup, "reflectAs");
++    private static final EventCounter EC_lookup_in = eventCounter(EC_lookup, "in");
++    private static final EventCounter EC_lookup_findStatic = eventCounter(EC_lookup, "findStatic");
++    private static final EventCounter EC_lookup_findVirtual = eventCounter(EC_lookup, "findVirtual");
++    private static final EventCounter EC_lookup_findConstructor = eventCounter(EC_lookup, "findConstructor");
++    private static final EventCounter EC_lookup_findSpecial = eventCounter(EC_lookup, "findSpecial");
++    private static final EventCounter EC_lookup_findGetter = eventCounter(EC_lookup, "findGetter");
++    private static final EventCounter EC_lookup_findSetter = eventCounter(EC_lookup, "findSetter");
++    private static final EventCounter EC_lookup_findStaticGetter = eventCounter(EC_lookup, "findStaticGetter");
++    private static final EventCounter EC_lookup_findStaticSetter = eventCounter(EC_lookup, "findStaticSetter");
++    private static final EventCounter EC_lookup_bind = eventCounter(EC_lookup, "bind");
++    private static final EventCounter EC_lookup_unreflect = eventCounter(EC_lookup, "unreflect");
++    private static final EventCounter EC_lookup_unreflectSpecial = eventCounter(EC_lookup, "unreflectSpecial");
++    private static final EventCounter EC_lookup_unreflectConstructor = eventCounter(EC_lookup, "unreflectConstructor");
++    private static final EventCounter EC_lookup_unreflectGetter = eventCounter(EC_lookup, "unreflectGetter");
++    private static final EventCounter EC_lookup_unreflectSetter = eventCounter(EC_lookup, "unreflectSetter");
++    private static final EventCounter EC_lookup_revealDirect = eventCounter(EC_lookup, "revealDirect");
++    private static final EventCounter EC_lookup_arrayElementGetter = eventCounter(EC_lookup, "arrayElementGetter");
++    private static final EventCounter EC_lookup_arrayElementSetter = eventCounter(EC_lookup, "arrayElementSetter");
++    private static final EventCounter EC_transform_explicitCastArguments = eventCounter(EC_transform, "explicitCastArguments");
++    private static final EventCounter EC_transform_permuteArguments = eventCounter(EC_transform, "permuteArguments");
++    private static final EventCounter EC_lookup_constant = eventCounter(EC_lookup, "constant");
++    private static final EventCounter EC_lookup_identity = eventCounter(EC_lookup, "identity");
++    private static final EventCounter EC_transform_insertArguments = eventCounter(EC_transform, "insertArguments");
++    private static final EventCounter EC_transform_dropArguments = eventCounter(EC_transform, "dropArguments");
++    private static final EventCounter EC_transform_filterArguments = eventCounter(EC_transform, "filterArguments");
++    private static final EventCounter EC_transform_collectArguments = eventCounter(EC_transform, "collectArguments");
++    private static final EventCounter EC_transform_filterReturnValue = eventCounter(EC_transform, "filterReturnValue");
++    private static final EventCounter EC_transform_foldArguments = eventCounter(EC_transform, "foldArguments");
++    private static final EventCounter EC_transform_guardWithTest = eventCounter(EC_transform, "guardWithTest");
++    private static final EventCounter EC_transform_catchException = eventCounter(EC_transform, "catchException");
++    private static final EventCounter EC_transform_throwException = eventCounter(EC_transform, "throwException");
+ }
+diff --git a/src/share/classes/java/lang/invoke/SimpleMethodHandle.java b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
+--- a/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
++++ b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
+@@ -27,8 +27,7 @@
+ 
+ import static java.lang.invoke.LambdaForm.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
+-import java.util.logging.Level;
+-import java.util.logging.Logger;
++import static java.lang.invoke.MethodHandleStatics.*;
+ 
+ /**
+  * A method handle whose behavior is determined only by its LambdaForm.
+@@ -37,6 +36,7 @@
+ final class SimpleMethodHandle extends MethodHandle {
+     private SimpleMethodHandle(MethodType type, LambdaForm form) {
+         super(type, form);
++        bump(EC_methodHandle_simple);
+     }
+ 
+     /*non-public*/ static SimpleMethodHandle make(MethodType type, LambdaForm form) {
+@@ -67,4 +67,6 @@
+         return new SimpleMethodHandle(mt, lf);
+     }
+ 
++    // Event counters
++    private static final EventCounter EC_methodHandle_simple = eventCounter(EC_methodHandle, "simple");
+ }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/meth-lfc.patch	Sun Sep 15 17:15:12 2013 -0700
@@ -0,0 +1,7370 @@
+Lambda-form caching for common method handle patterns.
+Pending cleanups:  Remove dead code, address "FIXME" comments, etc.
+May help with JDK-8024840: OOME on JRuby tests
+
+diff --git a/src/share/classes/java/lang/invoke/BoundMethodHandle.java b/src/share/classes/java/lang/invoke/BoundMethodHandle.java
+--- a/src/share/classes/java/lang/invoke/BoundMethodHandle.java
++++ b/src/share/classes/java/lang/invoke/BoundMethodHandle.java
+@@ -26,7 +26,7 @@
+ package java.lang.invoke;
+ 
+ import static jdk.internal.org.objectweb.asm.Opcodes.*;
+-import static java.lang.invoke.LambdaForm.basicTypes;
++import static java.lang.invoke.LambdaForm.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
+ import static java.lang.invoke.MethodHandleStatics.*;
+ 
+@@ -71,92 +71,84 @@
+         }
+     }
+ 
+-    private int fieldCount() { return speciesData().fieldCount(); }
+-
+     //
+     // 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 newInternalError(t);
++    /*non-public*/
++    LambdaFormEditor editor() {
++        return form.editor();
++    }
++
++    @Override // there is a default binder in the super class, for 'L' types only
++    /*non-public*/
++    BoundMethodHandle bindArgumentL(int pos, Object value) {
++        return editor().bindArgumentL(this, pos, value);
++    }
++    /*non-public*/
++    BoundMethodHandle bindArgumentI(int pos, int value) {
++        return editor().bindArgumentI(this, pos, value);
++    }
++    /*non-public*/
++    BoundMethodHandle bindArgumentJ(int pos, long value) {
++        return editor().bindArgumentJ(this, pos, value);
++    }
++    /*non-public*/
++    BoundMethodHandle bindArgumentF(int pos, float value) {
++        return editor().bindArgumentF(this, pos, value);
++    }
++    /*non-public*/
++    BoundMethodHandle bindArgumentD(int pos, double value) {
++        return editor().bindArgumentD(this, pos, value);
++    }
++
++    @Override
++    BoundMethodHandle rebind() {
++        if (!tooComplex()) {
++            return this;
++        }
++        bump(EC_methodHandle_rebind_complex);
++        return makeRebind(this);
++    }
++
++    private boolean tooComplex() {
++        return (fieldCount() > FIELD_COUNT_THRESHOLD ||
++                form.expressionCount() > FORM_EXPRESSION_THRESHOLD);
++    }
++    private static int FIELD_COUNT_THRESHOLD = 12;  // largest convenient BMH field count
++    private static int FORM_EXPRESSION_THRESHOLD = 24;  // largest convenient BMH expression count
++
++    static BoundMethodHandle makeRebind(MethodHandle target) {
++        bump(EC_methodHandle_rebind);
++        LambdaForm form = DelegatingMethodHandle.makeReinvokerForm(
++                target, MethodTypeForm.LF_REBIND, Species_L.SPECIES_DATA.getterFunction(0) );
++        return Species_L.make(target.type(), form, target);
++    }
++
++    @Override
++    void compileToBytecode() {
++        super.compileToBytecode();
++        for (int i = 0, len = fieldCount(); i < len; i++) {
++            Object arg = arg(i);
++            if (arg instanceof MethodHandle)
++                ((MethodHandle)arg).compileToBytecode();
+         }
+     }
+ 
+-    static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
+-            return new Species_L(type, form, x);
+-    }
+-
+-    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 newInternalError(t);
+-        }
+-        throw new InternalError("unexpected type: " + xtype);
+-    }
+-
+-    @Override
+-    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);
+-    }
+-
+-    @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 newInternalError(t);
+-         }
+-    }
+-
+-    @Override
+-    MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+-        try {
+-             return clone(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
+-         } catch (Throwable t) {
+-             throw newInternalError(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;
++    static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
++        return Species_L.make(type, form, x);
+     }
+ 
+     /**
+      * 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();
++    public abstract SpeciesData speciesData();
++
++    /**
++     * Return the number of fields in this BMH.  Equivalent to speciesData().fieldCount().
++     */
++    public abstract int fieldCount();
+ 
+     @Override
+     final Object internalProperties() {
+@@ -175,42 +167,29 @@
+     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);
++            case L_TYPE: return          speciesData().getters[i].invokeBasic(this);
++            case I_TYPE: return (int)    speciesData().getters[i].invokeBasic(this);
++            case J_TYPE: return (long)   speciesData().getters[i].invokeBasic(this);
++            case F_TYPE: return (float)  speciesData().getters[i].invokeBasic(this);
++            case D_TYPE: return (double) speciesData().getters[i].invokeBasic(this);
+             }
+         } catch (Throwable ex) {
+             throw newInternalError(ex);
+         }
+-        throw new InternalError("unexpected type: " + speciesData().types+"."+i);
++        throw new InternalError("unexpected type: " + speciesData().typeChars+"."+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 newInternalError(ex);
+-        }
+-    }
++    @Override
++    public abstract BoundMethodHandle copyWith(MethodType mt, LambdaForm lf);
++    public abstract BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg);
++    public abstract BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int    narg);
++    public abstract BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long   narg);
++    public abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float  narg);
++    public abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg);
+ 
+     //
+     // concrete BMH classes required to close bootstrap loops
+@@ -219,150 +198,103 @@
+     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) {
++        private 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);
++        public int fieldCount() {
++            return 1;
++        }
++        public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class);
++        public static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) {
++            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, argL1, narg);
++        public final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
++            return new Species_L(mt, lf, argL0);
+         }
+         @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);
++        public final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
++            try {
++                return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
++            } catch (Throwable ex) {
++                throw uncaughtException(ex);
++            }
+         }
+         @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);
++        public final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
++            try {
++                return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
++            } catch (Throwable ex) {
++                throw uncaughtException(ex);
++            }
+         }
+         @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);
++        public final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
++            try {
++                return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
++            } catch (Throwable ex) {
++                throw uncaughtException(ex);
++            }
+         }
+         @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);
++        public final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
++            try {
++                return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
++            } catch (Throwable ex) {
++                throw uncaughtException(ex);
++            }
++        }
++        @Override
++        public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
++            try {
++                return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
++            } catch (Throwable ex) {
++                throw uncaughtException(ex);
++            }
+         }
+     }
+ 
+-    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.
++     * Meta-data wrapper for concrete BMH types.
++     * Each BMH type corresponds to a given sequence of basic field types (LIJFD).
++     * The fields are immutable; their values are fully specified at object construction.
++     * Each BMH type supplies an array of getter functions which may be used in lambda forms.
++     * A BMH is constructed by cloning a shorter BMH and adding one or more new field values.
++     * As a degenerate and common case, the "shorter BMH" can be missing, and contributes zero prior fields.
+      */
+     static class SpeciesData {
+-        final String                             types;
++        final String                             typeChars;
++        final byte[]                             typeCodes;
+         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 NamedFunction[]                    nominalGetters;
+         final SpeciesData[]                      extensions;
+ 
+         public int fieldCount() {
+-            return types.length();
++            return typeCodes.length;
+         }
+-        public char fieldType(int i) {
+-            return types.charAt(i);
++        public byte fieldType(int i) {
++            return typeCodes[i];
++        }
++        public char fieldTypeChar(int i) {
++            return typeChars.charAt(i);
+         }
+ 
+         public String toString() {
+-            return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]";
++            return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+typeChars+"]";
+         }
+ 
+         /**
+@@ -370,45 +302,46 @@
+          * 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);
+-        }
+-
+         NamedFunction getterFunction(int i) {
+-            return new NamedFunction(getters[i]);
++            return nominalGetters[i];
+         }
+ 
+         static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
+ 
+         private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
+-            this.types = types;
++            this.typeChars = types;
++            this.typeCodes = basicTypes(types);
+             this.clazz = clazz;
+             if (!INIT_DONE) {
+-                this.constructor = new MethodHandle[1];
++                this.constructor = new MethodHandle[1];  // only one ctor
+                 this.getters = new MethodHandle[types.length()];
++                this.nominalGetters = new NamedFunction[types.length()];
+             } else {
+                 this.constructor = Factory.makeCtors(clazz, types, null);
+                 this.getters = Factory.makeGetters(clazz, types, null);
++                this.nominalGetters = Factory.makeNominalGetters(clazz, types, null, this.getters);
+             }
+-            this.extensions = new SpeciesData[EXTENSION_TYPES.length()];
++            this.extensions = new SpeciesData[ARG_TYPE_LIMIT];
+         }
+ 
+         private void initForBootstrap() {
+             assert(!INIT_DONE);
+             if (constructor[0] == null) {
++                String types = typeChars;
+                 Factory.makeCtors(clazz, types, this.constructor);
+                 Factory.makeGetters(clazz, types, this.getters);
++                Factory.makeNominalGetters(clazz, types, this.nominalGetters, this.getters);
+             }
+         }
+ 
+-        private SpeciesData(String types) {
++        private SpeciesData(String typeChars) {
+             // Placeholder only.
+-            this.types = types;
++            this.typeChars = typeChars;
++            this.typeCodes = basicTypes(typeChars);
+             this.clazz = null;
+             this.constructor = null;
+             this.getters = null;
++            this.nominalGetters = null;
+             this.extensions = null;
+         }
+         private boolean isPlaceholder() { return clazz == null; }
+@@ -416,19 +349,19 @@
+         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];
++        SpeciesData extendWith(byte type) {
++            SpeciesData d = extensions[type];
+             if (d != null)  return d;
+-            extensions[i] = d = get(types+type);
++            extensions[type] = d = get(typeChars+basicTypeChar(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;
++        SpeciesData extendWith(Class<?> type) {
++            return extendWith(basicType(type));
++        }
++
++        SpeciesData extendWithChar(char type) {
++            return extendWith(basicType(type));
+         }
+ 
+         private static SpeciesData get(String types) {
+@@ -468,11 +401,11 @@
+             return d;
+         }
+ 
++        static void initStatics() {}
++
+         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)) {
+@@ -480,7 +413,7 @@
+                         SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
+                         assert(d != null) : cbmh.getName();
+                         assert(d.clazz == cbmh);
+-                        assert(d == lookupCache(d.types));
++                        assert(d == lookupCache(d.typeChars));
+                     }
+                 }
+             } catch (Throwable e) {
+@@ -531,11 +464,10 @@
+         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 INT_SIG    = "()I";
+ 
+         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" };
+ 
+         /**
+@@ -566,31 +498,35 @@
+          *     final Object argL0;
+          *     final Object argL1;
+          *     final int argI2;
+-         *     public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
++         *     private 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 final int fieldCount() { return 3; }
+          *     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 BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
++         *         return new Species_LLI(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 copyWith(MethodType mt, LambdaForm lf) {
++         *         return new Species_LLI(mt, lf, argL0, argL1, argI2);
+          *     }
+-         *     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 copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
++         *         return SPECIES_DATA.extendWith(L_TYPE).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 copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
++         *         return SPECIES_DATA.extendWith(I_TYPE).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 copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
++         *         return SPECIES_DATA.extendWith(J_TYPE).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);
++         *     public final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
++         *         return SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
++         *     }
++         *     public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
++         *         return SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+          *     }
+          * }
+          * </pre>
+@@ -620,11 +556,11 @@
+             MethodVisitor mv;
+ 
+             // emit constructor
+-            mv = cw.visitMethod(ACC_PUBLIC, "<init>", makeSignature(types, true), null, null);
++            mv = cw.visitMethod(ACC_PRIVATE, "<init>", makeSignature(types, true), null, null);
+             mv.visitCode();
+-            mv.visitVarInsn(ALOAD, 0);
+-            mv.visitVarInsn(ALOAD, 1);
+-            mv.visitVarInsn(ALOAD, 2);
++            mv.visitVarInsn(ALOAD, 0); // this
++            mv.visitVarInsn(ALOAD, 1); // type
++            mv.visitVarInsn(ALOAD, 2); // form
+ 
+             mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true));
+ 
+@@ -643,16 +579,6 @@
+             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();
+@@ -661,39 +587,72 @@
+             mv.visitMaxs(0, 0);
+             mv.visitEnd();
+ 
+-            // emit clone()
+-            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE);
++            // emit implementation of fieldCount()
++            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "fieldCount", INT_SIG, null, null);
+             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);
++            int fc = types.length();
++            if (fc <= (ICONST_5 - ICONST_0))
++                mv.visitInsn(ICONST_0 + fc);
++            else
++                mv.visitIntInsn(SIPUSH, fc);
++            mv.visitInsn(IRETURN);
++            mv.visitMaxs(0, 0);
++            mv.visitEnd();
++
++            // emit make()  ...factory method wrapping constructor
++            mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "make", makeSignature(types, false), null, null);
++            mv.visitCode();
++            // make instance
++            mv.visitTypeInsn(NEW, className);
++            mv.visitInsn(DUP);
++            // load mt, lf
++            mv.visitVarInsn(ALOAD, 0);  // type
++            mv.visitVarInsn(ALOAD, 1);  // form
++            // load factory method arguments
++            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(typeLoadOp(t), j + 2); // parameters start at 3
++                if (t == 'J' || t == 'D') {
++                    ++j; // adjust argument register access
++                }
++            }
++
++            // finally, invoke the constructor and return
++            mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", makeSignature(types, true));
++            mv.visitInsn(ARETURN);
++            mv.visitMaxs(0, 0);
++            mv.visitEnd();
++
++            // emit copyWith()
++            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "copyWith", makeSignature("", false), null, null);
++            mv.visitCode();
++            // make instance
++            mv.visitTypeInsn(NEW, className);
++            mv.visitInsn(DUP);
+             // 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.visitMethodInsn(INVOKESPECIAL, className, "<init>", makeSignature(types, true));
+             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);
++            // for each type, emit copyWithExtendT()
++            for (byte bt = 0; bt < ARG_TYPE_LIMIT; bt++) {
++                char t = basicTypeChar(bt);
++                mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE);
+                 mv.visitCode();
+-                // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
++                // return SPECIES_DATA.extendWith(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);
++                int iconstInsn = ICONST_0 + bt;
+                 assert(iconstInsn <= ICONST_5);
+                 mv.visitInsn(iconstInsn);
+-                mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG);
++                mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG);
+                 mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
+                 mv.visitInsn(ICONST_0);
+                 mv.visitInsn(AALOAD);
+@@ -789,6 +748,14 @@
+             return mhs;
+         }
+ 
++        static NamedFunction[] makeNominalGetters(Class<?> cbmhClass, String types, NamedFunction[] nfs, MethodHandle[] getters) {
++            if (nfs == null)  nfs = new NamedFunction[types.length()];
++            for (int i = 0; i < nfs.length; ++i) {
++                nfs[i] = new NamedFunction(getters[i]);
++            }
++            return nfs;
++        }
++
+         //
+         // Auxiliary methods.
+         //
+@@ -822,60 +789,28 @@
+ 
+         static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
+             try {
+-                return linkConstructor(LOOKUP.findConstructor(cbmh, MethodType.fromMethodDescriptorString(makeSignature(types, true), null)));
++                return LOOKUP.findStatic(cbmh, "make", MethodType.fromMethodDescriptorString(makeSignature(types, false), null));
+             } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
+                 throw newInternalError(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 newInternalError(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;
++    static void initStatics() {}
++    static { SpeciesData.initStatics(); }
++
++    private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[4];
++    private static SpeciesData checkCache(int index, String types) {
++        SpeciesData data = SPECIES_DATA_CACHE[index];
++        if (data != null)  return data;
++        SPECIES_DATA_CACHE[index] = data = getSpeciesData(types);
++        return data;
++    }
++    static SpeciesData speciesData_L()     { return checkCache(1, "L"); }
++    static SpeciesData speciesData_LL()    { return checkCache(2, "LL"); }
++    static SpeciesData speciesData_LLL()   { return checkCache(3, "LLL"); }
+ 
+     // Event counters
+     private static final EventCounter EC_methodHandle_bound = eventCounter(EC_methodHandle, "bound", true);  // autoSum=true
+@@ -885,4 +820,6 @@
+     private static final EventCounter EC_methodHandle_bound_arg3 = eventCounter(EC_methodHandle_bound, "arg3");
+     private static final EventCounter EC_methodHandle_bound_arg4 = eventCounter(EC_methodHandle_bound, "arg4");
+     private static final EventCounter EC_methodHandle_bound_arg5up = eventCounter(EC_methodHandle_bound, "arg5up");
++    private static final EventCounter EC_methodHandle_rebind = eventCounter(EC_methodHandle_bound, "rebind");
++    private static final EventCounter EC_methodHandle_rebind_complex = eventCounter(EC_methodHandle_rebind, "complex");
+ }
+diff --git a/src/share/classes/java/lang/invoke/CallSite.java b/src/share/classes/java/lang/invoke/CallSite.java
+--- a/src/share/classes/java/lang/invoke/CallSite.java
++++ b/src/share/classes/java/lang/invoke/CallSite.java
+@@ -102,7 +102,7 @@
+      */
+     /*package-private*/
+     CallSite(MethodType type) {
+-        target = type.invokers().uninitializedCallSite();
++        target = makeUninitializedCallSite(type);
+     }
+ 
+     /**
+@@ -114,6 +114,7 @@
+     CallSite(MethodHandle target) {
+         target.type();  // null check
+         this.target = target;
++        target.maybeCompileToBytecode();
+     }
+ 
+     /**
+@@ -133,6 +134,7 @@
+         MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
+         checkTargetChange(this.target, boundTarget);
+         this.target = boundTarget;
++        boundTarget.maybeCompileToBytecode();
+     }
+ 
+     /**
+@@ -188,6 +190,7 @@
+         MethodType newType = newTarget.type();  // null check!
+         if (!newType.equals(oldType))
+             throw wrongTargetType(newTarget, oldType);
++        newTarget.maybeCompileToBytecode();
+     }
+ 
+     private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
+@@ -211,27 +214,41 @@
+     public abstract MethodHandle dynamicInvoker();
+ 
+     /*non-public*/ MethodHandle makeDynamicInvoker() {
+-        MethodHandle getTarget = GET_TARGET.bindReceiver(this);
++        MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this);
+         MethodHandle invoker = MethodHandles.exactInvoker(this.type());
+         return MethodHandles.foldArguments(invoker, getTarget);
+     }
+ 
+     private static final MethodHandle GET_TARGET;
++    private static final MethodHandle THROW_UCS;
+     static {
+         try {
+             GET_TARGET = IMPL_LOOKUP.
+                 findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
++            THROW_UCS = IMPL_LOOKUP.
++                findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class));
+         } catch (ReflectiveOperationException e) {
+             throw newInternalError(e);
+         }
+     }
+ 
+     /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
+-    /*package-private*/
+-    static Empty uninitializedCallSite() {
++    private static Object uninitializedCallSite(Object... ignore) {
+         throw new IllegalStateException("uninitialized call site");
+     }
+ 
++    private MethodHandle makeUninitializedCallSite(MethodType targetType) {
++        MethodType basicType = targetType.basicType();
++        MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS);
++        if (invoker == null) {
++            invoker = THROW_UCS.asType(basicType);
++            invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker);
++        }
++        // unchecked view is OK since no values will be received or returned
++        return invoker.viewAsType(targetType, false);
++    }
++
++
+     // unsafe stuff:
+     private static final long TARGET_OFFSET;
+     static {
+@@ -320,6 +337,7 @@
+             if (!site.getTarget().type().equals(type))
+                 throw new WrongMethodTypeException("wrong type: "+site.getTarget());
+         } catch (Throwable ex) {
++            ex.printStackTrace();//@@
+             BootstrapMethodError bex;
+             if (ex instanceof BootstrapMethodError)
+                 bex = (BootstrapMethodError) ex;
+diff --git a/src/share/classes/java/lang/invoke/DelegatingMethodHandle.java b/src/share/classes/java/lang/invoke/DelegatingMethodHandle.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/lang/invoke/DelegatingMethodHandle.java
+@@ -0,0 +1,132 @@
++/*
++ * Copyright (c) 2013, 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.util.Arrays;
++import static java.lang.invoke.LambdaForm.*;
++import static java.lang.invoke.MethodHandleStatics.*;
++
++/**
++ * A method handle whose invocation behavior is determined by a target.
++ * The delegating MH itself can hold extra "intentions" beyond the simple behavior.
++ * @author jrose
++ */
++/*non-public*/
++abstract class DelegatingMethodHandle extends MethodHandle {
++    protected DelegatingMethodHandle(MethodHandle target) {
++        this(target.type(), target);
++    }
++
++    protected DelegatingMethodHandle(MethodType type, MethodHandle target) {
++        super(target.type(), makeReinvokerForm(target, MethodTypeForm.LF_DELEGATE, NF_getTarget));
++    }
++
++    /** Define this to extract the delegated target which supplies the invocation behavior. */
++    abstract protected MethodHandle getTarget();
++
++    @Override
++    MemberName internalMemberName() {
++        return getTarget().internalMemberName();
++    }
++
++    @Override
++    boolean isInvokeSpecial() {
++        return getTarget().isInvokeSpecial();
++    }
++
++    @Override
++    Class<?> internalCallerClass() {
++        return getTarget().internalCallerClass();
++    }
++
++    @Override
++    MethodHandle copyWith(MethodType mt, LambdaForm lf) {
++        // FIXME: rethink 'copyWith' protocol; it is too low-level for use on all MHs
++        throw newIllegalArgumentException("do not use this");
++    }
++
++    @Override
++    BoundMethodHandle rebind() {
++        return getTarget().rebind();
++    }
++
++    @Override
++    void compileToBytecode() {
++        super.compileToBytecode();
++        getTarget().compileToBytecode();
++    }
++
++    /** Create a LF which simply reinvokes a target of the given basic type.
++     */
++    static LambdaForm makeReinvokerForm(MethodHandle target,
++                                        int whichCache,
++                                        NamedFunction getTargetFn) {
++        MethodType mtype = target.type().basicType();
++        boolean customized = (whichCache < 0 ||
++                mtype.parameterSlotCount() > MethodType.MAX_MH_INVOKER_ARITY);
++        LambdaForm form;
++        if (!customized) {
++            form = mtype.form().cachedLambdaForm(whichCache);
++            if (form != null)  return form;
++        }
++        final int THIS_DMH    = 0;
++        final int ARG_BASE    = 1;
++        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
++        int nameCursor = ARG_LIMIT;
++        final int NEXT_MH     = customized ? -1 : nameCursor++;
++        final int REINVOKE    = nameCursor++;
++        LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
++        assert(names.length == nameCursor);
++        Object[] targetArgs;
++        if (customized) {
++            targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
++            names[REINVOKE] = new LambdaForm.Name(target, targetArgs);  // the invoker is the target itself
++        } else {
++            names[NEXT_MH] = new LambdaForm.Name(getTargetFn, names[THIS_DMH]);
++            targetArgs = Arrays.copyOfRange(names, THIS_DMH, ARG_LIMIT, Object[].class);
++            targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
++            names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs);
++        }
++        String debugString =
++                (whichCache == MethodTypeForm.LF_REBIND ? "BMH.reinvoke" :
++                whichCache == MethodTypeForm.LF_DELEGATE ? "MH.delegate" : "MH.reinvoke");
++        form = new LambdaForm(debugString, ARG_LIMIT, names);
++        if (!customized) {
++            form = mtype.form().setCachedLambdaForm(whichCache, form);
++        }
++        return form;
++    }
++
++    private static final NamedFunction NF_getTarget;
++    static {
++        try {
++            NF_getTarget = new NamedFunction(DelegatingMethodHandle.class
++                                             .getDeclaredMethod("getTarget"));
++        } catch (ReflectiveOperationException ex) {
++            throw newInternalError(ex);
++        }
++    }
++}
+diff --git a/src/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+--- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java
++++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+@@ -58,6 +58,7 @@
+             MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind());
+             m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null);
+             if (m != null && m.isPublic()) {
++                assert(member.getReferenceKind() == m.getReferenceKind());  // else this.form is wrong
+                 member = m;
+             }
+         }
+@@ -127,6 +128,7 @@
+ 
+     @Override
+     MethodHandle copyWith(MethodType mt, LambdaForm lf) {
++        assert(this.getClass() == DirectMethodHandle.class);  // must override in subclasses
+         return new DirectMethodHandle(mt, lf, member);
+     }
+ 
+@@ -137,54 +139,13 @@
+ 
+     //// Implementation methods.
+     @Override
+-    MethodHandle viewAsType(MethodType newType) {
+-        return new DirectMethodHandle(newType, form, member);
+-    }
+-    @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
+@@ -394,8 +355,8 @@
+             return true;
+         }
+         @Override
+-        MethodHandle viewAsType(MethodType newType) {
+-            return new Special(newType, form, member);
++        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
++            return new Special(mt, lf, member);
+         }
+     }
+ 
+@@ -412,8 +373,8 @@
+             assert(initMethod.isResolved());
+         }
+         @Override
+-        MethodHandle viewAsType(MethodType newType) {
+-            return new Constructor(newType, form, member, initMethod, instanceClass);
++        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
++            return new Constructor(mt, lf, member, initMethod, instanceClass);
+         }
+     }
+ 
+@@ -442,8 +403,8 @@
+             return fieldType.cast(obj);
+         }
+         @Override
+-        MethodHandle viewAsType(MethodType newType) {
+-            return new Accessor(newType, form, member, fieldOffset);
++        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
++            return new Accessor(mt, lf, member, fieldOffset);
+         }
+     }
+ 
+@@ -485,8 +446,8 @@
+             return fieldType.cast(obj);
+         }
+         @Override
+-        MethodHandle viewAsType(MethodType newType) {
+-            return new StaticAccessor(newType, form, member, staticBase, staticOffset);
++        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
++            return new StaticAccessor(mt, lf, member, staticBase, staticOffset);
+         }
+     }
+ 
+diff --git a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
+--- a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
++++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
+@@ -25,12 +25,6 @@
+ 
+ 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.*;
+ 
+@@ -40,8 +34,11 @@
+ import static java.lang.invoke.MethodHandleStatics.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
+ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
++import static java.lang.invoke.LambdaForm.*;
+ import sun.invoke.util.ValueConversions;
+ import sun.invoke.util.VerifyType;
++import sun.invoke.util.VerifyAccess;
++import sun.invoke.util.Wrapper;
+ 
+ /**
+  * Code generation backend for LambdaForm.
+@@ -117,7 +114,7 @@
+         Name[] names = form.names;
+         for (int i = 0, index = 0; i < localsMap.length; i++) {
+             localsMap[i] = index;
+-            index += Wrapper.forBasicType(names[i].type).stackSlots();
++            index += Wrapper.forBasicType(names[i].typeChar()).stackSlots();
+         }
+     }
+ 
+@@ -374,38 +371,38 @@
+     /*
+      * NOTE: These load/store methods use the localsMap to find the correct index!
+      */
+-    private void emitLoadInsn(char type, int index) {
++    private void emitLoadInsn(byte 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;
++        case I_TYPE:  opcode = Opcodes.ILOAD;  break;
++        case J_TYPE:  opcode = Opcodes.LLOAD;  break;
++        case F_TYPE:  opcode = Opcodes.FLOAD;  break;
++        case D_TYPE:  opcode = Opcodes.DLOAD;  break;
++        case L_TYPE:  opcode = Opcodes.ALOAD;  break;
+         default:
+             throw new InternalError("unknown type: " + type);
+         }
+         mv.visitVarInsn(opcode, localsMap[index]);
+     }
+     private void emitAloadInsn(int index) {
+-        emitLoadInsn('L', index);
++        emitLoadInsn(L_TYPE, index);
+     }
+ 
+-    private void emitStoreInsn(char type, int index) {
++    private void emitStoreInsn(byte 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;
++        case I_TYPE:  opcode = Opcodes.ISTORE;  break;
++        case J_TYPE:  opcode = Opcodes.LSTORE;  break;
++        case F_TYPE:  opcode = Opcodes.FSTORE;  break;
++        case D_TYPE:  opcode = Opcodes.DSTORE;  break;
++        case L_TYPE:  opcode = Opcodes.ASTORE;  break;
+         default:
+             throw new InternalError("unknown type: " + type);
+         }
+         mv.visitVarInsn(opcode, localsMap[index]);
+     }
+     private void emitAstoreInsn(int index) {
+-        emitStoreInsn('L', index);
++        emitStoreInsn(L_TYPE, index);
+     }
+ 
+     /**
+@@ -413,8 +410,7 @@
+      *
+      * @param type primitive type class to box.
+      */
+-    private void emitBoxing(Class<?> type) {
+-        Wrapper wrapper = Wrapper.forPrimitiveType(type);
++    private void emitBoxing(Wrapper wrapper) {
+         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+         String name  = "valueOf";
+         String desc  = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
+@@ -426,8 +422,7 @@
+      *
+      * @param type wrapper type class to unbox.
+      */
+-    private void emitUnboxing(Class<?> type) {
+-        Wrapper wrapper = Wrapper.forWrapperType(type);
++    private void emitUnboxing(Wrapper wrapper) {
+         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+         String name  = wrapper.primitiveSimpleName() + "Value";
+         String desc  = "()" + wrapper.basicTypeChar();
+@@ -436,56 +431,60 @@
+     }
+ 
+     /**
+-     * Emit an implicit conversion.
++     * Emit an implicit conversion for an argument which must be of the given pclass.
++     * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface.
+      *
+      * @param ptype type of value present on stack
+      * @param pclass type of value required on stack
+      */
+-    private void emitImplicitConversion(char ptype, Class<?> pclass) {
++    private void emitImplicitConversion(byte ptype, Class<?> pclass) {
++        assert(basicType(pclass) == ptype);  // boxing/unboxing handled by caller
++        if (pclass == basicTypeClass(ptype) && ptype != L_TYPE)
++            return;   // nothing to do
+         switch (ptype) {
+-        case 'L':
+-            if (VerifyType.isNullConversion(Object.class, pclass))
++        case L_TYPE:
++            if (VerifyType.isNullConversion(Object.class, pclass, false)) {
++                if (PROFILE_LEVEL >= 0)
++                    mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ);
+                 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);
++                else if (PROFILE_LEVEL >= 0)
++                    mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ);
++                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
+             }
+             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);
++        case I_TYPE:
++            if (!VerifyType.isNullConversion(int.class, pclass, false))
++                emitPrimCast(basicTypeWrapper(ptype), Wrapper.forPrimitiveType(pclass));
+             return;
+         }
+         throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass);
+     }
+ 
++    private void emitReferenceCast(Class<?> pclass) {
++        
++    }
++
+     /**
+      * Emits an actual return instruction conforming to the given return type.
+      */
+-    private void emitReturnInsn(Class<?> type) {
++    private void emitReturnInsn(byte 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;
++        switch (type) {
++        case I_TYPE:  opcode = Opcodes.IRETURN;  break;
++        case J_TYPE:  opcode = Opcodes.LRETURN;  break;
++        case F_TYPE:  opcode = Opcodes.FRETURN;  break;
++        case D_TYPE:  opcode = Opcodes.DRETURN;  break;
++        case L_TYPE:  opcode = Opcodes.ARETURN;  break;
++        case V_TYPE:  opcode = Opcodes.RETURN;   break;
+         default:
+             throw new InternalError("unknown return type: " + type);
+         }
+@@ -529,11 +528,15 @@
+         for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
+             Name name = lambdaForm.names[i];
+             MemberName member = name.function.member();
++            Name nextName;
+ 
+-            if (isSelectAlternative(member)) {
++            if (isSelectAlternative(name) &&
++                    i+1 < lambdaForm.names.length &&
++                    isInvokeBasic(nextName = lambdaForm.names[i+1]) &&
++                    nextName.lastUseIndex(name) == 0 &&
++                    lambdaForm.lastUseIndex(name) == i+1) {
+                 // selectAlternative idiom
+-                // FIXME: make sure this idiom is really present!
+-                emitSelectAlternative(name, lambdaForm.names[i + 1]);
++                emitSelectAlternative(name, nextName);
+                 i++;  // skip MH.invokeBasic of the selectAlternative result
+             } else if (isStaticallyInvocable(member)) {
+                 emitStaticInvoke(member, name);
+@@ -546,7 +549,7 @@
+             // 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') {
++            } else if (name.type != V_TYPE) {
+                 // non-void: actually assign
+                 emitStoreInsn(name.type, name.index());
+             }
+@@ -694,13 +697,30 @@
+      * @param member
+      * @return true if member is a call to MethodHandleImpl.selectAlternative
+      */
+-    private boolean isSelectAlternative(MemberName member) {
++    private boolean isSelectAlternative(Name name) {
++        if (name.function == null)  return false;
++        MemberName member = name.function.member();
+         return member != null &&
+                member.getDeclaringClass() == MethodHandleImpl.class &&
+                member.getName().equals("selectAlternative");
+     }
+ 
+     /**
++     * Check if MemberName is a call to MethodHandle.invokeBasic.
++     *
++     * @param member
++     * @return true if member is a call to MethodHandleImpl.selectAlternative
++     */
++    private boolean isInvokeBasic(Name name) {
++        if (name.function == null)  return false;
++        if (name.arguments.length < 1)  return false;  // must have MH argument
++        MemberName member = name.function.member();
++        return member != null &&
++               member.getDeclaringClass() == MethodHandle.class &&
++               member.getName().equals("invokeBasic");
++    }
++
++    /**
+      * Emit bytecode for the selectAlternative idiom.
+      *
+      * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
+@@ -723,13 +743,11 @@
+ 
+         // load test result
+         emitPushArgument(selectAlternativeName, 0);
+-        mv.visitInsn(Opcodes.ICONST_1);
+ 
+         // if_icmpne L_fallback
+-        mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
++        mv.visitJumpInsn(Opcodes.IFEQ, 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);
+@@ -741,7 +759,6 @@
+         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);
+@@ -757,20 +774,20 @@
+      */
+     private void emitPushArgument(Name name, int paramIndex) {
+         Object arg = name.arguments[paramIndex];
+-        char ptype = name.function.parameterType(paramIndex);
++        byte 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') {
++        } else if ((arg == null || arg instanceof String) && ptype == L_TYPE) {
+             emitConst(arg);
+         } else {
+-            if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
++            if (Wrapper.isWrapperType(arg.getClass()) && ptype != L_TYPE) {
+                 emitConst(arg);
+             } else {
+                 mv.visitLdcInsn(constantPlaceholder(arg));
+-                emitImplicitConversion('L', mtype.parameterType(paramIndex));
++                emitImplicitConversion(L_TYPE, mtype.parameterType(paramIndex));
+             }
+         }
+     }
+@@ -780,52 +797,37 @@
+      */
+     private void emitReturn() {
+         // return statement
+-        if (lambdaForm.result == -1) {
++        Class<?> rclass = invokerType.returnType();
++        byte rtype = lambdaForm.returnType();
++        assert(rtype == basicType(rclass));  // must agree
++        if (rtype == V_TYPE) {
+             // void
+             mv.visitInsn(Opcodes.RETURN);
++            // it doesn't matter what rclass is; the JVM will discard any value
+         } else {
+-            LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
+-            char rtype = Wrapper.basicTypeChar(invokerType.returnType());
++            Name rn = lambdaForm.names[lambdaForm.result];
++            assert(rtype == rn.type);
+ 
+             // put return value on the stack if it is not already there
+-            if (lambdaForm.result != lambdaForm.names.length - 1) {
+-                emitLoadInsn(rn.type, lambdaForm.result);
++            if (lambdaForm.result != lambdaForm.names.length - 1 ||
++                    lambdaForm.result < lambdaForm.arity) {
++                emitLoadInsn(rtype, 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");
+-                    }
+-                }
+-            }
++            emitImplicitConversion(rtype, rclass);
+ 
+             // generate actual return statement
+-            emitReturnInsn(invokerType.returnType());
++            emitReturnInsn(rtype);
+         }
+     }
+ 
+     /**
+      * Emit a type conversion bytecode casting from "from" to "to".
+      */
+-    private void emitPrimCast(char from, char to) {
++    private void emitPrimCast(Wrapper from, Wrapper to) {
+         // Here's how.
+         // -   indicates forbidden
+         // <-> indicates implicit
+@@ -842,17 +844,15 @@
+             // no cast required, should be dead code anyway
+             return;
+         }
+-        Wrapper wfrom = Wrapper.forBasicType(from);
+-        Wrapper wto   = Wrapper.forBasicType(to);
+-        if (wfrom.isSubwordOrInt()) {
++        if (from.isSubwordOrInt()) {
+             // cast from {byte,short,char,int} to anything
+             emitI2X(to);
+         } else {
+             // cast from {long,float,double} to anything
+-            if (wto.isSubwordOrInt()) {
++            if (to.isSubwordOrInt()) {
+                 // cast to {byte,short,char,int}
+                 emitX2I(from);
+-                if (wto.bitWidth() < 32) {
++                if (to.bitWidth() < 32) {
+                     // targets other than int require another conversion
+                     emitI2X(to);
+                 }
+@@ -860,20 +860,26 @@
+                 // 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;
++                case LONG:
++                    switch (to) {
++                    case FLOAT:   mv.visitInsn(Opcodes.L2F);  break;
++                    case DOUBLE:  mv.visitInsn(Opcodes.L2D);  break;
++                    default:      error = true;               break;
++                    }
+                     break;
+-                case 'F':
+-                         if (to == 'J') { mv.visitInsn(Opcodes.F2L); }
+-                    else if (to == 'D') { mv.visitInsn(Opcodes.F2D); }
+-                    else error = true;
++                case FLOAT:
++                    switch (to) {
++                    case LONG :   mv.visitInsn(Opcodes.F2L);  break;
++                    case DOUBLE:  mv.visitInsn(Opcodes.F2D);  break;
++                    default:      error = true;               break;
++                    }
+                     break;
+-                case 'D':
+-                         if (to == 'J') { mv.visitInsn(Opcodes.D2L); }
+-                    else if (to == 'F') { mv.visitInsn(Opcodes.D2F); }
+-                    else error = true;
++                case DOUBLE:
++                    switch (to) {
++                    case LONG :   mv.visitInsn(Opcodes.D2L);  break;
++                    case FLOAT:   mv.visitInsn(Opcodes.D2F);  break;
++                    default:      error = true;               break;
++                    }
+                     break;
+                 default:
+                     error = true;
+@@ -886,16 +892,16 @@
+         }
+     }
+ 
+-    private void emitI2X(char type) {
++    private void emitI2X(Wrapper 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':
++        case BYTE:    mv.visitInsn(Opcodes.I2B);  break;
++        case SHORT:   mv.visitInsn(Opcodes.I2S);  break;
++        case CHAR:    mv.visitInsn(Opcodes.I2C);  break;
++        case INT:     /* naught */                break;
++        case LONG:    mv.visitInsn(Opcodes.I2L);  break;
++        case FLOAT:   mv.visitInsn(Opcodes.I2F);  break;
++        case DOUBLE:  mv.visitInsn(Opcodes.I2D);  break;
++        case BOOLEAN:
+             // For compatibility with ValueConversions and explicitCastArguments:
+             mv.visitInsn(Opcodes.ICONST_1);
+             mv.visitInsn(Opcodes.IAND);
+@@ -904,12 +910,12 @@
+         }
+     }
+ 
+-    private void emitX2I(char type) {
++    private void emitX2I(Wrapper 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);
++        case LONG:    mv.visitInsn(Opcodes.L2I);  break;
++        case FLOAT:   mv.visitInsn(Opcodes.F2I);  break;
++        case DOUBLE:  mv.visitInsn(Opcodes.D2I);  break;
++        default:      throw new InternalError("unknown type: " + type);
+         }
+     }
+ 
+@@ -928,16 +934,16 @@
+      * @return
+      */
+     static MemberName generateLambdaFormInterpreterEntryPoint(String sig) {
+-        assert(LambdaForm.isValidSignature(sig));
++        assert(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);
++        byte tret = signatureReturn(sig);
++        MethodType type = MethodType.methodType(basicTypeClass(tret), MethodHandle.class);
+         // other parameter types
+-        int arity = LambdaForm.signatureArity(sig);
++        int arity = signatureArity(sig);
+         for (int i = 1; i < arity; i++) {
+-            type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i)));
++            type = type.appendParameterTypes(basicTypeClass(basicType(sig.charAt(i))));
+         }
+         InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type);
+         return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
+@@ -961,10 +967,10 @@
+             Class<?> ptype = invokerType.parameterType(i);
+             mv.visitInsn(Opcodes.DUP);
+             emitIconstInsn(i);
+-            emitLoadInsn(Wrapper.basicTypeChar(ptype), i);
++            emitLoadInsn(basicType(ptype), i);
+             // box if primitive type
+             if (ptype.isPrimitive()) {
+-                emitBoxing(ptype);
++                emitBoxing(Wrapper.forPrimitiveType(ptype));
+             }
+             mv.visitInsn(Opcodes.AASTORE);
+         }
+@@ -977,11 +983,11 @@
+         // maybe unbox
+         Class<?> rtype = invokerType.returnType();
+         if (rtype.isPrimitive() && rtype != void.class) {
+-            emitUnboxing(Wrapper.asWrapperType(rtype));
++            emitUnboxing(Wrapper.forPrimitiveType(rtype));
+         }
+ 
+         // return statement
+-        emitReturnInsn(rtype);
++        emitReturnInsn(basicType(rtype));
+ 
+         classFileEpilogue();
+         bogusMethod(invokerType);
+@@ -999,7 +1005,7 @@
+      * @return
+      */
+     static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
+-        MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE;
++        MethodType invokerType = NamedFunction.INVOKER_METHOD_TYPE;
+         String invokerName = basicTypeCharSignature("invoke_", typeForm.erasedType());
+         InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
+         return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
+@@ -1032,8 +1038,8 @@
+                 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());
++                emitUnboxing(srcWrapper);
++                emitPrimCast(srcWrapper, dstWrapper);
+             }
+         }
+ 
+@@ -1047,15 +1053,15 @@
+             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());
++            emitPrimCast(srcWrapper, dstWrapper);
++            emitBoxing(dstWrapper);
+         }
+ 
+         // 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.
++        emitReturnInsn(L_TYPE);  // NOTE: NamedFunction invokers always return a reference value.
+ 
+         classFileEpilogue();
+         bogusMethod(dstType);
+diff --git a/src/share/classes/java/lang/invoke/Invokers.java b/src/share/classes/java/lang/invoke/Invokers.java
+--- a/src/share/classes/java/lang/invoke/Invokers.java
++++ b/src/share/classes/java/lang/invoke/Invokers.java
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2008, 2013, 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
+@@ -16,7 +16,7 @@
+  *
+  * 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.
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-13*1 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
+@@ -31,91 +31,109 @@
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
+ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+ import static java.lang.invoke.LambdaForm.*;
++import java.lang.reflect.Array;
++import sun.invoke.util.ValueConversions;
+ 
+ /**
+  * Construction and caching of often-used invokers.
+  * @author jrose
+  */
+ class Invokers {
+-    // exact type (sans leading taget MH) for the outgoing call
++    // exact type (sans leading target MH) for the outgoing call
+     private final MethodType targetType;
+ 
+-    // FIXME: Get rid of the invokers that are not useful.
++    // Cached adapter information:
++    private final /*lazy*/ MethodHandle[] invokers = new MethodHandle[INV_LIMIT];
++    // Indexes into invokers:
++    static final int
++            INV_EXACT          =  0,  // MethodHandles.exactInvoker
++            INV_GENERIC        =  1,  // MethodHandles.invoker (generic invocation)
++            INV_BASIC          =  2,  // MethodHandles.basicInvoker
++            INV_CASTING        =  3,  // for calls to MHs of the same 'form'
++            INV_FROM_UNTYPED   =  4,  // for untyped calls to MHs of this type
++            INV_FROM_U_LOOSE   =  5,  // non-strict version of above
++            INV_TO_UNTYPED     =  6,  // for calls of this type to untyped MHs
++            INV_TO_U_LOOSE     =  7,  // non-strict version of above
++            INV_LIMIT          =  8;
+ 
+-    // exact invoker for the outgoing call
+-    private /*lazy*/ MethodHandle exactInvoker;
+-    private /*lazy*/ MethodHandle basicInvoker;  // invokeBasic (unchecked exact)
++    // cache for MethodHandles.spreadInvoker (generic invocation, with spread from C[])
++    private /*lazy*/ MethodHandle[] spreadInvokers;
+ 
+-    // 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
++    // cache for MethodHandle.asCollector (generic invocation, with collection to C[])
++    private /*lazy*/ MethodHandle[] collectInvokers;
+ 
+-    // general invoker for the outgoing call
+-    private /*lazy*/ MethodHandle generalInvoker;
++    // cache for MethodHandles filter (single argument #N)
++    //@@private /*lazy*/ MethodHandle[] filterInvokers;
+ 
+-    // general invoker for the outgoing call, uses varargs
+-    private /*lazy*/ MethodHandle varargsInvoker;
++    // cache for MethodHandles fold
++    //@@private /*lazy*/ MethodHandle[] foldInvokers;
+ 
+-    // general invoker for the outgoing call; accepts a trailing Object[]
+-    private final /*lazy*/ MethodHandle[] spreadInvokers;
+-
+-    // invoker for an unbound callsite
+-    private /*lazy*/ MethodHandle uninitializedCallSite;
++    /** Limit on implicitly generated spreader size.
++     *  Above this limit, we use explicit argument arrays.
++     */
++    //@@ REMOVE?
++    private static final int MAX_SPREAD_ARITY =
++            Math.max(2,
++                     Math.min(MethodType.MAX_JVM_ARITY,
++                              (SPREAD_THRESHOLD == 0 ? Integer.MAX_VALUE : SPREAD_THRESHOLD)));
+ 
+     /** Compute and cache information common to all collecting adapters
+      *  that implement members of the erasure-family of the given erased type.
+      */
+     /*non-public*/ Invokers(MethodType targetType) {
+         this.targetType = targetType;
+-        this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
+     }
+ 
+     /*non-public*/ MethodHandle exactInvoker() {
+-        MethodHandle invoker = exactInvoker;
++        MethodHandle invoker = invokers[INV_EXACT];
+         if (invoker != null)  return invoker;
+         MethodType mtype = targetType;
+         MethodType invokerType = mtype.invokerType();
+         LambdaForm lform = invokeHandleForm(mtype, false, MethodTypeForm.LF_EX_INVOKER);
+         invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype);
+-        invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invokeExact", mtype));
++        invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invokeExact", mtype), false);
+         assert(checkInvoker(invoker));
+         if (targetType == targetType.erase() && targetType.parameterCount() < 10)
+             invoker.form.compileToBytecode();
+-        exactInvoker = invoker;
++        //invoker.form.compileToBytecode();
++        invokers[INV_EXACT] = invoker;
+         return invoker;
+     }
+ 
+-    /*non-public*/ MethodHandle generalInvoker() {
+-        MethodHandle invoker = generalInvoker;
++    /*non-public*/ MethodHandle genericInvoker() {
++        MethodHandle invoker = invokers[INV_GENERIC];
+         if (invoker != null)  return invoker;
+         MethodType mtype = targetType;
+         MethodType invokerType = mtype.invokerType();
+         LambdaForm lform = invokeHandleForm(mtype, false, MethodTypeForm.LF_GEN_INVOKER);
+         invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype);
+-        invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invoke", mtype));
++        invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invoke", mtype), false);
+         assert(checkInvoker(invoker));
+         if (targetType == targetType.erase() && targetType.parameterCount() < 10)
+             invoker.form.compileToBytecode();
+-        generalInvoker = invoker;
++        //invoker.form.compileToBytecode();
++        invokers[INV_GENERIC] = invoker;
+         return invoker;
+     }
+ 
+ 
+     /*non-public*/ MethodHandle basicInvoker() {
+-        MethodHandle invoker = basicInvoker;
++        MethodHandle invoker = invokers[INV_BASIC];
+         if (invoker != null)  return invoker;
+         MethodType basicType = targetType.basicType();
+         if (basicType != targetType) {
+             // double cache; not used significantly
+-            return basicInvoker = basicType.invokers().basicInvoker();
++            return invokers[INV_BASIC] = basicType.invokers().basicInvoker();
+         }
+-        MemberName method = invokeBasicMethod(basicType);
+-        invoker = DirectMethodHandle.make(method);
+-        assert(checkInvoker(invoker));
+-        //invoker.form.compileToBytecode();
+-        basicInvoker = invoker;
++        invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_BASIC_INV);
++        if (invoker == null) {
++            MemberName method = invokeBasicMethod(basicType);
++            invoker = DirectMethodHandle.make(method);
++            assert(checkInvoker(invoker));
++            invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_BASIC_INV, invoker);
++            //invoker.form.compileToBytecode();
++        }
++        invokers[INV_BASIC] = invoker;
+         return invoker;
+     }
+ 
+@@ -130,6 +148,52 @@
+         }
+     }
+ 
++    /*non-public*/ MethodHandle castingInvoker() {
++        // (R) ci.invoke(mh,arg0:T0,...) := (R) (V) mh.invokeExact((U0)arg0,...)
++        // The Invoker.targetType is the "outer" type (T0...)=>R
++        // The "inner" type of the target MH must be of the same form as the outer type.
++        MethodHandle invoker = invokers[INV_CASTING];
++        if (invoker != null)  return invoker;
++        LambdaForm lform = invokeConvertingForm(targetType.erase(), MethodTypeForm.LF_CASTING_INV);
++        invoker = BoundMethodHandle.bindSingle(targetType.invokerType(), lform, targetType);
++        assert(checkInvoker(invoker));
++        //invoker.form.compileToBytecode();
++        invokers[INV_CASTING] = invoker;
++        return invoker;
++    }
++
++    /*non-public*/ MethodHandle fromUntypedInvoker(boolean strict) {
++        // (Object) uti.invoke(mh,obj0:Object,...) := (Object) (R) mh.invokeExact(arg0:T0,...)
++        //    where arg0 := (T0) obj0, ...
++        int which = strict ? INV_FROM_UNTYPED : INV_FROM_U_LOOSE;
++        MethodHandle invoker = invokers[which];
++        if (invoker != null)  return invoker;
++        MethodHandle postInvoker = exactInvoker();
++        MethodType preInvokeType = targetType.generic();
++        invoker = postInvoker;
++        invoker = MethodHandleImpl.makePairwiseConvert(invoker, preInvokeType, strict);
++        assert(targetType.generic().invokers().checkInvoker(invoker));
++        //invoker.form.compileToBytecode();
++        invokers[which] = invoker;
++        return invoker;
++    }
++
++    /*non-public*/ MethodHandle fromTypedInvoker(boolean strict) {
++        // (R) uti.invoke(mh,arg0:T0,...) := (R) (Object) mh.invokeExact(obj0:Object,...)
++        //    where obj0 := (Object) (T0) arg0, ...
++        int which = strict ? INV_TO_UNTYPED : INV_TO_U_LOOSE;
++        MethodHandle invoker = invokers[which];
++        if (invoker != null)  return invoker;
++        MethodHandle postInvoker = targetType.generic().invokers().exactInvoker();
++        MethodType preInvokeType = targetType;
++        invoker = postInvoker;
++        invoker = MethodHandleImpl.makePairwiseConvert(invoker, preInvokeType, strict);
++        assert(checkInvoker(invoker));
++        //invoker.form.compileToBytecode();
++        invokers[which] = invoker;
++        return invoker;
++    }
++
+     private boolean checkInvoker(MethodHandle invoker) {
+         assert(targetType.invokerType().equals(invoker.type()))
+                 : java.util.Arrays.asList(targetType, targetType.invokerType(), invoker);
+@@ -139,88 +203,197 @@
+         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(erasedType.invokerType());
+-        erasedInvoker = invoker;
+-        return invoker;
++    // Return an adapter which performs pairwise conversions.
++    // outerType : the caller's method type (as opposed to the "inner" type of the MH being invoked)
++    // The inner and outer types must have the same form.
++    private static LambdaForm invokeConvertingForm(MethodType outerType, int which) {
++        assert(outerType == outerType.erase());
++        String debugName;
++        switch (which) {
++        case MethodTypeForm.LF_CASTING_INV:   debugName = "checkCast"; break;
++        default: throw new InternalError();
++        }
++        boolean haveRetConv = (outerType.returnType() == Object.class);  // not a primitive
++        int[] argConvs = new int[outerType.parameterCount()];
++        int numArgConvs = 0;
++        for (int i = 0; i < outerType.parameterCount(); i++) {
++            if (outerType.parameterType(i) == Object.class)  // not a primitive
++                argConvs[numArgConvs++] = i;
++        }
++        MethodType cacheType = outerType.basicType();
++        LambdaForm lform = cacheType.form().cachedLambdaForm(which);
++        if (lform != null)  return lform;
++        final int THIS_MH       = 0;
++        final int NEXT_MH       = THIS_MH + 1;
++        final int ARG_BASE      = NEXT_MH + 1;
++        final int ARG_LIMIT     = ARG_BASE + outerType.parameterCount();
++        int nameCursor = ARG_LIMIT;
++        final int NEXT_TYPE     = nameCursor++;  // outer type
++        final int NEXT_PTYPES   = nameCursor++;  // inner parameter types
++        final int ARG_CONV      = nameCursor;    // argument conversion(s)
++        nameCursor += numArgConvs;
++        final int NEXT_CALL     = nameCursor++;  // the inner invocation of NEXT_MH
++        final int RET_CONV      = haveRetConv ? nameCursor++ : -1;  // return value conversion
++        Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, outerType.invokerType().invokerType());
++        assert(names.length == nameCursor);
++        names[NEXT_TYPE] = new Name(BoundMethodHandle.speciesData_L().getterFunction(0), names[THIS_MH]);
++        names[NEXT_PTYPES] = new Name(NF_nextParameterTypes, names[NEXT_MH], names[NEXT_TYPE]);
++        Object[] callArgs = Arrays.copyOfRange(names, NEXT_MH, ARG_LIMIT, Object[].class);
++        for (int n = 0; n < numArgConvs; n++) {
++            int i = argConvs[n];
++            names[ARG_CONV+n] = new Name(NF_convertParameter, names[NEXT_PTYPES], i, names[ARG_BASE+i]);
++            // update the outgoing argument:
++            callArgs[(ARG_BASE-NEXT_MH)+i] = names[ARG_CONV+n];
++        }
++        // Make the inner call.
++        names[NEXT_CALL] = new Name(cacheType, callArgs);
++        if (RET_CONV >= 0)
++            names[RET_CONV] = new Name(NF_convertReturn, names[NEXT_TYPE], names[NEXT_CALL]);
++        lform = new LambdaForm(debugName, ARG_LIMIT, names);
++        lform = cacheType.form().setCachedLambdaForm(which, lform);
++        return lform;
+     }
+ 
++    /*non-public*/ static
++    @ForceInline
++    Object nextParameterTypes(Object nextMH, Object thisType) {
++        MethodType nextType = ((MethodHandle) nextMH).type();
++        assert(((MethodType) thisType).form() == nextType.form()) : Arrays.asList(thisType, nextType);
++        return nextType.ptypes();
++    }
++
++    /*non-public*/ static
++    @ForceInline
++    Object convertParameter(Object ptypes, int n, Object arg) {
++        //@@ FIXME:  This is wrong if ptype is a fictitious interface type.  Cannot reassert correctly.
++        Class<?> ptype = ((Class<?>[]) ptypes)[n];
++        assert(!ptype.isPrimitive());
++        // inlined Class.cast because we can't ForceInline it
++        if (ptype != Object.class && arg != null && !ptype.isInstance(arg))
++            throw newClassCastException(ptype, arg);
++        return arg;
++    }
++
++    /*non-public*/ static
++    @ForceInline
++    Object convertReturn(Object thisType, Object ret) {
++        //@@ FIXME:  This is wrong if rtype is a fictitious interface type.  Cannot reassert correctly.
++        Class<?> rtype = ((MethodType) thisType).returnType();
++        assert(!rtype.isPrimitive());
++        // inlined Class.cast because we can't ForceInline it
++        if (rtype != Object.class && ret != null && !rtype.isInstance(ret))
++            throw newClassCastException(rtype, ret);
++        return ret;
++    }
++
++    /**
++     * Find or create an invoker which passes unchanged a given number of arguments
++     * and spreads the rest from a trailing array argument.
++     * The invoker target type is the post-spread type {@code (TYPEOF(uarg*), TYPEOF(sarg*))=>RT}.
++     * All the {@code sarg}s must have a common type {@code C}.  (If there are none, {@code Object} is assumed.}
++     * @param leadingArgCount the number of unchanged (non-spread) arguments
++     * @return {@code invoker.invokeExact(mh, uarg*, C[]{sarg*}) := (RT)mh.invoke(uarg*, sarg*)}
++     */
+     /*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) {
+-        MethodHandle vaInvoker = spreadInvokers[leadingArgCount];
++        MethodHandle[] invs = spreadInvokers;
++        if (invs == null)
++            spreadInvokers = invs = new MethodHandle[targetType.parameterCount()+1];
++        MethodHandle vaInvoker = invs[leadingArgCount];
+         if (vaInvoker != null)  return vaInvoker;
+         int spreadArgCount = targetType.parameterCount() - leadingArgCount;
+-        MethodType spreadInvokerType = targetType
+-            .replaceParameterTypes(leadingArgCount, targetType.parameterCount(), Object[].class);
+-        if (targetType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) {
++        MethodType postSpreadType = targetType;
++        Class<?> argArrayType = impliedRestargType(postSpreadType, leadingArgCount);
++        MethodType preSpreadType = postSpreadType
++            .replaceParameterTypes(leadingArgCount, postSpreadType.parameterCount(), argArrayType);
++        if (postSpreadType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) {
+             // Factor sinvoker.invoke(mh, a) into ginvoker.asSpreader().invoke(mh, a)
+             // where ginvoker.invoke(mh, a*) => mh.invoke(a*).
+-            MethodHandle genInvoker = generalInvoker();
+-            vaInvoker = genInvoker.asSpreader(Object[].class, spreadArgCount);
++            MethodHandle postInvoker = MethodHandles.invoker(postSpreadType);
++            vaInvoker = MethodHandleImpl.makeSpreadArguments(postInvoker, argArrayType, 1+leadingArgCount, spreadArgCount);
+         } else {
+             // Cannot build a general invoker here of type ginvoker.invoke(mh, a*[254]).
+             // Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a)
+             // where filter(mh) == mh.asSpreader(Object[], spreadArgCount)
+-            MethodHandle arrayInvoker = MethodHandles.exactInvoker(spreadInvokerType);
++            MethodHandle arrayInvoker = MethodHandles.invoker(preSpreadType);
+             MethodHandle makeSpreader;
+             try {
+                 makeSpreader = IMPL_LOOKUP
+-                    .findVirtual(MethodHandle.class, "asSpreader",
++                    .findVirtual(MethodHandle.class, "asSpreaderCustom",
++                        MethodType.methodType(MethodHandle.class, MethodType.class, Class.class, int.class));
++            } catch (ReflectiveOperationException ex) {
++                throw newInternalError(ex);
++            }
++            makeSpreader = MethodHandles.insertArguments(makeSpreader, 1, postSpreadType, argArrayType, spreadArgCount);
++            vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader);
++        }
++        assert(vaInvoker.type().equals(preSpreadType.invokerType()));
++        if (targetType == targetType.erase() && targetType.parameterCount() < 10)
++            vaInvoker.form.compileToBytecode();
++        invs[leadingArgCount] = vaInvoker;
++        return vaInvoker;
++    }
++
++    /**
++     * Find or create an invoker which passes unchanged a given number of arguments
++     * and collects the rest into a trailing array argument.
++     * The invoker target type is the pre-collected type {@code (TYPEOF(uarg*), TYPEOF(carg*))=>RT}.
++     * All the {@code carg}s must have a common type {@code C}.  (If there are none, {@code Object} is assumed.}
++     * @param leadingArgCount the number of unchanged (non-collected) arguments
++     * @return {@code invoker.invokeExact(mh, uarg*, carg*) := (RT)mh.invoke(uarg*, C[]{carg*})}
++     */
++    /*non-public*/ MethodHandle collectInvoker(int leadingArgCount) {
++        MethodHandle[] invs = collectInvokers;
++        if (invs == null)
++            collectInvokers = invs = new MethodHandle[targetType.parameterCount()+1];
++        MethodHandle coInvoker = invs[leadingArgCount];
++        if (coInvoker != null)  return coInvoker;
++        MethodType preCollectType = targetType;
++        int collectArgCount = preCollectType.parameterCount() - leadingArgCount;
++        Class<?> argArrayType = impliedRestargType(preCollectType, leadingArgCount);
++        MethodType postCollectType = preCollectType
++            .replaceParameterTypes(leadingArgCount, preCollectType.parameterCount(), argArrayType);
++        if (preCollectType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) {
++            // Factor cinvoker.invoke(mh, a) into ginvoker.asCollector().invoke(mh, a)
++            // where ginvoker.invoke(mh, a*) => mh.invoke(a*).
++            MethodHandle postInvoker = MethodHandles.invoker(postCollectType);
++            MethodHandle collector = ValueConversions.varargsArray(argArrayType, collectArgCount);
++            assert(postInvoker.type().lastParameterType() == argArrayType);
++            assert(collector.type().returnType() == argArrayType);
++            coInvoker = MethodHandleImpl.makeCollectArguments(postInvoker, collector, 1+leadingArgCount, false);
++        } else {
++            // Cannot build a general invoker here of type ginvoker.invoke(mh, a*[254]).
++            // Instead, factor sinvoker.invoke(mh, a*) into ainvoker.invoke(filter(mh), a*)
++            // where filter(mh) == mh.asCollector(argArrayType, collectArgCount)
++            MethodHandle normalInvoker = MethodHandles.invoker(preCollectType);
++            MethodHandle makeCollector;
++            try {
++                makeCollector = IMPL_LOOKUP
++                    .findVirtual(MethodHandle.class, "asCollectorCustom",
+                         MethodType.methodType(MethodHandle.class, Class.class, int.class));
+             } catch (ReflectiveOperationException ex) {
+                 throw newInternalError(ex);
+             }
+-            makeSpreader = MethodHandles.insertArguments(makeSpreader, 1, Object[].class, spreadArgCount);
+-            vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader);
++            makeCollector = MethodHandles.insertArguments(makeCollector, 1, argArrayType, collectArgCount);
++            coInvoker = MethodHandles.filterArgument(normalInvoker, 0, makeCollector);
+         }
+-        assert(vaInvoker.type().equals(spreadInvokerType.invokerType()));
++        assert(coInvoker.type().equals(preCollectType.invokerType()));
+         if (targetType == targetType.erase() && targetType.parameterCount() < 10)
+-            vaInvoker.form.compileToBytecode();
+-        spreadInvokers[leadingArgCount] = vaInvoker;
+-        return vaInvoker;
++            coInvoker.form.compileToBytecode();
++        invs[leadingArgCount] = coInvoker;
++        return coInvoker;
+     }
+ 
+-    /*non-public*/ MethodHandle varargsInvoker() {
+-        MethodHandle vaInvoker = varargsInvoker;
+-        if (vaInvoker != null)  return vaInvoker;
+-        vaInvoker = spreadInvoker(0).asType(MethodType.genericMethodType(0, true).invokerType());
+-        varargsInvoker = vaInvoker;
+-        return vaInvoker;
+-    }
+-
+-    private static MethodHandle THROW_UCS = null;
+-
+-    /*non-public*/ MethodHandle uninitializedCallSite() {
+-        MethodHandle invoker = uninitializedCallSite;
+-        if (invoker != null)  return invoker;
+-        if (targetType.parameterCount() > 0) {
+-            MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount());
+-            Invokers invokers0 = type0.invokers();
+-            invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(),
+-                                                  0, targetType.parameterList());
+-            assert(invoker.type().equals(targetType));
+-            uninitializedCallSite = invoker;
+-            return invoker;
++    private static Class<?> impliedRestargType(MethodType restargType, int fromPos) {
++        if (restargType.isGeneric())  return Object[].class;  // can be nothing else
++        int maxPos = restargType.parameterCount();
++        if (fromPos >= maxPos)  return Object[].class;  // reasonable default
++        Class<?> argType = restargType.parameterType(fromPos);
++        for (int i = fromPos+1; i < maxPos; i++) {
++            if (argType != restargType.parameterType(i))
++                throw newIllegalArgumentException("need homogeneous rest arguments", restargType);
+         }
+-        invoker = THROW_UCS;
+-        if (invoker == null) {
+-            try {
+-                THROW_UCS = invoker = IMPL_LOOKUP
+-                    .findStatic(CallSite.class, "uninitializedCallSite",
+-                                MethodType.methodType(Empty.class));
+-            } catch (ReflectiveOperationException ex) {
+-                throw newInternalError(ex);
+-            }
+-        }
+-        invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType()));
+-        invoker = invoker.dropArguments(targetType, 0, targetType.parameterCount());
+-        assert(invoker.type().equals(targetType));
+-        uninitializedCallSite = invoker;
+-        return invoker;
++        if (argType == Object.class)  return Object[].class;
++        return Array.newInstance(argType, 0).getClass();
+     }
+ 
+     public String toString() {
+@@ -306,7 +479,7 @@
+                 : Arrays.asList(mtype, customized, which, nameCursor, names.length);
+         if (MTYPE_ARG >= INARG_LIMIT) {
+             assert(names[MTYPE_ARG] == null);
+-            NamedFunction getter = BoundMethodHandle.getSpeciesData("L").getterFunction(0);
++            NamedFunction getter = BoundMethodHandle.speciesData_L().getterFunction(0);
+             names[MTYPE_ARG] = new Name(getter, names[THIS_MH]);
+             // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM)
+         }
+@@ -361,6 +534,7 @@
+         if (COUNT_EVENTS) {
+             bump(EC_checkGenericType);
+             if (mh.type() == expected)  bump(EC_checkGenericType_exact);
++            else if (mh.asTypeCache != null && mh.asTypeCache.type() == expected)  bump(EC_checkGenericType_hit);
+             else  bump(EC_checkGenericType_miss);
+         }
+         if (mh.type() == expected)  return mh;
+@@ -385,6 +559,10 @@
+          *  => MH.asType(MT0).invokeBasic(A*)
+          */
+     }
++    private static final EventCounter EC_checkGenericType = eventCounter("checkGenericType");
++    private static final EventCounter EC_checkGenericType_exact = eventCounter(EC_checkGenericType, "exact");
++    private static final EventCounter EC_checkGenericType_hit = eventCounter(EC_checkGenericType, "hit");
++    private static final EventCounter EC_checkGenericType_miss = eventCounter(EC_checkGenericType, "miss");
+ 
+     static MemberName linkToCallSiteMethod(MethodType mtype) {
+         LambdaForm lform = callSiteForm(mtype, false);
+@@ -395,10 +573,6 @@
+         LambdaForm lform = callSiteForm(mtype, true);
+         return lform.vmentry;
+     }
+-    private static final EventCounter EC_checkGenericType = eventCounter("checkGenericType");
+-    private static final EventCounter EC_checkGenericType_exact = eventCounter(EC_checkGenericType, "exact");
+-    private static final EventCounter EC_checkGenericType_hit = eventCounter(EC_checkGenericType, "hit");
+-    private static final EventCounter EC_checkGenericType_miss = eventCounter(EC_checkGenericType, "miss");
+ 
+     // skipCallSite is true if we are optimizing a ConstantCallSite
+     private static LambdaForm callSiteForm(MethodType mtype, boolean skipCallSite) {
+@@ -443,24 +617,34 @@
+     }
+ 
+     // Local constant functions:
+-    private static final NamedFunction NF_checkExactType;
+-    private static final NamedFunction NF_checkGenericType;
+-    private static final NamedFunction NF_asType;
+-    private static final NamedFunction NF_getCallSiteTarget;
++    private static final NamedFunction
++        NF_checkExactType,
++        NF_checkGenericType,
++        NF_nextParameterTypes,
++        NF_convertParameter,
++        NF_convertReturn,
++        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_asType = new NamedFunction(MethodHandle.class
+-                    .getDeclaredMethod("asType", MethodType.class));
+-            NF_getCallSiteTarget = new NamedFunction(Invokers.class
+-                    .getDeclaredMethod("getCallSiteTarget", Object.class));
+-            NF_checkExactType.resolve();
+-            NF_checkGenericType.resolve();
+-            NF_getCallSiteTarget.resolve();
+-            // bound
++            NamedFunction nfs[] = {
++                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_nextParameterTypes = new NamedFunction(Invokers.class
++                        .getDeclaredMethod("nextParameterTypes", Object.class, Object.class)),
++                NF_convertParameter = new NamedFunction(Invokers.class
++                        .getDeclaredMethod("convertParameter", Object.class, int.class, Object.class)),
++                NF_convertReturn = new NamedFunction(Invokers.class
++                        .getDeclaredMethod("convertReturn", Object.class, Object.class)),
++                NF_getCallSiteTarget = new NamedFunction(Invokers.class
++                        .getDeclaredMethod("getCallSiteTarget", 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 newInternalError(ex);
+         }
+diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java
+--- a/src/share/classes/java/lang/invoke/LambdaForm.java
++++ b/src/share/classes/java/lang/invoke/LambdaForm.java
+@@ -124,12 +124,51 @@
+     final String debugName;
+     MemberName vmentry;   // low-level behavior, or null if not yet prepared
+     private boolean isCompiled;
+-
+-    // Caches for common structural transforms:
+-    LambdaForm[] bindCache;
++    LambdaFormEditor editor;  // information about what transform if any created this LF, and how to create others
+ 
+     public static final int VOID_RESULT = -1, LAST_RESULT = -2;
+ 
++    // Numeric indexes for basic type characters.
++    public static final byte
++            L_TYPE = 0,  // all reference types
++            I_TYPE = 1, J_TYPE = 2, F_TYPE = 3, D_TYPE = 4,  // all primitive types
++            V_TYPE = 5,  // not valid in all contexts
++            ARG_TYPE_LIMIT = V_TYPE, TYPE_LIMIT = V_TYPE+1;
++    public static final String BASIC_TYPE_CHARS = "LIJFDV";
++    private static final Class<?>[] BASIC_TYPE_CLASSES = {
++        Object.class,
++        int.class, long.class, float.class, double.class,
++        void.class
++    };
++    private static final Wrapper[] BASIC_TYPE_WRAPPERS = {
++        Wrapper.OBJECT,
++        Wrapper.INT, Wrapper.LONG, Wrapper.FLOAT, Wrapper.DOUBLE,
++        Wrapper.VOID
++    };
++    static { assert(verifyBasicTypeChars()); }
++    private static boolean verifyBasicTypeChars() {
++        Object[] data = {
++            'L', L_TYPE, 'I', I_TYPE, 'J', J_TYPE, 'F', F_TYPE, 'D', D_TYPE, 'V', V_TYPE
++        };
++        assert(data.length == TYPE_LIMIT*2);  // all cases covered
++        for (int i = 0; i < TYPE_LIMIT; i++) {
++            char btc = (char) data[2*i+0];
++            byte bti = (byte) data[2*i+1];
++            assert(BASIC_TYPE_CHARS.charAt(bti) == btc);
++            Class<?> c = BASIC_TYPE_CLASSES[bti];
++            assert(Wrapper.basicTypeChar(c) == btc);
++            if (btc != 'V')
++                assert(bti < ARG_TYPE_LIMIT);  // all arg types must come before void
++            else
++                assert(bti == ARG_TYPE_LIMIT);  // void type must be beyond arg type sequence
++        }
++        assert(ARG_TYPE_LIMIT == V_TYPE);  // V must be after all arg types
++        assert(BASIC_TYPE_CHARS.length() == TYPE_LIMIT);
++        assert(BASIC_TYPE_CLASSES.length == TYPE_LIMIT);
++        assert(BASIC_TYPE_WRAPPERS.length == TYPE_LIMIT);
++        return true;
++    }
++
+     LambdaForm(String debugName,
+                int arity, Name[] names, int result) {
+         assert(namesOK(arity, names));
+@@ -169,12 +208,12 @@
+         // Called only from getPreparedForm.
+         assert(isValidSignature(sig));
+         this.arity = signatureArity(sig);
+-        this.result = (signatureReturn(sig) == 'V' ? -1 : arity);
++        this.result = (signatureReturn(sig) == V_TYPE ? -1 : arity);
+         this.names = buildEmptyNames(arity, sig);
+         this.debugName = "LF.zero";
+         assert(nameRefsAreLegal());
+         assert(isEmpty());
+-        assert(sig.equals(basicTypeSignature()));
++        assert(sig.equals(basicTypeSignature())) : sig + " != " + basicTypeSignature();
+         bump(EC_lambdaForm);
+         bump(EC_lambdaForm_blank);
+     }
+@@ -184,17 +223,17 @@
+         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);
++        int numRes = (basicType(basicTypeSignature.charAt(resultPos)) == V_TYPE ? 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));
++            names[arity + i] = constantZero(arity + i, basicType(basicTypeSignature.charAt(resultPos + i)));
+         }
+         return names;
+     }
+ 
+     private static int fixResult(int result, Name[] names) {
+         if (result >= 0) {
+-            if (names[result].type == 'V')
++            if (names[result].type == V_TYPE)
+                 return -1;
+         } else if (result == LAST_RESULT) {
+             return names.length - 1;
+@@ -264,7 +303,7 @@
+      * This allows Name references to be freely reused to construct
+      * fresh lambdas, without confusion.
+      */
+-    private boolean nameRefsAreLegal() {
++    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?
+@@ -297,14 +336,14 @@
+     // }
+ 
+     /** Report the return type. */
+-    char returnType() {
+-        if (result < 0)  return 'V';
++    byte returnType() {
++        if (result < 0)  return V_TYPE;
+         Name n = names[result];
+         return n.type;
+     }
+ 
+     /** Report the N-th argument type. */
+-    char parameterType(int n) {
++    byte parameterType(int n) {
+         assert(n < arity);
+         return names[n].type;
+     }
+@@ -314,6 +353,11 @@
+         return arity;
+     }
+ 
++    /** Report the number of expressions (non-parameter names). */
++    int expressionCount() {
++        return names.length - arity;
++    }
++
+     /** Return the method type corresponding to my basic type signature. */
+     MethodType methodType() {
+         return signatureType(basicTypeSignature());
+@@ -322,15 +366,15 @@
+     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();
++            buf.append(basicTypeChar(parameterType(i)));
++        return buf.append('_').append(basicTypeChar(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 byte signatureReturn(String sig) {
++        return basicType(sig.charAt(signatureArity(sig)+1));
+     }
+     static boolean isValidSignature(String sig) {
+         int arity = sig.indexOf('_');
+@@ -342,27 +386,15 @@
+             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]
++            if (BASIC_TYPE_CHARS.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));
++            ptypes[i] = basicTypeClass(basicType(sig.charAt(i)));
++        Class<?> rtype = basicTypeClass(signatureReturn(sig));
+         return MethodType.methodType(rtype, ptypes);
+     }
+ 
+@@ -446,7 +478,7 @@
+         // TO DO: Maybe add invokeGeneric, invokeWithArguments
+         bump(EC_lambdaForm_prep);
+         if (false && COUNT_EVENTS)//@@
+-        if ((EC_lambdaForm_prep.count() & (0x2000-1)) == 0)  Thread.dumpStack();//@@
++        if ((EC_lambdaForm_prep.count() % 100) == 0)  Thread.dumpStack();//@@
+ 
+     }
+ 
+@@ -463,10 +495,17 @@
+                 traceInterpreter("compileToBytecode", this);
+             isCompiled = true;
+             bump(EC_lambdaForm_comp);
+-            return vmentry;
+         } catch (Error | Exception ex) {
+-            throw newInternalError("compileToBytecode", ex);
++            throw newInternalError(this.toString(), ex);
+         }
++        // Also compile functions used inside:
++        for (Name n : names) {
++            if (n.isParam())  continue;
++            MethodHandle fn = n.function.resolvedHandle;
++            if (fn != null)
++                fn.form.compileToBytecode();
++        }
++        return vmentry;
+     }
+ 
+     private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
+@@ -490,7 +529,7 @@
+                 assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_'))));
+                 LambdaForm form = new LambdaForm(sig);
+                 form.vmentry = m;
+-                mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form);
++                //mt.form().setCachedLambdaForm(MethodTypeForm.LF_?, form);
+                 // FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only
+                 forms.put(sig, form);
+             }
+@@ -551,21 +590,21 @@
+         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]));
++            assert(valueMatches(basicType(sig.charAt(i)), pt, av[i]));
+         }
+         return true;
+     }
+-    private static boolean valueMatches(char tc, Class<?> type, Object x) {
++    private static boolean valueMatches(byte 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
++        if (type == void.class)  tc = V_TYPE;   // 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
++        case I_TYPE: assert checkInt(type, x)   : "checkInt(" + type + "," + x +")";   break;
++        case J_TYPE: assert x instanceof Long   : "instanceof Long: " + x;             break;
++        case F_TYPE: assert x instanceof Float  : "instanceof Float: " + x;            break;
++        case D_TYPE: assert x instanceof Double : "instanceof Double: " + x;           break;
++        case L_TYPE: assert checkRef(type, x)   : "checkRef(" + type + "," + x + ")";  break;
++        case V_TYPE: break;  // allow anything here; will be dropped
+         default:  assert(false);
+         }
+         return true;
+@@ -590,14 +629,23 @@
+     }
+ 
+     /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
+-    private static final int COMPILE_THRESHOLD;
++    static final int COMPILE_THRESHOLD;
+     static {
+-        if (MethodHandleStatics.COMPILE_THRESHOLD != null)
+-            COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD;
++        if (MethodHandleStatics.COMPILE_THRESHOLD == 0)
++            COMPILE_THRESHOLD = 30;  // default value
+         else
+-            COMPILE_THRESHOLD = 30;  // default value
++            COMPILE_THRESHOLD = Math.max(-1, MethodHandleStatics.COMPILE_THRESHOLD);
+     }
+     private int invocationCounter = 0;
++    int getInvocationsRemaining() {
++        return Math.max(0, COMPILE_THRESHOLD - invocationCounter);
++    }
++    void setInvocationsRemaining(int n) {
++        if (invocationCounter >= COMPILE_THRESHOLD)  return;
++        if (n < 0)  n = 0;
++        if (n > COMPILE_THRESHOLD)  n = COMPILE_THRESHOLD;
++        invocationCounter = Math.max(invocationCounter, COMPILE_THRESHOLD - n);
++    }
+ 
+     @Hidden
+     @DontInline
+@@ -612,7 +660,9 @@
+         for (int i = argumentValues.length; i < values.length; i++) {
+             values[i] = interpretName(names[i], values);
+         }
+-        return (result < 0) ? null : values[result];
++        Object rv = (result < 0) ? null : values[result];
++        assert(resultCheck(argumentValues, rv));
++        return rv;
+     }
+ 
+     @Hidden
+@@ -702,8 +752,16 @@
+         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);
++        MethodHandle mh = (MethodHandle) argumentValues[0];
++        assert(mh.internalForm() == this);
+         // note:  argument #0 could also be an interface wrapper, in the future
++        argumentTypesMatch(basicTypeSignature(), argumentValues);
++        return true;
++    }
++    private boolean resultCheck(Object[] argumentValues, Object result) {
++        MethodHandle mh = (MethodHandle) argumentValues[0];
++        MethodType mt = mh.type();
++        assert(valueMatches(returnType(), mt.returnType(), result));
+         return true;
+     }
+ 
+@@ -730,6 +788,7 @@
+             buf.append("=").append(n.exprString());
+             buf.append(";");
+         }
++        if (arity == names.length)  buf.append(")=>{");
+         buf.append(result < 0 ? "void" : names[result]).append("}");
+         if (TRACE_INTERPRETER && INIT_DONE) {
+             // Extra verbosity:
+@@ -739,135 +798,11 @@
+         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);
++    LambdaFormEditor editor() {
++        // races are OK here; worst case is duplicate LFT records active
++        LambdaFormEditor ed = editor;
++        if (ed != null)  return ed;
++        return LambdaFormEditor.lambdaFormEditor(this);
+     }
+ 
+     boolean contains(Name name) {
+@@ -882,102 +817,6 @@
+         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;
+@@ -987,13 +826,14 @@
+             this(resolvedHandle.internalMemberName(), resolvedHandle);
+         }
+         NamedFunction(MemberName member, MethodHandle resolvedHandle) {
++            bump(EC_namedFunction);
+             this.member = member;
+-            //resolvedHandle = eraseSubwordTypes(resolvedHandle);
+             this.resolvedHandle = resolvedHandle;
++            // The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
++            //assert(!isInvokeBasic());
++        }
++        NamedFunction(MethodType basicInvokerType) {
+             bump(EC_namedFunction);
+-        }
+-        private static final EventCounter EC_namedFunction = eventCounter("namedFunction");
+-        NamedFunction(MethodType basicInvokerType) {
+             assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
+             if (basicInvokerType.parameterSlotCount() < MethodType.MAX_MH_INVOKER_ARITY) {
+                 this.resolvedHandle = basicInvokerType.invokers().basicInvoker();
+@@ -1002,6 +842,12 @@
+                 // necessary to pass BigArityTest
+                 this.member = Invokers.invokeBasicMethod(basicInvokerType);
+             }
++            assert(isInvokeBasic());
++        }
++        private static final EventCounter EC_namedFunction = eventCounter("namedFunction");
++
++        private boolean isInvokeBasic() {
++            return member != null && member.isMethodHandleInvoke() && "invokeBasic".equals(member.getName());
+         }
+ 
+         // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc.
+@@ -1054,10 +900,10 @@
+                     String sig = m.getName().substring("invoke_".length());
+                     int arity = LambdaForm.signatureArity(sig);
+                     MethodType srcType = MethodType.genericMethodType(arity);
+-                    if (LambdaForm.signatureReturn(sig) == 'V')
++                    if (LambdaForm.signatureReturn(sig) == V_TYPE)
+                         srcType = srcType.changeReturnType(void.class);
+                     MethodTypeForm typeForm = srcType.form();
+-                    typeForm.namedFunctionInvoker = DirectMethodHandle.make(m);
++                    typeForm.setCachedMethodHandle(MethodTypeForm.MH_NF_INV, DirectMethodHandle.make(m));
+                 }
+             }
+         }
+@@ -1137,16 +983,17 @@
+             MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
+ 
+         private static MethodHandle computeInvoker(MethodTypeForm typeForm) {
+-            MethodHandle mh = typeForm.namedFunctionInvoker;
++            typeForm = typeForm.basicType().form();  // normalize to basic type
++            MethodHandle mh = typeForm.cachedMethodHandle(MethodTypeForm.MH_NF_INV);
+             if (mh != null)  return mh;
+             MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm);  // this could take a while
+             mh = DirectMethodHandle.make(invoker);
+-            MethodHandle mh2 = typeForm.namedFunctionInvoker;
++            MethodHandle mh2 = typeForm.cachedMethodHandle(MethodTypeForm.MH_NF_INV);
+             if (mh2 != null)  return mh2;  // benign race
+             if (!mh.type().equals(INVOKER_METHOD_TYPE))
+                 throw new InternalError(mh.debugString());
+             bump(EC_lambdaForm_invoker);
+-            return typeForm.namedFunctionInvoker = mh;
++            return typeForm.setCachedMethodHandle(MethodTypeForm.MH_NF_INV, mh);
+         }
+ 
+         @Hidden
+@@ -1236,11 +1083,11 @@
+             return (member == null) ? null : member.getDeclaringClass();
+         }
+ 
+-        char returnType() {
++        byte returnType() {
+             return basicType(methodType().returnType());
+         }
+ 
+-        char parameterType(int n) {
++        byte parameterType(int n) {
+             return basicType(methodType().parameterType(n));
+         }
+ 
+@@ -1261,38 +1108,67 @@
+         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 basicTypeChar(byte type) {
++        return BASIC_TYPE_CHARS.charAt(type);
+     }
+-    public static char[] basicTypes(List<Class<?>> types) {
+-        char[] btypes = new char[types.size()];
++    public static byte basicType(char type) {
++        int bti = BASIC_TYPE_CHARS.indexOf(type);
++        if (bti >= 0)  return (byte) bti;
++        assert("ZBSC".indexOf(type) >= 0);
++        return I_TYPE;  // all subword types are represented as ints
++    }
++
++    public static Wrapper basicTypeWrapper(byte type) {
++        return BASIC_TYPE_WRAPPERS[type];
++    }
++    public static byte basicType(Wrapper type) {
++        char c = type.basicTypeChar();
++        return basicType(c);
++    }
++
++    public static Class<?> basicTypeClass(byte type) {
++        return BASIC_TYPE_CLASSES[type];
++    }
++    public static byte basicType(Class<?> type) {
++        if (!type.isPrimitive())  return L_TYPE;
++        return basicType(Wrapper.forPrimitiveType(type));
++    }
++    public static char basicTypeChar(Class<?> type) {
++        return BASIC_TYPE_CHARS.charAt(basicType(type));
++    }
++    public static byte[] basicTypes(List<Class<?>> types) {
++        byte[] btypes = new byte[types.size()];
+         for (int i = 0; i < btypes.length; i++) {
+             btypes[i] = basicType(types.get(i));
+         }
+         return btypes;
+     }
++    public static byte[] basicTypes(String types) {
++        byte[] btypes = new byte[types.length()];
++        for (int i = 0; i < btypes.length; i++) {
++            btypes[i] = basicType(types.charAt(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++] = basicTypeChar(pt);
+         }
+         sig[sigp++] = '_';
+-        sig[sigp++] = basicType(type.returnType());
++        sig[sigp++] = basicTypeChar(type.returnType());
+         assert(sigp == sig.length);
+         return String.valueOf(sig);
+     }
+ 
+     static final class Name {
+-        final char type;
++        final byte type;
+         private short index;
+         final NamedFunction function;
+         final Object[] arguments;
+ 
+-        private Name(int index, char type, NamedFunction function, Object[] arguments) {
++        private Name(int index, byte type, NamedFunction function, Object[] arguments) {
+             this.index = (short)index;
+             this.type = type;
+             this.function = function;
+@@ -1304,7 +1180,7 @@
+         }
+         Name(MethodType functionType, Object... arguments) {
+             this(new NamedFunction(functionType), arguments);
+-            assert(arguments[0] instanceof Name && ((Name)arguments[0]).type == 'L');
++            assert(arguments[0] instanceof Name && ((Name)arguments[0]).type == L_TYPE);
+         }
+         Name(MemberName function, Object... arguments) {
+             this(new NamedFunction(function), arguments);
+@@ -1315,14 +1191,16 @@
+             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) {
++        /** Create a raw parameter of the given type, with an expected index. */
++        Name(int index, byte type) {
+             this(index, type, null, null);
+         }
+-        Name(char type) {
++        /** Create a raw parameter of the given type. */
++        Name(byte type) {
+             this(-1, type);
+         }
+ 
+-        char type() { return type; }
++        byte type() { return type; }
+         int index() { return index; }
+         boolean initIndex(int i) {
+             if (index != i) {
+@@ -1331,7 +1209,9 @@
+             }
+             return true;
+         }
+-
++        char typeChar() {
++            return basicTypeChar(type);
++        }
+ 
+         void resolve() {
+             if (function != null)
+@@ -1364,7 +1244,11 @@
+             if (!replaced)  return this;
+             return new Name(function, arguments);
+         }
++        /** In the arguments of this Name, replace oldNames[i] pairwise by newNames[i].
++         *  Limit such replacements to {@code start<=i<end}.  Return possibly changed self.
++         */
+         Name replaceNames(Name[] oldNames, Name[] newNames, int start, int end) {
++            if (start >= end)  return this;
+             @SuppressWarnings("LocalVariableHidesMemberVariable")
+             Object[] arguments = this.arguments;
+             boolean replaced = false;
+@@ -1413,14 +1297,14 @@
+         }
+ 
+         public String toString() {
+-            return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+type;
++            return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+typeChar();
+         }
+         public String debugString() {
+             String s = toString();
+             return (function == null) ? s : s + "=" + exprString();
+         }
+         public String exprString() {
+-            if (function == null)  return "null";
++            if (function == null)  return toString();
+             StringBuilder buf = new StringBuilder(function.toString());
+             buf.append("(");
+             String cma = "";
+@@ -1435,48 +1319,32 @@
+             return buf.toString();
+         }
+ 
+-        private static boolean typesMatch(char parameterType, Object object) {
++        static boolean typesMatch(byte 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;
++                case I_TYPE:  return object instanceof Integer;
++                case J_TYPE:  return object instanceof Long;
++                case F_TYPE:  return object instanceof Float;
++                case D_TYPE:  return object instanceof Double;
+             }
+-            assert(parameterType == 'L');
++            assert(parameterType == L_TYPE);
+             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
++        /** Return the index of the last occurrence of n in the argument array.
++         *  Return -1 if the name is not used.
+          */
+-        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;
++        int lastUseIndex(Name n) {
++            if (arguments == null)  return -1;
++            for (int i = arguments.length; --i >= 0; ) {
++                if (arguments[i] == n)  return i;
+             }
+-            return false;
++            return -1;
++        }
++        boolean contains(Name n) {
++            return this == n || lastUseIndex(n) >= 0;
+         }
+ 
+         public boolean equals(Name that) {
+@@ -1502,11 +1370,27 @@
+         }
+     }
+ 
++    /** Return the index of the last name which contains n as an argument.
++     *  Return -1 if the name is not used.  Return names.length if it is the return value.
++     */
++    int lastUseIndex(Name n) {
++        int ni = n.index, nmax = names.length;
++        assert(names[ni] == n);
++        if (result == ni)  return nmax;  // live all the way beyond the end
++        for (int i = nmax; --i > ni; ) {
++            if (names[i].lastUseIndex(n) >= 0)
++                return i;
++        }
++        return -1;
++    }
++
+     static Name argument(int which, char type) {
+-        int tn = ALL_TYPES.indexOf(type);
+-        if (tn < 0 || which >= INTERNED_ARGUMENT_LIMIT)
++        return argument(which, basicType(type));
++    }
++    static Name argument(int which, byte type) {
++        if (which >= INTERNED_ARGUMENT_LIMIT)
+             return new Name(which, type);
+-        return INTERNED_ARGUMENTS[tn][which];
++        return INTERNED_ARGUMENTS[type][which];
+     }
+     static Name internArgument(Name n) {
+         assert(n.isParam()) : "not param: " + n;
+@@ -1548,31 +1432,28 @@
+             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];
++            = new Name[ARG_TYPE_LIMIT][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);
++        for (byte type = 0; type < ARG_TYPE_LIMIT; type++) {
++            for (int i = 0; i < INTERNED_ARGUMENTS[type].length; i++) {
++                INTERNED_ARGUMENTS[type][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);
++    static Name constantZero(int which, byte type) {
++        return CONSTANT_ZERO[type].newIndex(which);
+     }
+     private static final Name[] CONSTANT_ZERO
+-            = new Name[ALL_TYPES.length()];
++            = new Name[ARG_TYPE_LIMIT];
+     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);
++        for (byte bt = 0; bt < ARG_TYPE_LIMIT; bt++) {
++            Wrapper wrap = Wrapper.forBasicType(basicTypeChar(bt));
++            MemberName zmem = new MemberName(LambdaForm.class, "zero"+basicTypeChar(bt), MethodType.methodType(wrap.primitiveType()), REF_invokeStatic);
+             try {
+                 zmem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zmem, null, NoSuchMethodException.class);
+             } catch (IllegalAccessException|NoSuchMethodException ex) {
+@@ -1580,8 +1461,8 @@
+             }
+             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.type == bt);
++            CONSTANT_ZERO[bt] = n;
+             assert(n.isConstantZero());
+         }
+     }
+diff --git a/src/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/share/classes/java/lang/invoke/LambdaFormEditor.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/lang/invoke/LambdaFormEditor.java
+@@ -0,0 +1,768 @@
++/*
++ * Copyright (c) 2013, 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.MethodHandleStatics.*;
++import static java.lang.invoke.MethodHandleNatives.Constants.*;
++import java.util.Arrays;
++import java.lang.invoke.LambdaForm.*;
++import static java.lang.invoke.LambdaForm.*;
++import java.util.ArrayList;
++import java.util.HashMap;
++import sun.misc.Unsafe;
++
++/** Transforms on LFs.
++ *  A lambda-form editor can derive new LFs from its base LF.
++ *  To support caching, a LF has an optional pointer to its editor.
++ */
++class LambdaFormEditor {
++    final LambdaForm lambdaForm;
++
++    // Caches for common structural transforms:
++    LambdaForm[] bindCache;  // cache for bindArgumentForm
++    LambdaForm[] addArgCache;  // cache for addArgumentForm
++    LambdaForm[] filterArgCache;  // cache for filterArgumentForm
++    LambdaForm[] foldArgCache;  // cache for foldArgumentForm
++    HashMap<Object,LambdaForm> permuteArgsCache;  // cache for permuteArguments
++    // FIXME:  Investigate using a more flexible cache format.
++
++    private LambdaFormEditor(LambdaForm lambdaForm) {
++        this.lambdaForm = lambdaForm;
++        bump(EC_lambdaForm_editor);
++    }
++
++    // Factory method.
++    static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
++        // TO DO:  Consider placing intern logic here, to cut down on duplication.
++        LambdaFormEditor editor = new LambdaFormEditor(lambdaForm);
++        lambdaForm.editor = editor;  // cache it for next time
++        return editor;
++    }
++
++    private Buffer buffer() {
++        return new Buffer(lambdaForm);
++    }
++
++    /** Working storage for an LF that is being transformed. */
++    private static final class Buffer {
++        int arity, length;
++        Name[] names;
++        Name[] originalNames;  // snapshot of pre-transaction names
++        byte flags;
++        int firstChange;
++        Name resultName;
++        String debugName;
++        ArrayList<Name> dups;
++
++        static final int F_TRANS = 0x10, F_OWNED = 0x03;
++
++        Buffer(LambdaForm lf) {
++            this(lf.arity, lf.names, lf.result);
++            debugName = lf.debugName;
++            assert (lf.nameRefsAreLegal());
++        }
++        Buffer(int arity, Name[] names, int result) {
++            this.arity = arity;
++            setNames(names);
++            if (result == LAST_RESULT)  result = length-1;
++            if (result >= 0 && names[result].type != V_TYPE)
++                resultName =  names[result];
++        }
++
++        LambdaForm lambdaForm() {
++            assert(!inTrans());  // need endEdit call to tidy things up
++            return new LambdaForm(debugName, arity, nameArray(), resultIndex());
++        }
++
++        Name name(int i) {
++            assert(i < length);
++            return names[i];
++        }
++
++        Name[] nameArray() {
++           return Arrays.copyOf(names, length);
++        }
++
++        int resultIndex() {
++            if (resultName == null)  return VOID_RESULT;
++            int index = indexOf(resultName, names);
++            assert(index >= 0);
++            return index;
++        }
++
++        void setNames(Name[] names2) {
++            names = originalNames = names2;  // keep a record of where everything was to start with
++            length = names2.length;
++            flags = 0;
++        }
++
++        private boolean verifyArity() {
++            for (int i = 0; i < arity; i++) {
++                assert(names[i].isParam()) : "#"+i+"="+names[i];
++            }
++            for (int i = arity; i < length; i++) {
++                assert(!names[i].isParam()) : "#"+i+"="+names[i];
++            }
++            for (int i = length; i < names.length; i++) {
++                assert(names[i] == null) : "#"+i+"="+names[i];
++            }
++            // check resultName also
++            if (resultName != null) {
++                int resultIndex = indexOf(resultName, names);
++                assert(resultIndex >= 0) : "not found: "+resultName.exprString()+Arrays.asList(names);
++                assert(names[resultIndex] == resultName);
++            }
++            return true;
++        }
++        private boolean verifyFirstChange() {
++            assert(inTrans());
++            for (int i = 0; i < length; i++) {
++                if (names[i] != originalNames[i]) {
++                    assert(firstChange == i) : Arrays.asList(firstChange, i, originalNames[i].exprString(), Arrays.asList(names));
++                    return true;
++                }
++            }
++            assert(firstChange == length) : Arrays.asList(firstChange, Arrays.asList(names));
++            return true;
++        }
++
++        private static int indexOf(NamedFunction fn, NamedFunction[] fns) {
++            for (int i = 0; i < fns.length; i++) {
++                if (fns[i] == fn)  return i;
++            }
++            return -1;
++        }
++
++        private static int indexOf(Name n, Name[] ns) {
++            for (int i = 0; i < ns.length; i++) {
++                if (ns[i] == n)  return i;
++            }
++            return -1;
++        }
++
++        boolean inTrans() { return (flags & F_TRANS) != 0; }
++        int ownedCount() { return (flags & F_OWNED); }
++
++        void growNames(int insertPos, int growLength) {
++            int oldLength = length;
++            int newLength = oldLength + growLength;
++            int oc = ownedCount();
++            if (oc == 0 || newLength > names.length) {
++                names = Arrays.copyOf(names, (names.length + growLength) * 5 / 4);
++                if (oc == 0) { flags++; oc++; assert(ownedCount() == oc); }
++            }
++            if (originalNames != null && originalNames.length < names.length) {
++                originalNames = Arrays.copyOf(originalNames, names.length);
++                if (oc == 1) { flags++; oc++; assert(ownedCount() == oc); }
++            }
++            int insertEnd = insertPos + growLength;
++            int tailLength = oldLength - insertPos;
++            System.arraycopy(names, insertPos, names, insertEnd, tailLength);
++            Arrays.fill(names, insertPos, insertEnd, null);
++            if (originalNames != null) {
++                System.arraycopy(originalNames, insertPos, originalNames, insertEnd, tailLength);
++                Arrays.fill(originalNames, insertPos, insertEnd, null);
++            }
++            length = newLength;
++            if (firstChange >= insertPos)
++                firstChange += growLength;
++        }
++
++        int lastIndexOf(Name n) {
++            int result = -1;
++            for (int i = 0; i < length; i++) {
++                if (names[i] == n)  result = i;
++            }
++            return result;
++        }
++
++        void noteDuplicate(int pos1, int pos2) {
++            Name n = names[pos1];
++            assert(n == names[pos2]);
++            assert(originalNames[pos1] != null);
++            assert(originalNames[pos2] == null);
++            assert(ownedCount() == 2);
++            originalNames[pos2] = originalNames[pos1];
++            if (dups == null)  dups = new ArrayList<>();
++            dups.add(n);
++        }
++
++        /** Create a private, writable copy of names.
++         *  Preserve the original copy, for reference.
++         */
++        void startEdit() {
++            assert(verifyArity());
++            int oc = ownedCount();
++            assert(!inTrans());  // no nested transactions
++            flags |= F_TRANS;
++            Name[] oldNames = names;
++            Name[] ownBuffer = (oc == 2 ? originalNames : null);
++            assert(ownBuffer != oldNames);
++            if (ownBuffer != null && ownBuffer.length >= length) {
++                names = copyNamesInto(ownBuffer);
++            } else {
++                // make a new buffer to hold the names
++                final int SLOP = 2;
++                names = Arrays.copyOf(oldNames, Math.max(length+SLOP, oldNames.length));
++                if (oc < 2)  ++flags;
++                assert(ownedCount() == oc+1);
++            }
++            originalNames = oldNames;
++            assert(originalNames != names);
++            firstChange = length;
++            assert(inTrans());
++        }
++        void changeName(int i, Name name) {
++            assert(inTrans());
++            assert(i < length);
++            Name oldName = names[i];
++            assert(!name.contains(oldName));  // no multiple changes
++            assert(oldName == originalNames[i]);  // no multiple changes
++            assert(verifyFirstChange());
++            names[i] = name;
++            if (firstChange > i)  firstChange = i;
++            if (resultName == oldName)
++                resultName = name;
++        }
++        void setResult(Name name) {
++            assert(name == null || lastIndexOf(name) >= 0);
++            resultName = name;
++        }
++        void endEdit() {
++            assert(verifyFirstChange());
++            if (dups != null) {
++                // Remove duplicates.
++                assert(ownedCount() == 2);
++                for (Name dup : dups) {
++                    for (int i = firstChange; i < length; i++) {
++                        if (names[i] == dup) {
++                            names[i] = null;
++                            originalNames[i] = null;
++                            assert(Arrays.asList(names).subList(i+1, length).contains(dup));
++                            break;  // kill only one dup
++                        }
++                    }
++                }
++                dups.clear();
++            }
++            // Assuming names have been changed pairwise from originalNames[i] to names[i],
++            // update arguments to ensure referential integrity.
++            for (int i = Math.max(firstChange, arity); i < length; i++) {
++                Name name = names[i];
++                if (name == null)  continue;  // space for removed duplicate
++                Name newName = name.replaceNames(originalNames, names, firstChange, i);
++                if (newName != name) {
++                    names[i] = newName;
++                    if (resultName == name)
++                        resultName = newName;
++                }
++            }
++            assert(inTrans());
++            flags &= ~F_TRANS;
++            // Now that we are done with originalNames, remove "killed" names.
++            int oldLength = length;
++            for (int i = firstChange; i < length; i++) {
++                if (names[i] == null) {
++                    System.arraycopy(names, i+1, names, i, (--length - i));
++                }
++            }
++            if (length < oldLength)  Arrays.fill(names, length, oldLength, null);
++            assert(!Arrays.asList(names).subList(0, length).contains(null));
++            // If any parameters have been changed, then reorder them as needed.
++            // This is a "sheep-and-goats" stable sort, pushing all non-parameters
++            // to the right of all parameters.
++            if (firstChange < arity) {
++                Name[] exprs = new Name[arity - firstChange];
++                int argp = firstChange, exprp = 0;
++                for (int i = firstChange; i < arity; i++) {
++                    Name name = names[i];
++                    if (name.isParam()) {
++                        names[argp++] = name;
++                    } else {
++                        exprs[exprp++] = name;
++                    }
++                }
++                assert(exprp == (arity - argp));
++                // copy the exprs just after the last remaining param
++                System.arraycopy(exprs, 0, names, argp, exprp);
++                // adjust arity
++                arity -= exprp;
++            }
++            assert(verifyArity());
++        }
++        Name[] copyNamesInto(Name[] buffer) {
++            System.arraycopy(names, 0, buffer, 0, length);
++            Arrays.fill(buffer, length, buffer.length, null);
++            return buffer;
++        }
++
++        Buffer replaceFunctions(NamedFunction[] oldFns, NamedFunction[] newFns) {
++            assert(inTrans());
++            if (oldFns.length == 0)  return this;
++            for (int i = arity; i < length; i++) {
++                Name n = names[i];
++                // maybe check n.function.memberDeclaringClassOrNull() == oldData.clazz
++                int nfi = indexOf(n.function, oldFns);
++                if (nfi >= 0) {
++                    changeName(i, new Name(newFns[nfi], n.arguments));
++                }
++            }
++            return this;
++        }
++
++        private void replaceName(int pos, Name binding, int duplicatePos) {
++            assert(inTrans());
++            assert(verifyArity());
++            assert(pos < arity);
++            Name param = names[pos];
++            assert(!binding.isParam());
++            assert(duplicatePos >= 0 || lastIndexOf(binding) < 0);  // use other overloading of replaceParameter
++            assert(param.isParam());
++            changeName(pos, binding);
++            if (duplicatePos >= 0)
++                noteDuplicate(pos, duplicatePos);  // temporarily, will occur twice in the names array
++        }
++
++        Buffer replaceParameter(int pos, Name binding) {
++            replaceName(pos, binding, -1);
++            return this;
++        }
++
++        Buffer replaceParameter(int pos, int valuePos) {
++            replaceName(pos, names[valuePos], valuePos);
++            return this;
++        }
++
++        private void insertName(int pos, Name expr, boolean isParameter) {
++            assert(inTrans());
++            assert(verifyArity());
++            assert(isParameter ? pos <= arity : pos >= arity);
++            growNames(pos, 1);
++            if (isParameter)  arity += 1;
++            changeName(pos, expr);
++        }
++
++        Buffer insertExpression(int pos, Name expr) {
++            assert(!expr.isParam());
++            insertName(pos, expr, false);
++            return this;
++        }
++        Buffer insertParameter(int pos, Name param) {
++            assert(param.isParam());
++            insertName(pos, param, true);
++            return this;
++        }
++
++    }
++
++    /// Editing methods for method handles.  These need to be fast paths.
++
++    BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) {
++        byte bt = L_TYPE;
++        MethodType type2 = bindArgumentType(mh, pos, bt);
++        LambdaForm form2 = bindArgumentForm(1+pos, mh.speciesData());
++        return mh.copyWithExtendL(type2, form2, value);
++    }
++    BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) {
++        byte bt = I_TYPE;
++        MethodType type2 = bindArgumentType(mh, pos, bt);
++        LambdaForm form2 = bindArgumentForm(1+pos, mh.speciesData());
++        return mh.copyWithExtendI(type2, form2, value);
++    }
++
++    BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) {
++        byte bt = J_TYPE;
++        MethodType type2 = bindArgumentType(mh, pos, bt);
++        LambdaForm form2 = bindArgumentForm(1+pos, mh.speciesData());
++        return mh.copyWithExtendJ(type2, form2, value);
++    }
++
++    BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) {
++        byte bt = F_TYPE;
++        MethodType type2 = bindArgumentType(mh, pos, bt);
++        LambdaForm form2 = bindArgumentForm(1+pos, mh.speciesData());
++        return mh.copyWithExtendF(type2, form2, value);
++    }
++
++    BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) {
++        byte bt = D_TYPE;
++        MethodType type2 = bindArgumentType(mh, pos, bt);
++        LambdaForm form2 = bindArgumentForm(1+pos, mh.speciesData());
++        return mh.copyWithExtendD(type2, form2, value);
++    }
++
++    private MethodType bindArgumentType(BoundMethodHandle mh, int pos, byte bt) {
++        assert(mh.form == lambdaForm);
++        assert(mh.form.names[1+pos].type == bt);
++        assert(LambdaForm.basicType(mh.type().parameterType(pos)) == bt);
++        return mh.type().dropParameterTypes(pos, pos+1);
++    }
++
++    /// Editing methods for lambda forms.
++    // Each editing method can (potentially) cache the edited LF so that it can be reused later.
++
++    LambdaForm bindArgumentForm(int pos, BoundMethodHandle.SpeciesData oldData) {
++        bump(EC_lambdaForm_bind);
++        assert(pos > 0);  // cannot bind the MH arg itself
++        Name name = lambdaForm.names[pos];
++        BoundMethodHandle.SpeciesData newData = oldData.extendWith(name.type);
++        NamedFunction getter = newData.getterFunction(oldData.fieldCount());
++        Name binding = new Name(getter, lambdaForm.names[0]);
++        assert(pos == name.index());
++        assert(name.isParam());
++        assert(!binding.isParam());
++        assert(name.type == binding.type);
++        assert(0 <= pos && pos < lambdaForm.arity && lambdaForm.names[pos] == name);
++        assert(binding.function.memberDeclaringClassOrNull() == newData.clazz);
++        assert(oldData.getters.length == newData.getters.length - 1);
++        assert(newData.nominalGetters[newData.nominalGetters.length - 1] == binding.function);
++        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[lambdaForm.arity];
++        }
++
++        bump(EC_lambdaForm_bind_miss);
++        Buffer buf = buffer();
++        buf.startEdit();
++
++        // The newly created LF will run with a different BMH.
++        // Switch over any pre-existing BMH field references to the new BMH class.
++        buf.replaceFunctions(oldData.nominalGetters, newData.nominalGetters);
++        buf.replaceParameter(pos, binding);
++        buf.endEdit();
++
++        LambdaForm form2 = buf.lambdaForm();
++        if (false) { //@@
++            String key = form2.toString();//@@
++            if (BINDERS.containsKey(key)) {
++                System.out.println(key);
++                BINDERS.get(key).printStackTrace();
++                Thread.dumpStack();
++            }
++            BINDERS.put(key, new Exception());
++        }//@@
++        return bindCache[pos] = form2;
++    }
++    private static final HashMap<String,Exception> BINDERS = new HashMap<>();//@@
++
++    LambdaForm addArgumentForm(int pos, byte type) {
++        bump(EC_lambdaForm_addArg);
++        assert((type & 0xFF) < ARG_TYPE_LIMIT);
++        int cachePos = (pos * ARG_TYPE_LIMIT) + type;
++        if (addArgCache != null) {
++            LambdaForm form = addArgCache[cachePos];
++            if (form != null) {
++                assert(form.names[pos].type == type) : form;
++                assert(form.arity == lambdaForm.arity+1) : form;
++                return form;
++            }
++        } else {
++            addArgCache = new LambdaForm[(lambdaForm.arity+1) * ARG_TYPE_LIMIT];
++        }
++        bump(EC_lambdaForm_addArg_miss);
++        Name arg = new Name(type);
++        assert (pos <= lambdaForm.arity);
++        int length = lambdaForm.names.length;
++        Name[] names2 = Arrays.copyOf(lambdaForm.names, length + 1);
++        int arity2 = lambdaForm.arity + 1;
++        int result2 = lambdaForm.result;
++        if (result2 >= pos) {
++            result2 += 1;
++        }
++        System.arraycopy(lambdaForm.names, pos, names2, pos + 1, length - pos);
++        names2[pos] = arg;
++        LambdaForm form2 = new LambdaForm(lambdaForm.debugName, arity2, names2, result2);
++        return addArgCache[cachePos] = form2;
++    }
++
++    LambdaForm filterArgumentForm(int pos, byte newType, BoundMethodHandle.SpeciesData oldData) {
++        bump(EC_lambdaForm_filter);
++        assert((newType & 0xFF) < ARG_TYPE_LIMIT);
++        assert(pos < lambdaForm.arity);
++        int cachePos = pos * ARG_TYPE_LIMIT + newType;
++        if (filterArgCache != null && cachePos >= 0) {
++            LambdaForm form = filterArgCache[cachePos];
++            if (form != null) {
++                assert(form.arity == lambdaForm.arity) : form;
++                assert(form.names[pos].type == newType) : form;
++                return form;
++            }
++        } else {
++            filterArgCache = new LambdaForm[lambdaForm.arity * ARG_TYPE_LIMIT + TYPE_LIMIT];
++        }
++
++        bump(EC_lambdaForm_filter_miss);
++        assert(pos > 0);  // cannot filter the MH arg itself
++        Buffer buf = buffer();
++        buf.startEdit();
++
++        BoundMethodHandle.SpeciesData newData = oldData.extendWith(L_TYPE);
++        NamedFunction getter = newData.getterFunction(oldData.fieldCount());
++        Name getFilter = new Name(getter, lambdaForm.names[0]);
++        Name oldParam = lambdaForm.names[pos];
++        Name newParam = new Name(pos, newType);
++        MethodType filterType = MethodType.methodType(basicTypeClass(oldParam.type), basicTypeClass(newType));
++        Name callFilter = new Name(filterType, getFilter, newParam);
++
++        // The newly created LF will run with a different BMH.
++        // Switch over any pre-existing BMH field references to the new BMH class.
++        buf.replaceFunctions(oldData.nominalGetters, newData.nominalGetters);
++        int insPos = lambdaForm.arity();
++        buf.insertExpression(insPos+0, getFilter);
++        buf.insertExpression(insPos+1, callFilter);
++        buf.insertParameter(pos, newParam);
++        assert(buf.lastIndexOf(callFilter) == insPos+2);
++        buf.replaceParameter(pos+1, insPos+2);
++        buf.endEdit();
++        LambdaForm lform = buf.lambdaForm();
++        if (cachePos >= 0)  filterArgCache[cachePos] = lform;
++        return lform;
++    }
++
++    LambdaForm filterReturnForm(byte newType, BoundMethodHandle.SpeciesData oldData) {
++        bump(EC_lambdaForm_filterReturn);
++        assert((newType & 0xFF) < TYPE_LIMIT);
++        int cachePos = lambdaForm.arity * ARG_TYPE_LIMIT + newType;
++        if (filterArgCache != null && cachePos >= 0) {
++            LambdaForm form = filterArgCache[cachePos];
++            if (form != null) {
++                assert(form.arity == lambdaForm.arity) : form;
++                assert(form.returnType() == newType) : form;
++                return form;
++            }
++        } else {
++            filterArgCache = new LambdaForm[lambdaForm.arity * ARG_TYPE_LIMIT + TYPE_LIMIT];
++        }
++
++        bump(EC_lambdaForm_filterReturn_miss);
++        Buffer buf = buffer();
++        buf.startEdit();
++
++        BoundMethodHandle.SpeciesData newData = oldData.extendWith(L_TYPE);
++        NamedFunction getter = newData.getterFunction(oldData.fieldCount());
++        Name getFilter = new Name(getter, lambdaForm.names[0]);
++        Name callFilter;
++        byte oldType = lambdaForm.returnType();
++        if (oldType == V_TYPE) {
++            MethodType filterType = MethodType.methodType(basicTypeClass(newType));
++            callFilter = new Name(filterType, getFilter);
++        } else {
++            MethodType filterType = MethodType.methodType(basicTypeClass(newType), basicTypeClass(oldType));
++            callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]);
++        }
++
++        // The newly created LF will run with a different BMH.
++        // Switch over any pre-existing BMH field references to the new BMH class.
++        buf.replaceFunctions(oldData.nominalGetters, newData.nominalGetters);
++        int insPos = lambdaForm.names.length;
++        buf.insertExpression(insPos+0, getFilter);
++        buf.insertExpression(insPos+1, callFilter);
++        buf.setResult(callFilter);
++        buf.endEdit();
++        LambdaForm lform = buf.lambdaForm();
++        if (cachePos >= 0)  filterArgCache[cachePos] = lform;
++        return lform;
++    }
++
++    LambdaForm foldArgumentsForm(int foldPos, MethodType combinerType, BoundMethodHandle.SpeciesData oldData) {
++        bump(EC_lambdaForm_fold);
++        assert(combinerType == combinerType.basicType());
++        int combinerArity = combinerType.parameterCount();
++        assert(combinerArity <= lambdaForm.arity);
++        int foldArgCount = (combinerType.returnType() == void.class ? 0 : 1);
++        int cachePos = combinerArity * 2 + foldArgCount;
++        if (foldPos != 1)  cachePos = -1;
++        if (foldArgCache != null && cachePos >= 0) {
++            LambdaForm form = foldArgCache[cachePos];
++            if (form != null) {
++                assert(form.arity == lambdaForm.arity-foldArgCount) : form;
++                return form;
++            }
++        } else {
++            foldArgCache = new LambdaForm[(lambdaForm.arity+1) * 2];
++        }
++
++        bump(EC_lambdaForm_fold_miss);
++        assert(foldPos > 0);  // cannot fold the MH arg itself
++        Buffer buf = buffer();
++        buf.startEdit();
++
++        BoundMethodHandle.SpeciesData newData = oldData.extendWith(L_TYPE);
++        NamedFunction getter = newData.getterFunction(oldData.fieldCount());
++        Name getCombiner = new Name(getter, lambdaForm.names[0]);
++        int combArgPos = foldPos + foldArgCount, combArgLimit = combArgPos + combinerArity;
++        Object[] combinerArgs = Arrays.copyOfRange(lambdaForm.names, combArgPos - 1, combArgLimit, Object[].class);
++        combinerArgs[0] = getCombiner;
++        for (int i = 0; i < combinerArity; i++) {
++            assert(((Name)combinerArgs[1+i]).type() == LambdaForm.basicType(combinerType.parameterType(i)));
++        }
++        Name callCombiner = new Name(combinerType, combinerArgs);
++
++        // The newly created LF will run with a different BMH.
++        // Switch over any pre-existing BMH field references to the new BMH class.
++        buf.replaceFunctions(oldData.nominalGetters, newData.nominalGetters);
++        int insPos = lambdaForm.arity();
++        buf.insertExpression(insPos+0, getCombiner);
++        buf.insertExpression(insPos+1, callCombiner);
++        if (foldArgCount != 0) {
++            buf.replaceParameter(foldPos, insPos+1);
++        }
++        buf.endEdit();
++        LambdaForm lform = buf.lambdaForm();
++        if (cachePos >= 0)  foldArgCache[cachePos] = lform;
++        return lform;
++    }
++
++    LambdaForm permuteArguments(int skip, int[] reorder) {
++        bump(EC_lambdaForm_permute);
++        assert(skip == 1);  // skip only the leading MH argument, names[0]
++        int length = lambdaForm.names.length;
++        int outArgs = reorder.length;
++        int inTypes = 0;
++        for (int inArg : reorder) {
++            inTypes = Math.max(inTypes, inArg+1);
++        }
++        Object permKey = makePermKey(reorder, inTypes);
++        if (permuteArgsCache != null && permKey != null) {
++            LambdaForm form = permuteArgsCache.get(permKey);
++            if (form != null) {
++                assert(form.arity == skip+inTypes) : form;
++                return form;
++            }
++        } else {
++            permuteArgsCache = new HashMap<>();
++        }
++
++        bump(EC_lambdaForm_permute_miss);
++        byte[] types = new byte[inTypes];
++        for (int i = 0; i < outArgs; i++) {
++            int inArg = reorder[i];
++            types[inArg] = lambdaForm.names[skip + i].type;
++        }
++        assert (skip + outArgs == lambdaForm.arity);
++        assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip));
++        int pos = 0;
++        while (pos < outArgs && reorder[pos] == pos) {
++            pos += 1;
++        }
++        Name[] names2 = new Name[length - outArgs + inTypes];
++        System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos);
++        int bodyLength = length - lambdaForm.arity;
++        System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength);
++        int arity2 = names2.length - bodyLength;
++        int result2 = lambdaForm.result;
++        if (result2 >= 0) {
++            if (result2 < skip + outArgs) {
++                result2 = reorder[result2 - skip];
++            } else {
++                result2 = result2 - outArgs + inTypes;
++            }
++        }
++        for (int j = pos; j < outArgs; j++) {
++            Name n = lambdaForm.names[skip + j];
++            int i = reorder[j];
++            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);
++            }
++        }
++        for (int i = skip + pos; i < arity2; i++) {
++            if (names2[i] == null) {
++                names2[i] = argument(i, types[i - skip]);
++            }
++        }
++        for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) {
++            int i = j - lambdaForm.arity + arity2;
++            Name n = lambdaForm.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);
++                }
++            }
++        }
++        LambdaForm form = new LambdaForm(lambdaForm.debugName, arity2, names2, result2);
++        if (permKey != null)
++            permuteArgsCache.put(permKey, form);
++        return form;
++    }
++
++    static boolean permutedTypesMatch(int[] reorder, byte[] 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;
++    }
++
++    private Object makePermKey(int[] reorder, int inTypes) {
++        int outArgs = reorder.length;
++        if (outArgs < 15 && inTypes < 15) {
++            long mask = 0;
++            for (int i = 0; i < outArgs; i++) {
++                int inArg = reorder[i];
++                assert(inArg+1 < 16);
++                mask |= (long)(inArg+1) << (4*i);
++            }
++            return mask;
++        } else {
++            char[] keyChars = new char[outArgs];
++            for (int i = 0; i < outArgs; i++) {
++                int inArg = reorder[i];
++                assert(inArg == (char)inArg);
++                keyChars[i] = (char)inArg;
++            }
++            return new String(keyChars);
++        }
++    }
++
++    // Event counters
++    private static final EventCounter EC_lambdaForm_editor = eventCounter(EC_lambdaForm, "editor");
++    private static final EventCounter EC_lambdaForm_bind = eventCounter(EC_lambdaForm, "bind");
++    private static final EventCounter EC_lambdaForm_bind_miss = eventCounter(EC_lambdaForm_bind, "miss");
++    private static final EventCounter EC_lambdaForm_addArg = eventCounter(EC_lambdaForm, "addArg");
++    private static final EventCounter EC_lambdaForm_addArg_miss = eventCounter(EC_lambdaForm_addArg, "miss");
++    private static final EventCounter EC_lambdaForm_filter = eventCounter(EC_lambdaForm, "filter");
++    private static final EventCounter EC_lambdaForm_filter_miss = eventCounter(EC_lambdaForm_filter, "miss");
++    private static final EventCounter EC_lambdaForm_filterReturn = eventCounter(EC_lambdaForm, "filterReturn");
++    private static final EventCounter EC_lambdaForm_filterReturn_miss = eventCounter(EC_lambdaForm_filterReturn, "miss");
++    private static final EventCounter EC_lambdaForm_fold = eventCounter(EC_lambdaForm, "fold");
++    private static final EventCounter EC_lambdaForm_fold_miss = eventCounter(EC_lambdaForm_fold, "miss");
++    private static final EventCounter EC_lambdaForm_permute = eventCounter(EC_lambdaForm, "permute");
++    private static final EventCounter EC_lambdaForm_permute_miss = eventCounter(EC_lambdaForm_permute, "miss");
++}
+diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java
+--- a/src/share/classes/java/lang/invoke/MemberName.java
++++ b/src/share/classes/java/lang/invoke/MemberName.java
+@@ -320,7 +320,7 @@
+ 
+     /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
+     public boolean isMethodHandleInvoke() {
+-        final int bits = MH_INVOKE_MODS;
++        final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
+         final int negs = Modifier.STATIC;
+         if (testFlags(bits | negs, bits) &&
+             clazz == MethodHandle.class) {
+@@ -329,7 +329,14 @@
+         return false;
+     }
+     public static boolean isMethodHandleInvokeName(String name) {
+-        return name.equals("invoke") || name.equals("invokeExact");
++        switch (name) {
++        case "invoke":
++        case "invokeExact":
++        case "invokeBasic":  // internal sig-poly method
++            return true;
++        default:
++            return false;
++        }
+     }
+     private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC;
+ 
+@@ -559,6 +566,7 @@
+         assert(this.referenceKindIsConsistentWith(result.getReferenceKind()));
+         return result;
+     }
++
+     /** Create a name for the given reflected constructor.  The resulting name will be in a resolved state. */
+     @SuppressWarnings("LeakingThisInConstructor")
+     public MemberName(Constructor<?> ctor) {
+@@ -689,16 +697,8 @@
+         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, 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.
++    /** Create a method or constructor name from the given components:
++     *  Declaring class, name, type, reference kind.
+      *  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.
+diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java
+--- a/src/share/classes/java/lang/invoke/MethodHandle.java
++++ b/src/share/classes/java/lang/invoke/MethodHandle.java
+@@ -26,11 +26,11 @@
+ package java.lang.invoke;
+ 
+ 
+-import java.util.*;
+ import sun.invoke.util.*;
+ import sun.misc.Unsafe;
+ 
+ import static java.lang.invoke.MethodHandleStatics.*;
++import java.util.Arrays;
+ 
+ /**
+  * A method handle is a typed, directly executable reference to an underlying method,
+@@ -608,15 +608,8 @@
+      * @see MethodHandles#spreadInvoker
+      */
+     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
+-            return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
+-        }
+-        MethodHandle invoker = type.invokers().varargsInvoker();
+-        return invoker.invokeExact(this, arguments);
++        MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length);
++        return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments);
+     }
+ 
+     /**
+@@ -740,15 +733,16 @@
+      * @see MethodHandles#explicitCastArguments
+      */
+     public MethodHandle asType(MethodType newType) {
+-        bump(EC_transform_asType);
+         // Fast path alternative to a heavyweight {@code asType} call.
+         // Return 'this' if the conversion will be a no-op.
+         if (newType == type) {
+             return this;
+         }
++        bump(EC_transform_asType);
+         // Return 'this.asTypeCache' if the conversion is already memoized.
+         MethodHandle atc = asTypeCache;
+         if (atc != null && newType == atc.type) {
++            bump(EC_transform_asType_miss);
+             return atc;
+         }
+         return asTypeUncached(newType);
+@@ -758,7 +752,7 @@
+     /*non-public*/ MethodHandle asTypeUncached(MethodType newType) {
+         if (!type.isConvertibleTo(newType))
+             throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
+-        return asTypeCache = convertArguments(newType);
++        return asTypeCache = TypeChanger.strictTypeChanger(type, newType).changeType(this, newType);
+     }
+ 
+     /**
+@@ -840,34 +834,63 @@
+      */
+     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
+         bump(EC_transform_asSpreader);
+-        asSpreaderChecks(arrayType, arrayLength);
+-        int spreadArgPos = type.parameterCount() - arrayLength;
+-        return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
++        MethodType resultType = asSpreaderChecks(arrayType, arrayLength);
++        @SuppressWarnings("LocalVariableHidesMemberVariable")
++        int spreadArgPos = resultType.parameterCount() - arrayLength;
++        if (arrayLength == 0 && arrayType != Object[].class) {
++            // Corner case:  Require a null array.
++            // The cache on Invokers requires at least one spread argument as a type witness.
++            MethodHandle requireNullArray = MethodHandleImpl.checkNullSpreadArgument();
++            //@@requireNullArray = requireNullArray.asType(MethodType.methodType(void.class, arrayType));
++            MethodHandle mh = MethodHandles.collectArguments(this, spreadArgPos, requireNullArray);
++            return mh.viewAsType(resultType.appendParameterTypes(arrayType), true);
++        }
++        if (resultType.parameterSlotCount() >= MethodType.MAX_MH_ARITY) {
++            // High-arity corner case.
++            return asSpreaderCustom(type, arrayType, arrayLength);
++        }
++        return resultType.invokers().spreadInvoker(spreadArgPos).bindTo(this);
+     }
+ 
+-    private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
++    /**
++     * Makes a work-alike for asSpreader, but full-custom.
++     * Caller is responsible for the validity checks of {@code asSpreaderChecks}.
++     */
++    /*non-public*/ MethodHandle asSpreaderCustom(MethodType spreadType, Class<?> arrayType, int arrayLength) {
++        int spreadArgPos = type.parameterCount() - arrayLength;
++        MethodHandle target = this.asType(spreadType);
++        return MethodHandleImpl.makeSpreadArguments(target, arrayType, spreadArgPos, arrayLength);
++    }
++
++    /**
++     * See if {@code asSpreader} can be validly called with the given arguments.
++     * Return false if the last parameters are not an exact match to the arrayType component type.
++     */
++    private MethodType asSpreaderChecks(Class<?> arrayType, int arrayLength) {
+         spreadArrayChecks(arrayType, arrayLength);
+         int nargs = type().parameterCount();
+         if (nargs < arrayLength || arrayLength < 0)
+             throw newIllegalArgumentException("bad spread array length");
+-        if (arrayType != Object[].class && arrayLength != 0) {
+-            boolean sawProblem = false;
+-            Class<?> arrayElement = arrayType.getComponentType();
+-            for (int i = nargs - arrayLength; i < nargs; i++) {
+-                if (!MethodType.canConvert(arrayElement, type().parameterType(i))) {
+-                    sawProblem = true;
++        Class<?> arrayElement = arrayType.getComponentType();
++        @SuppressWarnings("LocalVariableHidesMemberVariable")
++        MethodType type = type();
++        boolean match = true, fail = false;
++        for (int i = nargs - arrayLength; i < nargs; i++) {
++            Class<?> ptype = type.parameterType(i);
++            if (ptype != arrayElement) {
++                match = false;
++                if (!MethodType.canConvert(arrayElement, ptype)) {
++                    fail = true;
+                     break;
+                 }
+             }
+-            if (sawProblem) {
+-                ArrayList<Class<?>> ptypes = new ArrayList<>(type().parameterList());
+-                for (int i = nargs - arrayLength; i < nargs; i++) {
+-                    ptypes.set(i, arrayElement);
+-                }
+-                // elicit an error:
+-                this.asType(MethodType.methodType(type().returnType(), ptypes));
+-            }
+         }
++        if (match)  return type;
++        MethodType needType = type.asSpreaderType(arrayType, arrayLength);
++        if (!fail)  return needType;
++        // elicit an error:
++        this.asType(needType);
++        throw newInternalError("should not return", null);
+     }
+ 
+     private void spreadArrayChecks(Class<?> arrayType, int arrayLength) {
+@@ -956,16 +979,42 @@
+     public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
+         bump(EC_transform_asCollector);
+         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 MethodHandles.collectArguments(target, collectArgPos, collector);
++        @SuppressWarnings("LocalVariableHidesMemberVariable")
++        MethodType type = type();
++        int collectArgPos = type.parameterCount()-1;
++        if (arrayLength == 0 && arrayType != Object[].class) {
++            // Corner case:  Pass a constant null array.
++            // The cache on Invokers requires at least one collected argument as a type witness.
++            return this.bindArgumentL(collectArgPos, ValueConversions.emptyArray(arrayType));
++        }
++        MethodType resultType = type.asCollectorType(arrayType, arrayLength);
++        if (resultType.parameterSlotCount() >= MethodType.MAX_MH_ARITY) {
++            // High-arity corner case.
++            return asCollectorCustom(arrayType, arrayLength);
++        }
++        return resultType.invokers().collectInvoker(collectArgPos).bindTo(this);
+     }
+ 
+-    // private API: return true if last param exactly matches arrayType
+-    private boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
++    /**
++     * Makes a work-alike for asCollector, but full-custom.
++     * Caller is responsible for the validity checks of {@code asCollectorChecks}.
++     */
++    /*non-public*/ MethodHandle asCollectorCustom(Class<?> arrayType, int arrayLength) {
++        int collectArgPos = type.parameterCount()-1;
++        MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
++        MethodHandle target = this;
++        if (arrayType != type().parameterType(collectArgPos)) {
++            MethodType newType = type().changeParameterType(collectArgPos, arrayType);
++            target = MethodHandleImpl.makePairwiseConvert(this, newType, /*strict=*/ true, false);
++        }
++        return MethodHandleImpl.makeCollectArguments(target, collector, collectArgPos, false);
++    }
++
++    /**
++     * See if {@code asCollector} can be validly called with the given arguments.
++     * Return false if the last parameter is not an exact match to arrayType.
++     */
++    /*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
+         spreadArrayChecks(arrayType, arrayLength);
+         int nargs = type().parameterCount();
+         if (nargs != 0) {
+@@ -1127,11 +1176,11 @@
+      * @see #asFixedArity
+      */
+     public MethodHandle asVarargsCollector(Class<?> arrayType) {
+-        bump(EC_transform_asVarargsCollector);
+         Class<?> arrayElement = arrayType.getComponentType();
+         boolean lastMatch = asCollectorChecks(arrayType, 0);
+         if (isVarargsCollector() && lastMatch)
+             return this;
++        bump(EC_transform_asVarargsCollector);
+         return MethodHandleImpl.makeVarargsCollector(this, arrayType);
+     }
+ 
+@@ -1231,14 +1280,8 @@
+      */
+     public MethodHandle bindTo(Object x) {
+         bump(EC_transform_bindTo);
+-        Class<?> ptype;
+-        @SuppressWarnings("LocalVariableHidesMemberVariable")
+-        MethodType type = type();
+-        if (type.parameterCount() == 0 ||
+-            (ptype = type.parameterType(0)).isPrimitive())
+-            throw newIllegalArgumentException("no leading reference parameter", x);
+-        x = ptype.cast(x);  // throw CCE if needed
+-        return bindReceiver(x);
++        x = type.leadingReferenceParameter().cast(x);  // throw CCE if needed
++        return bindArgumentL(0, x);
+     }
+ 
+     /**
+@@ -1286,10 +1329,28 @@
+         }
+         throw member.makeAccessException("cannot make variable arity", null);
+     }
++
+     /*non-public*/
+-    MethodHandle viewAsType(MethodType newType) {
++    MethodHandle viewAsType(MethodType newType, boolean strict) {
+         // No actual conversions, just a new view of the same method.
+-        return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
++        // Note that this operation must not produce a DirectMethodHandle,
++        // because retyped DMHs, like any transformed MHs,
++        // cannot be cracked into MethodHandleInfo.
++        assert(viewAsTypeChecks(newType, strict));
++        BoundMethodHandle mh = rebind();
++        assert(!((MethodHandle)mh instanceof DirectMethodHandle));
++        return mh.copyWith(newType, mh.form);
++    }
++
++    /*non-public*/
++    boolean viewAsTypeChecks(MethodType newType, boolean strict) {
++        if (strict)
++            assert(type().isViewableAs(newType, strict))
++                    : Arrays.asList(this, newType);
++        else
++            assert(type().basicType().form() == newType.basicType().form())
++                    : Arrays.asList(this, newType);
++        return true;
+     }
+ 
+     // Decoding
+@@ -1304,10 +1365,16 @@
+         return null;  // DMH returns DMH.member
+     }
+ 
++     /*non-public*/
++    Class<?> internalCallerClass() {
++        return null;  // caller-bound MH for @CallerSensitive method returns caller
++    }
++
+     /*non-public*/
+-    MethodHandle withInternalMemberName(MemberName member) {
++    /*non-public*/
++    MethodHandle withInternalMemberName(MemberName member, boolean isInvokeSpecial) {
+         if (member != null) {
+-            return MethodHandleImpl.makeWrappedMember(this, member);
++            return MethodHandleImpl.makeWrappedMember(this, member, isInvokeSpecial);
+         } else if (internalMemberName() == null) {
+             // The required internaMemberName is null, and this MH (like most) doesn't have one.
+             return this;
+@@ -1339,113 +1406,37 @@
+     //// 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*/
++    abstract MethodHandle copyWith(MethodType mt, LambdaForm lf);
++
++    /*non-public*/
++    /** Require this method handle to be a BMH, or else replace it with a "wrapper" BMH.
++     *  Many transforms are implemented only for BMHs.
++     *  @return a behaviorally equivalent BMH
++     */
++    BoundMethodHandle rebind() {
++        // lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
++        // Bind 'this' into a new invoker, of the known class BMH.
++        return BoundMethodHandle.makeRebind(this);
+     }
+ 
+     /*non-public*/
+-    MethodHandle bindArgument(int pos, char basicType, Object value) {
+-        // Override this if it can be improved.
+-        return rebind().bindArgument(pos, basicType, value);
++    BoundMethodHandle bindArgumentL(int pos, Object value) {
++        return rebind().bindArgumentL(pos, value);
+     }
+ 
+     /*non-public*/
+-    MethodHandle bindReceiver(Object receiver) {
+-        // Override this if it can be improved.
+-        return bindArgument(0, 'L', receiver);
++    void compileToBytecode() {
++        form.compileToBytecode();
+     }
+ 
+     /*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(this);
+-        // 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(MethodHandle target) {
+-        MethodType mtype = target.type().basicType();
+-        LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
+-        if (reinvoker != null)  return reinvoker;
+-        if (mtype.parameterSlotCount() >= MethodType.MAX_MH_ARITY)
+-            return makeReinvokerForm(target.type(), target);  // cannot cache this
+-        reinvoker = makeReinvokerForm(mtype, null);
+-        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, reinvoker);
+-    }
+-    private static LambdaForm makeReinvokerForm(MethodType mtype, MethodHandle customTargetOrNull) {
+-        boolean customized = (customTargetOrNull != null);
+-        MethodHandle MH_invokeBasic = customized ? null : 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     = customized ? -1 : nameCursor++;
+-        final int REINVOKE    = nameCursor++;
+-        LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+-        Object[] targetArgs;
+-        MethodHandle targetMH;
+-        if (customized) {
+-            targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
+-            targetMH = customTargetOrNull;
+-        } else {
+-            names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
+-            targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
+-            targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
+-            targetMH = MethodHandles.basicInvoker(mtype);
+-        }
+-        names[REINVOKE] = new LambdaForm.Name(targetMH, targetArgs);
+-        return 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 newInternalError(ex);
+-        }
++    void maybeCompileToBytecode() {
++        final int NEW_THRESH = Math.max(1, LambdaForm.COMPILE_THRESHOLD / 10);
++        if (form.getInvocationsRemaining() < NEW_THRESH)
++            compileToBytecode();
++        else
++            form.setInvocationsRemaining(NEW_THRESH);
+     }
+ 
+     /**
+@@ -1459,6 +1450,7 @@
+     /*non-public*/
+     void updateForm(LambdaForm newForm) {
+         if (form == newForm)  return;
++        assert(this instanceof DirectMethodHandle && this.internalMemberName().isStatic());
+         // ISSUE: Should we have a memory fence here?
+         UNSAFE.putObject(this, FORM_OFFSET, newForm);
+         this.form.prepare();  // as in MethodHandle.<init>
+@@ -1475,6 +1467,7 @@
+ 
+     // Event counters
+     private static final EventCounter EC_transform_asType = eventCounter(EC_transform, "asType");
++    private static final EventCounter EC_transform_asType_miss = eventCounter(EC_transform_asType, "miss");
+     private static final EventCounter EC_transform_asSpreader = eventCounter(EC_transform, "asSpreader");
+     private static final EventCounter EC_transform_asCollector = eventCounter(EC_transform, "asCollector");
+     private static final EventCounter EC_transform_asVarargsCollector = eventCounter(EC_transform, "asVarargsCollector");
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+@@ -53,27 +53,43 @@
+     }
+ 
+     static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
++        if (arrayClass == Object[].class)
++            return (isSetter ? ArrayAccessor.OBJECT_ARRAY_SETTER : ArrayAccessor.OBJECT_ARRAY_GETTER);
+         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 = SimpleMethodHandle.make(srcType, form);
+-        if (ArrayAccessor.needCast(arrayClass)) {
+-            mh = mh.bindTo(arrayClass);
++        MethodHandle[] cache = ArrayAccessor.TYPED_ACCESSORS.get(arrayClass);
++        int cacheIndex = (isSetter ? ArrayAccessor.SETTER_INDEX : ArrayAccessor.GETTER_INDEX);
++        MethodHandle mh = cache[cacheIndex];
++        if (mh != null)  return mh;
++        mh = ArrayAccessor.getAccessor(arrayClass, isSetter);
++        MethodType correctType = ArrayAccessor.correctType(arrayClass, isSetter);
++        if (mh.type() != correctType) {
++            assert(mh.type().parameterType(0) == Object[].class);
++            assert((isSetter ? mh.type().parameterType(2) : mh.type().returnType()) == Object.class);
++            assert(isSetter || correctType.parameterType(0).getComponentType() == correctType.returnType());
++            // safe to view non-strictly, because element type follows from array type
++            mh = mh.viewAsType(correctType, false);
+         }
+-        mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter));
++        cache[cacheIndex] = mh;
+         return mh;
+     }
+ 
+     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 int GETTER_INDEX = 0, SETTER_INDEX = 1, INDEX_LIMIT = 2;
++        static final ClassValue<MethodHandle[]> TYPED_ACCESSORS
++                = new ClassValue<MethodHandle[]>() {
++                    @Override
++                    protected MethodHandle[] computeValue(Class<?> type) {
++                        return new MethodHandle[INDEX_LIMIT];
++                    }
++                };
++        static final MethodHandle OBJECT_ARRAY_GETTER, OBJECT_ARRAY_SETTER;
++        static {
++            MethodHandle[] cache = TYPED_ACCESSORS.get(Object[].class);
++            cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = getAccessor(Object[].class, false);
++            cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = getAccessor(Object[].class, true);
++        }
+ 
+         static int     getElementI(int[]     a, int i)            { return              a[i]; }
+         static long    getElementJ(long[]    a, int i)            { return              a[i]; }
+@@ -95,45 +111,21 @@
+         static void    setElementC(char[]    a, int i, char    x) {              a[i] = x; }
+         static void    setElementL(Object[]  a, int i, Object  x) {              a[i] = x; }
+ 
+-        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;
+-        }
+         static String name(Class<?> arrayClass, boolean isSetter) {
+             Class<?> elemClass = arrayClass.getComponentType();
+-            if (elemClass == null)  throw new IllegalArgumentException();
++            if (elemClass == null)  throw newIllegalArgumentException("not an array", arrayClass);
+             return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
+         }
+-        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;
++                elemClass = Object.class;
+             }
+-            if (!needCast(arrayClass)) {
+-                return !isSetter ?
++            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();
+@@ -160,13 +152,14 @@
+      * 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
++     * @param strict only allow the conversions specified by asType, not explicitCastArguments
++     * @param monobox unbox primitives exactly, with neither widening nor narrowing
+      * @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);
++    static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType,
++                                            boolean strict, boolean monobox) {
+         MethodType dstType = target.type();
+         assert(dstType.parameterCount() == target.type().parameterCount());
+         if (srcType == dstType)
+@@ -181,8 +174,7 @@
+         for (int i = 0; i <= INARG_COUNT; i++) {
+             Class<?> src = (i == INARG_COUNT) ? dstType.returnType() : srcType.parameterType(i);
+             Class<?> dst = (i == INARG_COUNT) ? srcType.returnType() : dstType.parameterType(i);
+-            if (!VerifyType.isNullConversion(src, dst) ||
+-                level <= 1 && dst.isInterface() && !dst.isAssignableFrom(src)) {
++            if (!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)) {
+                 needConv[i] = true;
+                 conversions++;
+                 bump(EC_makePairwiseConvert_arg);
+@@ -217,49 +209,12 @@
+             }
+ 
+             // 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);
+-                }
+-            }
+-            Name conv = new Name(fn, names[INARG_BASE + i]);
++            MethodHandle fn = valueConversion(src, dst, strict, monobox);
++            Name conv;
++            if (fn == null)
++                conv = new Name(ValueConversions.cast(), dst, names[INARG_BASE + i]);
++            else
++                conv = new Name(fn, names[INARG_BASE + i]);
+             assert(names[nameCursor] == null);
+             names[nameCursor++] = conv;
+             assert(outArgs[OUTARG_BASE + i] == null);
+@@ -283,9 +238,12 @@
+                 fn = MethodHandles.constant(needReturn, zero);
+                 arg = new Object[0];  // don't pass names[OUT_CALL] to conversion
+             } else {
+-                MethodHandle identity = MethodHandles.identity(needReturn);
+-                MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
+-                fn = makePairwiseConvert(identity, needConversion, level);
++                fn = valueConversion(haveReturn, needReturn, strict, monobox);
++                if (fn == null) {
++                    fn = ValueConversions.cast();
++                    // add the extra Class argument to allow cast:
++                    arg = new Object[] { needReturn, arg[0] };
++                }
+             }
+             assert(names[RETURN_CONV] == null);
+             names[RETURN_CONV] = new Name(fn, arg);
+@@ -295,13 +253,67 @@
+         LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
+         return SimpleMethodHandle.make(srcType, form);
+     }
++    static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType,
++                                            boolean strict) {
++        return makePairwiseConvert(target, srcType, strict, /*monobox=*/ false);
++    }
+ 
+-    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 SimpleMethodHandle.make(MethodType.methodType(refType, refType), form);
++    /**
++     * Find a conversion function from the given source to the given destination.
++     * This conversion function will be used as a LF NamedFunction.
++     */
++    static MethodHandle valueConversion(Class<?> src, Class<?> dst, boolean strict, boolean monobox) {
++        assert(!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict));  // caller responsibility
++        MethodHandle fn;
++        Class<?> expectRT = dst, expectPT = src;//@@
++        if (src.isPrimitive()) {
++            if (dst.isPrimitive()) {
++                // Examples: int->byte, byte->int, boolean->int (!strict)
++                fn = ValueConversions.convertPrimitive(src, dst);
++            } else {
++                // Examples: int->Integer, boolean->Object, float->Number
++                Wrapper wsrc = Wrapper.forPrimitiveType(src);
++                fn = ValueConversions.boxExact(wsrc);
++                assert(fn.type().parameterType(0) == wsrc.primitiveType());
++                assert(fn.type().returnType() == wsrc.wrapperType());
++                if (!VerifyType.isNullConversion(wsrc.wrapperType(), dst, strict)) {
++                    // Corner case, such as int->Long, which will probably fail.
++                    MethodType mt = MethodType.methodType(dst, src);
++                    if (strict)
++                        fn = fn.asType(mt);
++                    else
++                        fn = MethodHandleImpl.makePairwiseConvert(fn, mt, /*strict=*/ false);
++                }
++                if (dst.isAssignableFrom(fn.type().returnType()))  expectRT = fn.type().returnType(); //@@
++            }
++        } else {
++            if (dst.isPrimitive()) {
++                Wrapper wdst = Wrapper.forPrimitiveType(dst);
++                if (monobox || src == wdst.wrapperType()) {
++                    // Use a strongly-typed unboxer, if possible.
++                    fn = ValueConversions.unboxExact(wdst, strict);
++                    expectPT = wdst.wrapperType();
++                } else {
++                    // Examples:  Object->int, Number->int, Comparable->int, Byte->int
++                    // must include additional conversions
++                    // src must be examined at runtime, to detect Byte, Character, etc.
++                    fn = (strict
++                          ? ValueConversions.unboxWiden(wdst)
++                          : ValueConversions.unboxCast(wdst));
++                    expectPT = Object.class;//@@
++                }
++            } 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.
++                return null;
++            }
++        }
++        assert(fn.type().parameterCount() == 1) : "pc"+Arrays.asList(src.getSimpleName(), dst.getSimpleName(), fn);
++        assert(fn.type().parameterType(0) == expectPT) : "pt"+Arrays.asList(src.getSimpleName(), dst.getSimpleName(), fn);
++        assert(fn.type().returnType() == expectRT) : "rt"+Arrays.asList(src.getSimpleName(), dst.getSimpleName(), fn);
++        return fn;
+     }
+ 
+     static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
+@@ -310,30 +322,36 @@
+         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);
++        return new AsVarargsCollector(target, arrayType);
+     }
+ 
+-    static class AsVarargsCollector extends MethodHandle {
++    private static final class AsVarargsCollector extends DelegatingMethodHandle {
+         private final MethodHandle target;
+         private final Class<?> arrayType;
+         private /*@Stable*/ MethodHandle asCollectorCache;
+ 
+-        AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
+-            super(type, reinvokerForm(target));
++        AsVarargsCollector(MethodHandle target, Class<?> arrayType) {
++            this(target.type(), target, arrayType);
++        }
++        AsVarargsCollector(MethodType type, MethodHandle target, Class<?> arrayType) {
++            super(type, target);
+             this.target = target;
+             this.arrayType = arrayType;
+             this.asCollectorCache = target.asCollector(arrayType, 0);
+             bump(EC_methodHandle_varargsCollector);
+         }
+ 
+-        @Override MethodHandle reinvokerTarget() { return target; }
+-
+         @Override
+         public boolean isVarargsCollector() {
+             return true;
+         }
+ 
+         @Override
++        protected MethodHandle getTarget() {
++            return target;
++        }
++
++        @Override
+         public MethodHandle asFixedArity() {
+             return target;
+         }
+@@ -372,44 +390,15 @@
+         }
+ 
+         @Override
+-        MethodHandle viewAsType(MethodType newType) {
+-            if (newType.lastParameterType() != type().lastParameterType())
+-                throw new InternalError();
+-            MethodHandle newTarget = asFixedArity().viewAsType(newType);
+-            // put back the varargs bit:
+-            return new AsVarargsCollector(newTarget, newType, arrayType);
+-        }
+-
+-        @Override
+-        MemberName internalMemberName() {
+-            return asFixedArity().internalMemberName();
+-        }
+-
+-        /*non-public*/
+-        @Override
+-        boolean isInvokeSpecial() {
+-            return asFixedArity().isInvokeSpecial();
+-        }
+-
+-
+-        @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);
++        boolean viewAsTypeChecks(MethodType newType, boolean strict) {
++            super.viewAsTypeChecks(newType, true);
++            if (strict)  return true;
++            // extra assertion for non-strict checks:
++            assert(type().lastParameterType().getComponentType()
++                    .isAssignableFrom(
++                        newType.lastParameterType().getComponentType() ))
++                    : Arrays.asList(this, newType);
++            return true;
+         }
+     }
+ 
+@@ -451,12 +440,12 @@
+         assert(nameCursor == names.length-1);  // leave room for the final call
+ 
+         // Build argument array for the call.
+-        Name[] targetArgs = new Name[targetType.parameterCount()];
++        Object[] targetArgs = new Object[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);
++        names[names.length - 1] = new Name(target, targetArgs);
+ 
+         LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names);
+         return SimpleMethodHandle.make(srcType, form);
+@@ -494,6 +483,14 @@
+             throw newInternalError(ex);
+         }
+     }
++    private static MethodHandle CHECK_NULL_SPREAD_ARGUMENT;
++    static MethodHandle checkNullSpreadArgument() {
++        MethodHandle res = CHECK_NULL_SPREAD_ARGUMENT;
++        if (res == null)
++            CHECK_NULL_SPREAD_ARGUMENT = res
++                    = NF_checkSpreadArgument.resolvedHandle().rebind().bindArgumentI(1, 0);
++        return res;
++    }
+ 
+     /** Factory method:  Collect or filter selected argument(s). */
+     static MethodHandle makeCollectArguments(MethodHandle target,
+@@ -571,29 +568,65 @@
+     MethodHandle makeGuardWithTest(MethodHandle test,
+                                    MethodHandle target,
+                                    MethodHandle fallback) {
+-        MethodType basicType = target.type().basicType();
+-        MethodHandle invokeBasic = MethodHandles.basicInvoker(basicType);
+-        int arity = basicType.parameterCount();
+-        int extraNames = 3;
++        MethodType type = target.type();
++        assert(test.type().equals(type.changeReturnType(boolean.class)) && fallback.type().equals(type));
++        MethodType basicType = type.basicType();
++        LambdaForm form = makeGuardWithTestForm(basicType);
++        BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
++        BoundMethodHandle mh;
++        try {
++            mh = (BoundMethodHandle)
++                    data.constructor[0].invokeBasic(type, form,
++                        (Object) test, (Object) target, (Object) fallback);
++        } catch (Throwable ex) {
++            throw uncaughtException(ex);
++        }
++        assert(mh.type() == type);
++        return mh;
++    }
++
++    static
++    LambdaForm makeGuardWithTestForm(MethodType basicType) {
++        LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_GWT);
++        if (lform != null)  return lform;
++        final int THIS_MH      = 0;  // the BMH_LLL
++        final int ARG_BASE     = 1;  // start of incoming arguments
++        final int ARG_LIMIT    = ARG_BASE + basicType.parameterCount();
++        int nameCursor = ARG_LIMIT;
++        final int GET_TEST     = nameCursor++;
++        final int GET_TARGET   = nameCursor++;
++        final int GET_FALLBACK = nameCursor++;
++        final int CALL_TEST    = nameCursor++;
++        final int SELECT_ALT   = nameCursor++;
++        final int CALL_TARGET  = nameCursor++;
++        assert(CALL_TARGET == SELECT_ALT+1);  // must be true to trigger IBG.emitSelectAlternative
++
+         MethodType lambdaType = basicType.invokerType();
+-        Name[] names = arguments(extraNames, lambdaType);
++        Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
+ 
+-        Object[] testArgs   = Arrays.copyOfRange(names, 1, 1 + arity, Object[].class);
+-        Object[] targetArgs = Arrays.copyOfRange(names, 0, 1 + arity, Object[].class);
++        BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
++        names[GET_TEST]     = new Name(data.getterFunction(0), names[THIS_MH]);
++        names[GET_TARGET]   = new Name(data.getterFunction(1), names[THIS_MH]);
++        names[GET_FALLBACK] = new Name(data.getterFunction(2), names[THIS_MH]);
++
++        Object[] invokeArgs = Arrays.copyOfRange(names, 0, ARG_LIMIT, Object[].class);
+ 
+         // call test
+-        names[arity + 1] = new Name(test, testArgs);
++        MethodType testType = basicType.changeReturnType(boolean.class).basicType();
++        invokeArgs[0] = names[GET_TEST];
++        names[CALL_TEST] = new Name(testType, invokeArgs);
+ 
+         // call selectAlternative
+-        Object[] selectArgs = { names[arity + 1], target, fallback };
+-        names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs);
+-        targetArgs[0] = names[arity + 2];
++        names[SELECT_ALT] = new Name(MethodHandleImpl.selectAlternative(),
++                names[CALL_TEST], names[GET_TARGET], names[GET_FALLBACK]);
+ 
+         // call target or fallback
+-        names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs);
++        invokeArgs[0] = names[SELECT_ALT];
++        names[CALL_TARGET] = new Name(basicType, invokeArgs);
+ 
+-        LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names);
+-        return SimpleMethodHandle.make(target.type(), form);
++        lform = new LambdaForm("guard", lambdaType.parameterCount(), names);
++
++        return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWT, lform);
+     }
+ 
+     private static class GuardWithCatch {
+@@ -734,26 +767,26 @@
+         MethodType type = target.type();
+         MethodType ctype = catcher.type();
+         int nargs = type.parameterCount();
++        MethodHandle ginvoker;
+         if (nargs < GuardWithCatch.INVOKES.length) {
+             MethodType gtype = type.generic();
+             MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
+-            // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0)
+-            MethodHandle gtarget = makePairwiseConvert(target, gtype, 2);
+-            MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2);
++            // Note: convertArguments(...strict=false) avoids interface casts otherwise present
++            MethodHandle gtarget = makePairwiseConvert(target, gtype, false, true);
++            MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, false, true);
+             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);
++            ginvoker = GuardWithCatch.INVOKES[nargs].bindArgumentL(0, gguard);
+         } else {
+             MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs);
+             catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class));
+             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);
+-            MethodHandle gcollect = makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
+-            return makePairwiseConvert(gcollect, type, 2);
++            ginvoker = GuardWithCatch.VARARGS_INVOKE.bindArgumentL(0, gguard);
++            ginvoker = makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
+         }
++        return makePairwiseConvert(ginvoker, type, false, true);
+     }
+ 
+     static
+@@ -761,9 +794,11 @@
+         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);
++            MethodHandle mh = throwException(type.dropParameterTypes(1, arity));
++            mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity));
++            return mh;
+         }
+-        return makePairwiseConvert(throwException(), type, 2);
++        return makePairwiseConvert(throwException(), type, false, true);
+     }
+ 
+     static MethodHandle THROW_EXCEPTION;
+@@ -799,7 +834,7 @@
+         mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle"));
+         if (!method.getInvocationType().equals(mh.type()))
+             throw new InternalError(method.toString());
+-        mh = mh.withInternalMemberName(method);
++        mh = mh.withInternalMemberName(method, false);
+         mh = mh.asVarargsCollector(Object[].class);
+         assert(method.isVarargs());
+         FAKE_METHOD_HANDLE_INVOKE[idx] = mh;
+@@ -836,7 +871,7 @@
+             MethodHandle vamh = prepareForInvoker(mh);
+             // Cache the result of makeInjectedInvoker once per argument class.
+             MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
+-            return restoreToType(bccInvoker.bindTo(vamh), mh.type(), mh.internalMemberName());
++            return restoreToType(bccInvoker.bindTo(vamh), mh, hostClass);
+         }
+ 
+         private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
+@@ -891,10 +926,14 @@
+         }
+ 
+         // Undo the adapter effect of prepareForInvoker:
+-        private static MethodHandle restoreToType(MethodHandle vamh, MethodType type, MemberName member) {
++        private static MethodHandle restoreToType(MethodHandle vamh,
++                                                  MethodHandle original,
++                                                  Class<?> hostClass) {
++            MethodType type = original.type();
+             MethodHandle mh = vamh.asCollector(Object[].class, type.parameterCount());
++            MemberName member = original.internalMemberName();
+             mh = mh.asType(type);
+-            mh = mh.withInternalMemberName(member);
++            mh = new WrappedMember(type, mh, member, original.isInvokeSpecial(), hostClass);
+             return mh;
+         }
+ 
+@@ -958,21 +997,27 @@
+         }
+     }
+ 
+-
+     /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */
+-    static class WrappedMember extends MethodHandle {
++    private static final class WrappedMember extends DelegatingMethodHandle {
+         private final MethodHandle target;
+         private final MemberName member;
++        private final Class<?> callerClass;
++        private final boolean isInvokeSpecial;
+ 
+-        private WrappedMember(MethodHandle target, MethodType type, MemberName member) {
+-            super(type, reinvokerForm(target));
++        private WrappedMember(MethodType type, MethodHandle target,
++                              MemberName member, boolean isInvokeSpecial,
++                              Class<?> callerClass) {
++            super(target.type(), target);
+             this.target = target;
+             this.member = member;
++            this.callerClass = callerClass;
++            this.isInvokeSpecial = isInvokeSpecial;
+             bump(EC_methodHandle_wrappedMember);
++            assert(member != null);
+         }
+ 
+         @Override
+-        MethodHandle reinvokerTarget() {
++        protected MethodHandle getTarget() {
+             return target;
+         }
+         @Override
+@@ -987,18 +1032,18 @@
+         }
+         @Override
+         boolean isInvokeSpecial() {
+-            return target.isInvokeSpecial();
++            return isInvokeSpecial;
+         }
+         @Override
+-        MethodHandle viewAsType(MethodType newType) {
+-            return new WrappedMember(target, newType, member);
++        Class<?> internalCallerClass() {
++            return callerClass;
+         }
+     }
+ 
+-    static MethodHandle makeWrappedMember(MethodHandle target, MemberName member) {
+-        if (member.equals(target.internalMemberName()))
++    static MethodHandle makeWrappedMember(MethodHandle target, MemberName member, boolean isInvokeSpecial) {
++        if (member.equals(target.internalMemberName()) && isInvokeSpecial == target.isInvokeSpecial())
+             return target;
+-        return new WrappedMember(target, target.type(), member);
++        return new WrappedMember(target.type(), target, member, isInvokeSpecial, null);
+     }
+ 
+     // Event counters
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/share/classes/java/lang/invoke/MethodHandleNatives.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java
+@@ -329,6 +329,8 @@
+                                               type,
+                                               staticArguments,
+                                               caller);
++        // eagerly generate bytecodes for invokedynamic bindings
++        callSite.getTarget().compileToBytecode();
+         if (callSite instanceof ConstantCallSite) {
+             bump(EC_link_callSite_constant);
+             appendixResult[0] = callSite.dynamicInvoker();
+@@ -533,4 +535,8 @@
+     private static final EventCounter EC_link_invokeGeneric = eventCounter(EC_link, "invokeGeneric");
+     private static final EventCounter EC_link_invokeExact = eventCounter(EC_link, "invokeExact");
+     private static final EventCounter EC_link_methodHandle = eventCounter(EC_link, "methodHandle");
++
++    // bumped from MethodHandles:
++    static final EventCounter EC_link_methodHandle_lookasidePut = eventCounter(EC_link_methodHandle, "lookasidePut");
++    static final EventCounter EC_link_methodHandle_lookasideHit = eventCounter(EC_link_methodHandle, "lookasideHit");
+ }
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleStatics.java b/src/share/classes/java/lang/invoke/MethodHandleStatics.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java
+@@ -30,6 +30,7 @@
+ import sun.misc.Unsafe;
+ import java.io.PrintStream;
+ import java.util.ArrayList;
++import java.util.Collections;
+ 
+ /**
+  * This class consists exclusively of static names internal to the
+@@ -48,9 +49,11 @@
+     static final boolean TRACE_INTERPRETER;
+     static final boolean TRACE_METHOD_LINKAGE;
+     static final boolean COUNT_EVENTS;
+-    static final Integer COMPILE_THRESHOLD;
++    static final int COMPILE_THRESHOLD;
++    static final int SPREAD_THRESHOLD;
++    static final int PROFILE_LEVEL;
+     static {
+-        final Object[] values = { false, false, false, false, false, null };
++        final Object[] values = { false, false, false, false, false, null, null, null };
+         AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                 public Void run() {
+                     values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+@@ -58,7 +61,9 @@
+                     values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
+                     values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
+                     values[4] = Boolean.getBoolean("java.lang.invoke.MethodHandle.COUNT_EVENTS");
+-                    values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
++                    values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 0);
++                    values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.SPREAD_THRESHOLD", 0);
++                    values[7] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
+                     return null;
+                 }
+             });
+@@ -68,6 +73,8 @@
+         TRACE_METHOD_LINKAGE      = (Boolean) values[3];
+         COUNT_EVENTS              = (Boolean) values[4];
+         COMPILE_THRESHOLD         = (Integer) values[5];
++        SPREAD_THRESHOLD          = (Integer) values[6];
++        PROFILE_LEVEL             = (Integer) values[7];
+     }
+ 
+     /*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
+@@ -98,6 +105,9 @@
+     }
+ 
+     // handy shared exception makers (they simplify the common case code)
++    /*non-public*/ static ClassCastException newClassCastException(Class<?> type, Object obj) {
++        return new ClassCastException("Cannot cast " + obj.getClass().getName() + " to " + type.getName());
++    }
+     /*non-public*/ static InternalError newInternalError(String message, Throwable cause) {
+         return new InternalError(message, cause);
+     }
+@@ -119,7 +129,10 @@
+     /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
+         return new IllegalArgumentException(message(message, obj, obj2));
+     }
++    /** Propagate unchecked exceptions and errors, but wrap anything checked and throw that instead. */
+     /*non-public*/ static Error uncaughtException(Throwable ex) {
++        if (ex instanceof Error)  throw (Error) ex;
++        if (ex instanceof RuntimeException)  throw (RuntimeException) ex;
+         throw newInternalError("uncaught exception", ex);
+     }
+     static Error NYI() {
+@@ -163,7 +176,7 @@
+         if (!COUNT_EVENTS)  return;
+         ec.bump();
+     }
+-    static class EventCounter {
++    static class EventCounter implements Comparable<EventCounter> {
+         final String name;
+         final boolean autoSum;
+         final EventCounter sup;  // used for toString
+@@ -177,11 +190,17 @@
+             for (EventCounter ec : ecs) {
+                 if (name.equals(ec.name))  throw new IllegalArgumentException("duplicate: "+ec);
+             }
+-            ecs.add(this);
++            synchronized (EventCounter.class) {
++                ecs.add(this);
++                Collections.sort(ecs);  // keep in order
++            }
+         }
+         public String toString() {
+             return (sup == null ? "EventCounter:"+name : sup+"."+name);
+         }
++        public int compareTo(EventCounter that) {
++            return this.name.compareTo(that.name);
++        }
+         long count() {
+             if (autoSum && count == 0) {
+                 for (EventCounter ec : subs) {
+diff --git a/src/share/classes/java/lang/invoke/MethodHandles.java b/src/share/classes/java/lang/invoke/MethodHandles.java
+--- a/src/share/classes/java/lang/invoke/MethodHandles.java
++++ b/src/share/classes/java/lang/invoke/MethodHandles.java
+@@ -39,6 +39,9 @@
+ import sun.security.util.SecurityConstants;
+ import static java.lang.invoke.MethodHandleStatics.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
++import java.util.BitSet;
++import java.util.EnumMap;
++import java.util.HashMap;
+ import java.util.concurrent.ConcurrentHashMap;
+ import sun.security.util.SecurityConstants;
+ 
+@@ -685,6 +688,8 @@
+                 return invoker(type);
+             if ("invokeExact".equals(name))
+                 return exactInvoker(type);
++            if ("invokeBasic".equals(name))
++                return basicInvoker(type);
+             assert(!MemberName.isMethodHandleInvokeName(name));
+             return null;
+         }
+@@ -913,7 +918,7 @@
+             MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
+             checkSecurityManager(refc, method);
+             MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
+-            return mh.bindReceiver(receiver).setVarargs(method);
++            return mh.bindArgumentL(0, receiver).setVarargs(method);
+         }
+ 
+         /**
+@@ -1303,7 +1308,10 @@
+             MethodType rawType = mh.type();
+             if (rawType.parameterType(0) == caller)  return mh;
+             MethodType narrowType = rawType.changeParameterType(0, caller);
+-            return mh.viewAsType(narrowType);
++            assert(!mh.isVarargsCollector());  // viewAsType will lose varargs-ness
++            assert(mh.viewAsTypeChecks(narrowType, true));
++            assert(mh instanceof DirectMethodHandle);  // DirectMethodHandle.copyWith
++            return mh.copyWith(narrowType, mh.form);
+         }
+ 
+         private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
+@@ -1389,6 +1397,7 @@
+             MethodHandle mh = LOOKASIDE_TABLE.get(member);
+             if (mh != null) {
+                 checkSymbolicClass(defc);
++                bump(MethodHandleNatives.EC_link_methodHandle_lookasideHit);
+                 return mh;
+             }
+             MemberName resolved = resolveOrFail(refKind, member);
+@@ -1400,6 +1409,7 @@
+                     key = key.asNormalOriginal();
+                 }
+                 if (member.equals(key)) {  // better safe than sorry
++                    bump(MethodHandleNatives.EC_link_methodHandle_lookasidePut);
+                     LOOKASIDE_TABLE.put(key, (DirectMethodHandle) mh);
+                 }
+             }
+@@ -1532,7 +1542,8 @@
+     static public
+     MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
+         if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
+-            throw new IllegalArgumentException("bad argument count "+leadingArgCount);
++            throw newIllegalArgumentException("bad argument count", leadingArgCount);
++        type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount);
+         return type.invokers().spreadInvoker(leadingArgCount);
+     }
+ 
+@@ -1606,12 +1617,12 @@
+      */
+     static public
+     MethodHandle invoker(MethodType type) {
+-        return type.invokers().generalInvoker();
++        return type.invokers().genericInvoker();
+     }
+ 
+     static /*non-public*/
+     MethodHandle basicInvoker(MethodType type) {
+-        return type.form().basicInvoker();
++        return type.invokers().basicInvoker();
+     }
+ 
+      /// method handle modification (creation from other method handles)
+@@ -1662,11 +1673,15 @@
+      */
+     public static
+     MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
++        MethodType oldType = target.type();
++        // use the asTypeCache when possible:
++        if (oldType == newType)  return target;
+         bump(EC_transform_explicitCastArguments);
+-        if (!target.type().isCastableTo(newType)) {
+-            throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType);
++        if (oldType.explicitCastEquivalentToAsType(newType)) {
++            bump(EC_transform_explicitCastArguments_cachable);
++            return target.asType(newType);
+         }
+-        return MethodHandleImpl.makePairwiseConvert(target, newType, 2);
++        return TypeChanger.explicitCastTypeChanger(target.type(), newType).changeType(target, newType);
+     }
+ 
+     /**
+@@ -1731,11 +1746,26 @@
+     public static
+     MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
+         bump(EC_transform_permuteArguments);
+-        checkReorder(reorder, newType, target.type());
+-        return target.permuteArguments(newType, reorder);
++        permuteArgumentChecks(reorder, newType, target.type());
++
++        // first detect dropped arguments and handle them separately
++        MethodHandle originalTarget = target;
++        int newArity = newType.parameterCount();
++        for (int dropIdx; (dropIdx = findFirstDrop(reorder, newArity)) >= 0; ) {
++            // dropIdx is missing from reorder; add it in at the end
++            int oldArity = reorder.length;
++            target = dropArguments(target, oldArity, newType.parameterType(dropIdx));
++            reorder = Arrays.copyOf(reorder, oldArity+1);
++            reorder[oldArity] = dropIdx;
++        }
++        assert(target == originalTarget || permuteArgumentChecks(reorder, newType, target.type()));
++        // Note:  This may cache too many distinct LFs. Consider backing off to varargs code.
++        BoundMethodHandle result = target.rebind();
++        LambdaForm form = result.editor().permuteArguments(1, reorder);
++        return result.copyWith(newType, form);
+     }
+ 
+-    private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
++    private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) {
+         if (newType.returnType() != oldType.returnType())
+             throw newIllegalArgumentException("return types do not match",
+                     oldType, newType);
+@@ -1753,11 +1783,41 @@
+                     throw newIllegalArgumentException("parameter types do not match after reorder",
+                             oldType, newType);
+             }
+-            if (!bad)  return;
++            if (!bad)  return true;
+         }
+         throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
+     }
+ 
++    /** Return the first value in [0..newArity-1] that is not present in reorder. */
++    private static int findFirstDrop(int[] reorder, int newArity) {
++        final int BIT_LIMIT = 63;  // max number of bits in bit mask
++        if (newArity < 2) {
++            long mask = 0;
++            for (int arg : reorder) {
++                assert(arg < newArity);
++                mask |= (1 << arg);
++            }
++            if (mask == (1 << newArity) - 1) {
++                assert(Long.numberOfTrailingZeros(Long.lowestOneBit(~mask)) == newArity);
++                return -1;
++            }
++            // find first zero
++            long zeroBit = Long.lowestOneBit(~mask);
++            int zeroPos = Long.numberOfTrailingZeros(zeroBit);
++            assert(zeroPos < newArity);
++            return zeroPos;
++        }
++        BitSet mask = new BitSet(newArity);
++        for (int arg : reorder) {
++            assert(arg < newArity);
++            mask.set(arg);
++        }
++        int zeroPos = mask.nextClearBit(0);
++        if (zeroPos == newArity)
++            return -1;
++        return zeroPos;
++    }
++
+     /**
+      * Produces a method handle of the requested return type which returns the given
+      * constant value every time it is invoked.
+@@ -1782,7 +1842,7 @@
+             Wrapper w = Wrapper.forPrimitiveType(type);
+             return insertArguments(identity(type), 0, w.convert(value, type));
+         } else {
+-            return identity(type).bindTo(type.cast(value));
++            return identity(type).bindTo(value);
+         }
+     }
+ 
+@@ -1796,14 +1856,26 @@
+     public static
+     MethodHandle identity(Class<?> type) {
+         bump(EC_lookup_identity);
+-        if (type == void.class)
+-            throw newIllegalArgumentException("void type");
+-        else if (type == Object.class)
+-            return ValueConversions.identity();
+-        else if (type.isPrimitive())
+-            return ValueConversions.identity(Wrapper.forPrimitiveType(type));
+-        else
+-            return MethodHandleImpl.makeReferenceIdentity(type);
++        Wrapper btw = (type.isPrimitive() ? Wrapper.forPrimitiveType(type) : Wrapper.OBJECT);
++        MethodHandle ident = IDENTITY.get(btw);
++        if (ident == null)
++            IDENTITY.put(btw, ident = makeBasicIdentity(btw));
++        if (ident.type().returnType() == type)
++            return ident;
++        return ident.viewAsType(MethodType.methodType(type, type), false);
++    }
++    private static final EnumMap<Wrapper,MethodHandle> IDENTITY = new EnumMap<>(Wrapper.class);
++    private static MethodHandle makeBasicIdentity(Wrapper btw) {
++        Class<?> ptype = btw.primitiveType();
++        MethodType mtype = MethodType.methodType(ptype, ptype);
++        MethodType btype = mtype.basicType();
++        if (btw.isSubwordOrInt() && btw != Wrapper.INT) {
++            // subword identity is just a "views" of int identity
++            return identity(int.class).viewAsType(mtype, false);
++        }
++        assert(btype == mtype);
++        LambdaForm lform = new LambdaForm("identity", 2, LambdaForm.arguments(0, btype.invokerType()), 1);
++        return SimpleMethodHandle.make(mtype, lform);
+     }
+ 
+     /**
+@@ -1838,8 +1910,38 @@
+      */
+     public static
+     MethodHandle insertArguments(MethodHandle target, int pos, Object... values) {
++        int insCount = values.length;
++        Class<?>[] ptypes = insertArgumentsChecks(target, insCount, pos);
++        if (insCount == 0)  return target;
+         bump(EC_transform_insertArguments);
+-        int insCount = values.length;
++        BoundMethodHandle result = target.rebind();
++        for (int i = 0; i < insCount; i++) {
++            Object value = values[i];
++            Class<?> ptype = ptypes[pos+i];
++            if (ptype.isPrimitive()) {
++                bump(EC_transform_insertArguments_prim);
++                Wrapper w = Wrapper.forPrimitiveType(ptype);
++                // perform unboxing and/or primitive conversion
++                value = w.convert(value, ptype);
++                int intValue;
++                switch (w) {
++                default:      intValue = ValueConversions.widenSubword(value);    break;
++                case INT:     intValue = (int)value;                              break;
++                case LONG:    result = result.bindArgumentJ(pos, (long)value);    continue;
++                case FLOAT:   result = result.bindArgumentF(pos, (float)value);   continue;
++                case DOUBLE:  result = result.bindArgumentD(pos, (double)value);  continue;
++                }
++                result = result.bindArgumentI(pos, intValue);
++                continue;
++            }
++            bump(EC_transform_insertArguments_ref);
++            value = ptype.cast(value);  // throw CCE if needed
++            result = result.bindArgumentL(pos, value);
++        }
++        return result;
++    }
++
++    private static Class<?>[] insertArgumentsChecks(MethodHandle target, int insCount, int pos) throws RuntimeException {
+         MethodType oldType = target.type();
+         int outargs = oldType.parameterCount();
+         int inargs  = outargs - insCount;
+@@ -1847,31 +1949,7 @@
+             throw newIllegalArgumentException("too many values to insert");
+         if (pos < 0 || pos > inargs)
+             throw newIllegalArgumentException("no argument type to append");
+-        MethodHandle result = target;
+-        for (int i = 0; i < insCount; i++) {
+-            Object value = values[i];
+-            Class<?> ptype = oldType.parameterType(pos+i);
+-            if (ptype.isPrimitive()) {
+-                char btype = 'I';
+-                Wrapper w = Wrapper.forPrimitiveType(ptype);
+-                switch (w) {
+-                case LONG:    btype = 'J'; break;
+-                case FLOAT:   btype = 'F'; break;
+-                case DOUBLE:  btype = 'D'; break;
+-                }
+-                // perform unboxing and/or primitive conversion
+-                value = w.convert(value, ptype);
+-                result = result.bindArgument(pos, btype, value);
+-                continue;
+-            }
+-            value = ptype.cast(value);  // throw CCE if needed
+-            if (pos == 0) {
+-                result = result.bindReceiver(value);
+-            } else {
+-                result = result.bindArgument(pos, 'L', value);
+-            }
+-        }
+-        return result;
++        return oldType.ptypes();
+     }
+ 
+     /**
+@@ -1918,19 +1996,31 @@
+      */
+     public static
+     MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
++        MethodType oldType = target.type();  // get NPE
++        int dropped = dropArgumentChecks(oldType, pos, valueTypes);
++        if (dropped == 0)  return target;
+         bump(EC_transform_dropArguments);
+-        MethodType oldType = target.type();  // get NPE
++        BoundMethodHandle result = target.rebind();
++        LambdaForm lform = result.form;
++        int insertFormArg = 1+pos;
++        for (Class<?> ptype : valueTypes) {
++            lform = lform.editor().addArgumentForm(insertFormArg++, LambdaForm.basicType(ptype));
++        }
++        MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
++        result = result.copyWith(newType, lform);
++        return result;
++    }
++
++    private static int dropArgumentChecks(MethodType oldType, int pos, List<Class<?>> valueTypes) {
+         int dropped = valueTypes.size();
+         MethodType.checkSlotCount(dropped);
+-        if (dropped == 0)  return target;
+         int outargs = oldType.parameterCount();
+         int inargs  = outargs + dropped;
+-        if (pos < 0 || pos >= inargs)
+-            throw newIllegalArgumentException("no argument type to remove");
+-        ArrayList<Class<?>> ptypes = new ArrayList<>(oldType.parameterList());
+-        ptypes.addAll(pos, valueTypes);
+-        MethodType newType = MethodType.methodType(oldType.returnType(), ptypes);
+-        return target.dropArguments(newType, pos, dropped);
++        if (pos < 0 || pos > outargs)
++            throw newIllegalArgumentException("no argument type to remove"
++                    +Arrays.asList(oldType, pos, valueTypes, inargs, outargs)
++                    );
++        return dropped;
+     }
+ 
+     /**
+@@ -2050,32 +2140,44 @@
+     public static
+     MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
+         bump(EC_transform_filterArguments);
+-        MethodType targetType = target.type();
++        filterArgumentsCheckArity(target, pos, filters);
+         MethodHandle adapter = target;
+-        MethodType adapterType = null;
+-        assert((adapterType = targetType) != null);
+-        int maxPos = targetType.parameterCount();
+-        if (pos + filters.length > maxPos)
+-            throw newIllegalArgumentException("too many filters");
+         int curPos = pos-1;  // pre-incremented
+         for (MethodHandle filter : filters) {
+             curPos += 1;
+             if (filter == null)  continue;  // ignore null elements of filters
+             adapter = filterArgument(adapter, curPos, filter);
+-            assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null);
+         }
+-        assert(adapterType.equals(adapter.type()));
+         return adapter;
+     }
+ 
+     /*non-public*/ static
+     MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
++        filterArgumentChecks(target, pos, filter);
++        //@@ return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
++        MethodType targetType = target.type();
++        MethodType filterType = filter.type();
++        BoundMethodHandle result = target.rebind();
++        Class<?> newParamType = filterType.parameterType(0);
++        LambdaForm lform = result.editor().filterArgumentForm(1+pos, LambdaForm.basicType(newParamType), result.speciesData());
++        MethodType newType = targetType.changeParameterType(pos, newParamType);
++        result = result.copyWithExtendL(newType, lform, filter);
++        return result;
++    }
++
++    private static void filterArgumentsCheckArity(MethodHandle target, int pos, MethodHandle[] filters) {
++        MethodType targetType = target.type();
++        int maxPos = targetType.parameterCount();
++        if (pos + filters.length > maxPos)
++            throw newIllegalArgumentException("too many filters");
++    }
++
++    private static void filterArgumentChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
+         MethodType targetType = target.type();
+         MethodType filterType = filter.type();
+         if (filterType.parameterCount() != 1
+-            || filterType.returnType() != targetType.parameterType(pos))
++                || filterType.returnType() != targetType.parameterType(pos))
+             throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+-        return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
+     }
+ 
+     // FIXME: Make this public in M1.
+@@ -2152,15 +2254,27 @@
+         bump(EC_transform_filterReturnValue);
+         MethodType targetType = target.type();
+         MethodType filterType = filter.type();
++        filterReturnValueChecks(targetType, filterType);
++        if (Boolean.FALSE) {
++            // result = fold( lambda(retval, arg...) { filter(retval) },
++            //                lambda(        arg...) { target(arg...) } )
++            return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
++        }
++        BoundMethodHandle result = target.rebind();
++        byte rtype = LambdaForm.basicType(filterType.returnType());
++        LambdaForm lform = result.editor().filterReturnForm(rtype, result.speciesData());
++        MethodType newType = targetType.changeReturnType(filterType.returnType());
++        result = result.copyWithExtendL(newType, lform, filter);
++        return result;
++    }
++
++    private static void filterReturnValueChecks(MethodType targetType, MethodType filterType) throws RuntimeException {
+         Class<?> rtype = targetType.returnType();
+         int filterValues = filterType.parameterCount();
+         if (filterValues == 0
+                 ? (rtype != void.class)
+                 : (rtype != filterType.parameterType(0)))
+-            throw newIllegalArgumentException("target and filter types do not match", target, filter);
+-        // result = fold( lambda(retval, arg...) { filter(retval) },
+-        //                lambda(        arg...) { target(arg...) } )
+-        return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
++            throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+     }
+ 
+     /**
+@@ -2242,24 +2356,35 @@
+     public static
+     MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
+         bump(EC_transform_foldArguments);
+-        int pos = 0;
++        int foldPos = 0;
+         MethodType targetType = target.type();
+         MethodType combinerType = combiner.type();
+-        int foldPos = pos;
+-        int foldArgs = combinerType.parameterCount();
+-        int foldVals = combinerType.returnType() == void.class ? 0 : 1;
++        Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
++        BoundMethodHandle result = target.rebind();
++        // Note:  This may cache too many distinct LFs. Consider backing off to varargs code.
++        LambdaForm lform = result.editor().foldArgumentsForm(1+foldPos, combinerType.basicType(), result.speciesData());
++        MethodType newType = targetType;
++        if (rtype != void.class)
++            newType = newType.dropParameterTypes(foldPos, foldPos+1);
++        result = result.copyWithExtendL(newType, lform, combiner);
++        return result;
++    }
++
++    private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
++        int foldArgs   = combinerType.parameterCount();
++        Class<?> rtype = combinerType.returnType();
++        int foldVals = rtype == void.class ? 0 : 1;
+         int afterInsertPos = foldPos + foldVals;
+         boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
+         if (ok && !(combinerType.parameterList()
+-                    .equals(targetType.parameterList().subList(afterInsertPos,
++                .equals(targetType.parameterList().subList(afterInsertPos,
+                                                                afterInsertPos + foldArgs))))
+             ok = false;
+-        if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0)))
++        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
+             ok = false;
+         if (!ok)
+             throw misMatchedTypes("target and combiner types", targetType, combinerType);
+-        MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
+-        return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
++        return rtype;
+     }
+ 
+     /**
+@@ -2433,10 +2558,13 @@
+     private static final EventCounter EC_lookup_arrayElementGetter = eventCounter(EC_lookup, "arrayElementGetter");
+     private static final EventCounter EC_lookup_arrayElementSetter = eventCounter(EC_lookup, "arrayElementSetter");
+     private static final EventCounter EC_transform_explicitCastArguments = eventCounter(EC_transform, "explicitCastArguments");
++    private static final EventCounter EC_transform_explicitCastArguments_cachable = eventCounter(EC_transform_explicitCastArguments, "cachable");
+     private static final EventCounter EC_transform_permuteArguments = eventCounter(EC_transform, "permuteArguments");
+     private static final EventCounter EC_lookup_constant = eventCounter(EC_lookup, "constant");
+     private static final EventCounter EC_lookup_identity = eventCounter(EC_lookup, "identity");
+     private static final EventCounter EC_transform_insertArguments = eventCounter(EC_transform, "insertArguments");
++    private static final EventCounter EC_transform_insertArguments_prim = eventCounter(EC_transform_insertArguments, "prim");
++    private static final EventCounter EC_transform_insertArguments_ref = eventCounter(EC_transform_insertArguments, "ref");
+     private static final EventCounter EC_transform_dropArguments = eventCounter(EC_transform, "dropArguments");
+     private static final EventCounter EC_transform_filterArguments = eventCounter(EC_transform, "filterArguments");
+     private static final EventCounter EC_transform_collectArguments = eventCounter(EC_transform, "collectArguments");
+diff --git a/src/share/classes/java/lang/invoke/MethodType.java b/src/share/classes/java/lang/invoke/MethodType.java
+--- a/src/share/classes/java/lang/invoke/MethodType.java
++++ b/src/share/classes/java/lang/invoke/MethodType.java
+@@ -29,6 +29,7 @@
+ import java.lang.ref.WeakReference;
+ import java.lang.ref.Reference;
+ import java.lang.ref.ReferenceQueue;
++import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Collections;
+ import java.util.List;
+@@ -455,6 +456,75 @@
+         return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert);
+     }
+ 
++    /** Return the leading parameter type, which must exist and be a reference.
++     *  @return the leading parameter type, after error checks
++     */
++    /*non-public*/ Class<?> leadingReferenceParameter() {
++        Class<?> ptype;
++        if (ptypes.length == 0 ||
++            (ptype = ptypes[0]).isPrimitive())
++            throw newIllegalArgumentException("no leading reference parameter");
++        return ptype;
++    }
++
++    /** Replace the last arrayLength parameter types with the component type of arrayType.
++     * @param arrayType any array type
++     * @param arrayLength the number of parameter types to change
++     * @return the resulting type
++     */
++    /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int arrayLength) {
++        assert(parameterCount() >= arrayLength);
++        int spreadPos = ptypes.length - arrayLength;
++        if (arrayLength == 0)  return this;  // nothing to change
++        if (arrayType == Object[].class) {
++            if (isGeneric())  return this;  // nothing to change
++            if (spreadPos == 0) {
++                // no leading arguments to preserve; go generic
++                MethodType res = genericMethodType(arrayLength);
++                if (rtype != Object.class) {
++                    res = res.changeReturnType(rtype);
++                }
++                return res;
++            }
++        }
++        Class<?> elemType = arrayType.getComponentType();
++        assert(elemType != null);
++        for (int i = spreadPos; i < ptypes.length; i++) {
++            if (ptypes[i] != elemType) {
++                Class<?>[] fixedPtypes = ptypes.clone();
++                Arrays.fill(fixedPtypes, i, ptypes.length, elemType);
++                return methodType(rtype, fixedPtypes);
++            }
++        }
++        return this;  // arguments check out; no change
++    }
++
++    /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType.
++     * @param arrayType any array type
++     * @param arrayLength the number of parameter types to insert
++     * @return the resulting type
++     */
++    /*non-public*/ MethodType asCollectorType(Class<?> arrayType, int arrayLength) {
++        assert(parameterCount() >= 1);
++        assert(lastParameterType().isAssignableFrom(arrayType));
++        MethodType res;
++        if (arrayType == Object[].class) {
++            res = genericMethodType(arrayLength);
++            if (rtype != Object.class) {
++                res = res.changeReturnType(rtype);
++            }
++        } else {
++            Class<?> elemType = arrayType.getComponentType();
++            assert(elemType != null);
++            res = methodType(rtype, Collections.nCopies(arrayLength, elemType));
++        }
++        if (ptypes.length == 1) {
++            return res;
++        } else {
++            return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1));
++        }
++    }
++
+     /**
+      * Finds or creates a method type with some parameter types omitted.
+      * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+@@ -525,6 +595,20 @@
+     }
+ 
+     /**
++     * Reports if this type contains an interface argument.
++     * @return true if any of the parameter types are interfaces
++     */
++    /*non-public*/
++    boolean hasInterfaceParameters() {
++        if (this == erase())  return false;  // only Object references
++        int len = ptypes.length;
++        for (int i = 0; i < len; i++) {
++            if (ptypes[i].isInterface())  return true;
++        }
++        return false;
++    }
++
++    /**
+      * Erases all reference types to {@code Object}.
+      * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+      * All primitive types (including {@code void}) will remain unchanged.
+@@ -562,6 +646,10 @@
+         return genericMethodType(parameterCount());
+     }
+ 
++    /*non-public*/ boolean isGeneric() {
++        return this == erase() && !hasPrimitives();
++    }
++
+     /**
+      * Converts all primitive types to their corresponding wrapper types.
+      * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+@@ -718,43 +806,136 @@
+     }
+ 
+ 
++    /** True if the old return type can always be viewed (w/o casting) under new return type,
++     *  and the new parameters can be viewed (w/o casting) under the old parameter types.
++     */
+     /*non-public*/
+-    boolean isViewableAs(MethodType newType) {
+-        if (!VerifyType.isNullConversion(returnType(), newType.returnType()))
++    boolean isViewableAs(MethodType newType, boolean keepInterfaces) {
++        if (!VerifyType.isNullConversion(returnType(), newType.returnType(), keepInterfaces))
+             return false;
++        return parametersAreViewableAs(newType, keepInterfaces);
++    }
++    /** True if the new parameters can be viewed (w/o casting) under the old parameter types. */
++    /*non-public*/
++    boolean parametersAreViewableAs(MethodType newType, boolean keepInterfaces) {
++        if (form == newType.form && form.erasedType == this)
++            return true;  // my reference parameters are all Object
++        if (ptypes == newType.ptypes)
++            return true;
+         int argc = parameterCount();
+         if (argc != newType.parameterCount())
+             return false;
+         for (int i = 0; i < argc; i++) {
+-            if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i)))
++            if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i), keepInterfaces))
+                 return false;
+         }
+         return true;
+     }
+     /*non-public*/
+     boolean isCastableTo(MethodType newType) {
++        MethodTypeForm oldForm = this.form();
++        MethodTypeForm newForm = newType.form();
++        if (oldForm == newForm)
++            // same parameter count, same primitive/object mix
++            return true;
+         int argc = parameterCount();
+         if (argc != newType.parameterCount())
+             return false;
+-        return true;
++        // Corner case:  boxing (primitive-to-reference) must have a plausible target type
++        // Therefore, we may have to return false for a boxing operation.
++        if (!canCast(returnType(), newType.returnType()))
++            return false;
++        if (newForm.primitiveParameterCount() == 0)
++            return true;  // no primitive sources to mess things up
++        if (oldForm.erasedType == this)
++            return true;  // no funny target references to mess things up
++        return canCastParameters(newType.ptypes, ptypes);
+     }
+     /*non-public*/
+     boolean isConvertibleTo(MethodType newType) {
++        MethodTypeForm oldForm = this.form();
++        MethodTypeForm newForm = newType.form();
++        if (oldForm == newForm)
++            // same parameter count, same primitive/object mix
++            return true;
+         if (!canConvert(returnType(), newType.returnType()))
+             return false;
+-        int argc = parameterCount();
+-        if (argc != newType.parameterCount())
++        Class<?>[] srcTypes = newType.ptypes;
++        Class<?>[] dstTypes = ptypes;
++        if (srcTypes == dstTypes)
++            return true;
++        int argc;
++        if ((argc = srcTypes.length) != dstTypes.length)
+             return false;
+-        for (int i = 0; i < argc; i++) {
+-            if (!canConvert(newType.parameterType(i), parameterType(i)))
++        if (argc <= 1) {
++            if (argc == 1 && !canConvert(srcTypes[0], dstTypes[0]))
+                 return false;
++            return true;
++        }
++        if ((oldForm.primitiveParameterCount() == 0 && oldForm.erasedType == this) ||
++            (newForm.primitiveParameterCount() == 0 && newForm.erasedType == newType)) {
++            // Somewhat complicated test to avoid a loop of 2 or more trips.
++            // If either type has only Object parameters, we know we can convert.
++            assert(canConvertParameters(srcTypes, dstTypes));
++            return true;
++        }
++        return canConvertParameters(srcTypes, dstTypes);
++    }
++
++    /** Returns true if MHs.explicitCastArguemnts produces the same result as MH.asType.
++     *  If the type conversion is impossible for either, the result should be false.
++     */
++    /*non-public*/
++    boolean explicitCastEquivalentToAsType(MethodType newType) {
++        if (this == newType)  return true;
++        if (!explicitCastEquivalentToAsType(rtype, newType.rtype)) {
++            return false;
++        }
++        Class<?>[] srcTypes = newType.ptypes;
++        Class<?>[] dstTypes = ptypes;
++        if (dstTypes == srcTypes) {
++            return true;
++        }
++        if (dstTypes.length != srcTypes.length) {
++            return false;
++        }
++        for (int i = 0; i < dstTypes.length; i++) {
++            if (!explicitCastEquivalentToAsType(srcTypes[i], dstTypes[i])) {
++                return false;
++            }
++        }
++        return true;
++    }
++
++    private boolean canCastParameters(Class<?>[] srcTypes, Class<?>[] dstTypes) {
++        for (int i = 0; i < srcTypes.length; i++) {
++            if (!canCast(srcTypes[i], dstTypes[i])) {
++                return false;
++            }
++        }
++        return true;
++    }
++    private boolean canConvertParameters(Class<?>[] srcTypes, Class<?>[] dstTypes) {
++        for (int i = 0; i < srcTypes.length; i++) {
++            if (!canConvert(srcTypes[i], dstTypes[i])) {
++                return false;
++            }
++        }
++        return true;
++    }
++    private static boolean canCast(Class<?> src, Class<?> dst) {
++        if (src.isPrimitive() && !dst.isPrimitive()) {
++            if (dst == Object.class || dst.isInterface())  return true;
++            // Here is the corner case that is not castable.  Example:  int -> String
++            Wrapper sw = Wrapper.forPrimitiveType(src);
++            return dst.isAssignableFrom(sw.wrapperType());
+         }
+         return true;
+     }
+     /*non-public*/
+     static boolean canConvert(Class<?> src, Class<?> dst) {
+         // short-circuit a few cases:
+-        if (src == dst || dst == Object.class)  return true;
++        if (src == dst || src == Object.class || dst == Object.class)  return true;
+         // the remainder of this logic is documented in MethodHandle.asType
+         if (src.isPrimitive()) {
+             // can force void to an explicit null, a la reflect.Method.invoke
+@@ -803,6 +984,43 @@
+             return true;
+         }
+     }
++    /** Reports true if the src can be converted to the dst, by both asType and MHs.eCE,
++     *  and with the same effect.
++     *  MHs.eCA has the following "upgrades" to MH.asType:
++     *  1. interfaces are unchecked (that is, treated as if aliased to Object)
++     *     Therefore, {@code Object->CharSequence} is possible in both cases but has different semantics
++     *  2. the full matrix of primitive-to-primitive conversions is supported
++     *     Narrowing like {@code long->byte} and basic-typing like {@code boolean->int}
++     *     are not supported by asType, but anything supported by asType is equivalent
++     *     with MHs.eCE.
++     *  3a. unboxing conversions can be followed by the full matrix of primitive conversions
++     *  3b. unboxing of null is permitted (creates a zero primitive value)
++     *     Most unboxing conversions, like {@code Object->int}, has potentially
++     *     different behaviors for asType vs. MHs.eCE, because the dynamic value
++     *     might be a wrapper of a type that requires narrowing, like {@code (Object)1L->byte}.
++     *     The equivalence is only certain if the static src type is a wrapper,
++     *     and the conversion will be a widening one.
++     * Other than interfaces, reference-to-reference conversions are the same.
++     * Boxing primitives to references is the same for both operators.
++     */
++    private static boolean explicitCastEquivalentToAsType(Class<?> src, Class<?> dst) {
++        if (src == dst || dst == Object.class || dst == void.class)  return true;
++        if (src.isPrimitive()) {
++            // Could be a prim/prim conversion, where casting is a strict superset.
++            // Or a boxing conversion, which is always to an exact wrapper class.
++            return canConvert(src, dst);
++        } else if (dst.isPrimitive()) {
++            Wrapper dw = Wrapper.forPrimitiveType(dst);
++            // Watch out:  If src is Number or Object, we could get dynamic narrowing conversion.
++            // The conversion is known to be widening only if the wrapper type is statically visible.
++            return (Wrapper.isWrapperType(src) &&
++                    dw.isConvertibleFrom(Wrapper.forWrapperType(src)));
++        } else {
++            // R->R always works, but we have to avoid a check-cast to an interface.
++            return !dst.isInterface() || dst.isAssignableFrom(src);
++        }
++    }
++
+ 
+     /// Queries which have to do with the bytecode architecture
+ 
+diff --git a/src/share/classes/java/lang/invoke/MethodTypeForm.java b/src/share/classes/java/lang/invoke/MethodTypeForm.java
+--- a/src/share/classes/java/lang/invoke/MethodTypeForm.java
++++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java
+@@ -47,14 +47,17 @@
+     final int[] argToSlotTable, slotToArgTable;
+     final long argCounts;               // packed slot & value counts
+     final long primCounts;              // packed prim & double counts
+-    final int vmslots;                  // total number of parameter slots
+     final MethodType erasedType;        // the canonical erasure
+     final MethodType basicType;         // the canonical erasure, with primitives simplified
+ 
+     // Cached adapter information:
+-    /*lazy*/ MethodHandle genericInvoker; // JVM hook for inexact invoke
+-    /*lazy*/ MethodHandle basicInvoker;   // cached instance of MH.invokeBasic
+-    /*lazy*/ MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
++    final MethodHandle[] methodHandles;
++    // Indexes into methodHandles:
++    static final int
++            MH_BASIC_INV      =  0,  // cached instance of MH.invokeBasic
++            MH_NF_INV         =  1,  // cached helper for LF.NamedFunction
++            MH_UNINIT_CS      =  2,  // uninitialized call site
++            MH_LIMIT          =  3;
+ 
+     // Cached lambda form information, for basic types only:
+     final LambdaForm[] lambdaForms;
+@@ -67,25 +70,53 @@
+             LF_INVINTERFACE   =  4,
+             LF_INVSTATIC_INIT =  5,  // DMH invokeStatic with <clinit> barrier
+             LF_INTERPRET      =  6,  // LF interpreter
+-            LF_COUNTER        =  7,  // CMH wrapper
+-            LF_REINVOKE       =  8,  // other wrapper
+-            LF_EX_LINKER      =  9,  // invokeExact_MT
+-            LF_EX_INVOKER     = 10,  // invokeExact MH
+-            LF_GEN_LINKER     = 11,
+-            LF_GEN_INVOKER    = 12,
++            LF_REBIND         =  7,  // BoundMethodHandle
++            LF_DELEGATE       =  8,  // DelegatingMethodHandle
++            LF_EX_LINKER      =  9,  // invokeExact_MT (for invokehandle)
++            LF_EX_INVOKER     = 10,  // MHs.invokeExact
++            LF_GEN_LINKER     = 11,  // generic invoke_MT (for invokehandle)
++            LF_GEN_INVOKER    = 12,  // generic MHs.invoke
+             LF_CS_LINKER      = 13,  // linkToCallSite_CS
+             LF_MH_LINKER      = 14,  // linkToCallSite_MH
+-            LF_LIMIT          = 15;
++            LF_CASTING_INV    = 15,  // Invokers.castingInvoker
++            LF_GWT            = 16,  // guardWithTest
++            LF_LIMIT          = 17;
+ 
++    /** Return the type corresponding uniquely (1-1) to this MT-form.
++     *  It might have any primitive returns or arguments, but will have no references except Object.
++     */
+     public MethodType erasedType() {
+         return erasedType;
+     }
+ 
++    /** Return the basic type derived from the erased type of this MT-form.
++     *  A basic type is erased (all references Object) and also has all primitive
++     *  types (except int, long, float, double, void) normalized to int.
++     *  Such basic types correspond to low-level JVM calling sequences.
++     */
+     public MethodType basicType() {
+         return basicType;
+     }
+ 
++    private boolean assertIsBasicType() {
++        // primitives must be flattened also
++        assert(erasedType == basicType)
++                : "erasedType: " + erasedType + " != basicType: " + basicType;
++        return true;
++    }
++
++    public MethodHandle cachedMethodHandle(int which) {
++        assert(assertIsBasicType());
++        return methodHandles[which];
++    }
++
++    public MethodHandle setCachedMethodHandle(int which, MethodHandle form) {
++        // Should we perform some sort of CAS, to avoid racy duplication?
++        return methodHandles[which] = form;
++    }
++
+     public LambdaForm cachedLambdaForm(int which) {
++        assert(assertIsBasicType());
+         return lambdaForms[which];
+     }
+ 
+@@ -94,15 +125,6 @@
+         return lambdaForms[which] = form;
+     }
+ 
+-    public MethodHandle basicInvoker() {
+-        assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType;  // primitives must be flattened also
+-        MethodHandle invoker = basicInvoker;
+-        if (invoker != null)  return invoker;
+-        invoker = DirectMethodHandle.make(invokeBasicMethod(basicType));
+-        basicInvoker = invoker;
+-        return invoker;
+-    }
+-
+     // This next one is called from LambdaForm.NamedFunction.<init>.
+     /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) {
+         assert(basicType == basicType.basicType());
+@@ -166,6 +188,16 @@
+             this.basicType = erasedType;
+         } else {
+             this.basicType = MethodType.makeImpl(bt, bpts, true);
++            // fill in rest of data from the basic type:
++            MethodTypeForm that = this.basicType.form();
++            assert(this != that);
++            this.primCounts = that.primCounts;
++            this.argCounts = that.argCounts;
++            this.argToSlotTable = that.argToSlotTable;
++            this.slotToArgTable = that.slotToArgTable;
++            this.methodHandles = null;
++            this.lambdaForms = null;
++            return;
+         }
+         if (lac != 0) {
+             int slot = ptypeCount + lac;
+@@ -181,10 +213,14 @@
+                 argToSlotTab[1+i]  = slot;
+             }
+             assert(slot == 0);  // filled the table
+-        }
+-        this.primCounts = pack(lrc, prc, lac, pac);
+-        this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
+-        if (slotToArgTab == null) {
++        } else if (pac != 0) {
++            // have primitives but no long primitives; share slot counts with generic
++            assert(ptypeCount == pslotCount);
++            MethodTypeForm that = MethodType.genericMethodType(ptypeCount).form();
++            assert(this != that);
++            slotToArgTab = that.slotToArgTable;
++            argToSlotTab = that.argToSlotTable;
++        } else {
+             int slot = ptypeCount; // first arg is deepest in stack
+             slotToArgTab = new int[slot+1];
+             argToSlotTab = new int[1+ptypeCount];
+@@ -195,19 +231,17 @@
+                 argToSlotTab[1+i]  = slot;
+             }
+         }
++        this.primCounts = pack(lrc, prc, lac, pac);
++        this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
+         this.argToSlotTable = argToSlotTab;
+         this.slotToArgTable = slotToArgTab;
+ 
+         if (pslotCount >= 256)  throw newIllegalArgumentException("too many arguments");
+ 
+-        // send a few bits down to the JVM:
+-        this.vmslots = parameterSlotCount();
+-
+-        if (basicType == erasedType) {
+-            lambdaForms = new LambdaForm[LF_LIMIT];
+-        } else {
+-            lambdaForms = null;  // could be basicType.form().lambdaForms;
+-        }
++        // Initialize caches, but only for basic types
++        assert(basicType == erasedType);
++        this.lambdaForms = new LambdaForm[LF_LIMIT];
++        this.methodHandles = new MethodHandle[MH_LIMIT];
+     }
+ 
+     private static long pack(int a, int b, int c, int d) {
+@@ -294,7 +328,7 @@
+      */
+     public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
+         Class<?>[] ptypes = mt.ptypes();
+-        Class<?>[] ptc = MethodTypeForm.canonicalizes(ptypes, howArgs);
++        Class<?>[] ptc = MethodTypeForm.canonicalizeAll(ptypes, howArgs);
+         Class<?> rtype = mt.returnType();
+         Class<?> rtc = MethodTypeForm.canonicalize(rtype, howRet);
+         if (ptc == null && rtc == null) {
+@@ -362,7 +396,7 @@
+     /** Canonicalize each param type in the given array.
+      *  Return null if all types are already canonicalized.
+      */
+-    static Class<?>[] canonicalizes(Class<?>[] ts, int how) {
++    static Class<?>[] canonicalizeAll(Class<?>[] ts, int how) {
+         Class<?>[] cs = null;
+         for (int imax = ts.length, i = 0; i < imax; i++) {
+             Class<?> c = canonicalize(ts[i], how);
+diff --git a/src/share/classes/java/lang/invoke/SimpleMethodHandle.java b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
+--- a/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
++++ b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2008, 2013, 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
+@@ -28,12 +28,13 @@
+ import static java.lang.invoke.LambdaForm.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
+ import static java.lang.invoke.MethodHandleStatics.*;
++import sun.invoke.util.ValueConversions;
+ 
+ /**
+  * A method handle whose behavior is determined only by its LambdaForm.
+  * @author jrose
+  */
+-final class SimpleMethodHandle extends MethodHandle {
++final class SimpleMethodHandle extends BoundMethodHandle {
+     private SimpleMethodHandle(MethodType type, LambdaForm form) {
+         super(type, form);
+         bump(EC_methodHandle_simple);
+@@ -43,30 +44,66 @@
+         return new SimpleMethodHandle(type, form);
+     }
+ 
++    public static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
++
+     @Override
+-    MethodHandle bindArgument(int pos, char basicType, Object value) {
+-        MethodType type2 = type().dropParameterTypes(pos, pos+1);
+-        LambdaForm form2 = internalForm().bind(1+pos, BoundMethodHandle.SpeciesData.EMPTY);
+-        return BoundMethodHandle.bindSingle(type2, form2, basicType, value);
++    public SimpleMethodHandle copyWith(MethodType mt, LambdaForm lf) {
++        return make(mt, lf);
+     }
+ 
+     @Override
+-    MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+-        LambdaForm newForm = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
+-        return new SimpleMethodHandle(srcType, newForm);
++    public SpeciesData speciesData() {
++        return SPECIES_DATA;
+     }
+ 
+     @Override
+-    MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+-        LambdaForm form2 = internalForm().permuteArguments(1, reorder, basicTypes(newType.parameterList()));
+-        return new SimpleMethodHandle(newType, form2);
++    public int fieldCount() {
++        return 0;
+     }
+ 
+     @Override
+-    MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+-        return new SimpleMethodHandle(mt, lf);
++    public final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
++        if (true)  return BoundMethodHandle.bindSingle(mt, lf, narg);
++        try {
++            return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, narg);
++        } catch (Throwable ex) {
++            throw uncaughtException(ex);
++        }
++    }
++    @Override
++    public final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
++        try {
++            return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, narg);
++        } catch (Throwable ex) {
++            throw uncaughtException(ex);
++        }
++    }
++    @Override
++    public final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
++        try {
++            return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, narg);
++        } catch (Throwable ex) {
++            throw uncaughtException(ex);
++        }
++    }
++    @Override
++    public final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
++        try {
++            return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, narg);
++        } catch (Throwable ex) {
++            throw uncaughtException(ex);
++        }
++    }
++    @Override
++    public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
++        try {
++            return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, narg);
++        } catch (Throwable ex) {
++            throw uncaughtException(ex);
++        }
+     }
+ 
+     // Event counters
+     private static final EventCounter EC_methodHandle_simple = eventCounter(EC_methodHandle, "simple");
++
+ }
+diff --git a/src/share/classes/java/lang/invoke/TypeChanger.java b/src/share/classes/java/lang/invoke/TypeChanger.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/lang/invoke/TypeChanger.java
+@@ -0,0 +1,254 @@
++/*
++ * Copyright (c) 2013, 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-13*1 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.MethodHandleStatics.*;
++import sun.invoke.util.ValueConversions;
++import sun.invoke.util.VerifyType;
++
++/**
++ * Logic for pairwise argument and return type conversions.
++ * Handles MH.asType, MHs.explicitCastArguments, and viewing transforms.
++ * @author jrose
++ */
++/*non-public*/
++abstract class TypeChanger {
++
++    /** Actually create the required method handle.
++     *  The new type is supplied redundantly so that TC's can be cached and reused.
++     */
++    abstract MethodHandle changeType(MethodHandle mh, MethodType newType);
++
++    /** Tell whether this TC is applicable to the given pair of types.
++     *  May be used for cache checking.
++     *  The types may be assumed to have the same arity.
++     * FIXME: Remove this. //@@
++     */
++    boolean isApplicableTo(MethodType oldType, MethodType newType) { return false; }
++
++    // event counters
++    private static final EventCounter EC_convert = eventCounter("convert");
++    private static final EventCounter EC_convert_explicitCast = eventCounter(EC_convert, "explicitCast");
++    private static final EventCounter EC_convert_sameForm = eventCounter(EC_convert, "sameForm");
++    private static final EventCounter EC_convert_viewOnly = eventCounter(EC_convert, "viewOnly");
++    private static final EventCounter EC_convert_returnOnly = eventCounter(EC_convert, "returnOnly");
++    private static final EventCounter EC_convert_fromGeneric = eventCounter(EC_convert, "fromGeneric");
++    private static final EventCounter EC_convert_toGeneric = eventCounter(EC_convert, "toGeneric");
++    private static final EventCounter EC_convert_lose = eventCounter(EC_convert, "lose");
++
++    /** Throw this when things go bad. */
++    private static WrongMethodTypeException lose(MethodType oldType, MethodType newType, boolean strict) {
++        bump(EC_convert_lose);
++        if (strict)
++            return new WrongMethodTypeException("cannot convert "+oldType+" to "+newType);
++        else
++            return new WrongMethodTypeException("cannot explicitly cast "+oldType+" to "+newType);
++    }
++
++    /** Find or create a TC which can convert between the given types, as if from MH.asType.
++     *  Primitives may not take part in narrowing conversions.
++     *  Interface types must be checked.
++     */
++    static TypeChanger strictTypeChanger(MethodType oldType, MethodType newType) {
++        return typeChanger(oldType, newType, true);
++    }
++    /** Find or create a TC which can convert between the given types, as if from MHs.explicitCastArguments.
++     *  All primitive conversions are allowed.
++     *  Interface types must not be checked.
++     */
++    static TypeChanger explicitCastTypeChanger(MethodType oldType, MethodType newType) {
++        bump(EC_convert_explicitCast);
++        return typeChanger(oldType, newType, false);
++    }
++    static TypeChanger typeChanger(MethodType oldType, MethodType newType, boolean strict) {
++        bump(EC_convert);
++        int arity = oldType.parameterCount();
++        if (newType.parameterCount() == arity &&
++                arity <= MethodType.MAX_MH_INVOKER_ARITY) {
++            MethodTypeForm oldForm = oldType.form();
++            MethodTypeForm newForm = newType.form();
++            if (oldForm == newForm) {
++                // only reference casts will be involved here
++                bump(EC_convert_sameForm);
++                assert(strict ? oldType.isConvertibleTo(newType) : oldType.isCastableTo(newType));
++                if (!strict && (oldType.hasInterfaceParameters() || newType.returnType().isInterface()))
++                    return checkCaster(oldType, newType, /*keepInterfaces=*/ false);
++                return checkCaster(oldType, newType, /*keepInterfaces=*/ true);
++            }
++
++            //@@
++        }
++
++        // Fall back:
++        if (strict) {
++            if (oldType.isConvertibleTo(newType)) {
++                return Slow.STRICT;
++            }
++        } else {
++            if (oldType.isCastableTo(newType)) {
++                return Slow.NON_STRICT;
++            }
++        }
++        throw lose(oldType, newType, strict);
++    }
++
++    /** Uses the viewAsType transform, in cases where that is appropriate. */
++    private static final class Viewer extends TypeChanger {
++        private final boolean strict;
++        public Viewer(boolean strict) {
++            this.strict = strict;
++        }
++        @Override MethodHandle changeType(MethodHandle mh, MethodType newType) {
++            return mh.viewAsType(newType, strict);
++        }
++        static final TypeChanger STRICT = new Viewer(true);
++        static final TypeChanger NON_STRICT = new Viewer(false);
++    }
++
++    /** Assume only check-casts are needed. */
++    static TypeChanger checkCaster(MethodType oldType, MethodType newType,
++                                   boolean keepInterfaces) {
++        assert(oldType.form() == newType.form());
++        Class<?> oldRet = oldType.returnType();
++        Class<?> newRet = newType.returnType();
++        if (oldType.parametersAreViewableAs(newType, keepInterfaces)) {
++            // no conversions needed on parameter types
++            if (VerifyType.isNullConversion(oldRet, newRet, keepInterfaces)) {
++                bump(EC_convert_viewOnly);
++                return keepInterfaces ? Viewer.STRICT : Viewer.NON_STRICT;
++            }
++            // need a return-value cast; implement with a return value transform
++            bump(EC_convert_returnOnly);
++            return ChangeReturnValue.STRICT;
++        }
++        // decide in detail where the check casts occur
++        return keepInterfaces ? CheckCaster.STRICT : CheckCaster.NON_STRICT;
++    }
++
++    /** Assume only check-casts are needed. */
++    private static final class CheckCaster extends TypeChanger {
++        private final boolean strict;
++        CheckCaster(boolean strict) {
++            this.strict = strict;
++        }
++        @Override MethodHandle changeType(MethodHandle mh, MethodType newType) {
++            MethodType oldType = mh.type();
++            // We handle check-casts and view transforms only:
++            assert(oldType.basicType() == newType.basicType());
++            // In general, we will have to apply check-casts on incoming parameters.
++            // Each old parameter type will have to be checked after receipt.
++            // There may also be need for a check-cast on the return value.
++            // The new return type will have to be checked before return.
++            if (!strict)  return  changeTypeNonStrict(mh, newType);
++            return newType.invokers().castingInvoker().bindTo(mh);
++        }
++        private MethodHandle changeTypeNonStrict(MethodHandle mh, MethodType newType) {
++            MethodType oldType = mh.type();
++            MethodType midType = oldType;
++            Class<?> newRT = newType.returnType();
++            if (newRT != oldType.returnType() && newRT.isInterface()) {
++                assert(!oldType.returnType().isPrimitive());
++                midType = midType.changeReturnType(newRT);
++            }
++            for (int i = 0; i < oldType.parameterCount(); i++) {
++                Class<?> oldPT = oldType.parameterType(i);
++                if (oldPT != newType.parameterType(i) && oldPT.isInterface()) {
++                    assert(!newType.parameterType(i).isPrimitive());
++                    midType = midType.changeParameterType(i, Object.class);
++                }
++            }
++            if (midType != oldType) {
++                mh = mh.viewAsType(midType, false);  // avoid interface casts
++            }
++            return newType.invokers().castingInvoker().bindTo(mh);
++        }
++        static final TypeChanger STRICT = new CheckCaster(true);
++        static final TypeChanger NON_STRICT = new CheckCaster(false);
++    }
++
++    private static class ChangeReturnValue extends TypeChanger {
++        static final TypeChanger STRICT = new ChangeReturnValue();
++
++        @Override
++        MethodHandle changeType(MethodHandle mh, MethodType newType) {
++            Class<?> rtype = newType.returnType();
++            assert(!rtype.isPrimitive() && !mh.type().returnType().isPrimitive());
++            assert(!rtype.isAssignableFrom(mh.type().returnType()));  // else use viewAsType
++            assert(mh.type().changeReturnType(rtype).isViewableAs(newType, true));
++            // add a transform to the end of mh's LambdaForm
++            MethodHandle conv = valueConversion(Object.class, rtype, true);
++            MethodHandle result = MethodHandleImpl.makeCollectArguments(conv, mh, 0, false);
++            // FIXME: argument types should be rolled into makeCollectArguments
++            result = result.viewAsType(newType, true);
++            return result;
++        }
++    }
++
++    static MethodHandle valueConversion(Class<?> src, Class<?> dst, boolean strict) {
++        MethodHandle conv = MethodHandleImpl.valueConversion(src, dst, strict, /*monobox=*/ false);
++        if (conv != null)  return conv;
++        assert(!src.isPrimitive() && !dst.isPrimitive());
++        MethodHandle[] cache = REFERENCE_CONVERTERS.get(dst);
++        int cacheIndex = (strict ? CV_STRICT_TO_TYPE : CV_CONVERT_TO_TYPE);
++        conv = cache[cacheIndex];
++        if (conv != null)  return conv;
++        if (strict) {
++            conv = ValueConversions.cast().bindTo(dst);
++        } else if (dst.isInterface()) {
++            conv = MethodHandles.identity(Object.class);
++        } else {
++            conv = valueConversion(Object.class, dst, true);  // cache in other place
++        }
++        MethodType mt = MethodType.methodType(dst, Object.class);
++        if (conv.type() != mt)  conv = conv.viewAsType(mt, false);
++        return cache[cacheIndex] = conv;
++    }
++
++    private static final ClassValue<MethodHandle[]> REFERENCE_CONVERTERS
++        = new ClassValue<MethodHandle[]>() {
++            @Override
++            protected MethodHandle[] computeValue(Class<?> type) {
++                return new MethodHandle[CV_LIMIT];
++            }
++    };
++    static final int
++            CV_CONVERT_TO_TYPE = 0,  // Object -> T, strict asType conversions
++            CV_STRICT_TO_TYPE  = 1,  // Object -> T, all conversions, no interface check
++            CV_LIMIT           = 2;
++
++    /** Dumb slow implementation.  Always spins a new lambda form. */
++    private static final class Slow extends TypeChanger {
++        final boolean strict;
++        public Slow(boolean strict) {
++            this.strict = strict;
++        }
++        @Override MethodHandle changeType(MethodHandle mh, MethodType newType) {
++            return MethodHandleImpl.makePairwiseConvert(mh, newType, strict);
++        }
++        static final TypeChanger STRICT = new Slow(true);
++        static final TypeChanger NON_STRICT = new Slow(false);
++    }
++}
+diff --git a/src/share/classes/sun/invoke/util/ValueConversions.java b/src/share/classes/sun/invoke/util/ValueConversions.java
+--- a/src/share/classes/sun/invoke/util/ValueConversions.java
++++ b/src/share/classes/sun/invoke/util/ValueConversions.java
+@@ -71,63 +71,93 @@
+     //   implicit conversions sanctioned by JLS 5.1.2, etc.
+     //   explicit conversions as allowed by explicitCastArguments
+ 
++    static int unboxInteger(Integer x) {
++        return x;
++    }
+     static int unboxInteger(Object x, boolean cast) {
+         if (x instanceof Integer)
+-            return ((Integer) x).intValue();
++            return (Integer) x;
+         return primitiveConversion(Wrapper.INT, x, cast).intValue();
+     }
+ 
++    static byte unboxByte(Byte x) {
++        return x;
++    }
+     static byte unboxByte(Object x, boolean cast) {
+         if (x instanceof Byte)
+-            return ((Byte) x).byteValue();
++            return (Byte) x;
+         return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
+     }
+ 
++    static short unboxShort(Short x) {
++        return x;
++    }
+     static short unboxShort(Object x, boolean cast) {
+         if (x instanceof Short)
+-            return ((Short) x).shortValue();
++            return (Short) x;
+         return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
+     }
+ 
++    static boolean unboxBoolean(Boolean x) {
++        return x;
++    }
+     static boolean unboxBoolean(Object x, boolean cast) {
+         if (x instanceof Boolean)
+-            return ((Boolean) x).booleanValue();
++            return (Boolean) x;
+         return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
+     }
+ 
++    static char unboxCharacter(Character x) {
++        return x;
++    }
+     static char unboxCharacter(Object x, boolean cast) {
+         if (x instanceof Character)
+-            return ((Character) x).charValue();
++            return (Character) x;
+         return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
+     }
+ 
++    static long unboxLong(Long x) {
++        return x;
++    }
+     static long unboxLong(Object x, boolean cast) {
+         if (x instanceof Long)
+-            return ((Long) x).longValue();
++            return (Long) x;
+         return primitiveConversion(Wrapper.LONG, x, cast).longValue();
+     }
+ 
++    static float unboxFloat(Float x) {
++        return x;
++    }
+     static float unboxFloat(Object x, boolean cast) {
+         if (x instanceof Float)
+-            return ((Float) x).floatValue();
++            return (Float) x;
+         return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
+     }
+ 
++    static double unboxDouble(Double x) {
++        return x;
++    }
+     static double unboxDouble(Object x, boolean cast) {
+         if (x instanceof Double)
+-            return ((Double) x).doubleValue();
++            return (Double) x;
+         return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
+     }
+ 
+-    private static MethodType unboxType(Wrapper wrap) {
++    private static MethodType unboxType(Wrapper wrap, int kind) {
++        if (kind == 0)
++            return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType());
+         return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
+     }
+ 
+     private static final EnumMap<Wrapper, MethodHandle>[]
+-            UNBOX_CONVERSIONS = newWrapperCaches(2);
++            UNBOX_CONVERSIONS = newWrapperCaches(4);
+ 
+-    private static MethodHandle unbox(Wrapper wrap, boolean cast) {
+-        EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)];
++    private static MethodHandle unbox(Wrapper wrap, int kind) {
++        // kind 0 -> strongly typed with NPE
++        // kind 1 -> strongly typed but zero for null,
++        // kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE
++        // kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null
++        EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[kind];
+         MethodHandle mh = cache.get(wrap);
+         if (mh != null) {
+             return mh;
+@@ -135,41 +165,55 @@
+         // slow path
+         switch (wrap) {
+             case OBJECT:
+-                mh = IDENTITY; break;
+             case VOID:
+-                mh = IGNORE; break;
+-        }
+-        if (mh != null) {
+-            cache.put(wrap, mh);
+-            return mh;
++                throw new IllegalArgumentException("unbox "+wrap);
+         }
+         // look up the method
+         String name = "unbox" + wrap.wrapperSimpleName();
+-        MethodType type = unboxType(wrap);
++        MethodType type = unboxType(wrap, kind);
+         try {
+             mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+         } catch (ReflectiveOperationException ex) {
+             mh = null;
+         }
+         if (mh != null) {
+-            mh = MethodHandles.insertArguments(mh, 1, cast);
++            if (kind > 0) {
++                boolean cast = (kind != 2);
++                mh = MethodHandles.insertArguments(mh, 1, cast);
++            }
++            if (kind == 1) {  // casting but exact (null -> zero)
++                mh = mh.asType(unboxType(wrap, 0));
++            }
+             cache.put(wrap, mh);
+             return mh;
+         }
+         throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
+-                + (cast ? " (cast)" : ""));
++                + (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : ""));
+     }
+ 
+-    public static MethodHandle unboxCast(Wrapper type) {
+-        return unbox(type, true);
++    /** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.
++     *  The boolean says whether to throw an NPE on a null value (false means unbox a zero).
++     *  The type of the unboxer is of a form like (Integer)int.
++     */
++    public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) {
++        return unbox(type, throwNPE ? 0 : 1);
+     }
+ 
+-    public static MethodHandle unbox(Class<?> type) {
+-        return unbox(Wrapper.forPrimitiveType(type), false);
++    /** Return a widening unboxer for the given primitive type.
++     *  Widen narrower primitive boxes to the given type.
++     *  Do not narrow any primitive values or convert null to zero.
++     *  The type of the unboxer is of a form like (Object)int.
++     */
++    public static MethodHandle unboxWiden(Wrapper type) {
++        return unbox(type, 2);
+     }
+ 
+-    public static MethodHandle unboxCast(Class<?> type) {
+-        return unbox(Wrapper.forPrimitiveType(type), true);
++    /** Return a casting unboxer for the given primitive type.
++     *  Widen or narrow primitive values to the given type, or convert null to zero, as needed.
++     *  The type of the unboxer is of a form like (Object)int.
++     */
++    public static MethodHandle unboxCast(Wrapper type) {
++        return unbox(type, 3);
+     }
+ 
+     static private final Integer ZERO_INT = 0, ONE_INT = 1;
+@@ -267,64 +311,31 @@
+     }
+ 
+     private static final EnumMap<Wrapper, MethodHandle>[]
+-            BOX_CONVERSIONS = newWrapperCaches(2);
++            BOX_CONVERSIONS = newWrapperCaches(1);
+ 
+-    private static MethodHandle box(Wrapper wrap, boolean exact) {
+-        EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)];
++    public static MethodHandle boxExact(Wrapper wrap) {
++        EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[0];
+         MethodHandle mh = cache.get(wrap);
+         if (mh != null) {
+             return mh;
+         }
+-        // slow path
+-        switch (wrap) {
+-            case OBJECT:
+-                mh = IDENTITY; break;
+-            case VOID:
+-                mh = ZERO_OBJECT;
+-                break;
++        // look up the method
++        String name = "box" + wrap.wrapperSimpleName();
++        MethodType type = boxType(wrap);
++        try {
++            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
++        } catch (ReflectiveOperationException ex) {
++            mh = null;
+         }
+         if (mh != null) {
+             cache.put(wrap, mh);
+             return mh;
+         }
+-        // look up the method
+-        String name = "box" + wrap.wrapperSimpleName();
+-        MethodType type = boxType(wrap);
+-        if (exact) {
+-            try {
+-                mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+-            } catch (ReflectiveOperationException ex) {
+-                mh = null;
+-            }
+-        } else {
+-            mh = box(wrap, !exact).asType(type.erase());
+-        }
+-        if (mh != null) {
+-            cache.put(wrap, mh);
+-            return mh;
+-        }
+-        throw new IllegalArgumentException("cannot find box adapter for "
+-                + wrap + (exact ? " (exact)" : ""));
+-    }
+-
+-    public static MethodHandle box(Class<?> type) {
+-        boolean exact = false;
+-        // e.g., boxShort(short)Short if exact,
+-        // e.g., boxShort(short)Object if !exact
+-        return box(Wrapper.forPrimitiveType(type), exact);
+-    }
+-
+-    public static MethodHandle box(Wrapper type) {
+-        boolean exact = false;
+-        return box(type, exact);
++        throw new IllegalArgumentException("cannot find box adapter for " + wrap);
+     }
+ 
+     /// Constant functions
+ 
+-    static void ignore(Object x) {
+-        // no value to return; this is an unbox of null
+-    }
+-
+     static void empty() {
+     }
+ 
+@@ -389,61 +400,6 @@
+     /// Converting references to references.
+ 
+     /**
+-     * Identity function.
+-     * @param x an arbitrary reference value
+-     * @return the same value x
+-     */
+-    static <T> T identity(T x) {
+-        return x;
+-    }
+-
+-    static <T> T[] identity(T[] x) {
+-        return x;
+-    }
+-
+-    /**
+-     * Identity function on ints.
+-     * @param x an arbitrary int value
+-     * @return the same value x
+-     */
+-    static int identity(int x) {
+-        return x;
+-    }
+-
+-    static byte identity(byte x) {
+-        return x;
+-    }
+-
+-    static short identity(short x) {
+-        return x;
+-    }
+-
+-    static boolean identity(boolean x) {
+-        return x;
+-    }
+-
+-    static char identity(char x) {
+-        return x;
+-    }
+-
+-    /**
+-     * Identity function on longs.
+-     * @param x an arbitrary long value
+-     * @return the same value x
+-     */
+-    static long identity(long x) {
+-        return x;
+-    }
+-
+-    static float identity(float x) {
+-        return x;
+-    }
+-
+-    static double identity(double x) {
+-        return x;
+-    }
+-
+-    /**
+      * Identity function, with reference cast.
+      * @param t an arbitrary reference type
+      * @param x an arbitrary reference value
+@@ -461,7 +417,7 @@
+         return new ClassCastException("Cannot cast " + obj.getClass().getName() + " to " + t.getName());
+     }
+ 
+-    private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY,
++    private static final MethodHandle CAST_REFERENCE, EMPTY,
+             ARRAY_IDENTITY, FILL_NEW_TYPED_ARRAY, FILL_NEW_ARRAY;
+     static {
+         try {
+@@ -469,13 +425,10 @@
+             MethodType castType = idType.insertParameterTypes(0, Class.class);
+             MethodType ignoreType = idType.changeReturnType(void.class);
+             MethodType zeroObjectType = MethodType.genericMethodType(0);
+-            IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
+             //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
+             CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType);
+-            ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType);
+-            IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
+             EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
+-            ARRAY_IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(Object[].class, Object[].class));
++            ARRAY_IDENTITY = MethodHandles.identity(Object[].class);
+             FILL_NEW_ARRAY = IMPL_LOOKUP
+                     .findStatic(THIS_CLASS, "fillNewArray",
+                           MethodType.methodType(Object[].class, Integer.class, Object[].class));
+@@ -489,11 +442,10 @@
+ 
+     // Varargs methods need to be in a separately initialized class, to avoid bootstrapping problems.
+     static class LazyStatics {
+-        private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST;
++        private static final MethodHandle COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST;
+         static {
+             try {
+                 //MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class));
+-                COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class));
+                 COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class));
+                 MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class));
+             } catch (ReflectiveOperationException ex) {
+@@ -547,68 +499,10 @@
+         COLLECT_ARGUMENTS = mh;
+     }
+ 
+-    private static final EnumMap<Wrapper, MethodHandle>[] WRAPPER_CASTS
+-            = newWrapperCaches(1);
+-
+-    /** Return a method that casts its sole argument (an Object) to the given type
+-     *  and returns it as the given type.
++    /** Return a method that casts its second argument (an Object) to the given type (a Class).
+      */
+-    public static MethodHandle cast(Class<?> type) {
+-        if (type.isPrimitive())  throw new IllegalArgumentException("cannot cast primitive type "+type);
+-        MethodHandle mh;
+-        Wrapper wrap = null;
+-        EnumMap<Wrapper, MethodHandle> cache = null;
+-        if (Wrapper.isWrapperType(type)) {
+-            wrap = Wrapper.forWrapperType(type);
+-            cache = WRAPPER_CASTS[0];
+-            mh = cache.get(wrap);
+-            if (mh != null)  return mh;
+-        }
+-        mh = MethodHandles.insertArguments(CAST_REFERENCE, 0, type);
+-        if (cache != null)
+-            cache.put(wrap, mh);
+-        return mh;
+-    }
+-
+-    public static MethodHandle identity() {
+-        return IDENTITY;
+-    }
+-
+-    public static MethodHandle identity(Class<?> type) {
+-        if (!type.isPrimitive())
+-            // Reference identity has been moved into MethodHandles:
+-            return MethodHandles.identity(type);
+-        return identity(Wrapper.findPrimitiveType(type));
+-    }
+-
+-    public static MethodHandle identity(Wrapper wrap) {
+-        EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[1];
+-        MethodHandle mh = cache.get(wrap);
+-        if (mh != null) {
+-            return mh;
+-        }
+-        // slow path
+-        MethodType type = MethodType.methodType(wrap.primitiveType());
+-        if (wrap != Wrapper.VOID)
+-            type = type.appendParameterTypes(wrap.primitiveType());
+-        try {
+-            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type);
+-        } catch (ReflectiveOperationException ex) {
+-            mh = null;
+-        }
+-        if (mh == null && wrap == Wrapper.VOID) {
+-            mh = EMPTY;  // #(){} : #()void
+-        }
+-        if (mh != null) {
+-            cache.put(wrap, mh);
+-            return mh;
+-        }
+-
+-        if (mh != null) {
+-            cache.put(wrap, mh);
+-            return mh;
+-        }
+-        throw new IllegalArgumentException("cannot find identity for " + wrap);
++    public static MethodHandle cast() {
++        return CAST_REFERENCE;
+     }
+ 
+     /// Primitive conversions.
+@@ -831,15 +725,7 @@
+         Class<?> dst = wdst.primitiveType();
+         MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src);
+         if (wsrc == wdst) {
+-            mh = identity(src);
+-        } else if (wsrc == Wrapper.VOID) {
+-            mh = zeroConstantFunction(wdst);
+-        } else if (wdst == Wrapper.VOID) {
+-            mh = MethodHandles.dropArguments(EMPTY, 0, src);  // Defer back to MethodHandles.
+-        } else if (wsrc == Wrapper.OBJECT) {
+-            mh = unboxCast(dst);
+-        } else if (wdst == Wrapper.OBJECT) {
+-            mh = box(src);
++            mh = MethodHandles.identity(src);
+         } else {
+             assert(src.isPrimitive() && dst.isPrimitive());
+             try {
+@@ -868,36 +754,6 @@
+ 
+     /// Collection of multiple arguments.
+ 
+-    public static Object convertArrayElements(Class<?> arrayType, Object array) {
+-        Class<?> src = array.getClass().getComponentType();
+-        Class<?> dst = arrayType.getComponentType();
+-        if (src == null || dst == null)  throw new IllegalArgumentException("not array type");
+-        Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null);
+-        Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null);
+-        int length;
+-        if (sw == null) {
+-            Object[] a = (Object[]) array;
+-            length = a.length;
+-            if (dw == null)
+-                return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class));
+-            Object res = dw.makeArray(length);
+-            dw.copyArrayUnboxing(a, 0, res, 0, length);
+-            return res;
+-        }
+-        length = java.lang.reflect.Array.getLength(array);
+-        Object[] res;
+-        if (dw == null) {
+-            res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class));
+-        } else {
+-            res = new Object[length];
+-        }
+-        sw.copyArrayBoxing(array, 0, res, 0, length);
+-        if (dw == null)  return res;
+-        Object a = dw.makeArray(length);
+-        dw.copyArrayUnboxing(res, 0, a, 0, length);
+-        return a;
+-    }
+-
+     private static MethodHandle findCollector(String name, int nargs, Class<?> rtype, Class<?>... ptypes) {
+         MethodType type = MethodType.genericMethodType(nargs)
+                 .changeReturnType(rtype)
+@@ -961,6 +817,7 @@
+     }
+     private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) {
+         Object[] a = Arrays.copyOf(example, len);
++        assert(a.getClass() != Object[].class);
+         fillWithArguments(a, 0, args);
+         return a;
+     }
+@@ -1009,9 +866,6 @@
+     }
+     private static final MethodHandle[] FILL_ARRAYS = makeFillArrays();
+ 
+-    private static Object[] copyAsReferenceArray(Class<? extends Object[]> arrayType, Object... a) {
+-        return Arrays.copyOf(a, a.length, arrayType);
+-    }
+     private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
+         Object a = w.makeArray(boxes.length);
+         w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length);
+@@ -1137,16 +991,18 @@
+         MethodHandle cache[] = TYPED_COLLECTORS.get(elemType);
+         MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
+         if (mh != null)  return mh;
+-        if (elemType.isPrimitive()) {
++        if (nargs == 0) {
++            Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0);
++            mh = MethodHandles.constant(arrayType, example);
++        } else if (elemType.isPrimitive()) {
+             MethodHandle builder = FILL_NEW_ARRAY;
+             MethodHandle producer = buildArrayProducer(arrayType);
+             mh = buildVarargsArray(builder, producer, nargs);
+         } else {
+-            @SuppressWarnings("unchecked")
+-            Class<? extends Object[]> objArrayType = (Class<? extends Object[]>) arrayType;
++            Class<? extends Object[]> objArrayType = arrayType.asSubclass(Object[].class);
+             Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType);
+             MethodHandle builder = FILL_NEW_TYPED_ARRAY.bindTo(example);
+-            MethodHandle producer = ARRAY_IDENTITY;
++            MethodHandle producer = ARRAY_IDENTITY;  // must be weakly typed
+             mh = buildVarargsArray(builder, producer, nargs);
+         }
+         mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
+@@ -1156,12 +1012,19 @@
+         return mh;
+     }
+ 
++    public static Object emptyArray(Class<?> arrayType) {
++        if (arrayType == Object[].class)  return NO_ARGS_ARRAY;
++        try {
++            return varargsArray(arrayType, 0).invoke();
++        } catch (Throwable ex) {
++            throw newInternalError(ex);
++        }
++    }
++
+     private static MethodHandle buildArrayProducer(Class<?> arrayType) {
+         Class<?> elemType = arrayType.getComponentType();
+-        if (elemType.isPrimitive())
+-            return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType));
+-        else
+-            return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType);
++        assert(elemType.isPrimitive());
++        return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType));
+     }
+ 
+     // List version of varargs maker.
+diff --git a/src/share/classes/sun/invoke/util/VerifyType.java b/src/share/classes/sun/invoke/util/VerifyType.java
+--- a/src/share/classes/sun/invoke/util/VerifyType.java
++++ b/src/share/classes/sun/invoke/util/VerifyType.java
+@@ -45,11 +45,13 @@
+      * @param recv the type by which we'd like to treat it
+      * @return whether the retyping can be done without motion or reformatting
+      */
+-    public static boolean isNullConversion(Class<?> src, Class<?> dst) {
++    public static boolean isNullConversion(Class<?> src, Class<?> dst, boolean keepInterfaces) {
+         if (src == dst)            return true;
+         // Verifier allows any interface to be treated as Object:
+-        if (dst.isInterface())     dst = Object.class;
+-        if (src.isInterface())     src = Object.class;
++        if (!keepInterfaces) {
++            if (dst.isInterface())  dst = Object.class;
++            if (src.isInterface())  src = Object.class;
++        }
+         if (src == dst)            return true;  // check again
+         if (dst == void.class)     return true;  // drop any return value
+         if (isNullType(src))       return !dst.isPrimitive();
+@@ -112,14 +114,14 @@
+      * @param recv the type of the method handle receiving the call
+      * @return whether the retyping can be done without motion or reformatting
+      */
+-    public static boolean isNullConversion(MethodType call, MethodType recv) {
++    public static boolean isNullConversion(MethodType call, MethodType recv, boolean keepInterfaces) {
+         if (call == recv)  return true;
+         int len = call.parameterCount();
+         if (len != recv.parameterCount())  return false;
+         for (int i = 0; i < len; i++)
+-            if (!isNullConversion(call.parameterType(i), recv.parameterType(i)))
++            if (!isNullConversion(call.parameterType(i), recv.parameterType(i), keepInterfaces))
+                 return false;
+-        return isNullConversion(recv.returnType(), call.returnType());
++        return isNullConversion(recv.returnType(), call.returnType(), keepInterfaces);
+     }
+ 
+     /**
+diff --git a/src/share/classes/sun/invoke/util/Wrapper.java b/src/share/classes/sun/invoke/util/Wrapper.java
+--- a/src/share/classes/sun/invoke/util/Wrapper.java
++++ b/src/share/classes/sun/invoke/util/Wrapper.java
+@@ -230,14 +230,6 @@
+      */
+     public <T> T zero(Class<T> type) { return convert(zero, type); }
+ 
+-//    /** Produce a wrapper for the given wrapper or primitive type. */
+-//    public static Wrapper valueOf(Class<?> type) {
+-//        if (isPrimitiveType(type))
+-//            return forPrimitiveType(type);
+-//        else
+-//            return forWrapperType(type);
+-//    }
+-
+     /** Return the wrapper that wraps values of the given type.
+      *  The type may be {@code Object}, meaning the {@code OBJECT} wrapper.
+      *  Otherwise, the type must be a primitive.
+diff --git a/test/java/lang/invoke/JavaDocExamplesTest.java b/test/java/lang/invoke/JavaDocExamplesTest.java
+--- a/test/java/lang/invoke/JavaDocExamplesTest.java
++++ b/test/java/lang/invoke/JavaDocExamplesTest.java
+@@ -82,7 +82,6 @@
+     static final Class<?> THIS_CLASS = JavaDocExamplesTest.class;
+     static int verbosity = Integer.getInteger(THIS_CLASS.getSimpleName()+".verbosity", 0);
+ 
+-
+ {}
+ static final private Lookup LOOKUP = lookup();
+ // static final private MethodHandle CONCAT_1 = LOOKUP.findVirtual(String.class,
+diff --git a/test/java/lang/invoke/MethodHandlesTest.java b/test/java/lang/invoke/MethodHandlesTest.java
+--- a/test/java/lang/invoke/MethodHandlesTest.java
++++ b/test/java/lang/invoke/MethodHandlesTest.java
+@@ -2050,15 +2050,23 @@
+                     else
+                         type = type.changeParameterType(j, argType);
+                     if (done.add(type))
+-                        testInvokers(type);
++                        testInvokersWithCatch(type);
+                     MethodType vtype = type.changeReturnType(void.class);
+                     if (done.add(vtype))
+-                        testInvokers(vtype);
++                        testInvokersWithCatch(vtype);
+                 }
+             }
+         }
+     }
+ 
++    public void testInvokersWithCatch(MethodType type) throws Throwable {
++        try {
++            testInvokers(type);
++        } catch (Throwable ex) {
++            System.out.println("*** testInvokers on "+type+" => ");
++            ex.printStackTrace(System.out);
++        }
++    }
+     public void testInvokers(MethodType type) throws Throwable {
+         if (verbosity >= 3)
+             System.out.println("test invokers for "+type);
+diff --git a/test/sun/invoke/util/ValueConversionsTest.java b/test/sun/invoke/util/ValueConversionsTest.java
+--- a/test/sun/invoke/util/ValueConversionsTest.java
++++ b/test/sun/invoke/util/ValueConversionsTest.java
+@@ -28,6 +28,7 @@
+ import java.lang.invoke.MethodType;
+ import java.lang.invoke.MethodHandle;
+ import java.io.Serializable;
++import java.lang.invoke.MethodHandles;
+ import java.util.Arrays;
+ import java.util.Collections;
+ import org.junit.Test;
+@@ -55,6 +56,15 @@
+     private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0);
+     private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE");
+ 
++    public static void main(String... av) throws Throwable {
++        new ValueConversionsTest().testFirst();
++    }
++
++    @Test
++    public void testFirst() throws Throwable {
++        testTypedVarargsArray(String[].class);
++    }
++
+     @Test
+     public void testUnbox() throws Throwable {
+         testUnbox(false);
+@@ -78,6 +88,7 @@
+     private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable {
+         boolean expectThrow = !doCast && !dst.isConvertibleFrom(src);
+         if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT)  return;  // must have prims
++        if (dst == Wrapper.VOID   || src == Wrapper.VOID  )  return;  // must have values
+         if (dst == Wrapper.OBJECT)
+             expectThrow = false;  // everything (even VOID==null here) converts to OBJECT
+         try {
+@@ -91,9 +102,9 @@
+                 }
+                 MethodHandle unboxer;
+                 if (doCast)
+-                    unboxer = ValueConversions.unboxCast(dst.primitiveType());
++                    unboxer = ValueConversions.unboxCast(dst);
+                 else
+-                    unboxer = ValueConversions.unbox(dst.primitiveType());
++                    unboxer = ValueConversions.unboxWiden(dst);
+                 Object expResult = (box == null) ? dst.zero() : dst.wrap(box);
+                 Object result = null;
+                 switch (dst) {
+@@ -104,9 +115,7 @@
+                     case CHAR:    result = (char)    unboxer.invokeExact(box); break;
+                     case BYTE:    result = (byte)    unboxer.invokeExact(box); break;
+                     case SHORT:   result = (short)   unboxer.invokeExact(box); break;
+-                    case OBJECT:  result = (Object)  unboxer.invokeExact(box); break;
+                     case BOOLEAN: result = (boolean) unboxer.invokeExact(box); break;
+-                    case VOID:    result = null;     unboxer.invokeExact(box); break;
+                 }
+                 if (expectThrow) {
+                     expResult = "(need an exception)";
+@@ -125,23 +134,23 @@
+     public void testBox() throws Throwable {
+         //System.out.println("box");
+         for (Wrapper w : Wrapper.values()) {
+-            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
++            if (w == Wrapper.VOID)    continue;  // skip this; no unboxed form
++            if (w == Wrapper.OBJECT)  continue;  // skip this; already unboxed
+             //System.out.println(w);
+             for (int n = -5; n < 10; n++) {
+                 Object box = w.wrap(n);
+-                MethodHandle boxer = ValueConversions.box(w.primitiveType());
++                MethodHandle boxer = ValueConversions.boxExact(w);
+                 Object expResult = box;
+                 Object result = null;
+                 switch (w) {
+-                    case INT:     result = boxer.invokeExact(/*int*/n); break;
+-                    case LONG:    result = boxer.invokeExact((long)n); break;
+-                    case FLOAT:   result = boxer.invokeExact((float)n); break;
+-                    case DOUBLE:  result = boxer.invokeExact((double)n); break;
+-                    case CHAR:    result = boxer.invokeExact((char)n); break;
+-                    case BYTE:    result = boxer.invokeExact((byte)n); break;
+-                    case SHORT:   result = boxer.invokeExact((short)n); break;
+-                    case OBJECT:  result = boxer.invokeExact((Object)n); break;
+-                    case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break;
++                    case INT:     result = (Integer) boxer.invokeExact(/*int*/n); break;
++                    case LONG:    result = (Long)    boxer.invokeExact((long)n); break;
++                    case FLOAT:   result = (Float)   boxer.invokeExact((float)n); break;
++                    case DOUBLE:  result = (Double)  boxer.invokeExact((double)n); break;
++                    case CHAR:    result = (Character) boxer.invokeExact((char)n); break;
++                    case BYTE:    result = (Byte)    boxer.invokeExact((byte)n); break;
++                    case SHORT:   result = (Short)   boxer.invokeExact((short)n); break;
++                    case BOOLEAN: result = (Boolean) boxer.invokeExact((n & 1) != 0); break;
+                 }
+                 assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+                              expResult, result);
+@@ -155,8 +164,8 @@
+         Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class };
+         Object[] objects = { new Object(), Boolean.FALSE,      "hello",      (Long)12L,    (Integer)6    };
+         for (Class<?> dst : types) {
+-            MethodHandle caster = ValueConversions.cast(dst);
+-            assertEquals(caster.type(), ValueConversions.identity().type());
++            MethodHandle caster = ValueConversions.cast().bindTo(dst);
++            assertEquals(caster.type(), MethodHandles.identity(Object.class).type());
+             for (Object obj : objects) {
+                 Class<?> src = obj.getClass();
+                 boolean canCast = dst.isAssignableFrom(src);
+@@ -176,16 +185,6 @@
+     }
+ 
+     @Test
+-    public void testIdentity() throws Throwable {
+-        //System.out.println("identity");
+-        MethodHandle id = ValueConversions.identity();
+-        Object expResult = "foo";
+-        Object result = id.invokeExact(expResult);
+-        // compiler bug:  ValueConversions.identity().invokeExact("bar");
+-        assertEquals(expResult, result);
+-    }
+-
+-    @Test
+     public void testConvert() throws Throwable {
+         //System.out.println("convert");
+         for (long tval = 0, ctr = 0;;) {
+@@ -206,14 +205,12 @@
+     }
+     static void testConvert(Wrapper src, Wrapper dst, long tval) throws Throwable {
+         //System.out.println(src+" => "+dst);
++        if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT)  return;  // must have prims
++        if (dst == Wrapper.VOID   || src == Wrapper.VOID  )  return;  // must have values
+         boolean testSingleCase = (tval != 0);
+         final long tvalInit = tval;
+         MethodHandle conv = ValueConversions.convertPrimitive(src, dst);
+-        MethodType convType;
+-        if (src == Wrapper.VOID)
+-            convType = MethodType.methodType(dst.primitiveType() /* , void */);
+-        else
+-            convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
++        MethodType convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
+         assertEquals(convType, conv.type());
+         MethodHandle converter = conv.asType(conv.type().changeReturnType(Object.class));
+         for (;;) {
+@@ -229,9 +226,7 @@
+                 case CHAR:    result = converter.invokeExact((char)n); break;
+                 case BYTE:    result = converter.invokeExact((byte)n); break;
+                 case SHORT:   result = converter.invokeExact((short)n); break;
+-                case OBJECT:  result = converter.invokeExact((Object)n); break;
+                 case BOOLEAN: result = converter.invokeExact((n & 1) != 0); break;
+-                case VOID:    result = converter.invokeExact(); break;
+                 default:  throw new AssertionError();
+             }
+             assertEquals("(src,dst,n,testValue)="+Arrays.asList(src,dst,"0x"+Long.toHexString(n),testValue),
+@@ -341,6 +336,7 @@
+             assertEquals(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)),
+                          vaType);
+             Object res = varargsArray.invokeWithArguments(args);
++            assertEquals(res.getClass(), arrayType);
+             String resString = toArrayString(res);
+             assertEquals(Arrays.toString(args), resString);
+ 
--- a/series	Sat Sep 14 19:59:07 2013 -0700
+++ b/series	Sun Sep 15 17:15:12 2013 -0700
@@ -10,23 +10,23 @@
 
 # review pending before push to hotspot-comp (or tl):
 indy-bsm-8024761.patch          #-/meth #+b67c8099ba28
+meth-aclone-8001105.patch       #-/meth #+b67c8099ba28
+meth-counts.patch               #-/meth #+b67c8099ba28
+meth-lfc.patch                  #-/meth #+b67c8099ba28
 
 # spec maintenance
-meth-arity-8019417.patch        #-/meth #+b67c8099ba28
-meth-spr-8001109.patch          #-/meth #+b67c8099ba28
-meth-nsme-8001108.patch         #-/meth #+b67c8099ba28
-meth-clinit-8024599.patch       #-/meth #+b67c8099ba28
-meth-coll-8001110.patch         #-/meth #+b67c8099ba28
-meth-mr-8024438.patch           #-/meth #+b67c8099ba28
+meth-arity-8019417.patch        #-/meth #+b67c8099ba28+jdk8spec
+meth-spr-8001109.patch          #-/meth #+b67c8099ba28+jdk8spec
+meth-nsme-8001108.patch         #-/meth #+b67c8099ba28+jdk8spec
+meth-clinit-8024599.patch       #-/meth #+b67c8099ba28+jdk8spec
+meth-coll-8001110.patch         #-/meth #+b67c8099ba28+jdk8spec
+meth-mr-8024438.patch           #-/meth #+b67c8099ba28+jdk8spec
 
-meth-aclone-8001105.patch       #-/meth #+b67c8099ba28
-meth-catch-8005625.patch        #-/meth #+b67c8099ba28
 meth.patch                      #-/meth #+b67c8099ba28
 
 # non-pushed files are under review or development, or merely experimental:
 anno-stable-8001107.patch       #-/meth #+b67c8099ba28 #-testable
 meth-lfi-8001106.patch          #-/meth #+b67c8099ba28 #-testable
-indy-bsm-8005626.patch          #-/meth #+b67c8099ba28 #-testable
 
 # Keep these separate, for debugging and review:
 dyncast.patch   #+dyncast       #-/dyncast