changeset 421:0cd0e35d7fe6

meth-lazy: consolidate for review
author jrose
date Mon, 23 Jul 2012 10:44:17 -0700
parents 3b3f4347ab7e
children 1bb873eba051
files meth-lazy-7023639.patch meth-lazy-7023639.review.patch series
diffstat 3 files changed, 2579 insertions(+), 6437 deletions(-) [+]
line wrap: on
line diff
--- a/meth-lazy-7023639.patch	Sat Jul 21 16:55:21 2012 -0700
+++ b/meth-lazy-7023639.patch	Mon Jul 23 10:44:17 2012 -0700
@@ -1,7 +1,7 @@
 7023639: JSR 292 method handle invocation needs a fast path for compiled code
 6984705: JSR 292 method handle creation should not go through JNI
 Summary: remove assembly code for JDK 7 chained method handles
-Reviewed-by: jrose, twisti, mhaupt
+Reviewed-by: jrose, twisti, mhaupt, forax, David Schlosnagle, Aleksey Shipilev
 Contributed-by: jrose, twisti, mhaupt
 
 diff --git a/src/share/classes/java/lang/invoke/AdapterMethodHandle.java b/src/share/classes/java/lang/invoke/AdapterMethodHandle.java
@@ -1223,48 +1223,30 @@
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
-@@ -25,164 +25,819 @@
+@@ -25,164 +25,828 @@
  
  package java.lang.invoke;
  
 -import sun.invoke.util.VerifyType;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ACC_FINAL;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ACC_PUBLIC;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ACC_STATIC;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ACC_SUPER;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ALOAD;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ARETURN;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.DLOAD;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.FLOAD;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.GETFIELD;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ILOAD;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.INVOKESPECIAL;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.INVOKESTATIC;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.LLOAD;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.PUTFIELD;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.RETURN;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.V1_6;
-+import static java.lang.invoke.LambdaForm.arguments;
++import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.*;
 +import static java.lang.invoke.LambdaForm.basicTypes;
 +import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
-+import static java.lang.invoke.MethodType.methodType;
++import static java.lang.invoke.MethodHandleStatics.*;
 +
 +import java.lang.invoke.LambdaForm.Name;
 +import java.lang.invoke.LambdaForm.NamedFunction;
 +import java.lang.invoke.MethodHandles.Lookup;
 +import java.lang.reflect.Field;
 +import java.util.Arrays;
-+import java.util.IdentityHashMap;
-+import java.util.Map;
++import java.util.HashMap;
 +
 +import sun.invoke.util.ValueConversions;
  import sun.invoke.util.Wrapper;
 -import static java.lang.invoke.MethodHandleStatics.*;
-+import sun.misc.Unsafe;
 +
 +import com.sun.xml.internal.ws.org.objectweb.asm.ClassWriter;
 +import com.sun.xml.internal.ws.org.objectweb.asm.MethodVisitor;
++import com.sun.xml.internal.ws.org.objectweb.asm.Type;
  
  /**
   * The flavor of method handle which emulates an invoke instruction
@@ -1310,19 +1292,17 @@
 +        // for some type signatures, there exist pre-defined concrete BMH classes
 +        try {
 +            switch (xtype) {
-+            case 'L': return new BMH_L(type, form, x);
++            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':
-+                assert x instanceof Integer : "expected Integer, found " + x.getClass();
-+                return (BoundMethodHandle) Data.get("I").constructor.invokeBasic(type, form, ((Integer) x).intValue());
++                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('I').constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
 +            case 'J':
-+                assert x instanceof Long;
-+                return new BMH_J(type, form, ((Long) x).longValue());
++                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('J').constructor[0].invokeBasic(type, form, (long) x);
 +            case 'F':
-+                assert x instanceof Float;
-+                return (BoundMethodHandle) Data.get("F").constructor.invokeBasic(type, form, ((Float) x).floatValue());
++                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('F').constructor[0].invokeBasic(type, form, (float) x);
 +            case 'D':
-+                assert x instanceof Double;
-+                return (BoundMethodHandle) Data.get("D").constructor.invokeBasic(type, form, ((Double) x).doubleValue());
++                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('D').constructor[0].invokeBasic(type, form, (double) x);
 +            default : throw new InternalError("unexpected xtype: " + xtype);
 +            }
 +        } catch (Throwable t) {
@@ -1339,31 +1319,13 @@
 -            this.argument = bindPrimitiveArgument(argument, mh, argnum);
 -        else {
 -            this.argument = checkReferenceArgument(argument, mh, argnum);
-+    MethodHandle cloneExtend(MethodType type, LambdaForm form, char xtype, Object x) {
-+        try {
-+            switch (xtype) {
-+            case 'L': return cloneExtendL(type, form, x);
-+            case 'I':
-+                assert x instanceof Integer : "expected Integer, found " + x.getClass();
-+                return cloneExtendI(type, form, ((Integer) x).intValue());
-+            case 'J':
-+                assert x instanceof Long;
-+                return cloneExtendJ(type, form, ((Long) x).longValue());
-+            case 'F':
-+                assert x instanceof Float;
-+                return cloneExtendF(type, form, ((Float) x).floatValue());
-+            case 'D':
-+                assert x instanceof Double;
-+                return cloneExtendD(type, form, ((Double) x).doubleValue());
-+            default : throw new InternalError("unexpected type: " + xtype);
-+            }
-+        } catch (Throwable t) {
-+            throw new InternalError(t);
-         }
+-        }
 -        this.vmargslot = type.parameterSlotDepth(argnum);
 -        initTarget(mh, argnum);
--    }
--
++    static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
++            return new Species_L(type, form, x);
+     }
+ 
 -    private void initTarget(MethodHandle mh, int argnum) {
 -        //this.vmtarget = mh;  // maybe updated by JVM
 -        MethodHandleNatives.init(this, mh, argnum);
@@ -1406,7 +1368,18 @@
 -            return null;
 -        } else if (VerifyType.isNullReferenceConversion(argument.getClass(), ptype)) {
 -            return argument;
--        }
++    MethodHandle cloneExtend(MethodType type, LambdaForm form, char xtype, Object x) {
++        try {
++            switch (xtype) {
++            case 'L': return cloneExtendL(type, form, x);
++            case 'I': return cloneExtendI(type, form, ValueConversions.widenSubword(x));
++            case 'J': return cloneExtendJ(type, form, (long) x);
++            case 'F': return cloneExtendF(type, form, (float) x);
++            case 'D': return cloneExtendD(type, form, (double) x);
++            }
++        } catch (Throwable t) {
++            throw new InternalError(t);
+         }
 -        throw badBoundArgumentException(argument, mh, argnum);
 -    }
 -
@@ -1440,6 +1413,7 @@
 -    final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) {
 -        String atype = (argument == null) ? "null" : argument.getClass().toString();
 -        return new ClassCastException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
++        throw new InternalError("unexpected type: " + xtype);
      }
  
      @Override
@@ -1447,11 +1421,7 @@
 -        return addTypeString(baseName(), this);
 +    MethodHandle bindArgument(int pos, char basicType, Object value) {
 +        MethodType type = type().dropParameterTypes(pos, pos+1);
-+        LambdaForm form = internalForm().bind(basicType, 1+pos, tcount());
-+        if (basicType == 'I' && !(value instanceof Integer)) {
-+            // Cf. ValueConversions.unboxInteger
-+            value = ValueConversions.primitiveConversion(Wrapper.INT, value, true);
-+        }
++        LambdaForm form = internalForm().bind(1+pos, speciesData());
 +        return cloneExtend(type, form, basicType, value);
      }
  
@@ -1497,73 +1467,48 @@
 +         }
      }
 +
-+    public int tcount() {
-+        return types().length();
-+    }
-+
-+    protected final Data myData() {
-+        return Data.get(types());
++    static final String EXTENSION_TYPES = "LIJFD";
++    static final byte INDEX_L = 0, INDEX_I = 1, INDEX_J = 2, INDEX_F = 3, INDEX_D = 4;
++    static byte extensionIndex(char type) {
++        int i = EXTENSION_TYPES.indexOf(type);
++        if (i < 0)  throw new InternalError();
++        return (byte) i;
 +    }
 +
 +    /**
-+     * Return the type signature of a concrete BMH. Each concrete BMH is required
-+     * to implement this method to return its type signature as a constant String
-+     * (the {@link #types} field).
++     * 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.
 +     */
-+    public abstract String types();
-+
-+    /**
-+     * All subclasses must provide such a value describing their type signature.
-+     */
-+    public static final String types = "";
-+
-+    public char type(int i) {
-+        return types().charAt(i);
-+    }
-+
-+    /**
-+     * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
-+     * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
-+     * getter.
-+     */
-+    static Name getterName(Name mhName, char basicType, int i) {
-+        Class<?> paramCls = Wrapper.forBasicType(basicType).primitiveType();
-+        MethodHandle bmhGetter;
-+        try {
-+            bmhGetter = Lookup.IMPL_LOOKUP.findVirtual(BoundMethodHandle.class, "arg" + basicType, methodType(paramCls, int.class));
-+        } catch (IllegalAccessException | NoSuchMethodException e) {
-+            throw new InternalError(e);
-+        }
-+
-+        Name getterName = new Name(bmhGetter, mhName, i);
-+        return getterName;
-+    }
++    protected abstract SpeciesData speciesData();
 +
 +    @Override
 +    final Object internalValues() {
-+        Object[] boundValues = new Object[types().length()];
++        Object[] boundValues = new Object[speciesData().fieldCount()];
 +        for (int i = 0; i < boundValues.length; ++i) {
-+            try {
-+                switch (types().charAt(i)) {
-+                case 'L': boundValues[i] = argL(i); break;
-+                case 'I': boundValues[i] = argI(i); break;
-+                case 'F': boundValues[i] = argF(i); break;
-+                case 'D': boundValues[i] = argD(i); break;
-+                case 'J': boundValues[i] = argJ(i); break;
-+                default : throw new InternalError("unexpected type: " + types().charAt(i));
-+                }
-+            } catch (Throwable t) {
-+                throw new InternalError(t);
-+            }
++            boundValues[i] = arg(i);
 +        }
 +        return Arrays.asList(boundValues);
 +    }
 +
-+    public final Object argL(int i) throws Throwable { return          myData().getters[i].invokeBasic(this); }
-+    public final int    argI(int i) throws Throwable { return (int)    myData().getters[i].invokeBasic(this); }
-+    public final float  argF(int i) throws Throwable { return (float)  myData().getters[i].invokeBasic(this); }
-+    public final double argD(int i) throws Throwable { return (double) myData().getters[i].invokeBasic(this); }
-+    public final long   argJ(int i) throws Throwable { return (long)   myData().getters[i].invokeBasic(this); }
++    public final Object arg(int i) {
++        try {
++            switch (speciesData().fieldType(i)) {
++            case 'L': return argL(i);
++            case 'I': return argI(i);
++            case 'F': return argF(i);
++            case 'D': return argD(i);
++            case 'J': return argJ(i);
++            }
++        } catch (Throwable ex) {
++            throw new InternalError(ex);
++        }
++        throw new InternalError("unexpected type: " + speciesData().types+"."+i);
++    }
++    public final Object argL(int i) throws Throwable { return          speciesData().getters[i].invokeBasic(this); }
++    public final int    argI(int i) throws Throwable { return (int)    speciesData().getters[i].invokeBasic(this); }
++    public final float  argF(int i) throws Throwable { return (float)  speciesData().getters[i].invokeBasic(this); }
++    public final double argD(int i) throws Throwable { return (double) speciesData().getters[i].invokeBasic(this); }
++    public final long   argJ(int i) throws Throwable { return (long)   speciesData().getters[i].invokeBasic(this); }
 +
 +    //
 +    // cloning API
@@ -1576,302 +1521,330 @@
 +    public abstract BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float  narg) throws Throwable;
 +    public abstract BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable;
 +
-+    //
-+    // getters to close bootstrap loops
-+    //
-+
-+    public long   argJ0() throws Throwable { return argJ(0); }
-+    public Object argL1() throws Throwable { return argL(1); }
++    // The following is a grossly irregular hack:
++    @Override MethodHandle reinvokerTarget() {
++        try {
++            return (MethodHandle) argL(0);
++        } catch (Throwable ex) {
++            throw new InternalError(ex);
++        }
++    }
 +
 +    //
 +    // concrete BMH classes required to close bootstrap loops
 +    //
 +
-+    static final class BMH_L extends BoundMethodHandle {
++    private  // make it private to force users to access the enclosing class first
++    static final class Species_L extends BoundMethodHandle {
 +        final Object argL0;
-+        public BMH_L(MethodType mt, LambdaForm lf, Object argL0) {
++        public Species_L(MethodType mt, LambdaForm lf, Object argL0) {
 +            super(mt, lf);
 +            this.argL0 = argL0;
 +        }
++        // The following is a grossly irregular hack:
++        @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; }
 +        @Override
-+        public String types() {
-+            return types;
-+        }
-+        public static final String types = "L";
++        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 BMH_L(mt, lf, argL0);
++            return new Species_L(mt, lf, argL0);
 +        }
 +        @Override
 +        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
-+            return new BMH_LL(mt, lf, argL0, narg);
++            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) Data.get("LI").constructor.invokeBasic(mt, lf, argL0, narg);
++            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) Data.get("LJ").constructor.invokeBasic(mt, lf, argL0, narg);
++            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) Data.get("LF").constructor.invokeBasic(mt, lf, argL0, narg);
++            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) Data.get("LD").constructor.invokeBasic(mt, lf, argL0, narg);
-+        }
-+    }
-+
-+    static final class BMH_LL extends BoundMethodHandle {
++            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 BMH_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
++        public Species_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
 +            super(mt, lf);
 +            this.argL0 = argL0;
 +            this.argL1 = argL1;
 +        }
 +        @Override
-+        public String types() {
-+            return types;
-+        }
-+        public static final String types = "LL";
++        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 BMH_LL(mt, lf, argL0, argL1);
++            return new Species_LL(mt, lf, argL0, argL1);
 +        }
 +        @Override
 +        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("LLL").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
++            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
 +        }
 +        @Override
 +        public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("LLI").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
++            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
 +        }
 +        @Override
 +        public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("LLJ").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
++            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
 +        }
 +        @Override
 +        public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("LLF").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
++            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
 +        }
 +        @Override
 +        public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("LLD").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
-+        }
-+    }
-+
-+    static final class BMH_JL extends BoundMethodHandle {
++            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
++        }
++    }
++
++    static final class Species_JL extends BoundMethodHandle {
 +        final long argJ0;
 +        final Object argL1;
-+        public BMH_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
++        public Species_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
 +            super(mt, lf);
 +            this.argJ0 = argJ0;
 +            this.argL1 = argL1;
 +        }
 +        @Override
-+        public String types() {
-+            return types;
-+        }
-+        public static final String types = "JL";
++        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 BMH_JL(mt, lf, argJ0, argL1);
++            return new Species_JL(mt, lf, argJ0, argL1);
 +        }
 +        @Override
 +        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("JLL").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
++            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) Data.get("JLI").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
++            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) Data.get("JLJ").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
++            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) Data.get("JLF").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
++            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) Data.get("JLD").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
-+        }
-+    }
-+
-+    static final class BMH_J extends BoundMethodHandle {
-+        final long argJ0;
-+        public BMH_J(MethodType mt, LambdaForm lf, long argJ0) {
-+            super(mt, lf);
-+            this.argJ0 = argJ0;
-+        }
-+        @Override
-+        public String types() {
-+            return types;
-+        }
-+        public static final String types = "J";
-+        @Override public final long argJ0() { return argJ0; }
-+        @Override
-+        public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
-+            return new BMH_J(mt, lf, argJ0);
-+        }
-+        @Override
-+        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
-+            return new BMH_JL(mt, lf, argJ0, narg);
-+        }
-+        @Override
-+        public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("JI").constructor.invokeBasic(mt, lf, argJ0, narg);
-+        }
-+        @Override
-+        public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("JJ").constructor.invokeBasic(mt, lf, argJ0, narg);
-+        }
-+        @Override
-+        public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("JF").constructor.invokeBasic(mt, lf, argJ0, narg);
-+        }
-+        @Override
-+        public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
-+            return (BoundMethodHandle) Data.get("JD").constructor.invokeBasic(mt, lf, argJ0, narg);
-+        }
-+    }
-+
-+    public static final MethodHandle MH_argJ0;
-+    public static final MethodHandle MH_argL1;
-+
-+    static {
-+        final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
-+        final Class<?> BMH = BoundMethodHandle.class;
-+        try {
-+            MH_argJ0 = LOOKUP.findVirtual(BMH, "argJ0", MethodType.methodType(long.class));
-+            MH_argL1 = LOOKUP.findVirtual(BMH, "argL1", MethodType.methodType(Object.class));
-+        } catch (NoSuchMethodException | IllegalAccessException e) {
-+            throw new InternalError(e);
-+        }
-+    }
++            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
++        }
++    }
++*/
 +
 +    //
-+    // reinvocation
-+    //
-+
-+    /** Create a LF which simply reinvokes a target of the given basic type.
-+     *  The target MH must have the reinvocation target bound to value #0.
-+     */
-+    static LambdaForm reinvokerForm(MethodType mtype) {
-+        mtype = mtype.basicType();
-+        LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
-+        if (reinvoker != null)  return reinvoker;
-+        MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
-+        final int THIS_BMH    = 0;
-+        final int ARG_BASE    = 1;
-+        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
-+        int nameCursor = ARG_LIMIT;
-+        final int NEXT_MH     = nameCursor++;
-+        final int REINVOKE    = nameCursor++;
-+        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
-+        names[NEXT_MH] = getterName(names[THIS_BMH], 'L', 0);
-+        Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
-+        targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
-+        names[REINVOKE] = new Name(MH_invokeBasic, targetArgs);
-+        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
-+    }
-+
-+    //
-+    // BMH meta-data
++    // BMH species meta-data
 +    //
 +
 +    /**
 +     * Meta-data wrapper for concrete BMH classes.
 +     */
-+    static class Data {
++    static class SpeciesData {
 +        final String                             types;
 +        final Class<? extends BoundMethodHandle> clazz;
-+        final MethodHandle                       constructor;
++        // 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;
-+
-+        Data(String types, Class<? extends BoundMethodHandle> clazz, MethodHandle constructor, MethodHandle[] getters) {
++        final SpeciesData[]                      extensions;
++
++        public int fieldCount() {
++            return types.length();
++        }
++        public char fieldType(int i) {
++            return types.charAt(i);
++        }
++
++        public String toString() {
++            return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]";
++        }
++
++        /**
++         * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
++         * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
++         * getter.
++         */
++        Name getterName(Name mhName, int i) {
++            MethodHandle mh = getters[i];
++            assert(mh != null) : this+"."+i;
++            return new Name(mh, mhName);
++        }
++
++        static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
++
++        private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
 +            this.types = types;
 +            this.clazz = clazz;
-+            this.constructor = constructor;
-+            this.getters = getters;
-+        }
-+
-+        static Map<String, Data> CACHE = new IdentityHashMap<>();
-+
-+        static Data make(String types) {
-+            final Class<? extends BoundMethodHandle> cbmh = Factory.generateConcreteBMHClass(types);
-+            final MethodHandle ctor = Factory.makeCbmhCtor(cbmh, types);
-+            final MethodHandle[] getters = Factory.makeGetters(cbmh, types);
-+            return new Data(types, cbmh, ctor, getters);
-+        }
-+
-+        static Data get(String types) {
-+            final String key = types.intern();
-+            Data d = CACHE.get(key);
-+            if (d == null) {
-+                d = make(types);
-+                Data e = CACHE.get(key);
-+                if (e != null) {
-+                    d = e;
-+                } else {
-+                    CACHE.put(key, d);
-+                }
-+            }
++            if (!INIT_DONE) {
++                this.constructor = new MethodHandle[1];
++                this.getters = new MethodHandle[types.length()];
++            } else {
++                this.constructor = Factory.makeCtors(clazz, types, null);
++                this.getters = Factory.makeGetters(clazz, types, null);
++            }
++            this.extensions = new SpeciesData[EXTENSION_TYPES.length()];
++        }
++
++        private void initForBootstrap() {
++            assert(!INIT_DONE);
++            if (constructor[0] == null) {
++                Factory.makeCtors(clazz, types, this.constructor);
++                Factory.makeGetters(clazz, types, this.getters);
++            }
++        }
++
++        private SpeciesData(String types) {
++            // Placeholder only.
++            this.types = types;
++            this.clazz = null;
++            this.constructor = null;
++            this.getters = null;
++            this.extensions = null;
++        }
++        private boolean isPlaceholder() { return clazz == null; }
++
++        private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
++        private static final boolean INIT_DONE;  // set after <clinit> finishes...
++
++        SpeciesData extendWithType(char type) {
++            int i = extensionIndex(type);
++            SpeciesData d = extensions[i];
++            if (d != null)  return d;
++            extensions[i] = d = get(types+type);
 +            return d;
 +        }
 +
++        SpeciesData extendWithIndex(byte index) {
++            SpeciesData d = extensions[index];
++            if (d != null)  return d;
++            extensions[index] = d = get(types+EXTENSION_TYPES.charAt(index));
++            return d;
++        }
++
++        private static SpeciesData get(String types) {
++            // Acquire cache lock for query.
++            SpeciesData d = lookupCache(types);
++            if (!d.isPlaceholder())
++                return d;
++            Class<? extends BoundMethodHandle> cbmh;
++            synchronized (d) {
++                // Use synch. on the placeholder to prevent multiple instantiation of one species.
++                // Creating this class forces a recursive call to getForClass.
++                cbmh = Factory.generateConcreteBMHClass(types);
++            }
++            // Reacquire cache lock.
++            d = lookupCache(types);
++            // Class loading must have upgraded the cache.
++            assert(d != null && !d.isPlaceholder());
++            return d;
++        }
++        static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
++            // clazz is a new class which is initializing its SPECIES_DATA field
++            return updateCache(types, new SpeciesData(types, clazz));
++        }
++        private static synchronized SpeciesData lookupCache(String types) {
++            SpeciesData d = CACHE.get(types);
++            if (d != null)  return d;
++            d = new SpeciesData(types);
++            assert(d.isPlaceholder());
++            CACHE.put(types, d);
++            return d;
++        }
++        private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
++            SpeciesData d2;
++            assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
++            assert(!d.isPlaceholder());
++            CACHE.put(types, d);
++            return d;
++        }
++
 +        static {
-+            // pre-fill the BMH data cache with BMH's inner classes
-+            final Class<BoundMethodHandle> BMH = BoundMethodHandle.class;
++            // 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 : BMH.getDeclaredClasses()) {
-+                    if (BMH.isAssignableFrom(c)) {
++                for (Class<?> c : rootCls.getDeclaredClasses()) {
++                    if (rootCls.isAssignableFrom(c)) {
 +                        final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
-+                        final String types = Factory.typesFromConcreteBMHClass(cbmh);
-+                        final MethodHandle ctor = Factory.makeCbmhCtor(cbmh, types);
-+                        final MethodHandle[] getters = Factory.makeGetters(cbmh, types);
-+                        final Data d = new Data(types, cbmh, ctor, getters);
-+                        CACHE.put(types.intern(), d);
++                        SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
++                        assert(d != null) : cbmh.getName();
++                        assert(d.clazz == cbmh);
++                        assert(d == lookupCache(d.types));
 +                    }
 +                }
 +            } catch (Throwable e) {
 +                throw new InternalError(e);
 +            }
-+        }
++
++            for (SpeciesData d : CACHE.values()) {
++                d.initForBootstrap();
++            }
++            // Note:  Do not simplify this, because INIT_DONE must not be
++            // a compile-time constant during bootstrapping.
++            INIT_DONE = Boolean.TRUE;
++        }
++    }
++
++    static SpeciesData getSpeciesData(String types) {
++        return SpeciesData.get(types);
 +    }
 +
 +    /**
 +     * Generation of concrete BMH classes.
 +     *
-+     * A concrete BMH is fit for binding a number of values adhering to a
++     * A concrete BMH species is fit for binding a number of values adhering to a
 +     * given type pattern. Reference types are erased.
 +     *
-+     * BMH classes are cached by type pattern.
++     * BMH species are cached by type pattern.
 +     *
-+     * A concrete BMH has a number of fields with the concrete (possibly erased) types of
++     * A BMH species has a number of fields with the concrete (possibly erased) types of
 +     * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
 +     * which can be included as names in lambda forms.
 +     */
 +    static class Factory {
 +
-+        static final String JLO_SIG = "Ljava/lang/Object;";
-+        static final String JLS_SIG = "Ljava/lang/String;";
-+        static final String MH = "java/lang/invoke/MethodHandle";
-+        static final String MH_SIG = "Ljava/lang/invoke/MethodHandle;";
-+        static final String BMH = "java/lang/invoke/BoundMethodHandle";
-+        static final String BMH_SIG = "Ljava/lang/invoke/BoundMethodHandle;";
-+        static final String DATA = "java/lang/invoke/BoundMethodHandle$Data";
-+        static final String DATA_SIG = "Ljava/lang/invoke/BoundMethodHandle$Data;";
-+
-+        static final String BMHDATA_GET_SIG = "(" + JLS_SIG + ")" + DATA_SIG;
-+        static final String TYPES_SIG = "()" + JLS_SIG;
-+        static final String MYDATA_SIG = "()" + DATA_SIG;
++        static final String JLO_SIG  = "Ljava/lang/Object;";
++        static final String JLS_SIG  = "Ljava/lang/String;";
++        static final String JLC_SIG  = "Ljava/lang/Class;";
++        static final String MH       = "java/lang/invoke/MethodHandle";
++        static final String MH_SIG   = "L"+MH+";";
++        static final String BMH      = "java/lang/invoke/BoundMethodHandle";
++        static final String BMH_SIG  = "L"+BMH+";";
++        static final String SPECIES_DATA     = "java/lang/invoke/BoundMethodHandle$SpeciesData";
++        static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
++
++        static final String SPECIES_PREFIX_NAME = "Species_";
++        static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
++
++        static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
++        static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
++        static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
++        static final String VOID_SIG   = "()V";
 +
 +        static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
 +
@@ -1882,12 +1855,12 @@
 +        /**
 +         * Generate a concrete subclass of BMH for a given combination of bound types.
 +         *
-+         * A concrete BMH subclass adheres to the following schema:
++         * A concrete BMH species adheres to the following schema:
 +         *
 +         * <pre>
-+         * class BMH_<<types>> extends BMH {
++         * class Species_<<types>> extends BoundMethodHandle {
 +         *     <<fields>>
-+         *     final String dataValueTypes() { return <<types>>; }
++         *     final SpeciesData speciesData() { return SpeciesData.get("<<types>>"); }
 +         * }
 +         * </pre>
 +         *
@@ -1898,39 +1871,40 @@
 +         * the type signature, adhering to the naming schema described in the definition of
 +         * {@link #makeFieldName()}.
 +         *
-+         * For example, a concrete BMH class for two reference and one integral bound values
++         * For example, a concrete BMH species for two reference and one integral bound values
 +         * would have the following shape:
 +         *
 +         * <pre>
-+         * final class BMH_LLI extends BMH {
++         * class BoundMethodHandle { ... private static
++         * final class Species_LLI extends BoundMethodHandle {
 +         *     final Object argL0;
 +         *     final Object argL1;
 +         *     final int argI2;
-+         *     public BMH_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
++         *     public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
 +         *         super(mt, lf);
 +         *         this.argL0 = argL0;
 +         *         this.argL1 = argL1;
 +         *         this.argI2 = argI2;
 +         *     }
-+         *     public final String dataValueTypes() { return types; }
-+         *     public static final String types = "LLI";
++         *     public final SpeciesData speciesData() { return SPECIES_DATA; }
++         *     public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
 +         *     public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) {
-+         *         return myData().constructor.invokeBasic(mt, lf, argL0, argL1, argI2);
++         *         return SPECIES_DATA.constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2);
 +         *     }
 +         *     public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) {
-+         *         return BMHData.get("LLIL").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
++         *         return SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
 +         *     }
 +         *     public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) {
-+         *         return BMHData.get("LLII").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
++         *         return SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
 +         *     }
 +         *     public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) {
-+         *         return BMHData.get("LLIJ").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
++         *         return SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
 +         *     }
 +         *     public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) {
-+         *         return BMHData.get("LLIF").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
++         *         return SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
 +         *     }
 +         *     public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) {
-+         *         return BMHData.get("LLID").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
++         *         return SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
 +         *     }
 +         * }
 +         * </pre>
@@ -1941,14 +1915,13 @@
 +        static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
 +            final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 +
-+            final String className = "java/lang/invoke/BMH_" + types;
-+            final String sourceFile = "BMH_" + types;
-+
++            final String className  = SPECIES_PREFIX_PATH + types;
++            final String sourceFile = SPECIES_PREFIX_NAME + types;
 +            cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
 +            cw.visitSource(sourceFile, null);
 +
-+            // emit static types field
-+            cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "types", JLS_SIG, null, types).visitEnd();
++            // emit static types and SPECIES_DATA fields
++            cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd();
 +
 +            // emit bound argument fields
 +            for (int i = 0; i < types.length(); ++i) {
@@ -1984,22 +1957,34 @@
 +            mv.visitMaxs(0, 0);
 +            mv.visitEnd();
 +
-+            // emit implementation of types()
-+            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "types", TYPES_SIG, null, null);
++            // emit implementation of reinvokerTarget()
++            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null);
 +            mv.visitCode();
-+            mv.visitLdcInsn(types);
++            mv.visitVarInsn(ALOAD, 0);
++            mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG);
++            mv.visitTypeInsn(CHECKCAST, MH);
 +            mv.visitInsn(ARETURN);
 +            mv.visitMaxs(0, 0);
 +            mv.visitEnd();
 +
++            // emit implementation of speciesData()
++            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
++            mv.visitCode();
++            mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
++            mv.visitInsn(ARETURN);
++            mv.visitMaxs(0, 0);
++            mv.visitEnd();
++
 +            // emit clone()
 +            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE);
 +            mv.visitCode();
-+            // return myData().constructor.invokeBasic(mt, lf, argL0, ...)
++            // return speciesData().constructor[0].invokeBasic(mt, lf, argL0, ...)
 +            // obtain constructor
 +            mv.visitVarInsn(ALOAD, 0);
-+            mv.visitMethodInsn(INVOKESPECIAL, BMH, "myData", MYDATA_SIG);
-+            mv.visitFieldInsn(GETFIELD, DATA, "constructor", MH_SIG);
++            mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
++            mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
++            mv.visitInsn(ICONST_0);
++            mv.visitInsn(AALOAD);
 +            // load mt, lf
 +            mv.visitVarInsn(ALOAD, 1);
 +            mv.visitVarInsn(ALOAD, 2);
@@ -2014,14 +1999,18 @@
 +            // for each type, emit cloneExtendT()
 +            for (Class<?> c : TYPES) {
 +                char t = Wrapper.basicTypeChar(c);
-+                String extypes = types + t;
 +                mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "cloneExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE);
 +                mv.visitCode();
-+                // return BMHData.get(<extypes>).constructor.invokeBasic(mt, lf, argL0, ..., narg)
++                // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
 +                // obtain constructor
-+                mv.visitLdcInsn(extypes);
-+                mv.visitMethodInsn(INVOKESTATIC, DATA, "get", BMHDATA_GET_SIG);
-+                mv.visitFieldInsn(GETFIELD, DATA, "constructor", MH_SIG);
++                mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
++                int iconstInsn = ICONST_0 + extensionIndex(t);
++                assert(iconstInsn <= ICONST_5);
++                mv.visitInsn(iconstInsn);
++                mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG);
++                mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
++                mv.visitInsn(ICONST_0);
++                mv.visitInsn(AALOAD);
 +                // load mt, lf
 +                mv.visitVarInsn(ALOAD, 1);
 +                mv.visitVarInsn(ALOAD, 2);
@@ -2030,19 +2019,31 @@
 +                // put narg on stack
 +                mv.visitVarInsn(typeLoadOp(t), 3);
 +                // finally, invoke the constructor and return
-+                mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(extypes, false));
++                mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + t, false));
 +                mv.visitInsn(ARETURN);
 +                mv.visitMaxs(0, 0);
 +                mv.visitEnd();
 +            }
 +
++            // emit class initializer
++            mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null);
++            mv.visitCode();
++            mv.visitLdcInsn(types);
++            mv.visitLdcInsn(Type.getObjectType(className));
++            mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG);
++            mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
++            mv.visitInsn(RETURN);
++            mv.visitMaxs(0, 0);
++            mv.visitEnd();
++
 +            cw.visitEnd();
 +
 +            // load class
 +            final byte[] classFile = cw.toByteArray();
 +            InvokerBytecodeGenerator.maybeDump(className, classFile);
 +            Class<? extends BoundMethodHandle> bmhClass =
-+                UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
++                //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
++                UNSAFE.defineClass(className, classFile, 0, classFile.length).asSubclass(BoundMethodHandle.class);
 +            UNSAFE.ensureClassInitialized(bmhClass);
 +
 +            return bmhClass;
@@ -2085,11 +2086,18 @@
 +            }
 +        }
 +
-+        static MethodHandle[] makeGetters(Class<?> cbmhClass, String types) {
-+            MethodHandle[] mhs = new MethodHandle[types.length()];
++        static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
++            if (mhs == null)  mhs = new MethodHandle[types.length()];
 +            for (int i = 0; i < mhs.length; ++i) {
 +                mhs[i] = makeGetter(cbmhClass, types, i);
-+            }
++                assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
++            }
++            return mhs;
++        }
++
++        static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
++            if (mhs == null)  mhs = new MethodHandle[1];
++            mhs[0] = makeCbmhCtor(cbmh, types);
 +            return mhs;
 +        }
 +
@@ -2097,12 +2105,12 @@
 +        // Auxiliary methods.
 +        //
 +
-+        static String typesFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
++        static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
 +            try {
-+                Field ftypes = cbmh.getDeclaredField("types");
-+                return (String) ftypes.get(null);
-+            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
-+                throw new InternalError(e);
++                Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
++                return (SpeciesData) F_SPECIES_DATA.get(null);
++            } catch (ReflectiveOperationException ex) {
++                throw new InternalError(ex);
 +            }
 +        }
 +
@@ -2172,19 +2180,27 @@
 +            return cmh;
 +        }
 +
-+        //
-+        // Constants.
-+        //
-+
-+        private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
-+        private static final Unsafe UNSAFE = Unsafe.getUnsafe();
-+
-+    }
-+
++    }
++
++    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;
  }
 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
+@@ -26,7 +26,7 @@
+ package java.lang.invoke;
+ 
+ import sun.invoke.empty.Empty;
+-import sun.misc.Unsafe;
++import static java.lang.invoke.MethodHandleStatics.*;
+ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+ 
+ /**
 @@ -86,13 +86,9 @@
  public class CallSite {
      static { MethodHandleImpl.initStatics(); }
@@ -2234,7 +2250,30 @@
          MethodHandle invoker = MethodHandles.exactInvoker(this.type());
          return MethodHandles.foldArguments(invoker, getTarget);
      }
-@@ -284,8 +262,7 @@
+@@ -255,12 +233,10 @@
+     }
+ 
+     // unsafe stuff:
+-    private static final Unsafe unsafe = Unsafe.getUnsafe();
+     private static final long TARGET_OFFSET;
+-
+     static {
+         try {
+-            TARGET_OFFSET = unsafe.objectFieldOffset(CallSite.class.getDeclaredField("target"));
++            TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+         } catch (Exception ex) { throw new Error(ex); }
+     }
+ 
+@@ -270,7 +246,7 @@
+     }
+     /*package-private*/
+     MethodHandle getTargetVolatile() {
+-        return (MethodHandle) unsafe.getObjectVolatile(this, TARGET_OFFSET);
++        return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
+     }
+     /*package-private*/
+     void setTargetVolatile(MethodHandle newTarget) {
+@@ -284,8 +260,7 @@
                               // Extra arguments for BSM, if any:
                               Object info,
                               // Caller information:
@@ -2245,122 +2284,64 @@
          CallSite site;
          try {
 diff --git a/src/share/classes/java/lang/invoke/CountingMethodHandle.java b/src/share/classes/java/lang/invoke/CountingMethodHandle.java
+deleted file mode 100644
 --- a/src/share/classes/java/lang/invoke/CountingMethodHandle.java
-+++ b/src/share/classes/java/lang/invoke/CountingMethodHandle.java
-@@ -25,7 +25,10 @@
- 
- package java.lang.invoke;
- 
++++ /dev/null
+@@ -1,50 +0,0 @@
+-/*
+- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+- *
+- * This code is free software; you can redistribute it and/or modify it
+- * under the terms of the GNU General Public License version 2 only, as
+- * published by the Free Software Foundation.  Oracle designates this
+- * particular file as subject to the "Classpath" exception as provided
+- * by Oracle in the LICENSE file that accompanied this code.
+- *
+- * This code is distributed in the hope that it will be useful, but WITHOUT
+- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+- * version 2 for more details (a copy is included in the LICENSE file that
+- * accompanied this code).
+- *
+- * You should have received a copy of the GNU General Public License version
+- * 2 along with this work; if not, write to the Free Software Foundation,
+- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+- *
+- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+- * or visit www.oracle.com if you need additional information or have any
+- * questions.
+- */
+-
+-package java.lang.invoke;
+-
 -import static java.lang.invoke.MethodHandleNatives.Constants.*;
-+import java.util.Arrays;
-+import static java.lang.invoke.LambdaForm.*;
-+import static java.lang.invoke.MethodHandles.*;
-+import static java.lang.invoke.MethodType.*;
- 
- /**
-  * This method handle is used to optionally provide a count of how
-@@ -33,18 +36,94 @@
-  *
-  * @author never
-  */
+-
+-/**
+- * This method handle is used to optionally provide a count of how
+- * many times it was invoked.
+- *
+- * @author never
+- */
 -class CountingMethodHandle extends AdapterMethodHandle {
-+class CountingMethodHandle extends BoundMethodHandle {
-+    private MethodHandle target;
-     private int vmcount;
- 
-     private CountingMethodHandle(MethodHandle target) {
+-    private int vmcount;
+-
+-    private CountingMethodHandle(MethodHandle target) {
 -        super(target, target.type(), AdapterMethodHandle.makeConv(OP_RETYPE_ONLY));
-+        super(target.type(), countingReinvokerForm(target.type().basicType()));
-+        this.target = target;
-     }
- 
-     /** Wrap the incoming MethodHandle in a CountingMethodHandle if they are enabled */
-     static MethodHandle wrap(MethodHandle mh) {
-         if (MethodHandleNatives.COUNT_GWT) {
+-    }
+-
+-    /** Wrap the incoming MethodHandle in a CountingMethodHandle if they are enabled */
+-    static MethodHandle wrap(MethodHandle mh) {
+-        if (MethodHandleNatives.COUNT_GWT) {
 -            return new CountingMethodHandle(mh);
-+             return new CountingMethodHandle(mh);
-         }
-         return mh;
-     }
-+
-+    @Override
-+    public String types() {
-+        return types;
-+    }
-+
-+    public static final String types = "L";
-+
-+    @ForceInline
-+    void vmcountBump() {
-+        vmcount += 1;
-+    }
-+
-+    /** Create a LF which reinvokes a target of the given basic type, and also bumps CountingMethodHandle.vmcount. */
-+    private static LambdaForm countingReinvokerForm(MethodType mtype) {
-+        assert(mtype == mtype.basicType());  // caller resp
-+        LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_COUNTER);
-+        if (reinvoker != null)  return reinvoker;
-+        MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
-+        final int THIS_CMH    = 0;
-+        final int ARG_BASE    = 1;
-+        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
-+        int nameCursor = ARG_LIMIT;
-+        final int BUMP_COUNT  = nameCursor++;
-+        final int NEXT_MH     = nameCursor++;
-+        final int REINVOKE    = nameCursor++;
-+        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
-+        names[BUMP_COUNT] = new Name(CountingMethodHandle.NF_vmcountBump, names[THIS_CMH]);
-+        names[NEXT_MH] = BoundMethodHandle.getterName(names[THIS_CMH], 'L', 0);
-+        Object[] targetArgs = Arrays.copyOfRange(names, THIS_CMH, ARG_LIMIT, Object[].class);
-+        targetArgs[0] = names[NEXT_MH];  // overwrite CMH with next MH
-+        names[REINVOKE] = new Name(MH_invokeBasic, targetArgs);
-+        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, new LambdaForm("CMH.reinvoke", ARG_LIMIT, names));
-+    }
-+
-+    static final NamedFunction NF_vmcountBump;
-+    static {
-+        try {
-+            NF_vmcountBump = new NamedFunction(Lookup.IMPL_LOOKUP
-+                    .findVirtual(CountingMethodHandle.class, "vmcountBump", methodType(void.class)));
-+        } catch (ReflectiveOperationException ex) {
-+            throw new InternalError(ex);
-+        }
-+    }
-+
-+    @Override
-+    public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
-+        throw new IllegalStateException("NYI");
-+    }
-+
-+    @Override
-+    public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
-+        throw new IllegalStateException("NYI");
-+    }
-+
-+    @Override
-+    public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
-+        throw new IllegalStateException("NYI");
-+    }
-+
-+    @Override
-+    public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
-+        throw new IllegalStateException("NYI");
-+    }
-+
-+    @Override
-+    public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
-+        throw new IllegalStateException("NYI");
-+    }
-+
-+    @Override
-+    public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
-+        throw new IllegalStateException("NYI");
-+    }
- }
+-        }
+-        return mh;
+-    }
+-}
 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
-@@ -25,29 +25,252 @@
+@@ -25,29 +25,635 @@
  
  package java.lang.invoke;
  
@@ -2370,9 +2351,18 @@
 +import sun.invoke.util.VerifyAccess;
  import static java.lang.invoke.MethodHandleNatives.Constants.*;
 +import static java.lang.invoke.LambdaForm.*;
++import static java.lang.invoke.MethodTypeForm.*;
++import static java.lang.invoke.MethodHandleStatics.*;
++import java.lang.ref.WeakReference;
++import java.lang.reflect.Field;
++import sun.invoke.util.ValueConversions;
++import sun.invoke.util.VerifyType;
++import sun.invoke.util.Wrapper;
  
  /**
-  * The flavor of method handle which emulates invokespecial or invokestatic.
+- * The flavor of method handle which emulates invokespecial or invokestatic.
++ * The flavor of method handle which implements a constant reference
++ * to a class member.
   * @author jrose
   */
  class DirectMethodHandle extends MethodHandle {
@@ -2391,9 +2381,8 @@
 -
 -        MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass);
 +    // Constructors and factory methods in this class *must* be package scoped or private.
-+    private DirectMethodHandle(MethodType mtype, MemberName member, LambdaForm form) {
++    private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member) {
 +        super(mtype, form);
-+        assert(member.isMethod() || member.isConstructor());
 +        if (!member.isResolved())  throw new InternalError();
 +        this.member = member;
      }
@@ -2403,24 +2392,54 @@
 +    // Factory methods:
 +
 +    static DirectMethodHandle make(Class<?> receiver, MemberName member) {
-+        MethodType mtype = member.getMethodType();
++        MethodType mtype = member.getMethodOrFieldType();
 +        if (!member.isStatic()) {
-+            if (!member.getDeclaringClass().isAssignableFrom(receiver))
++            if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
 +                throw new InternalError(member.toString());
 +            mtype = mtype.insertParameterTypes(0, receiver);
 +        }
-+        return new DirectMethodHandle(mtype, member, preparedLambdaForm(mtype.basicType(), member));
-     }
++        if (!member.isField()) {
++            LambdaForm lform = preparedLambdaForm(member);
++            return new DirectMethodHandle(mtype, lform, member);
++        } else {
++            LambdaForm lform = preparedFieldLambdaForm(member);
++            if (member.isStatic()) {
++                long offset = MethodHandleNatives.staticFieldOffset(member);
++                Object base = MethodHandleNatives.staticFieldBase(member);
++                return new StaticAccessor(mtype, lform, member, base, offset);
++            } else {
++                long offset = MethodHandleNatives.objectFieldOffset(member);
++                assert(offset == (int)offset);
++                return new Accessor(mtype, lform, member, (int)offset);
++            }
++        }
++    }
 +    static DirectMethodHandle make(MemberName member) {
++        if (member.isConstructor())
++            return makeAllocator(member);
 +        return make(member.getDeclaringClass(), member);
 +    }
 +    static DirectMethodHandle make(Method method) {
 +        return make(method.getDeclaringClass(), new MemberName(method));
 +    }
++    static DirectMethodHandle make(Field field) {
++        return make(field.getDeclaringClass(), new MemberName(field));
++    }
++    private static DirectMethodHandle makeAllocator(MemberName ctor) {
++        assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
++        Class<?> instanceClass = ctor.getDeclaringClass();
++        ctor = ctor.asConstructor();
++        assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
++        MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
++        LambdaForm lform = preparedLambdaForm(ctor);
++        MemberName init = ctor.asSpecial();
++        assert(init.getMethodType().returnType() == void.class);
++        return new Constructor(mtype, lform, ctor, init, instanceClass);
++    }
 +
 +    @Override
 +    MethodHandle copyWith(MethodType mt, LambdaForm lf) {
-+        return new DirectMethodHandle(mt, member, lf);
++        return new DirectMethodHandle(mt, lf, member);
 +    }
 +
 +    @Override
@@ -2467,7 +2486,7 @@
 +                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(), concrete, preparedLambdaForm(type().basicType(), concrete));
++                    return new DirectMethodHandle(type(), preparedLambdaForm(concrete), concrete);
 +                break;
 +            }
 +        }
@@ -2479,58 +2498,117 @@
 +     * Cache and share this structure among all methods with
 +     * the same basicType and refKind.
 +     */
-+    private static LambdaForm preparedLambdaForm(MethodType mtype, MemberName m) {
-+        assert(m.isInvocable()) : "not a method: " + m;
-+        assert(mtype == mtype.basicType());
-+        assert(m.getInvocationType().basicType() == mtype);
++    private static LambdaForm preparedLambdaForm(MemberName m) {
++        assert(m.isInvocable()) : m;  // call preparedFieldLambdaForm instead
++        MethodType mtype = m.getInvocationType().basicType();
 +        assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
-+        byte refKind = m.getReferenceKind();
-+        int which = MethodTypeForm.LF_REF_KIND_FIRST + refKind;
-+        if (refKind == REF_invokeStatic && shouldBeInitialized(m))
-+            which = MethodTypeForm.LF_INVSTATIC_INIT;
++        int which;
++        switch (m.getReferenceKind()) {
++        case REF_invokeVirtual:    which = LF_INVVIRTUAL;    break;
++        case REF_invokeStatic:     which = LF_INVSTATIC;     break;
++        case REF_invokeSpecial:    which = LF_INVSPECIAL;    break;
++        case REF_invokeInterface:  which = LF_INVINTERFACE;  break;
++        case REF_newInvokeSpecial: which = LF_NEWINVSPECIAL; break;
++        default:  throw new InternalError(m.toString());
++        }
++        if (which == LF_INVSTATIC && shouldBeInitialized(m)) {
++            // precompute the barrier-free version:
++            preparedLambdaForm(mtype, which);
++            which = LF_INVSTATIC_INIT;
++        }
++        LambdaForm lform = preparedLambdaForm(mtype, which);
++        maybeCompile(lform, m);
++        assert(lform.methodType().dropParameterTypes(0, 1)
++                .equals(m.getInvocationType().basicType()))
++                : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
++        return lform;
++    }
++
++    private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
 +        LambdaForm lform = mtype.form().cachedLambdaForm(which);
 +        if (lform != null)  return lform;
-+        lform = makePreparedLambdaForm(mtype, refKind, which);
-+        if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
-+            // Help along bootstrapping...
-+            lform.compileToBytecode();
++        lform = makePreparedLambdaForm(mtype, which);
 +        return mtype.form().setCachedLambdaForm(which, lform);
 +    }
 +
-+    private static LambdaForm makePreparedLambdaForm(MethodType mtype, byte refKind, int which) {
++    private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
++        boolean needsInit = (which == LF_INVSTATIC_INIT);
++        boolean doesAlloc = (which == LF_NEWINVSPECIAL);
 +        String linkerName, lambdaName;
-+        switch (refKind) {
-+        case REF_invokeVirtual:    linkerName = "linkToVirtual";    lambdaName = "DMH.invokeVirtual";    break;
-+        case REF_invokeStatic:     linkerName = "linkToStatic";     lambdaName = "DMH.invokeStatic";     break;
-+        case REF_invokeSpecial:    linkerName = "linkToSpecial";    lambdaName = "DMH.invokeSpecial";    break;
-+        case REF_invokeInterface:  linkerName = "linkToInterface";  lambdaName = "DMH.invokeInterface";  break;
-+        default:  throw new InternalError("refKind="+refKind);
++        switch (which) {
++        case LF_INVVIRTUAL:    linkerName = "linkToVirtual";    lambdaName = "DMH.invokeVirtual";    break;
++        case LF_INVSTATIC:     linkerName = "linkToStatic";     lambdaName = "DMH.invokeStatic";     break;
++        case LF_INVSTATIC_INIT:linkerName = "linkToStatic";     lambdaName = "DMH.invokeStaticInit"; break;
++        case LF_INVSPECIAL:    linkerName = "linkToSpecial";    lambdaName = "DMH.invokeSpecial";    break;
++        case LF_INVINTERFACE:  linkerName = "linkToInterface";  lambdaName = "DMH.invokeInterface";  break;
++        case LF_NEWINVSPECIAL: linkerName = "linkToSpecial";    lambdaName = "DMH.newInvokeSpecial"; break;
++        default:  throw new InternalError("which="+which);
 +        }
 +        MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class);
++        if (doesAlloc)
++            mtypeWithArg = mtypeWithArg
++                    .insertParameterTypes(0, Object.class)  // insert newly allocated obj
++                    .changeReturnType(void.class);          // <init> returns void
 +        MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
 +        try {
 +            linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
-+            assert(linker.isStatic());
 +        } catch (ReflectiveOperationException ex) {
 +            throw new InternalError(ex);
 +        }
-+        boolean needEnsureInit = (which == MethodTypeForm.LF_INVSTATIC_INIT);
 +        final int DMH_THIS    = 0;
 +        final int ARG_BASE    = 1;
 +        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
 +        int nameCursor = ARG_LIMIT;
++        final int NEW_OBJ     = (doesAlloc ? nameCursor++ : -1);
 +        final int GET_MEMBER  = nameCursor++;
-+        final int ENSURE_INIT = (needEnsureInit ? nameCursor++ : -1);
 +        final int LINKER_CALL = nameCursor++;
 +        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
 +        assert(names.length == nameCursor);
-+        names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
-+        if (ENSURE_INIT > 0)
-+            names[ENSURE_INIT] = new Name(NF_ensureClassInitialized, names[GET_MEMBER]);
++        if (doesAlloc) {
++            // names = { argx,y,z,... new C, init method }
++            names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
++            names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
++        } else if (needsInit) {
++            names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
++        } else {
++            names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
++        }
 +        Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
 +        assert(outArgs[outArgs.length-1] == names[GET_MEMBER]);  // look, shifted args!
++        int result = LambdaForm.LAST_RESULT;
++        if (doesAlloc) {
++            assert(outArgs[outArgs.length-2] == names[NEW_OBJ]);  // got to move this one
++            System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
++            outArgs[0] = names[NEW_OBJ];
++            result = NEW_OBJ;
++        }
 +        names[LINKER_CALL] = new Name(linker, outArgs);
-+        return new LambdaForm(lambdaName, ARG_LIMIT, names);
++        lambdaName += "_" + LambdaForm.basicTypeSignature(mtype);
++        LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
++        // This is a tricky bit of code.  Don't send it through the LF interpreter.
++        lform.compileToBytecode();
++        return lform;
++    }
++
++    private static void maybeCompile(LambdaForm lform, MemberName m) {
++        if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
++            // Help along bootstrapping...
++            lform.compileToBytecode();
++    }
++
++    /** Static wrapper for DirectMethodHandle.internalMemberName. */
++    @ForceInline
++    /*non-public*/ static Object internalMemberName(Object mh) {
++        return ((DirectMethodHandle)mh).member;
++    }
++
++    /** Static wrapper for DirectMethodHandle.internalMemberName.
++     * This one also forces initialization.
++     */
++    /*non-public*/ static Object internalMemberNameEnsureInit(Object mh) {
++        DirectMethodHandle dmh = (DirectMethodHandle)mh;
++        dmh.ensureInitialized();
++        return dmh.member;
 +    }
 +
 +    /*non-public*/ static
@@ -2546,86 +2624,373 @@
 +            return false;
 +        }
 +        Class<?> cls = member.getDeclaringClass();
-+        if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
++        if (cls == ValueConversions.class ||
++            cls == MethodHandleImpl.class ||
++            cls == Invokers.class) {
++            // These guys have lots of <clinit> DMH creation but we know
++            // the MHs will not be used until the system is booted.
 +            return false;
-+        //return UNSAFE.shouldBeInitialized(cls);
-+        if (MH_shouldBeInitialized == null) {
-+            MethodHandleImpl.UNSAFE.ensureClassInitialized(cls);
++        }
++        if (VerifyAccess.isSamePackage(MethodHandle.class, cls) ||
++            VerifyAccess.isSamePackage(ValueConversions.class, cls)) {
++            // It is a system class.  It is probably in the process of
++            // being initialized, but we will help it along just to be safe.
++            if (UNSAFE.shouldBeInitialized(cls)) {
++                UNSAFE.ensureClassInitialized(cls);
++            }
 +            return false;
 +        }
++        return UNSAFE.shouldBeInitialized(cls);
++    }
++
++    private static class EnsureInitialized extends ClassValue<WeakReference<Thread>> {
++        @Override
++        protected WeakReference<Thread> computeValue(Class<?> type) {
++            UNSAFE.ensureClassInitialized(type);
++            if (UNSAFE.shouldBeInitialized(type))
++                // If the previous call didn't block, this can happen.
++                // We are executing inside <clinit>.
++                return new WeakReference<>(Thread.currentThread());
++            return null;
++        }
++        static final EnsureInitialized INSTANCE = new EnsureInitialized();
++    }
++
++    private void ensureInitialized() {
++        if (checkInitialized(member)) {
++            // The coast is clear.  Delete the <clinit> barrier.
++            if (member.isField())
++                updateForm(preparedFieldLambdaForm(member));
++            else
++                updateForm(preparedLambdaForm(member));
++        }
++    }
++    private static boolean checkInitialized(MemberName member) {
++        Class<?> defc = member.getDeclaringClass();
++        WeakReference<Thread> ref = EnsureInitialized.INSTANCE.get(defc);
++        if (ref == null) {
++            return true;  // the final state
++        }
++        Thread clinitThread = ref.get();
++        // Somebody may still be running defc.<clinit>.
++        if (clinitThread == Thread.currentThread()) {
++            // If anybody is running defc.<clinit>, it is this thread.
++            if (UNSAFE.shouldBeInitialized(defc))
++                // Yes, we are running it; keep the barrier for now.
++                return false;
++        } else {
++            // We are in a random thread.  Block.
++            UNSAFE.ensureClassInitialized(defc);
++        }
++        assert(!UNSAFE.shouldBeInitialized(defc));
++        // put it into the final state
++        EnsureInitialized.INSTANCE.remove(defc);
++        return true;
++    }
++
++    /*non-public*/ static void ensureInitialized(Object mh) {
++        ((DirectMethodHandle)mh).ensureInitialized();
++    }
++
++    /** This subclass handles constructor references. */
++    static class Constructor extends DirectMethodHandle {
++        final MemberName initMethod;
++        final Class<?>   instanceClass;
++
++        private Constructor(MethodType mtype, LambdaForm form, MemberName constructor,
++                            MemberName initMethod, Class<?> instanceClass) {
++            super(mtype, form, constructor);
++            this.initMethod = initMethod;
++            this.instanceClass = instanceClass;
++            assert(initMethod.isResolved());
++        }
++    }
++
++    /*non-public*/ static Object constructorMethod(Object mh) {
++        Constructor dmh = (Constructor)mh;
++        return dmh.initMethod;
++    }
++
++    /*non-public*/ static Object allocateInstance(Object mh) throws InstantiationException {
++        Constructor dmh = (Constructor)mh;
++        return UNSAFE.allocateInstance(dmh.instanceClass);
++    }
++
++    /** This subclass handles non-static field references. */
++    static class Accessor extends DirectMethodHandle {
++        final Class<?> fieldType;
++        final int      fieldOffset;
++        private Accessor(MethodType mtype, LambdaForm form, MemberName member,
++                         int fieldOffset) {
++            super(mtype, form, member);
++            this.fieldType   = member.getFieldType();
++            this.fieldOffset = fieldOffset;
++        }
++
++        @Override Object checkCast(Object obj) {
++            return fieldType.cast(obj);
++        }
++    }
++
++    @ForceInline
++    /*non-public*/ static long fieldOffset(Object accessorObj) {
++        // Note: We return a long because that is what Unsafe.getObject likes.
++        // We store a plain int because it is more compact.
++        return ((Accessor)accessorObj).fieldOffset;
++    }
++
++    @ForceInline
++    /*non-public*/ static Object checkBase(Object obj) {
++        // Note that the object's class has already been verified,
++        // since the parameter type of the Accessor method handle
++        // is either member.getDeclaringClass or a subclass.
++        // This was verified in DirectMethodHandle.make.
++        // Therefore, the only remaining check is for null.
++        // Since this check is *not* guaranteed by Unsafe.getInt
++        // and its siblings, we need to make an explicit one here.
++        obj.getClass();  // maybe throw NPE
++        return obj;
++    }
++
++    /** This subclass handles static field references. */
++    static class StaticAccessor extends DirectMethodHandle {
++        final private Class<?> fieldType;
++        final private Object   staticBase;
++        final private long     staticOffset;
++
++        private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member,
++                               Object staticBase, long staticOffset) {
++            super(mtype, form, member);
++            this.fieldType    = member.getFieldType();
++            this.staticBase   = staticBase;
++            this.staticOffset = staticOffset;
++        }
++
++        @Override Object checkCast(Object obj) {
++            return fieldType.cast(obj);
++        }
++    }
++
++    @ForceInline
++    /*non-public*/ static Object nullCheck(Object obj) {
++        obj.getClass();
++        return obj;
++    }
++
++    @ForceInline
++    /*non-public*/ static Object staticBase(Object accessorObj) {
++        return ((StaticAccessor)accessorObj).staticBase;
++    }
++
++    @ForceInline
++    /*non-public*/ static long staticOffset(Object accessorObj) {
++        return ((StaticAccessor)accessorObj).staticOffset;
++    }
++
++    @ForceInline
++    /*non-public*/ static Object checkCast(Object mh, Object obj) {
++        return ((DirectMethodHandle) mh).checkCast(obj);
++    }
++
++    Object checkCast(Object obj) {
++        return member.getReturnType().cast(obj);
++    }
++
++    // Caching machinery for field accessors:
++    private static byte
++            AF_GETFIELD        = 0,
++            AF_PUTFIELD        = 1,
++            AF_GETSTATIC       = 2,
++            AF_PUTSTATIC       = 3,
++            AF_GETSTATIC_INIT  = 4,
++            AF_PUTSTATIC_INIT  = 5,
++            AF_LIMIT           = 6;
++    // Enumerate the different field kinds using Wrapper,
++    // with an extra case added for checked references.
++    private static int
++            FT_LAST_WRAPPER    = Wrapper.values().length-1,
++            FT_UNCHECKED_REF   = Wrapper.OBJECT.ordinal(),
++            FT_CHECKED_REF     = FT_LAST_WRAPPER+1,
++            FT_LIMIT           = FT_LAST_WRAPPER+2;
++    private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) {
++        return ((formOp * FT_LIMIT * 2)
++                + (isVolatile ? FT_LIMIT : 0)
++                + ftypeKind);
++    }
++    private static final LambdaForm[] ACCESSOR_FORMS
++            = new LambdaForm[afIndex(AF_LIMIT, false, 0)];
++    private static int ftypeKind(Class<?> ftype) {
++        if (ftype.isPrimitive())
++            return Wrapper.forPrimitiveType(ftype).ordinal();
++        else if (VerifyType.isNullReferenceConversion(Object.class, ftype))
++            return FT_UNCHECKED_REF;
++        else
++            return FT_CHECKED_REF;
++    }
++
++    /**
++     * Create a LF which can access the given field.
++     * Cache and share this structure among all fields with
++     * the same basicType and refKind.
++     */
++    private static LambdaForm preparedFieldLambdaForm(MemberName m) {
++        Class<?> ftype = m.getFieldType();
++        boolean isVolatile = m.isVolatile();
++        byte formOp;
++        switch (m.getReferenceKind()) {
++        case REF_getField:      formOp = AF_GETFIELD;    break;
++        case REF_putField:      formOp = AF_PUTFIELD;    break;
++        case REF_getStatic:     formOp = AF_GETSTATIC;   break;
++        case REF_putStatic:     formOp = AF_PUTSTATIC;   break;
++        default:  throw new InternalError(m.toString());
++        }
++        if (shouldBeInitialized(m)) {
++            // precompute the barrier-free version:
++            preparedFieldLambdaForm(formOp, isVolatile, ftype);
++            assert((AF_GETSTATIC_INIT - AF_GETSTATIC) ==
++                   (AF_PUTSTATIC_INIT - AF_PUTSTATIC));
++            formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC);
++        }
++        LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype);
++        maybeCompile(lform, m);
++        assert(lform.methodType().dropParameterTypes(0, 1)
++                .equals(m.getInvocationType().basicType()))
++                : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
++        return lform;
++    }
++    private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class<?> ftype) {
++        int afIndex = afIndex(formOp, isVolatile, ftypeKind(ftype));
++        LambdaForm lform = ACCESSOR_FORMS[afIndex];
++        if (lform != null)  return lform;
++        lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind(ftype));
++        ACCESSOR_FORMS[afIndex] = lform;  // don't bother with a CAS
++        return lform;
++    }
++
++    private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
++        boolean isGetter  = (formOp & 1) == (AF_GETFIELD & 1);
++        boolean isStatic  = (formOp >= AF_GETSTATIC);
++        boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
++        boolean needsCast = (ftypeKind == FT_CHECKED_REF);
++        Wrapper fw = (needsCast ? Wrapper.OBJECT : Wrapper.values()[ftypeKind]);
++        Class<?> ft = fw.primitiveType();
++        assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
++        String tname  = fw.primitiveSimpleName();
++        String ctname = Character.toUpperCase(tname.charAt(0)) + tname.substring(1);
++        if (isVolatile)  ctname += "Volatile";
++        String getOrPut = (isGetter ? "get" : "put");
++        String linkerName = (getOrPut + ctname);  // getObject, putIntVolatile, etc.
++        MethodType linkerType;
++        if (isGetter)
++            linkerType = MethodType.methodType(ft, Object.class, long.class);
++        else
++            linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
++        MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual);
 +        try {
-+            return (boolean) MH_shouldBeInitialized.invokeExact(MethodHandleImpl.UNSAFE, cls);
-+        } catch (Throwable ex) {
++            linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
++        } catch (ReflectiveOperationException ex) {
 +            throw new InternalError(ex);
 +        }
-+    }
-+
-+    /*non-public*/ static
-+    @ForceInline
-+    void ensureClassInitialized(Object memberObj) {
-+        MemberName member = (MemberName) memberObj;
-+        EnsureInitialized.INSTANCE.get(member.getDeclaringClass());
-+    }
-+    private static class EnsureInitialized extends ClassValue<Void> {
-+        @Override
-+        protected Void computeValue(Class<?> type) {
-+            MethodHandleImpl.UNSAFE.ensureClassInitialized(type);
-+            return null;
-+        }
-+        static final EnsureInitialized INSTANCE = new EnsureInitialized();
-+    }
-+
-+    /** Static wrapper for DirectMethodHandle.internalMemberName. */
-+    /*non-public*/ static
-+    Object internalMemberName(Object mh) {
-+        return ((DirectMethodHandle)mh).member;
-+    }
-+
-+    private static final NamedFunction NF_internalMemberName;
-+    private static final NamedFunction NF_ensureClassInitialized;
-+    private static final MethodHandle MH_shouldBeInitialized;
++
++        // What is the external type of the lambda form?
++        MethodType mtype;
++        if (isGetter)
++            mtype = MethodType.methodType(ft);
++        else
++            mtype = MethodType.methodType(void.class, ft);
++        mtype = mtype.basicType();  // erase short to int, etc.
++        if (!isStatic)
++            mtype = mtype.insertParameterTypes(0, Object.class);
++        final int DMH_THIS  = 0;
++        final int ARG_BASE  = 1;
++        final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
++        // if this is for non-static access, the base pointer is stored at this index:
++        final int OBJ_BASE  = isStatic ? -1 : ARG_BASE;
++        // if this is for write access, the value to be written is stored at this index:
++        final int SET_VALUE  = isGetter ? -1 : ARG_LIMIT - 1;
++        int nameCursor = ARG_LIMIT;
++        final int F_HOLDER  = (isStatic ? nameCursor++ : -1);  // static base if any
++        final int F_OFFSET  = nameCursor++;  // Either static offset or field offset.
++        final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
++        final int INIT_BAR  = (needsInit ? nameCursor++ : -1);
++        final int PRE_CAST  = (needsCast && !isGetter ? nameCursor++ : -1);
++        final int LINKER_CALL = nameCursor++;
++        final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
++        final int RESULT    = nameCursor-1;  // either the call or the cast
++        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
++        if (needsInit)
++            names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
++        if (needsCast && !isGetter)
++            names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
++        Object[] outArgs = new Object[1 + linkerType.parameterCount()];
++        assert(outArgs.length == (isGetter ? 3 : 4));
++        outArgs[0] = UNSAFE;
++        if (isStatic) {
++            outArgs[1] = names[F_HOLDER]  = new Name(NF_staticBase, names[DMH_THIS]);
++            outArgs[2] = names[F_OFFSET]  = new Name(NF_staticOffset, names[DMH_THIS]);
++        } else {
++            outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
++            outArgs[2] = names[F_OFFSET]  = new Name(NF_fieldOffset, names[DMH_THIS]);
++        }
++        if (!isGetter) {
++            outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
++        }
++        for (Object a : outArgs)  assert(a != null);
++        names[LINKER_CALL] = new Name(linker, outArgs);
++        if (needsCast && isGetter)
++            names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
++        for (Name n : names)  assert(n != null);
++        String fieldOrStatic = (isStatic ? "Static" : "Field");
++        String lambdaName = (linkerName + fieldOrStatic);  // significant only for debugging
++        if (needsCast)  lambdaName += "Cast";
++        if (needsInit)  lambdaName += "Init";
++        return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT);
++    }
++
++    private static final NamedFunction
++            NF_internalMemberName,
++            NF_internalMemberNameEnsureInit,
++            NF_ensureInitialized,
++            NF_fieldOffset,
++            NF_checkBase,
++            NF_staticBase,
++            NF_staticOffset,
++            NF_checkCast,
++            NF_allocateInstance,
++            NF_constructorMethod;
 +    static {
 +        try {
-+            NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("internalMemberName", Object.class));
-+            NF_ensureClassInitialized = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("ensureClassInitialized", Object.class));
-+            MethodHandle mhOrNull = null;
-+            try {
-+                mhOrNull = make(Unsafe.class.getMethod("shouldBeInitialized", Class.class));
-+            } catch (NoSuchMethodException ex) {
-+                mhOrNull = null;  // new API call not in down-rev JVM
-+            }
-+            MH_shouldBeInitialized = mhOrNull;
-+            NF_internalMemberName.resolve();
-+            NF_ensureClassInitialized.resolve();
-+            MH_shouldBeInitialized.form.resolve();
-+            // bound
++            NamedFunction nfs[] = {
++                NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("internalMemberName", Object.class)),
++                NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
++                NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("ensureInitialized", Object.class)),
++                NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("fieldOffset", Object.class)),
++                NF_checkBase = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("checkBase", Object.class)),
++                NF_staticBase = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("staticBase", Object.class)),
++                NF_staticOffset = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("staticOffset", Object.class)),
++                NF_checkCast = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("checkCast", Object.class, Object.class)),
++                NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("allocateInstance", Object.class)),
++                NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
++                    .getDeclaredMethod("constructorMethod", Object.class))
++            };
++            for (NamedFunction nf : nfs) {
++                // Each nf must be statically invocable or we get tied up in our bootstraps.
++                assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
++                nf.resolve();
++            }
 +        } catch (ReflectiveOperationException ex) {
 +            throw new InternalError(ex);
 +        }
-+    }
-+
-+/*
-+    // Smoke-test:
-+    static void testDirectMethodHandles() throws Throwable {
-+        MemberName.Factory lookup = MemberName.getFactory();
-+        MethodHandle asList_MH = make(Arrays.class.getMethod("asList", Object[].class));
-+        System.out.println("about to call "+asList_MH);
-+        Object[] abc = { "a", "bc" };
-+        java.util.List<?> lst = (java.util.List<?>) asList_MH.invokeExact(abc);
-+        System.out.println("lst="+lst);
-+        MethodHandle toString_MH = make(new MemberName(Object.class.getMethod("toString")));
-+        String s1 = (String) toString_MH.invokeExact((Object) lst);
-+        toString_MH = make(new MemberName(Object.class.getMethod("toString"), true));
-+        String s2 = (String) toString_MH.invokeExact((Object) lst);
-+        System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString()));
-+        MethodHandle toArray_MH = make(new MemberName(java.util.List.class.getMethod("toArray")));
-+        Object[] arr = (Object[]) toArray_MH.invokeExact(lst);
-+        System.out.println("toArray="+Arrays.toString(arr));
-+    }
-+    static { try { testDirectMethodHandles(); } catch (Throwable ex) { throw new RuntimeException(ex); } }
-+//*/
+     }
  }
 diff --git a/src/share/classes/java/lang/invoke/DontInline.java b/src/share/classes/java/lang/invoke/DontInline.java
 new file mode 100644
@@ -2715,7 +3080,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
-@@ -0,0 +1,1066 @@
+@@ -0,0 +1,1065 @@
 +/*
 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -2748,7 +3113,6 @@
 +import java.lang.invoke.MethodHandles.Lookup;
 +
 +import sun.invoke.util.Wrapper;
-+import sun.misc.Unsafe;
 +
 +import java.io.*;
 +import java.util.*;
@@ -2800,7 +3164,6 @@
 +    private MethodVisitor mv;
 +
 +    private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
-+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
 +    private static final Class<?> HOST_CLASS = LambdaForm.class;
 +
 +    private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
@@ -3162,11 +3525,7 @@
 +        case 'L':
 +            if (VerifyType.isNullConversion(Object.class, pclass))
 +                return;
-+            // for BMH species, which are not representable as names in class files (anonymous classes!), cast to BMH instead
-+            if (BoundMethodHandle.class.isAssignableFrom(pclass)) {
-+                final String className = getInternalName(BoundMethodHandle.class);
-+                mv.visitTypeInsn(Opcodes.CHECKCAST, className);
-+            } else if (isStaticallyNameable(pclass)) {
++            if (isStaticallyNameable(pclass)) {
 +                mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
 +            } else {
 +                mv.visitLdcInsn(constantPlaceholder(pclass));
@@ -3243,7 +3602,7 @@
 +
 +        // iterate over the form's names, generating bytecode instructions for each
 +        // start iterating at the first name following the arguments
-+        for (int i = invokerType.parameterCount(); i < lambdaForm.names.length; i++) {
++        for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
 +            Name name = lambdaForm.names[i];
 +            MemberName member = name.function.member();
 +
@@ -3313,15 +3672,15 @@
 +
 +    static private Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
 +        // Sample classes from each package we are willing to bind to statically:
-+        Object.class,
-+        Arrays.class,
-+        Unsafe.class
++        java.lang.Object.class,
++        java.util.Arrays.class,
++        sun.misc.Unsafe.class
 +        //MethodHandle.class already covered
 +    };
 +
-+    boolean isStaticallyInvocable(MemberName member) {
++    static boolean isStaticallyInvocable(MemberName member) {
 +        if (member == null)  return false;
-+        assert !member.isConstructor();
++        if (member.isConstructor())  return false;
 +        Class<?> cls = member.getDeclaringClass();
 +        if (cls.isArray() || cls.isPrimitive())
 +            return false;  // FIXME
@@ -3336,7 +3695,7 @@
 +        return false;
 +    }
 +
-+    boolean isStaticallyNameable(Class<?> cls) {
++    static boolean isStaticallyNameable(Class<?> cls) {
 +        while (cls.isArray())
 +            cls = cls.getComponentType();
 +        if (cls.isPrimitive())
@@ -3361,10 +3720,9 @@
 +     */
 +    void emitStaticInvoke(MemberName member, Name name) {
 +        assert(member.equals(name.function.member()));
-+        assert(member.isMethod());
++        String cname = getInternalName(member.getDeclaringClass());
 +        String mname = member.getName();
-+        String mtype = member.getMethodType().toMethodDescriptorString();
-+        String cname = getInternalName(member.getDeclaringClass());
++        String mtype;
 +        byte refKind = member.getReferenceKind();
 +        if (refKind == REF_invokeSpecial) {
 +            // in order to pass the verifier, we need to convert this to invokevirtual in all cases
@@ -3378,22 +3736,26 @@
 +        }
 +
 +        // invocation
++        if (member.isMethod()) {
++            mtype = member.getMethodType().toMethodDescriptorString();
++            mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype);
++        } else {
++            mtype = MethodType.toFieldDescriptorString(member.getFieldType());
++            mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
++        }
++    }
++    int refKindOpcode(byte refKind) {
 +        switch (refKind) {
-+        case REF_invokeVirtual:
-+            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cname, mname, mtype);
-+            break;
-+        case REF_invokeStatic:
-+            mv.visitMethodInsn(Opcodes.INVOKESTATIC,  cname, mname, mtype);
-+            break;
-+        case REF_invokeSpecial:
-+            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, cname, mname, mtype);
-+            break;
-+        case REF_invokeInterface:
-+            mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, cname, mname, mtype);
-+            break;
-+        default:
-+            throw new InternalError(member.toString());
-+        }
++        case REF_invokeVirtual:      return Opcodes.INVOKEVIRTUAL;
++        case REF_invokeStatic:       return Opcodes.INVOKESTATIC;
++        case REF_invokeSpecial:      return Opcodes.INVOKESPECIAL;
++        case REF_invokeInterface:    return Opcodes.INVOKEINTERFACE;
++        case REF_getField:           return Opcodes.GETFIELD;
++        case REF_putField:           return Opcodes.PUTFIELD;
++        case REF_getStatic:          return Opcodes.GETSTATIC;
++        case REF_putStatic:          return Opcodes.PUTSTATIC;
++        }
++        throw new InternalError("refKind="+refKind);
 +    }
 +
 +    /**
@@ -3471,6 +3833,8 @@
 +            Name n = (Name) arg;
 +            emitLoadInsn(n.type, n.index());
 +            emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
++        } else if ((arg == null || arg instanceof String) && ptype == 'L') {
++            emitConst(arg);
 +        } else {
 +            if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
 +                emitConst(arg);
@@ -3827,7 +4191,7 @@
 -        invoker = lookupInvoker("invokeExact");
 +        MethodType mtype = targetType;
 +        LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_INVOKER);
-+        invoker = new BoundMethodHandle.BMH_L(mtype.invokerType(), lform, mtype);
++        invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
 +        assert(checkInvoker(invoker));
          exactInvoker = invoker;
          return invoker;
@@ -3840,7 +4204,7 @@
 +        MethodType mtype = targetType;
 +        prepareForGenericCall(mtype);
 +        LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_INVOKER);
-+        invoker = new BoundMethodHandle.BMH_L(mtype.invokerType(), lform, mtype);
++        invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
 +        assert(checkInvoker(invoker));
          generalInvoker = invoker;
          return invoker;
@@ -3925,7 +4289,7 @@
          assert(invoker.type().equals(targetType));
          uninitializedCallSite = invoker;
          return invoker;
-@@ -155,4 +184,210 @@
+@@ -155,4 +184,208 @@
      public String toString() {
          return "Invokers"+targetType;
      }
@@ -3986,7 +4350,7 @@
 +        assert(names.length == nameCursor);
 +        if (MTYPE_ARG >= INARG_LIMIT) {
 +            assert(names[MTYPE_ARG] == null);
-+            names[MTYPE_ARG] = BoundMethodHandle.getterName(names[THIS_MH], 'L', 0);
++            names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0);
 +            // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM)
 +        }
 +
@@ -4065,9 +4429,7 @@
 +            form.genericInvoker = gamh;
 +            return gamh;
 +        } catch (Exception ex) {
-+            Error err = new InternalError("Exception while resolving inexact invoke", ex);
-+            err.initCause(ex);
-+            throw err;
++            throw new InternalError("Exception while resolving inexact invoke", ex);
 +        }
 +    }
 +
@@ -4140,7 +4502,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/src/share/classes/java/lang/invoke/LambdaForm.java
-@@ -0,0 +1,1561 @@
+@@ -0,0 +1,1620 @@
 +/*
 + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -4179,6 +4541,8 @@
 +import sun.invoke.util.Wrapper;
 +import static java.lang.invoke.MethodHandleStatics.*;
 +import static java.lang.invoke.MethodHandleNatives.Constants.*;
++import java.lang.reflect.Field;
++import java.util.Objects;
 +
 +/**
 + * The symbolic, non-executable form of a method handle's invocation semantics.
@@ -4264,6 +4628,7 @@
 +    final Name[] names;
 +    final String debugName;
 +    MemberName vmentry;   // low-level behavior, or null if not yet prepared
++    private boolean isCompiled;
 +
 +    // Caches for common structural transforms:
 +    LambdaForm[] bindCache;
@@ -4571,6 +4936,9 @@
 +     * as a sort of pre-invocation linkage step.)
 +     */
 +    public void prepare() {
++        if (COMPILE_THRESHOLD == 0) {
++            compileToBytecode();
++        }
 +        if (this.vmentry != null) {
 +            // already prepared (e.g., a primitive DMH invoker form)
 +            return;
@@ -4584,10 +4952,14 @@
 +    MemberName compileToBytecode() {
 +        MethodType invokerType = methodType();
 +        assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
++        if (vmentry != null && isCompiled) {
++            return vmentry;  // already compiled somehow
++        }
 +        try {
 +            vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
 +            if (TRACE_INTERPRETER)
 +                traceInterpreter("compileToBytecode", this);
++            isCompiled = true;
 +            return vmentry;
 +        } catch (Error | Exception ex) {
 +            throw new InternalError(this.toString(), ex);
@@ -4715,7 +5087,13 @@
 +    }
 +
 +    /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
-+    private static final int invocationThreshold = 30;
++    private static final int COMPILE_THRESHOLD;
++    static {
++        if (MethodHandleStatics.COMPILE_THRESHOLD != null)
++            COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD;
++        else
++            COMPILE_THRESHOLD = 30;  // default value
++    }
 +    private int invocationCounter = 0;
 +
 +    @Hidden
@@ -4723,9 +5101,10 @@
 +    Object interpretWithArguments(Object... argumentValues) throws Throwable {
 +        if (TRACE_INTERPRETER)
 +            return interpretWithArgumentsTracing(argumentValues);
-+        if (invocationCounter < invocationThreshold) {
++        if (COMPILE_THRESHOLD != 0 &&
++            invocationCounter < COMPILE_THRESHOLD) {
 +            invocationCounter++;  // benign race
-+            if (invocationCounter >= invocationThreshold) {
++            if (invocationCounter >= COMPILE_THRESHOLD) {
 +                // Replace vmentry with a bytecode version of this LF.
 +                compileToBytecode();
 +            }
@@ -4758,10 +5137,10 @@
 +
 +    Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
 +        traceInterpreter("[ interpretWithArguments", this, argumentValues);
-+        if (invocationCounter < invocationThreshold) {
++        if (invocationCounter < COMPILE_THRESHOLD) {
 +            int ctr = invocationCounter++;  // benign race
 +            traceInterpreter("| invocationCounter", ctr);
-+            if (invocationCounter >= invocationThreshold) {
++            if (invocationCounter >= COMPILE_THRESHOLD) {
 +                compileToBytecode();
 +            }
 +        }
@@ -4900,17 +5279,21 @@
 +        return new LambdaForm(debugName, arity2, names2, result2);
 +    }
 +
-+    LambdaForm bind(char basicType, int namePos, int dataValuePos) {
-+        Name dataValueName = BoundMethodHandle.getterName(names[0], basicType, dataValuePos);
-+        return bind(names[namePos], dataValueName);
-+    }
-+
-+    LambdaForm bind(Name name, Name binding) {
++    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) {
@@ -4925,9 +5308,32 @@
 +        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);
@@ -5077,13 +5483,16 @@
 +            this.resolvedHandle = resolvedHandle;
 +        }
 +
-+        // The next 2 constructors are used to break circular dependencies on MH.invokeStatic, etc.
++        // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc.
 +        // Any LambdaForm containing such a member is not interpretable.
 +        // This is OK, since all such LFs are prepared with special primitive vmentry points.
 +        // And even without the resolvedHandle, the name can still be compiled and optimized.
 +        NamedFunction(Method method) {
 +            this(new MemberName(method));
 +        }
++        NamedFunction(Field field) {
++            this(new MemberName(field));
++        }
 +        NamedFunction(MemberName member) {
 +            this.member = member;
 +            this.resolvedHandle = null;
@@ -5107,6 +5516,13 @@
 +            return this.member != null && this.member.equals(that.member);
 +        }
 +
++        @Override
++        public int hashCode() {
++            if (member != null)
++                return member.hashCode();
++            return super.hashCode();
++        }
++
 +        // Put the predefined NamedFunction invokers into the table.
 +        static void initializeInvokers() {
 +            for (MemberName m : MemberName.getFactory().getMethods(NamedFunction.class, false, null, null, null)) {
@@ -5280,21 +5696,23 @@
 +        }
 +
 +        MemberName member() {
-+            if (member == null) {
-+                return resolvedHandle.internalMemberName();
-+            }
-+            assert(memberIsConsistent());
++            assert(assertMemberIsConsistent());
 +            return member;
 +        }
 +
-+        boolean memberIsConsistent() {
++        // Called only from assert.
++        private boolean assertMemberIsConsistent() {
 +            if (resolvedHandle instanceof DirectMethodHandle) {
 +                MemberName m = resolvedHandle.internalMemberName();
-+                assert(m.equals(member)) : Arrays.asList(member, m, resolvedHandle);
++                assert(m.equals(member));
 +            }
 +            return true;
 +        }
 +
++        Class<?> memberDeclaringClassOrNull() {
++            return (member == null) ? null : member.getDeclaringClass();
++        }
++
 +        char returnType() {
 +            return basicType(methodType().returnType());
 +        }
@@ -5504,9 +5922,6 @@
 +            return true;
 +        }
 +
-+        public boolean equals(Object x) {
-+            return x instanceof Name && equals((Name)x);
-+        }
 +        /**
 +         * Does this Name precede the given binding node in some canonical order?
 +         * This predicate is used to order data bindings (via insertion sort)
@@ -5536,6 +5951,7 @@
 +            }
 +            return false;
 +        }
++
 +        public boolean equals(Name that) {
 +            if (this == that)  return true;
 +            if (isParam())
@@ -5547,6 +5963,11 @@
 +                this.function.equals(that.function) &&
 +                Arrays.equals(this.arguments, that.arguments);
 +        }
++        @Override
++        public boolean equals(Object x) {
++            return x instanceof Name && equals((Name)x);
++        }
++        @Override
 +        public int hashCode() {
 +            if (isParam())
 +                return index | (type << 8);
@@ -5714,43 +6135,27 @@
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
-@@ -71,19 +73,40 @@
+@@ -38,6 +40,7 @@
+ import java.util.List;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
+ import static java.lang.invoke.MethodHandleStatics.*;
++import java.util.Objects;
+ 
+ /**
+  * A {@code MemberName} is a compact symbolic datum which fully characterizes
+@@ -71,19 +74,14 @@
      private String     name;        // may be null if not yet materialized
      private Object     type;        // may be null if not yet materialized
      private int        flags;       // modifier bits; see reflect.Modifier
+-
+-    private Object     vmtarget;    // VM-specific target value
+-    private int        vmindex;     // method index within class or interface
+-
+-    { vmindex = VM_INDEX_UNINITIALIZED; }
 +    //@Injected JVM_Method* vmtarget;
 +    //@Injected int         vmindex;
 +    private Object     resolution;  // if null, this guy is resolved
  
--    private Object     vmtarget;    // VM-specific target value
--    private int        vmindex;     // method index within class or interface
-+    @Override
-+    public boolean equals(Object other) {
-+        if (this == other) return true;
-+        if (other == null) return false;
-+        if (!(other instanceof MemberName)) return false;
-+        MemberName that = (MemberName) other;
-+        return this.clazz == that.clazz
-+                && this.flags == that.flags
-+                && this.name.equals(that.name)
-+                && typesEqual(this.getType(), that.getType());
-+    }
- 
--    { vmindex = VM_INDEX_UNINITIALIZED; }
-+    private static boolean typesEqual(Object a, Object b) {
-+        if (a == null && b == null)
-+            return true;
-+        if (a == null ^ b == null)
-+            return false;
-+        if (a.getClass() != b.getClass())
-+            return false;
-+        if (a instanceof Class<?>)
-+            return a == b;
-+        if (a instanceof MethodType)
-+            return a.equals(b);
-+        return false;
-+    }
- 
      /** Return the declaring class of this member.
       *  In the case of a bare name and type, the declaring class will be null.
       */
@@ -5761,7 +6166,7 @@
          return clazz;
      }
  
-@@ -105,6 +128,16 @@
+@@ -105,6 +103,16 @@
          return name;
      }
  
@@ -5778,16 +6183,21 @@
      /** Return the declared type of this member, which
       *  must be a method or constructor.
       */
-@@ -140,7 +173,7 @@
+@@ -140,9 +148,11 @@
       *  a reference to declaring class.  For static methods, it is the same as the declared type.
       */
      public MethodType getInvocationType() {
 -        MethodType itype = getMethodType();
 +        MethodType itype = getMethodOrFieldType();
++        if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
++            return itype.changeReturnType(clazz);
          if (!isStatic())
-             itype = itype.insertParameterTypes(0, clazz);
+-            itype = itype.insertParameterTypes(0, clazz);
++            return itype.insertParameterTypes(0, clazz);
          return itype;
-@@ -208,9 +241,94 @@
+     }
+ 
+@@ -208,9 +218,98 @@
          return (flags & RECOGNIZED_MODIFIERS);
      }
  
@@ -5872,6 +6282,10 @@
 +        assert(getReferenceKind() == oldKind);
 +        assert(MethodHandleNatives.refKindIsValid(refKind));
 +        flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
++//        if (isConstructor() && refKind != REF_newInvokeSpecial)
++//            flags += (IS_METHOD - IS_CONSTRUCTOR);
++//        else if (refKind == REF_newInvokeSpecial && isMethod())
++//            flags += (IS_CONSTRUCTOR - IS_METHOD);
 +        return this;
 +    }
 +
@@ -5882,7 +6296,7 @@
      }
  
      private boolean testFlags(int mask, int value) {
-@@ -223,6 +341,17 @@
+@@ -223,6 +322,17 @@
          return !testFlags(mask, 0);
      }
  
@@ -5900,7 +6314,7 @@
      /** Utility method to query the modifier flags of this member. */
      public boolean isStatic() {
          return Modifier.isStatic(flags);
-@@ -243,10 +372,22 @@
+@@ -243,10 +353,22 @@
      public boolean isFinal() {
          return Modifier.isFinal(flags);
      }
@@ -5923,7 +6337,7 @@
      // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
  
      // unofficial modifier flags, used by HotSpot:
-@@ -279,15 +420,12 @@
+@@ -279,15 +401,12 @@
              IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
              IS_FIELD       = MN_IS_FIELD,       // field
              IS_TYPE        = MN_IS_TYPE;        // nested type
@@ -5940,7 +6354,7 @@
  
      /** Utility method to query whether this member is a method or constructor. */
      public boolean isInvocable() {
-@@ -318,6 +456,12 @@
+@@ -318,6 +437,12 @@
          return !testAnyFlags(ALL_ACCESS);
      }
  
@@ -5953,7 +6367,7 @@
      /** Initialize a query.   It is not resolved. */
      private void init(Class<?> defClass, String name, Object type, int flags) {
          // defining class is allowed to be null (for a naked name/type pair)
-@@ -328,7 +472,7 @@
+@@ -328,7 +453,7 @@
          this.name = name;
          this.type = type;
          setFlags(flags);
@@ -5962,7 +6376,7 @@
      }
  
      private void expandFromVM() {
-@@ -339,39 +483,83 @@
+@@ -339,39 +464,91 @@
      }
  
      // Capturing information from the Core Reflection API:
@@ -5996,8 +6410,16 @@
 +    }
 +    public MemberName asSpecial() {
 +        switch (getReferenceKind()) {
-+        case REF_invokeSpecial:  return this;
-+        case REF_invokeVirtual:  return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
++        case REF_invokeSpecial:     return this;
++        case REF_invokeVirtual:     return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
++        case REF_newInvokeSpecial:  return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
++        }
++        throw new IllegalArgumentException(this.toString());
++    }
++    public MemberName asConstructor() {
++        switch (getReferenceKind()) {
++        case REF_invokeSpecial:     return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial);
++        case REF_newInvokeSpecial:  return this;
 +        }
 +        throw new IllegalArgumentException(this.toString());
      }
@@ -6060,7 +6482,7 @@
      }
  
      // bare-bones constructor; the JVM will fill it in
-@@ -386,41 +574,84 @@
+@@ -386,41 +563,89 @@
          }
       }
  
@@ -6081,22 +6503,27 @@
 +        return res;
 +    }
 +
-+    /*
 +    @Override
 +    public int hashCode() {
-+        Object[] elements = { getDeclaringClass(), getName(), getMethodOrFieldType(), getReferenceKind() };
-+        return Arrays.hashCode(elements);
++        return Objects.hash(clazz, flags, name, getType());
 +    }
 +    @Override
 +    public boolean equals(Object that) {
-+        return (that instanceof MemberName && this.sameReference((MemberName)that));
-+    }
-+    */
-+    public boolean sameReference(MemberName that) {
-+        return (this.getDeclaringClass().equals(that.getDeclaringClass()) &&
-+                this.getName().equals(that.getName()) &&
-+                this.getMethodOrFieldType().equals(that.getMethodOrFieldType()) &&
-+                this.getReferenceKind() == that.getReferenceKind());
++        return (that instanceof MemberName && this.equals((MemberName)that));
++    }
++
++    /** Decide if two member names have exactly the same symbolic content.
++     *  Does not take into account any actual class members, so even if
++     *  two member names resolve to the same actual member, they may
++     *  be distinct references.
++     */
++    public boolean equals(MemberName that) {
++        if (this == that)  return true;
++        if (that == null)  return false;
++        return this.clazz == that.clazz
++                && this.flags == that.flags
++                && Objects.equals(this.name, that.name)
++                && Objects.equals(this.getType(), that.getType());
 +    }
  
      // Construction from symbolic parts, for queries:
@@ -6161,7 +6588,7 @@
      }
  
      /** Query whether this member name is resolved.
-@@ -429,15 +660,38 @@
+@@ -429,15 +654,38 @@
       *  (Document?)
       */
      public boolean isResolved() {
@@ -6205,7 +6632,7 @@
      /** Produce a string form of this member name.
       *  For types, it is simply the type's own string (as reported by {@code toString}).
       *  For fields, it is {@code "DeclaringClass.name/type"}.
-@@ -445,6 +699,7 @@
+@@ -445,6 +693,7 @@
       *  If the declaring class is null, the prefix {@code "DeclaringClass."} is omitted.
       *  If the member is unresolved, a prefix {@code "*."} is prepended.
       */
@@ -6213,7 +6640,7 @@
      @Override
      public String toString() {
          if (isType())
-@@ -464,22 +719,12 @@
+@@ -464,22 +713,12 @@
          } else {
              buf.append(type == null ? "(*)*" : getName(type));
          }
@@ -6241,7 +6668,7 @@
          return buf.toString();
      }
      private static String getName(Object obj) {
-@@ -488,19 +733,6 @@
+@@ -488,19 +727,6 @@
          return String.valueOf(obj);
      }
  
@@ -6261,7 +6688,7 @@
      public IllegalAccessException makeAccessException(String message, Object from) {
          message = message + ": "+ toString();
          if (from != null)  message += ", from " + from;
-@@ -518,14 +750,19 @@
+@@ -518,14 +744,19 @@
      }
      public ReflectiveOperationException makeAccessException() {
          String message = message() + ": "+ toString();
@@ -6286,7 +6713,7 @@
      }
  
      /** Actually making a query requires an access check. */
-@@ -539,7 +776,7 @@
+@@ -539,7 +770,7 @@
          private Factory() { } // singleton pattern
          static Factory INSTANCE = new Factory();
  
@@ -6295,7 +6722,7 @@
  
          /// Queries
          List<MemberName> getMembers(Class<?> defc,
-@@ -573,14 +810,14 @@
+@@ -573,14 +804,14 @@
                  // JVM returned to us with an intentional overflow!
                  totalCount += buf.length;
                  int excess = bufCount - buf.length;
@@ -6312,7 +6739,7 @@
              if (bufs != null) {
                  for (MemberName[] buf0 : bufs) {
                      Collections.addAll(result, buf0);
-@@ -599,47 +836,29 @@
+@@ -599,47 +830,29 @@
              }
              return result;
          }
@@ -6377,7 +6804,7 @@
          }
          /** Produce a resolved version of the given member.
           *  Super types are searched (for inherited members) if {@code searchSupers} is true.
-@@ -649,16 +868,29 @@
+@@ -649,16 +862,29 @@
           */
          public
          <NoSuchMemberException extends ReflectiveOperationException>
@@ -6414,7 +6841,7 @@
 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,8 +26,10 @@
+@@ -26,9 +26,13 @@
  package java.lang.invoke;
  
  
@@ -6425,9 +6852,12 @@
 +import sun.misc.Unsafe;
 +
  import static java.lang.invoke.MethodHandleStatics.*;
++import java.util.logging.Level;
++import java.util.logging.Logger;
  
  /**
-@@ -208,8 +210,8 @@
+  * A method handle is a typed, directly executable reference to an underlying method,
+@@ -208,8 +212,8 @@
   * refers directly to an associated {@code CONSTANT_Methodref},
   * {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
   * constant pool entry.
@@ -6438,7 +6868,7 @@
   * <p>
   * Method handles produced by lookups or constant loads from methods or
   * constructors with the variable arity modifier bit ({@code 0x0080})
-@@ -224,6 +226,19 @@
+@@ -224,6 +228,19 @@
   * (E.g., if a non-static method handle is obtained via {@code ldc},
   * the type of the receiver is the class named in the constant pool entry.)
   * <p>
@@ -6458,7 +6888,7 @@
   * When a method handle to a virtual method is invoked, the method is
   * always looked up in the receiver (that is, the first argument).
   * <p>
-@@ -390,39 +405,8 @@
+@@ -390,39 +407,8 @@
   * @author John Rose, JSR 292 EG
   */
  public abstract class MethodHandle {
@@ -6498,7 +6928,7 @@
      /**
       * Internal marker interface which distinguishes (to the Java compiler)
       * those methods which are <a href="MethodHandle.html#sigpoly">signature polymorphic</a>.
-@@ -431,7 +415,9 @@
+@@ -431,7 +417,9 @@
      @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
      @interface PolymorphicSignature { }
  
@@ -6509,7 +6939,7 @@
  
      /**
       * Reports the type of this method handle.
-@@ -448,9 +434,13 @@
+@@ -448,9 +436,13 @@
       * the {@code java.lang.invoke} package.
       */
      // @param type type (permanently assigned) of the new method handle
@@ -6525,7 +6955,7 @@
      }
  
      /**
-@@ -506,6 +496,46 @@
+@@ -506,6 +498,46 @@
      public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
  
      /**
@@ -6572,7 +7002,7 @@
       * Performs a variable arity invocation, passing the arguments in the given array
       * to the method handle, as if via an inexact {@link #invoke invoke} from a call site
       * which mentions only the type {@code Object}, and whose arity is the length
-@@ -557,6 +587,7 @@
+@@ -557,6 +589,7 @@
       */
      public Object invokeWithArguments(Object... arguments) throws Throwable {
          int argc = arguments == null ? 0 : arguments.length;
@@ -6580,7 +7010,7 @@
          MethodType type = type();
          if (type.parameterCount() != argc || isVarargsCollector()) {
              // simulate invoke
-@@ -690,7 +721,7 @@
+@@ -690,7 +723,7 @@
          if (!type.isConvertibleTo(newType)) {
              throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
          }
@@ -6589,7 +7019,17 @@
      }
  
      /**
-@@ -790,7 +821,7 @@
+@@ -772,7 +805,8 @@
+      */
+     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
+         asSpreaderChecks(arrayType, arrayLength);
+-        return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
++        int spreadArgPos = type.parameterCount() - arrayLength;
++        return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
+     }
+ 
+     private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
+@@ -790,7 +824,7 @@
                  }
              }
              if (sawProblem) {
@@ -6598,17 +7038,30 @@
                  for (int i = nargs - arrayLength; i < nargs; i++) {
                      ptypes.set(i, arrayElement);
                  }
-@@ -1056,7 +1087,8 @@
+@@ -885,8 +919,12 @@
+      */
+     public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
+         asCollectorChecks(arrayType, arrayLength);
++        int collectArgPos = type().parameterCount()-1;
++        MethodHandle target = this;
++        if (arrayType != type().parameterType(collectArgPos))
++            target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
+         MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
+-        return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
++        return MethodHandleImpl.makeCollectArguments(target, collector, collectArgPos, false);
+     }
+ 
+     // private API: return true if last param exactly matches arrayType
+@@ -1056,7 +1094,7 @@
          boolean lastMatch = asCollectorChecks(arrayType, 0);
          if (isVarargsCollector() && lastMatch)
              return this;
 -        return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
-+        //return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
 +        return MethodHandleImpl.makeVarargsCollector(this, arrayType);
      }
  
      /**
-@@ -1155,14 +1187,13 @@
+@@ -1155,14 +1193,13 @@
       */
      public MethodHandle bindTo(Object x) {
          Class<?> ptype;
@@ -6629,7 +7082,7 @@
      }
  
      /**
-@@ -1183,11 +1214,117 @@
+@@ -1183,11 +1220,178 @@
      @Override
      public String toString() {
          if (DEBUG_METHOD_HANDLE_NAMES)  return debugString();
@@ -6655,7 +7108,6 @@
 +        if (argc != 0) {
 +            Class<?> arrayType = type().parameterType(argc-1);
 +            if (arrayType.isArray()) {
-+                //return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
 +                return MethodHandleImpl.makeVarargsCollector(this, arrayType);
 +            }
 +        }
@@ -6664,9 +7116,9 @@
 +    /*non-public*/
 +    MethodHandle viewAsType(MethodType newType) {
 +        // No actual conversions, just a new view of the same method.
-+        MethodHandle mh = MethodHandleImpl.convertArguments(this, newType, type(), 0);
-+        if (mh == null)  throw new InternalError();
-+        return mh;
++        if (!type.isViewableAs(newType))
++            throw new InternalError();
++        return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
 +    }
 +
 +    // Decoding
@@ -6694,7 +7146,7 @@
 +
 +    /*non-public*/ MethodHandle convertArguments(MethodType newType) {
 +        // Override this if it can be improved.
-+        return MethodHandleImpl.convertArguments(this, newType, 1);
++        return MethodHandleImpl.makePairwiseConvert(this, newType, 1);
 +    }
 +
 +    /*non-public*/
@@ -6741,23 +7193,86 @@
 +    }
 +
 +    /*non-public*/
-+    private BoundMethodHandle rebind() {
++    MethodHandle rebind() {
 +        // Bind 'this' into a new invoker, of the known class BMH.
 +        MethodType type2 = type();
-+        LambdaForm form2 = BoundMethodHandle.reinvokerForm(type2.basicType());
++        LambdaForm form2 = reinvokerForm(type2.basicType());
 +        // form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
-+        return new BoundMethodHandle.BMH_L(type2, form2, this);
++        return BoundMethodHandle.bindSingle(type2, form2, this);
++    }
++
++    /*non-public*/
++    MethodHandle reinvokerTarget() {
++        throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this);
++    }
++
++    /** Create a LF which simply reinvokes a target of the given basic type.
++     *  The target MH must override {@link #reinvokerTarget} to provide the target.
++     */
++    static LambdaForm reinvokerForm(MethodType mtype) {
++        mtype = mtype.basicType();
++        LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
++        if (reinvoker != null)  return reinvoker;
++        MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
++        final int THIS_BMH    = 0;
++        final int ARG_BASE    = 1;
++        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
++        int nameCursor = ARG_LIMIT;
++        final int NEXT_MH     = nameCursor++;
++        final int REINVOKE    = nameCursor++;
++        LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
++        names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
++        Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
++        targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
++        names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
++        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
++    }
++
++    private static final LambdaForm.NamedFunction NF_reinvokerTarget;
++    static {
++        try {
++            NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class
++                .getDeclaredMethod("reinvokerTarget"));
++        } catch (ReflectiveOperationException ex) {
++            throw new InternalError(ex);
++        }
++    }
++
++    /**
++     * Replace the old lambda form of this method handle with a new one.
++     * The new one must be functionally equivalent to the old one.
++     * Threads may continue running the old form indefinitely,
++     * but it is likely that the new one will be preferred for new executions.
++     * Use with discretion.
++     * @param newForm
++     */
++    /*non-public*/
++    void updateForm(LambdaForm newForm) {
++        if (form == newForm)  return;
++        // ISSUE: Should we have a memory fence here?
++        UNSAFE.putObject(this, FORM_OFFSET, newForm);
++        this.form.prepare();  // as in MethodHandle.<init>
++    }
++
++    private static final long FORM_OFFSET;
++    static {
++        try {
++            FORM_OFFSET = UNSAFE.objectFieldOffset(MethodHandle.class.getDeclaredField("form"));
++        } catch (ReflectiveOperationException ex) {
++            throw new InternalError(ex);
++        }
      }
  }
 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
-@@ -26,17 +26,15 @@
+@@ -26,17 +26,14 @@
  package java.lang.invoke;
  
  import sun.invoke.util.VerifyType;
 -import java.security.AccessController;
 -import java.security.PrivilegedAction;
++
  import java.util.ArrayList;
  import java.util.Arrays;
 -import java.util.Collections;
@@ -6766,13 +7281,12 @@
  import sun.invoke.empty.Empty;
  import sun.invoke.util.ValueConversions;
  import sun.invoke.util.Wrapper;
- import sun.misc.Unsafe;
+-import sun.misc.Unsafe;
 +import static java.lang.invoke.LambdaForm.*;
-+import static java.lang.invoke.MethodHandleNatives.Constants.*;
  import static java.lang.invoke.MethodHandleStatics.*;
  import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
  
-@@ -47,556 +45,270 @@
+@@ -47,673 +44,471 @@
  /*non-public*/ abstract class MethodHandleImpl {
      /// Factory methods to create method handles:
  
@@ -6812,7 +7326,20 @@
 -            // (in the case of invokespecial, this will be the calling class)
 -            Class<?> recvType = method.getDeclaringClass();
 -            mtype = mtype.insertParameterTypes(0, recvType);
--        }
++    static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
++        if (!arrayClass.isArray())
++            throw newIllegalArgumentException("not an array: "+arrayClass);
++        MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter);
++        MethodType srcType = accessor.type().erase();
++        MethodType lambdaType = srcType.invokerType();
++        Name[] names = arguments(1, lambdaType);
++        Name[] args  = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
++        names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
++        LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
++        MethodHandle mh = new SimpleMethodHandle(srcType, form);
++        if (ArrayAccessor.needCast(arrayClass)) {
++            mh = mh.bindTo(arrayClass);
+         }
 -        DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
 -        if (!mh.isValid())
 -            throw method.makeAccessException("no direct method handle", lookupClass);
@@ -6826,26 +7353,8 @@
 -                return AdapterMethodHandle.makeVarargsCollector(mh, arrayType);
 -        }
 -        throw method.makeAccessException("cannot make variable arity", null);
-+    /** Cache frequently used stuff. */
-+    static final Unsafe UNSAFE = Unsafe.getUnsafe();
-+
-+    static MethodHandle makeAllocator(MethodHandle rawConstructor) {
-+        MethodType rawConType = rawConstructor.type();
-+        Class<?> allocateClass = rawConType.parameterType(0);
-+        MethodType srcType = rawConType.dropParameterTypes(0, 1).changeReturnType(allocateClass);
-+        final int THIS_MH     = 0;
-+        final int ARG_BASE    = 1;
-+        final int ARG_LIMIT   = ARG_BASE + srcType.parameterCount();
-+        int nameCursor = ARG_LIMIT;
-+        final int NEW_OBJ     = nameCursor++;
-+        final int CALL_CTOR   = nameCursor++;
-+        Name[] names = arguments(nameCursor - ARG_LIMIT, srcType.invokerType());
-+        names[NEW_OBJ] = new Name(AllocatorData.UNSAFE_ALLOCATEINSTANCE, allocateClass);
-+        Object[] conArgs = Arrays.copyOfRange(names, ARG_BASE-1, ARG_LIMIT, Object[].class);
-+        conArgs[0] = names[NEW_OBJ];
-+        names[CALL_CTOR] = new Name(rawConstructor, conArgs);
-+        LambdaForm form = new LambdaForm("newInvokeSpecial", ARG_LIMIT, names, NEW_OBJ);
-+        return new SimpleMethodHandle(srcType, form);
++        mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter));
++        return mh;
      }
  
 -    static
@@ -6869,49 +7378,14 @@
 -        ctype = ctype.dropParameterTypes(0, 1);
 -        MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator);
 -        return fold;
-+    static final class AllocatorData {
-+        static final MethodHandle UNSAFE_ALLOCATEINSTANCE;
-+        static {
-+            try {
-+                assert(IMPL_LOOKUP != null) : "bootstrap problem";
-+                UNSAFE_ALLOCATEINSTANCE =
-+                    IMPL_LOOKUP.bind(UNSAFE, "allocateInstance", MethodType.methodType(Object.class, Class.class));
-+            } catch (NoSuchMethodException | IllegalAccessException e) {
-+                throw new InternalError(e);
-+            }
-+        }
-     }
- 
--    static final class AllocateObject /*<C>*/ extends BoundMethodHandle {
--        private static final Unsafe unsafe = Unsafe.getUnsafe();
-+    static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
-+        if (!arrayClass.isArray())
-+            throw newIllegalArgumentException("not an array: "+arrayClass);
-+        MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter);
-+        MethodType srcType = accessor.type().erase();
-+        MethodType lambdaType = srcType.invokerType();
-+        Name[] names = arguments(1, lambdaType);
-+        Name[] args  = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
-+        names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
-+        LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
-+        MethodHandle mh = new SimpleMethodHandle(srcType, form);
-+        if (ArrayAccessor.needCast(arrayClass)) {
-+            mh = mh.bindTo(arrayClass);
-+        }
-+        mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter));
-+        return mh;
-+    }
- 
--        private final Class<?> /*<C>*/ allocateClass;
+-    }
 +    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
  
--        // for allocation only:
--        private AllocateObject(Class<?> /*<C>*/ allocateClass) {
--            super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class)));
--            this.allocateClass = allocateClass;
+-    static final class AllocateObject /*<C>*/ extends BoundMethodHandle {
+-        private static final Unsafe unsafe = Unsafe.getUnsafe();
 +        static int     getElementI(int[]     a, int i)            { return              a[i]; }
 +        static long    getElementJ(long[]    a, int i)            { return              a[i]; }
 +        static float   getElementF(float[]   a, int i)            { return              a[i]; }
@@ -6921,7 +7395,8 @@
 +        static short   getElementS(short[]   a, int i)            { return              a[i]; }
 +        static char    getElementC(char[]    a, int i)            { return              a[i]; }
 +        static Object  getElementL(Object[]  a, int i)            { return              a[i]; }
-+
+ 
+-        private final Class<?> /*<C>*/ allocateClass;
 +        static void    setElementI(int[]     a, int i, int     x) {              a[i] = x; }
 +        static void    setElementJ(long[]    a, int i, long    x) {              a[i] = x; }
 +        static void    setElementF(float[]   a, int i, float   x) {              a[i] = x; }
@@ -6931,7 +7406,11 @@
 +        static void    setElementS(short[]   a, int i, short   x) {              a[i] = x; }
 +        static void    setElementC(char[]    a, int i, char    x) {              a[i] = x; }
 +        static void    setElementL(Object[]  a, int i, Object  x) {              a[i] = x; }
-+
+ 
+-        // for allocation only:
+-        private AllocateObject(Class<?> /*<C>*/ allocateClass) {
+-            super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class)));
+-            this.allocateClass = allocateClass;
 +        static Object  getElementL(Class<?> arrayClass, Object[] a, int i)           { arrayClass.cast(a); return a[i]; }
 +        static void    setElementL(Class<?> arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; }
 +
@@ -7001,50 +7480,130 @@
 -        // Use sun. misc.Unsafe to dig up the dirt on the field.
 -        FieldAccessor accessor = new FieldAccessor(member, isSetter);
 -        return accessor;
-+    static MethodHandle makeFieldAccessor(byte refKind, MemberName field, Class<?> receiver) {
-+        boolean isStatic = !MethodHandleNatives.refKindHasReceiver(refKind);
-+        MethodHandle accessor = FieldAccessor.getAccessor(refKind, field, receiver);
-+
-+        MethodType srcType = accessor.type();
-+        MethodType mhType = srcType.dropParameterTypes(isStatic ? 0 : 1, 2);
-+        MethodType lambdaType = mhType.insertParameterTypes(0, isStatic ? BoundMethodHandle.BMH_JL.class : BoundMethodHandle.BMH_J.class);
-+
-+        Name[] names = arguments(3, lambdaType);
-+
-+        final int _ARG    = 1;                // at this index in the names array, the object to be accessed is stored
-+        final int _VALUE  = isStatic ? 1 : 2; // if this is for write access, the value to be written is stored at this index
-+        final int _BASE   = names.length - 3; // the slots for _BASE and _NPE are shared
-+        final int _NPE    = names.length - 3; // (the NPE check is used in the non-static case only, where _BASE is not required)
-+        final int _OFFSET = names.length - 2;
-+        final int _RESULT = names.length - 1;
-+
-+        if (isStatic) {
-+            names[_OFFSET] = new Name(BoundMethodHandle.MH_argJ0, names[0]);
-+            names[_BASE]   = new Name(BoundMethodHandle.MH_argL1, names[0]);
-+        } else {
-+            names[_NPE] = new Name(FieldAccessor.OBJECT_GETCLASS, names[_ARG]); // NPE check
-+            names[_OFFSET] = new Name(BoundMethodHandle.MH_argJ0, names[0]);
-+        }
-+
-+        Object[] args;
-+        if (refKind == REF_putField || refKind == REF_putStatic) {
-+            args = new Object[] { names[isStatic ? _BASE : _ARG], names[_OFFSET], names[_VALUE] };
-+        } else {
-+            args = new Object[] { names[isStatic ? _BASE : _ARG], names[_OFFSET] };
-+        }
-+        names[_RESULT] = new Name(accessor, args);
-+        LambdaForm form = new LambdaForm(MethodHandleNatives.refKindName(refKind), lambdaType.parameterCount(), names);
-+
-+        BoundMethodHandle mh;
-+        if (isStatic) {
-+            long offset = MethodHandleNatives.staticFieldOffset(field);
-+            Object base = MethodHandleNatives.staticFieldBase(field);
-+            mh = new BoundMethodHandle.BMH_JL(mhType, form, offset, base);
-+        } else {
-+            long offset = MethodHandleNatives.objectFieldOffset(field);
-+            mh = new BoundMethodHandle.BMH_J(mhType, form, offset);
-+        }
-+        return mh;
++    /**
++     * Create a JVM-level adapter method handle to conform the given method
++     * handle to the similar newType, using only pairwise argument conversions.
++     * For each argument, convert incoming argument to the exact type needed.
++     * The argument conversions allowed are casting, boxing and unboxing,
++     * integral widening or narrowing, and floating point widening or narrowing.
++     * @param srcType required call type
++     * @param target original method handle
++     * @param level which strength of conversion is allowed
++     * @return an adapter to the original handle with the desired new type,
++     *          or the original target if the types are already identical
++     *          or null if the adaptation cannot be made
++     */
++    static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) {
++        assert(level >= 0 && level <= 2);
++        MethodType dstType = target.type();
++        assert(dstType.parameterCount() == target.type().parameterCount());
++        if (srcType == dstType)
++            return target;
++
++        // Calculate extra arguments (temporaries) required in the names array.
++        // FIXME: Use an ArrayList<Name>.  Some arguments require more than one conversion step.
++        int extra = 0;
++        for (int i = 0; i < srcType.parameterCount(); i++) {
++            Class<?> src = srcType.parameterType(i);
++            Class<?> dst = dstType.parameterType(i);
++            if (!VerifyType.isNullConversion(src, dst)) {
++                extra++;
++            }
++        }
++
++        Class<?> needReturn = srcType.returnType();
++        Class<?> haveReturn = dstType.returnType();
++        boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
++
++        // Now build a LambdaForm.
++        MethodType lambdaType = srcType.invokerType();
++        Name[] names = arguments(extra + 1, lambdaType);
++        int[] indexes = new int[lambdaType.parameterCount()];
++
++        MethodType midType = dstType;
++        for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) {
++            Class<?> src = srcType.parameterType(i);
++            Class<?> dst = midType.parameterType(i);
++
++            if (VerifyType.isNullConversion(src, dst)) {
++                // do nothing: difference is trivial
++                indexes[i] = argIndex;
++                continue;
++            }
++
++            // Work the current type backward toward the desired caller type:
++            midType = midType.changeParameterType(i, src);
++
++            // Tricky case analysis follows.
++            MethodHandle fn = null;
++            if (src.isPrimitive()) {
++                if (dst.isPrimitive()) {
++                    fn = ValueConversions.convertPrimitive(src, dst);
++                } else {
++                    Wrapper w = Wrapper.forPrimitiveType(src);
++                    MethodHandle boxMethod = ValueConversions.box(w);
++                    if (dst == w.wrapperType())
++                        fn = boxMethod;
++                    else
++                        fn = boxMethod.asType(MethodType.methodType(dst, src));
++                }
++            } else {
++                if (dst.isPrimitive()) {
++                    // Caller has boxed a primitive.  Unbox it for the target.
++                    Wrapper w = Wrapper.forPrimitiveType(dst);
++                    if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType())) {
++                        fn = ValueConversions.unbox(dst);
++                    } else if (src == Object.class || !Wrapper.isWrapperType(src)) {
++                        // Examples:  Object->int, Number->int, Comparable->int; Byte->int, Character->int
++                        // must include additional conversions
++                        // src must be examined at runtime, to detect Byte, Character, etc.
++                        MethodHandle unboxMethod = (level == 1
++                                                    ? ValueConversions.unbox(dst)
++                                                    : ValueConversions.unboxCast(dst));
++                        fn = unboxMethod;
++                    } else {
++                        // Example: Byte->int
++                        // Do this by reformulating the problem to Byte->byte.
++                        Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
++                        MethodHandle unbox = ValueConversions.unbox(srcPrim);
++                        // Compose the two conversions.  FIXME:  should make two Names for this job
++                        fn = unbox.asType(MethodType.methodType(dst, src));
++                    }
++                } else {
++                    // Simple reference conversion.
++                    // Note:  Do not check for a class hierarchy relation
++                    // between src and dst.  In all cases a 'null' argument
++                    // will pass the cast conversion.
++                    fn = ValueConversions.cast(dst);
++                }
++            }
++            names[tmpIndex] = new Name(fn, names[argIndex]);
++            indexes[i] = tmpIndex;
++            tmpIndex++;
++        }
++        if (retConv) {
++            MethodHandle adjustReturn;
++            if (haveReturn == void.class) {
++                // synthesize a zero value for the given void
++                Object zero = Wrapper.forBasicType(needReturn).zero();
++                adjustReturn = MethodHandles.constant(needReturn, zero);
++            } else {
++                MethodHandle identity = MethodHandles.identity(needReturn);
++                MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
++                adjustReturn = makePairwiseConvert(identity, needConversion, level);
++            }
++            target = makeCollectArguments(adjustReturn, target, 0, false);
++        }
++
++        // Build argument array for the call.
++        Name[] targetArgs = new Name[dstType.parameterCount()];
++        for (int i = 0; i < dstType.parameterCount(); i++) {
++            int idx = indexes[i];
++            targetArgs[i] = names[idx];
++        }
++        names[names.length - 1] = new Name(target, (Object[]) targetArgs);
++        LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
++        return new SimpleMethodHandle(srcType, form);
      }
  
 -    static
@@ -7063,28 +7622,33 @@
 -            if (mhs[0].type().parameterType(0) == Class.class) {
 -                mhs[0] = mhs[0].bindTo(elemClass);
 -                mhs[1] = mhs[1].bindTo(elemClass);
-+    static final class FieldAccessor {
-+        static final MethodHandle OBJECT_GETCLASS;
-+        static {
-+            try {
-+                assert(IMPL_LOOKUP != null) : "bootstrap problem";
-+                OBJECT_GETCLASS =
-+                    IMPL_LOOKUP.findStatic(FieldAccessor.class, "getClass", MethodType.methodType(Object.class, Object.class));
-+            } catch (NoSuchMethodException | IllegalAccessException e) {
-+                throw new InternalError(e);
-             }
+-            }
 -            synchronized (FieldAccessor.ARRAY_CACHE) {}  // memory barrier
 -            FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
 -        }
 -        return mhs[isSetter ? 1 : 0];
--    }
--
++    static MethodHandle makeReferenceIdentity(Class<?> refType) {
++        MethodType lambdaType = MethodType.genericMethodType(1).invokerType();
++        Name[] names = arguments(1, lambdaType);
++        names[names.length - 1] = new Name(ValueConversions.identity(), names[1]);
++        LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names);
++        return new SimpleMethodHandle(MethodType.methodType(refType, refType), form);
+     }
+ 
 -    static final class FieldAccessor /*<C,V>*/ extends BoundMethodHandle {
 -        private static final Unsafe unsafe = Unsafe.getUnsafe();
 -        final Object base;  // for static refs only
 -        final long offset;
 -        final String name;
--
++    static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
++        MethodType type = target.type();
++        int last = type.parameterCount() - 1;
++        if (type.parameterType(last) != arrayType)
++            target = target.asType(type.changeParameterType(last, arrayType));
++        target = target.asFixedArity();  // make sure this attribute is turned off
++        return new AsVarargsCollector(target, target.type(), arrayType);
++    }
+ 
 -        FieldAccessor(MemberName field, boolean isSetter) {
 -            super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic()));
 -            this.offset = (long) field.getVMIndex();
@@ -7093,10 +7657,19 @@
 -        }
 -        @Override
 -        String debugString() { return addTypeString(name, this); }
--
++    static class AsVarargsCollector extends MethodHandle {
++        MethodHandle target;
++        final Class<?> arrayType;
++        MethodHandle cache;
+ 
 -        private static Object nullCheck(Object obj) {
 -            obj.getClass();  // NPE
 -            return obj;
++        AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
++            super(type, reinvokerForm(type));
++            this.target = target;
++            this.arrayType = arrayType;
++            this.cache = target.asCollector(arrayType, 0);
          }
  
 -        int getFieldI(Object /*C*/ obj) { return unsafe.getInt(nullCheck(obj), offset); }
@@ -7117,7 +7690,8 @@
 -        void setFieldC(Object /*C*/ obj, char x) { unsafe.putChar(nullCheck(obj), offset, x); }
 -        Object /*V*/ getFieldL(Object /*C*/ obj) { return unsafe.getObject(nullCheck(obj), offset); }
 -        void setFieldL(Object /*C*/ obj, Object /*V*/ x) { unsafe.putObject(nullCheck(obj), offset, x); }
--
++        @Override MethodHandle reinvokerTarget() { return target; }
+ 
 -        static Object staticBase(final MemberName field) {
 -            if (!field.isStatic())  return null;
 -            return AccessController.doPrivileged(new PrivilegedAction<Object>() {
@@ -7132,11 +7706,9 @@
 -                        }
 -                    }
 -                });
-+        /** Static definition of Object.getClass for null-pointer checking. */
-+        /*non-public*/ static
-+        @ForceInline
-+        Object getClass(Object obj) {
-+            return obj.getClass();
++        @Override
++        public boolean isVarargsCollector() {
++            return true;
          }
  
 -        int getStaticI() { return unsafe.getInt(base, offset); }
@@ -7162,36 +7734,10 @@
 -            String stem;
 -            if (!isStatic)
 -                stem = (!isSetter ? "getField" : "setField");
-+        static String name(byte refKind, MemberName field) {
-+            String prefix = MethodHandleNatives.refKindIsGetter(refKind) ? "get" : "put";
-+            String type   = Wrapper.forBasicType(field.getFieldType()).primitiveSimpleName();
-+            String suffix = field.isVolatile() ? "Volatile" : "";
-+            return prefix + Character.toUpperCase(type.charAt(0)) + type.substring(1) + suffix;
-+        }
-+        static MethodType type(byte refKind, MemberName field) {
-+            Class<?> fieldClass = field.getFieldType();
-+            return type(refKind, Object.class, fieldClass.isPrimitive() ? fieldClass : Object.class);
-+        }
-+        static MethodType strongType(byte refKind, MemberName field, Class<?> receiver) {
-+            Class<?> fieldClass = field.getFieldType();
-+            MethodType type;
-+            if (MethodHandleNatives.refKindHasReceiver(refKind)) {
-+                if (!field.getDeclaringClass().isAssignableFrom(receiver))
-+                    throw new InternalError(field.toString());
-+                type = type(refKind, receiver,     fieldClass);
-+            } else {
-+                type = type(refKind, Object.class, fieldClass);
-+            }
-+            return type.insertParameterTypes(0, Unsafe.class);
-+        }
-+        static MethodType type(byte refKind, Class<?> declaringClass, Class<?> fieldClass) {
-+            if (MethodHandleNatives.refKindIsGetter(refKind))
-+                return MethodType.methodType(fieldClass, declaringClass, long.class);
-             else
+-            else
 -                stem = (!isSetter ? "getStatic" : "setStatic");
 -            return stem + Wrapper.basicTypeChar(vclass);
-+                return MethodType.methodType(void.class, declaringClass, long.class, fieldClass);
-         }
+-        }
 -        static MethodType ftype(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
 -            MethodType type;
 -            if (!isStatic) {
@@ -7213,30 +7759,23 @@
 -            Class<?> evclass = vclass;
 -            if (!evclass.isPrimitive())  evclass = Object.class;
 -            MethodType type = FieldAccessor.ftype(ecclass, evclass, isSetter, isStatic);
-+        static MethodHandle getAccessor(byte refKind, MemberName field, Class<?> receiver) {
-+            if (!MethodHandleNatives.refKindIsField(refKind))
-+                throw newIllegalArgumentException("refKind not a field: " + refKind);
-+            String     name = name(refKind, field);
-+            MethodType type = type(refKind, field);
-             MethodHandle mh;
-             try {
+-            MethodHandle mh;
+-            try {
 -                mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
-+                mh = IMPL_LOOKUP.findVirtual(Unsafe.class, name, type);
-             } catch (ReflectiveOperationException ex) {
-                 throw uncaughtException(ex);
-             }
+-            } catch (ReflectiveOperationException ex) {
+-                throw uncaughtException(ex);
+-            }
 -            if (evclass != vclass || (!isStatic && ecclass != cclass)) {
 -                MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
 -                strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
-+            Class<?> declaringClass = field.getDeclaringClass();
-+            Class<?> fieldClass     = field.getFieldType();
-+            if ((!fieldClass.isPrimitive() && fieldClass != Object.class) || (MethodHandleNatives.refKindHasReceiver(refKind) && declaringClass != Object.class)) {
-+                MethodType strongType = strongType(refKind, field, receiver);
-                 mh = convertArguments(mh, strongType, 0);
-             }
+-                mh = convertArguments(mh, strongType, 0);
+-            }
 -            return mh;
--        }
--
++        @Override
++        public MethodHandle asFixedArity() {
++            return target;
+         }
+ 
 -        /// Support for array element access
 -        static final HashMap<Class<?>, MethodHandle[]> ARRAY_CACHE =
 -                new HashMap<Class<?>, MethodHandle[]>();
@@ -7245,7 +7784,30 @@
 -            if (elemClass.isPrimitive())  return true;
 -            ClassLoader cl = elemClass.getClassLoader();
 -            return cl == null || cl == ClassLoader.getSystemClassLoader();
--        }
++        @Override
++        public MethodHandle asType(MethodType newType) {
++            MethodType type = this.type();
++            int collectArg = type.parameterCount() - 1;
++            int newArity = newType.parameterCount();
++            if (newArity == collectArg+1 &&
++                type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
++                // if arity and trailing parameter are compatible, do normal thing
++                return asFixedArity().asType(newType);
++            }
++            // check cache
++            if (cache.type().parameterCount() == newArity)
++                return cache.asType(newType);
++            // build and cache a collector
++            int arrayLength = newArity - collectArg;
++            MethodHandle collector;
++            try {
++                collector = asFixedArity().asCollector(arrayType, arrayLength);
++            } catch (IllegalArgumentException ex) {
++                throw new WrongMethodTypeException("cannot build collector");
++            }
++            cache = collector;
++            return collector.asType(newType);
+         }
 -        static int getElementI(int[] a, int i) { return a[i]; }
 -        static void setElementI(int[] a, int i, int x) { a[i] = x; }
 -        static long getElementJ(long[] a, int i) { return a[i]; }
@@ -7266,19 +7828,31 @@
 -        static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
 -        static <V> V getElementL(Class<V[]> aclass, V[] a, int i) { return aclass.cast(a)[i]; }
 -        static <V> void setElementL(Class<V[]> aclass, V[] a, int i, V x) { aclass.cast(a)[i] = x; }
--
+ 
 -        static String aname(Class<?> aclass, boolean isSetter) {
 -            Class<?> vclass = aclass.getComponentType();
 -            if (vclass == null)  throw new IllegalArgumentException();
 -            return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(vclass);
--        }
++        @Override
++        MethodHandle setVarargs(MemberName member) {
++            if (member.isVarargs())  return this;
++            return asFixedArity();
+         }
 -        static MethodType atype(Class<?> aclass, boolean isSetter) {
 -            Class<?> vclass = aclass.getComponentType();
 -            if (!isSetter)
 -                return MethodType.methodType(vclass, aclass, int.class);
 -            else
 -                return MethodType.methodType(void.class, aclass, int.class, vclass);
--        }
++
++        @Override
++        MethodHandle viewAsType(MethodType newType) {
++            MethodHandle mh = super.viewAsType(newType);
++            // put back the varargs bit:
++            MethodType type = mh.type();
++            int arity = type.parameterCount();
++            return mh.asVarargsCollector(type.parameterType(arity-1));
+         }
 -        static MethodHandle ahandle(Class<?> aclass, boolean isSetter) {
 -            Class<?> vclass = aclass.getComponentType();
 -            String name = FieldAccessor.aname(aclass, isSetter);
@@ -7302,8 +7876,32 @@
 -                mh = mh.bindTo(caclass);
 -                mh = convertArguments(mh, strongType, 0);
 -            }
-+            mh = mh.bindImmediate(0, 'L', UNSAFE); // bind UNSAFE early
-             return mh;
+-            return mh;
++
++        @Override
++        MemberName internalMemberName() {
++            return asFixedArity().internalMemberName();
++        }
++
++
++        @Override
++        MethodHandle bindArgument(int pos, char basicType, Object value) {
++            return asFixedArity().bindArgument(pos, basicType, value);
++        }
++
++        @Override
++        MethodHandle bindReceiver(Object receiver) {
++            return asFixedArity().bindReceiver(receiver);
++        }
++
++        @Override
++        MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
++            return asFixedArity().dropArguments(srcType, pos, drops);
++        }
++
++        @Override
++        MethodHandle permuteArguments(MethodType newType, int[] reorder) {
++            return asFixedArity().permuteArguments(newType, reorder);
          }
      }
  
@@ -7327,14 +7925,58 @@
 -                    MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
 -                    MethodType newType = target.type().dropParameterTypes(0, 1);
 -                    return convertArguments(bmh, newType, bmh.type(), 0);
--                }
--            }
--        }
++    /** Factory method:  Spread selected argument. */
++    static MethodHandle makeSpreadArguments(MethodHandle target,
++                                            Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
++        MethodType targetType = target.type();
++
++        for (int i = 0; i < spreadArgCount; i++) {
++            Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
++            if (arg == null)  arg = Object.class;
++            targetType = targetType.changeParameterType(spreadArgPos + i, arg);
++        }
++        target = target.asType(targetType);
++
++        MethodType srcType = targetType
++                .replaceParameterTypes(spreadArgPos, spreadArgPos + spreadArgCount, spreadArgType);
++        // Now build a LambdaForm.
++        MethodType lambdaType = srcType.invokerType();
++        Name[] names = arguments(spreadArgCount + 2, lambdaType);
++        int nameCursor = lambdaType.parameterCount();
++        int[] indexes = new int[targetType.parameterCount()];
++
++        for (int i = 0, argIndex = 1; i < targetType.parameterCount() + 1; i++, argIndex++) {
++            Class<?> src = lambdaType.parameterType(i);
++            if (i == spreadArgPos) {
++                // Spread the array.
++                MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
++                Name array = names[argIndex];
++                names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
++                for (int j = 0; j < spreadArgCount; i++, j++) {
++                    indexes[i] = nameCursor;
++                    names[nameCursor++] = new Name(aload, array, j);
+                 }
++            } else if (i < indexes.length) {
++                indexes[i] = argIndex;
+             }
+         }
 -        if (target instanceof DirectMethodHandle)
 -            return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
 -        return null;   // let caller try something else
--    }
--
++        assert(nameCursor == names.length-1);  // leave room for the final call
++
++        // Build argument array for the call.
++        Name[] targetArgs = new Name[targetType.parameterCount()];
++        for (int i = 0; i < targetType.parameterCount(); i++) {
++            int idx = indexes[i];
++            targetArgs[i] = names[idx];
++        }
++        names[names.length - 1] = new Name(target, (Object[]) targetArgs);
++
++        LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names);
++        return new SimpleMethodHandle(srcType, form);
+     }
+ 
 -    /** Bind a predetermined argument to the given arbitrary method handle.
 -     * Callable only from MethodHandles.
 -     * @param token Proof that the caller has access to this package.
@@ -7345,8 +7987,20 @@
 -    static
 -    MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) {
 -        return new BoundMethodHandle(target, receiver, argnum);
--    }
--
++    static void checkSpreadArgument(Object av, int n) {
++        if (av == null) {
++            if (n == 0)  return;
++        } else if (av instanceof Object[]) {
++            int len = ((Object[])av).length;
++            if (len == n)  return;
++        } else {
++            int len = java.lang.reflect.Array.getLength(av);
++            if (len == n)  return;
++        }
++        // fall through to error:
++        throw newIllegalArgumentException("Array is not of length "+n);
+     }
+ 
 -    static MethodHandle permuteArguments(MethodHandle target,
 -                                                MethodType newType,
 -                                                MethodType oldType,
@@ -7371,7 +8025,15 @@
 -        // state represents the argument values coming into target
 -        for (int i = 0; i < outargs; i++) {
 -            state.add(permutationOrNull[i] * TOKEN);
--        }
++    private static final NamedFunction NF_checkSpreadArgument;
++    static {
++        try {
++            NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
++                    .getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
++            NF_checkSpreadArgument.resolve();
++        } catch (ReflectiveOperationException ex) {
++            throw new InternalError(ex);
+         }
 -        // goal represents the desired state
 -        for (int i = 0; i < inargs; i++) {
 -            if (state.contains(i * TOKEN)) {
@@ -7532,868 +8194,45 @@
 -        target = convertArguments(target, newType, oldType, 0);
 -        assert(target != null);
 -        return target;
--    }
--
-     /*non-public*/ static
+     }
+ 
+-    /*non-public*/ static
 -    MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) {
 -        MethodType oldType = target.type();
 -        if (oldType.equals(newType))
-+    MethodHandle convertArguments(MethodHandle target, MethodType srcType, int level) {
-+        MethodType dstType = target.type();
-+        if (dstType.equals(srcType))
-             return target;
+-            return target;
 -        assert(level > 1 || oldType.isConvertibleTo(newType));
-+        assert(level > 1 || dstType.isConvertibleTo(srcType));
-         MethodHandle retFilter = null;
+-        MethodHandle retFilter = null;
 -        Class<?> oldRT = oldType.returnType();
 -        Class<?> newRT = newType.returnType();
-+        Class<?> oldRT = dstType.returnType();
-+        Class<?> newRT = srcType.returnType();
-         if (!VerifyType.isNullConversion(oldRT, newRT)) {
-             if (oldRT == void.class) {
-                 Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
-@@ -605,17 +317,17 @@
-                 retFilter = MethodHandles.identity(newRT);
-                 retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
-             }
+-        if (!VerifyType.isNullConversion(oldRT, newRT)) {
+-            if (oldRT == void.class) {
+-                Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
+-                retFilter = ValueConversions.zeroConstantFunction(wrap);
+-            } else {
+-                retFilter = MethodHandles.identity(newRT);
+-                retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
+-            }
 -            newType = newType.changeReturnType(oldRT);
-+            srcType = srcType.changeReturnType(oldRT);
-         }
-         MethodHandle res = null;
-         Exception ex = null;
-         try {
--            res = convertArguments(target, newType, oldType, level);
-+            res = convertArguments(target, srcType, dstType, level);
-         } catch (IllegalArgumentException ex1) {
-             ex = ex1;
-         }
-         if (res == null) {
--            WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target);
-+            WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+srcType+": "+target);
-             wmt.initCause(ex);
-             throw wmt;
-         }
-@@ -625,42 +337,736 @@
-     }
- 
-     static MethodHandle convertArguments(MethodHandle target,
--                                                MethodType newType,
--                                                MethodType oldType,
--                                                int level) {
--        assert(oldType.parameterCount() == target.type().parameterCount());
--        if (newType == oldType)
-+                                         MethodType srcType,
-+                                         MethodType dstType,
-+                                         int level) {
-+        assert(dstType.parameterCount() == target.type().parameterCount());
-+        if (srcType == dstType)
-             return target;
--        if (oldType.parameterCount() != newType.parameterCount())
--            throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
--        return AdapterMethodHandle.makePairwiseConvert(newType, target, level);
-+        if (dstType.parameterCount() != srcType.parameterCount())
-+            throw newIllegalArgumentException("mismatched parameter count", dstType, srcType);
-+        return makePairwiseConvert(srcType, target, level);
-+    }
-+
-+    /** Can a JVM-level adapter directly implement the proposed
-+     *  argument conversions, as if by fixed-arity MethodHandle.asType?
-+     */
-+    private static boolean canPairwiseConvert(MethodType srcType, MethodType dstType, int level) {
-+        // same number of args, of course
-+        int len = srcType.parameterCount();
-+        if (len != dstType.parameterCount())
-+            return false;
-+
-+        // Check return type.
-+        Class<?> exp = srcType.returnType();
-+        Class<?> ret = dstType.returnType();
-+        if (!VerifyType.isNullConversion(ret, exp)) {
-+            if (!canConvertArgument(ret, exp, level))
-+                return false;
-+        }
-+
-+        // Check args pairwise.
-+        for (int i = 0; i < len; i++) {
-+            Class<?> src = srcType.parameterType(i); // source type
-+            Class<?> dst = dstType.parameterType(i); // destination type
-+            if (!canConvertArgument(src, dst, level))
-+                return false;
-+        }
-+
-+        return true;
-+    }
-+
-+    /** Can a JVM-level adapter directly implement the proposed
-+     *  argument conversion, as if by fixed-arity MethodHandle.asType?
-+     */
-+    private static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
-+        // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
-+        // so we don't need to repeat so much decision making.
-+        if (VerifyType.isNullConversion(src, dst)) {
-+            return true;
-+        // } else if (convOpSupported(OP_COLLECT_ARGS)) {
-+        //     // If we can build filters, we can convert anything to anything.
-+        //     return true;
-+        } else if (src.isPrimitive()) {
-+            if (dst.isPrimitive())
-+                return canPrimCast(src, dst);
-+            else
-+                return canBoxArgument(src, dst);
-+        } else {
-+            if (dst.isPrimitive())
-+                return canUnboxArgument(src, dst, level);
-+            else
-+                return true;  // any two refs can be interconverted
-+        }
-+    }
-+
-+    /**
-+     * Create a JVM-level adapter method handle to conform the given method
-+     * handle to the similar newType, using only pairwise argument conversions.
-+     * For each argument, convert incoming argument to the exact type needed.
-+     * The argument conversions allowed are casting, boxing and unboxing,
-+     * integral widening or narrowing, and floating point widening or narrowing.
-+     * @param srcType required call type
-+     * @param target original method handle
-+     * @param level which strength of conversion is allowed
-+     * @return an adapter to the original handle with the desired new type,
-+     *          or the original target if the types are already identical
-+     *          or null if the adaptation cannot be made
-+     */
-+    private static MethodHandle makePairwiseConvert(MethodType srcType, MethodHandle target, int level) {
-+        MethodType dstType = target.type();
-+        if (srcType == dstType)  return target;
-+
-+        if (!canPairwiseConvert(srcType, dstType, level))
-+            return null;
-+        // (after this point, it is an assertion error to fail to convert)
-+
-+        // Calculate extra arguments (temporaries) required in the names array.
-+        // FIXME: Use an ArrayList<Name>.  Some arguments require more than one conversion step.
-+        int extra = 0;
-+        for (int i = 0; i < srcType.parameterCount(); i++) {
-+            Class<?> src = srcType.parameterType(i);
-+            Class<?> dst = dstType.parameterType(i);
-+            if (!isTrivialConversion(src, dst, level)) {
-+                extra++;
-+            }
-+        }
-+
-+        Class<?> needReturn = srcType.returnType();
-+        Class<?> haveReturn = dstType.returnType();
-+        boolean retConv = !isTrivialConversion(haveReturn, needReturn, level);
-+
-+        // Now build a LambdaForm.
-+        MethodType lambdaType = srcType.invokerType();
-+        Name[] names = arguments(extra + 1, lambdaType);
-+        int[] indexes = new int[lambdaType.parameterCount()];
-+
-+        MethodHandle adapter = target;
-+        MethodType midType = dstType;
-+        for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) {
-+            Class<?> src = srcType.parameterType(i);
-+            Class<?> dst = midType.parameterType(i);
-+
-+            if (isTrivialConversion(src, dst, level)) {
-+                // do nothing: difference is trivial
-+                indexes[i] = argIndex;
-+                continue;
-+            }
-+
-+            // Work the current type backward toward the desired caller type:
-+            midType = midType.changeParameterType(i, src);
-+
-+            // Tricky case analysis follows.
-+            // It parallels canConvertArgument() above.
-+            MethodHandle fn = null;
-+            if (src.isPrimitive()) {
-+                if (dst.isPrimitive()) {
-+                    fn = makePrimCast(src, dst);
-+                } else {
-+                    fn = makeBoxArgument(midType, adapter, i, src);
-+                }
-+            } else {
-+                if (dst.isPrimitive()) {
-+                    // Caller has boxed a primitive.  Unbox it for the target.
-+                    fn = makeUnboxArgument(src, dst, level);
-+                } else {
-+                    // Simple reference conversion.
-+                    // Note:  Do not check for a class hierarchy relation
-+                    // between src and dst.  In all cases a 'null' argument
-+                    // will pass the cast conversion.
-+                    fn = makeCheckCast(midType, adapter, i, dst);
-+                }
-+            }
-+            names[tmpIndex] = new Name(fn, names[argIndex]);
-+            indexes[i] = tmpIndex;
-+            tmpIndex++;
-+        }
-+        if (retConv) {
-+            MethodHandle fn = makeReturnConversion(adapter, haveReturn, needReturn);
-+            assert(fn != null);
-+            target = fn;
-+        }
-+
-+        // Build argument array for the call.
-+        Name[] targetArgs = new Name[dstType.parameterCount()];
-+        for (int i = 0; i < dstType.parameterCount(); i++) {
-+            int idx = indexes[i];
-+            targetArgs[i] = names[idx];
-+        }
-+        names[names.length - 1] = new Name(target, (Object[]) targetArgs);
-+        LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
-+        return new SimpleMethodHandle(srcType, form);
-+    }
-+
-+    private static boolean isTrivialConversion(Class<?> src, Class<?> dst, int level) {
-+        if (src == dst || dst == void.class)  return true;
-+        if (!VerifyType.isNullConversion(src, dst))  return false;
-+        if (level > 1)  return true;  // explicitCastArguments
-+        boolean sp = src.isPrimitive();
-+        boolean dp = dst.isPrimitive();
-+        if (sp != dp)  return false;
-+        if (sp) {
-+            // in addition to being a null conversion, forbid boolean->int etc.
-+            return Wrapper.forPrimitiveType(dst)
-+                    .isConvertibleFrom(Wrapper.forPrimitiveType(src));
-+        } else {
-+            return dst.isAssignableFrom(src);
-+        }
-+    }
-+
-+    private static MethodHandle makeReturnConversion(MethodHandle target, Class<?> haveReturn, Class<?> needReturn) {
-+        MethodHandle adjustReturn;
-+        if (haveReturn == void.class) {
-+            // synthesize a zero value for the given void
-+            Object zero = Wrapper.forBasicType(needReturn).zero();
-+            adjustReturn = MethodHandles.constant(needReturn, zero);
-+        } else {
-+            MethodType needConversion = MethodType.methodType(needReturn, haveReturn);
-+            adjustReturn = MethodHandles.identity(needReturn).asType(needConversion);
-+        }
-+        return makeCollectArguments(adjustReturn, target, 0, false);
-+    }
-+
-+    /* Return one plus the position of the first non-trivial difference
-+     * between the given types.  This is not a symmetric operation;
-+     * we are considering adapting the targetType to adapterType.
-+     * Trivial differences are those which could be ignored by the JVM
-+     * without subverting the verifier.  Otherwise, adaptable differences
-+     * are ones for which we could create an adapter to make the type change.
-+     * Return zero if there are no differences (other than trivial ones).
-+     * Return 1+N if N is the only adaptable argument difference.
-+     * Return the -2-N where N is the first of several adaptable
-+     * argument differences.
-+     * Return -1 if there there are differences which are not adaptable.
-+     */
-+    private static int diffTypes(MethodType adapterType,
-+                                 MethodType targetType,
-+                                 boolean raw) {
-+        int diff;
-+        diff = diffReturnTypes(adapterType, targetType, raw);
-+        if (diff != 0)  return diff;
-+        int nargs = adapterType.parameterCount();
-+        if (nargs != targetType.parameterCount())
-+            return -1;
-+        return diffParamTypes(adapterType, 0, targetType, 0, nargs, raw);
-+    }
-+    private static int diffReturnTypes(MethodType adapterType,
-+                                       MethodType targetType,
-+                                       boolean raw) {
-+        Class<?> src = targetType.returnType();
-+        Class<?> dst = adapterType.returnType();
-+        if ((!raw
-+             ? VerifyType.canPassUnchecked(src, dst)
-+             : VerifyType.canPassRaw(src, dst)
-+             ) > 0)
-+            return 0;  // no significant difference
-+        if (raw && !src.isPrimitive() && !dst.isPrimitive())
-+            return 0;  // can force a reference return (very carefully!)
-+        //if (false)  return 1;  // never adaptable!
-+        return -1;  // some significant difference
-+    }
-+    private static int diffParamTypes(MethodType adapterType, int astart,
-+                                      MethodType targetType, int tstart,
-+                                      int nargs, boolean raw) {
-+        assert(nargs >= 0);
-+        int res = 0;
-+        for (int i = 0; i < nargs; i++) {
-+            Class<?> src  = adapterType.parameterType(astart+i);
-+            Class<?> dest = targetType.parameterType(tstart+i);
-+            if ((!raw
-+                 ? VerifyType.canPassUnchecked(src, dest)
-+                 : VerifyType.canPassRaw(src, dest)
-+                ) <= 0) {
-+                // found a difference; is it the only one so far?
-+                if (res != 0)
-+                    return -1-res; // return -2-i for prev. i
-+                res = 1+i;
-+            }
-+        }
-+        return res;
-+    }
-+
-+    /** Can a retyping adapter (alone) validly convert the target to newType? */
-+    // private static boolean canRetypeOnly(MethodType newType, MethodType targetType) {
-+    //     return canRetype(newType, targetType, false);
-+    // }
-+    /** Can a retyping adapter (alone) convert the target to newType?
-+     *  It is allowed to widen subword types and void to int, to make bitwise
-+     *  conversions between float/int and double/long, and to perform unchecked
-+     *  reference conversions on return.  This last feature requires that the
-+     *  caller be trusted, and perform explicit cast conversions on return values.
-+     */
-+    // private static boolean canRetypeRaw(MethodType newType, MethodType targetType) {
-+    //     return canRetype(newType, targetType, true);
-+    // }
-+    // private static boolean canRetype(MethodType newType, MethodType targetType, boolean raw) {
-+    //     int diff = diffTypes(newType, targetType, raw);
-+    //     // %%% This assert is too strong.  Factor diff into VerifyType and reconcile.
-+    //     assert(raw || (diff == 0) == VerifyType.isNullConversion(newType, targetType));
-+    //     return diff == 0;
-+    // }
-+
-+    /** Factory method:  Performs no conversions; simply retypes the adapter.
-+     *  Allows unchecked argument conversions pairwise, if they are safe.
-+     *  Returns null if not possible.
-+     */
-+    static MethodHandle makeRetype(MethodType srcType, MethodHandle target) {
-+        MethodType dstType = target.type();
-+        if (dstType == srcType)  return target;
-+        // if (!canRetype(srcType, dstType, raw))
-+        //     return null;
-+        // TO DO:  clone the target guy, whatever he is, with new type.
-+        //return AdapterMethodHandle.makeRetype(srcType, target, raw);
-+        MethodType lambdaType = srcType.invokerType();
-+        Name[] names = arguments(1, lambdaType);
-+
-+        Object[] targetArgs = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount(), Object[].class);
-+        names[names.length - 1] = new Name(target, targetArgs);
-+
-+        LambdaForm form = new LambdaForm("asType", lambdaType.parameterCount(), names);
-+        return new SimpleMethodHandle(srcType, form);
-+    }
-+
-+    static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
-+        MethodType type = target.type();
-+        int last = type.parameterCount() - 1;
-+        if (type.parameterType(last) != arrayType)
-+            target = target.asType(type.changeParameterType(last, arrayType));
-+        target = target.asFixedArity();  // make sure this attribute is turned off
-+        return new AsVarargsCollector(target, target.type(), arrayType);
-+    }
-+
-+    static class AsVarargsCollector extends BoundMethodHandle {
-+        MethodHandle target;
-+        final Class<?> arrayType;
-+        MethodHandle cache;
-+
-+        AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
-+            super(type, reinvokerForm(type));
-+            this.target = target;
-+            this.arrayType = arrayType;
-+            this.cache = target.asCollector(arrayType, 0);
-+        }
-+
-+        @Override
-+        public boolean isVarargsCollector() {
-+            return true;
-+        }
-+
-+        @Override
-+        public MethodHandle asFixedArity() {
-+            return target;
-+        }
-+
-+        @Override
-+        public String types() {
-+            return types;
-+        }
-+
-+        public static final String types = "L";
-+
-+        @Override
-+        public MethodHandle asType(MethodType newType) {
-+            MethodType type = this.type();
-+            int collectArg = type.parameterCount() - 1;
-+            int newArity = newType.parameterCount();
-+            if (newArity == collectArg+1 &&
-+                type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
-+                // if arity and trailing parameter are compatible, do normal thing
-+                return asFixedArity().asType(newType);
-+            }
-+            // check cache
-+            if (cache.type().parameterCount() == newArity)
-+                return cache.asType(newType);
-+            // build and cache a collector
-+            int arrayLength = newArity - collectArg;
-+            MethodHandle collector;
-+            try {
-+                collector = asFixedArity().asCollector(arrayType, arrayLength);
-+            } catch (IllegalArgumentException ex) {
-+                throw new WrongMethodTypeException("cannot build collector");
-+            }
-+            cache = collector;
-+            return collector.asType(newType);
-+        }
-+
-+        @Override
-+        MethodHandle setVarargs(MemberName member) {
-+            if (member.isVarargs())  return this;
-+            return asFixedArity();
-+        }
-+
-+        @Override
-+        MethodHandle viewAsType(MethodType newType) {
-+            MethodHandle mh = super.viewAsType(newType);
-+            // put back the varargs bit:
-+            MethodType type = mh.type();
-+            int arity = type.parameterCount();
-+            return mh.asVarargsCollector(type.parameterType(arity-1));
-+        }
-+
-+        @Override
-+        MemberName internalMemberName() {
-+            return asFixedArity().internalMemberName();
-+        }
-+
-+
-+        @Override
-+        MethodHandle bindArgument(int pos, char basicType, Object value) {
-+            return asFixedArity().bindArgument(pos, basicType, value);
-+        }
-+
-+        @Override
-+        MethodHandle bindReceiver(Object receiver) {
-+            return asFixedArity().bindReceiver(receiver);
-+        }
-+
-+        @Override
-+        MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
-+            return asFixedArity().dropArguments(srcType, pos, drops);
-+        }
-+
-+        @Override
-+        MethodHandle permuteArguments(MethodType newType, int[] reorder) {
-+            return asFixedArity().permuteArguments(newType, reorder);
-+        }
-+
-+        @Override
-+        public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
-+            throw new IllegalStateException("NYI");
-+        }
-+
-+        @Override
-+        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
-+            throw new IllegalStateException("NYI");
-+        }
-+
-+        @Override
-+        public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
-+            throw new IllegalStateException("NYI");
-+        }
-+
-+        @Override
-+        public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
-+            throw new IllegalStateException("NYI");
-+        }
-+
-+        @Override
-+        public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
-+            throw new IllegalStateException("NYI");
-+        }
-+
-+        @Override
-+        public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
-+            throw new IllegalStateException("NYI");
-+        }
-+    }
-+
-+    /** Can a checkcast adapter validly convert the target to srcType?
-+     *  The JVM supports all kind of reference casts, even silly ones.
-+     */
-+    private static boolean canCheckCast(MethodType srcType, MethodType targetType,
-+                                        int arg, Class<?> castType) {
-+        Class<?> src = srcType.parameterType(arg);
-+        Class<?> dst = targetType.parameterType(arg);
-+        if (!canCheckCast(src, castType)
-+                || !VerifyType.isNullConversion(castType, dst))
-+            return false;
-+        // int diff = diffTypes(srcType, targetType, false);
-+        // return (diff == arg+1) || (diff == 0);  // arg is sole non-trivial diff
-+        return true;
-+    }
-+    /** Can an primitive conversion adapter validly convert src to dst? */
-+    private static boolean canCheckCast(Class<?> src, Class<?> dst) {
-+        return (!src.isPrimitive() && !dst.isPrimitive());
-+    }
-+
-+    /** Factory method:  Forces a cast at the given argument.
-+     *  The castType is the target of the cast, and can be any type
-+     *  with a null conversion to the corresponding target parameter.
-+     *  Return null if this cannot be done.
-+     */
-+    private static MethodHandle makeCheckCast(MethodType srcType, MethodHandle target,
-+                                              int arg, Class<?> castType) {
-+        if (!canCheckCast(srcType, target.type(), arg, castType))
-+            return null;
-+        return ValueConversions.cast(castType);
-+    }
-+
-+    /** Can an primitive conversion adapter validly convert the target to newType?
-+     *  The JVM currently supports all conversions except those between
-+     *  floating and integral types.
-+     */
-+    private static boolean canPrimCast(MethodType newType, MethodType targetType,
-+                                       int arg, Class<?> convType) {
-+        Class<?> src = newType.parameterType(arg);
-+        Class<?> dst = targetType.parameterType(arg);
-+        if (!canPrimCast(src, convType)
-+                || !VerifyType.isNullConversion(convType, dst))
-+            return false;
-+        // int diff = diffTypes(newType, targetType, false);
-+        // return (diff == arg+1);  // arg is sole non-trivial diff
-+        return true;
-+    }
-+    /** Can an primitive conversion adapter validly convert src to dst? */
-+    static boolean canPrimCast(Class<?> src, Class<?> dst) {
-+        if (!src.isPrimitive() || !dst.isPrimitive()) {
-+            return false;
-+        }
-+        return true;
-+    }
-+
-+    /** Factory method:  Truncate the given argument with zero or sign extension,
-+     *  and/or convert between single and doubleword versions of integer or float.
-+     */
-+    private static MethodHandle makePrimCast(Class<?> src, Class<?> dst) {
-+        if (VerifyType.isNullConversion(src, dst))
-+            return ValueConversions.identity(dst);
-+        else
-+            return ValueConversions.convertPrimitive(src, dst);
-+    }
-+
-+    /** Can an unboxing conversion validly convert src to dst?
-+     *  The JVM currently supports all kinds of casting and unboxing.
-+     *  The convType is the unboxed type; it can be either a primitive or wrapper.
-+     */
-+    private static boolean canUnboxArgument(MethodType srcType, MethodType targetType,
-+                                            int arg, Class<?> convType, int level) {
-+        Class<?> src = srcType.parameterType(arg);
-+        Class<?> dst = targetType.parameterType(arg);
-+        Class<?> boxType = Wrapper.asWrapperType(convType);
-+        convType = Wrapper.asPrimitiveType(convType);
-+        if (!canCheckCast(src, boxType)
-+                || boxType == convType
-+                || !VerifyType.isNullConversion(convType, dst))
-+            return false;
-+        // int diff = diffTypes(srcType, targetType, false);
-+        // return (diff == arg+1);  // arg is sole non-trivial diff
-+        return true;
-+    }
-+    /** Can an primitive unboxing adapter validly convert src to dst? */
-+    private static boolean canUnboxArgument(Class<?> src, Class<?> dst, int level) {
-+        assert(dst.isPrimitive());
-+        // if we have JVM support for boxing, we can also do complex unboxing
-+        return true;
-+    }
-+
-+    /** Factory method:  Unbox the given argument.
-+     */
-+    private static MethodHandle makeUnboxArgument(Class<?> src, Class<?> dst, int level) {
-+        Class<?> convType = dst;
-+        Class<?> boxType = Wrapper.asWrapperType(convType);
-+        Class<?> primType = Wrapper.asPrimitiveType(convType);
-+        Class<?> castDone = src;
-+        if (!VerifyType.isNullConversion(src, boxType)) {
-+            // Examples:  Object->int, Number->int, Comparable->int; Byte->int, Character->int
-+            if (level != 0) {
-+                // must include additional conversions
-+                if (src == Object.class || !Wrapper.isWrapperType(src)) {
-+                    // src must be examined at runtime, to detect Byte, Character, etc.
-+                    MethodHandle unboxMethod = (level == 1
-+                                                ? ValueConversions.unbox(dst)
-+                                                : ValueConversions.unboxCast(dst));
-+                    // long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
-+                    // return new AdapterMethodHandle(target, srcType, conv, unboxMethod);
-+                    return unboxMethod;
-+                }
-+                // Example: Byte->int
-+                // Do this by reformulating the problem to Byte->byte.
-+                Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
-+                MethodHandle unbox = makeUnboxArgument(src, srcPrim, 0);
-+                // Compose the two conversions.  FIXME:  caller should make two Names for this job
-+                return unbox.asType(MethodType.methodType(dst, src));
-+            }
-+            castDone = boxType;
-+        }
-+        // long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType));
-+        // MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType);
-+        MethodHandle unboxMethod = ValueConversions.unbox(dst);
-+        if (castDone == src)
-+            return unboxMethod;
-+        return unboxMethod.asType(MethodType.methodType(src, dst));
-+    }
-+
-+    /** Can a boxing conversion validly convert src to dst? */
-+    private static boolean canBoxArgument(MethodType srcType, MethodType targetType,
-+                                          int arg, Class<?> convType) {
-+        Class<?> src = srcType.parameterType(arg);
-+        Class<?> dst = targetType.parameterType(arg);
-+        Class<?> boxType = Wrapper.asWrapperType(convType);
-+        convType = Wrapper.asPrimitiveType(convType);
-+        if (!canCheckCast(boxType, dst)
-+                || boxType == convType
-+                || !VerifyType.isNullConversion(src, convType))
-+            return false;
-+        // int diff = diffTypes(srcType, targetType, false);
-+        // return (diff == arg+1);  // arg is sole non-trivial diff
-+        return true;
-+    }
-+
-+    /** Can an primitive boxing adapter validly convert src to dst? */
-+    private static boolean canBoxArgument(Class<?> src, Class<?> dst) {
-+        return (src.isPrimitive() && !dst.isPrimitive());
-+    }
-+
-+    /** Factory method:  Box the given argument.
-+     *  Return null if this cannot be done.
-+     */
-+    private static MethodHandle makeBoxArgument(MethodType srcType, MethodHandle target,
-+                                                int arg, Class<?> convType) {
-+        MethodType dstType = target.type();
-+        Class<?> src = srcType.parameterType(arg);
-+        Class<?> dst = dstType.parameterType(arg);
-+        Class<?> boxType = Wrapper.asWrapperType(convType);
-+        Class<?> primType = Wrapper.asPrimitiveType(convType);
-+        if (!canBoxArgument(srcType, dstType, arg, convType)) {
-+            return null;
-+        }
-+        if (!VerifyType.isNullConversion(boxType, dst))
-+            target = makeCheckCast(dstType.changeParameterType(arg, boxType), target, arg, dst);
-+        return ValueConversions.box(Wrapper.forPrimitiveType(primType));
-     }
- 
-     static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) {
-         MethodType oldType = target.type();
-         int nargs = oldType.parameterCount();
-         int keepPosArgs = nargs - arrayLength;
--        MethodType newType = oldType
-+        MethodType srcType = oldType
-                 .dropParameterTypes(keepPosArgs, nargs)
-                 .insertParameterTypes(keepPosArgs, arrayType);
--        return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength);
-+        return spreadArguments(target, srcType, keepPosArgs, arrayType, arrayLength);
-     }
-     // called internally only
--    static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) {
-+    private static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType srcType, int spreadArgPos) {
-         int arrayLength = target.type().parameterCount() - spreadArgPos;
--        return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength);
-+        return spreadArguments(target, srcType, spreadArgPos, Object[].class, arrayLength);
-     }
--    static MethodHandle spreadArguments(MethodHandle target,
--                                               MethodType newType,
--                                               int spreadArgPos,
--                                               Class<?> arrayType,
--                                               int arrayLength) {
-+    private static MethodHandle spreadArguments(MethodHandle target,
-+                                                MethodType srcType,
-+                                                int spreadArgPos,
-+                                                Class<?> arrayType,
-+                                                int arrayLength) {
-         // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
--        MethodType oldType = target.type();
--        // spread the last argument of newType to oldType
--        assert(arrayLength == oldType.parameterCount() - spreadArgPos);
--        assert(newType.parameterType(spreadArgPos) == arrayType);
--        return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
-+        MethodType dstType = target.type();
-+        // spread the last argument of srcType to dstType
-+        assert(arrayLength == dstType.parameterCount() - spreadArgPos);
-+        assert(srcType.parameterType(spreadArgPos) == arrayType);
-+        return makeSpreadArguments(srcType, target, arrayType, spreadArgPos, arrayLength);
-+    }
-+
-+    /** Can an adapter spread an argument to convert the target to srcType? */
-+    private static boolean canSpreadArguments(MethodType srcType, MethodType targetType,
-+                                              Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
-+        if (diffReturnTypes(srcType, targetType, false) != 0)
-+            return false;
-+        int nptypes = srcType.parameterCount();
-+        // parameter types must be the same up to the spread point
-+        if (spreadArgPos != 0 && diffParamTypes(srcType, 0, targetType, 0, spreadArgPos, false) != 0)
-+            return false;
-+        int afterPos = spreadArgPos + spreadArgCount;
-+        int afterCount = nptypes - (spreadArgPos + 1);
-+        if (spreadArgPos < 0 || spreadArgPos >= nptypes ||
-+            spreadArgCount < 0 ||
-+            targetType.parameterCount() != afterPos + afterCount)
-+            return false;
-+        // parameter types after the spread point must also be the same
-+        if (afterCount != 0 && diffParamTypes(srcType, spreadArgPos+1, targetType, afterPos, afterCount, false) != 0)
-+            return false;
-+        // match the array element type to the spread arg types
-+        Class<?> rawSpreadArgType = srcType.parameterType(spreadArgPos);
-+        if (rawSpreadArgType != spreadArgType && !canCheckCast(rawSpreadArgType, spreadArgType))
-+            return false;
-+        for (int i = 0; i < spreadArgCount; i++) {
-+            Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
-+            Class<?> dst = targetType.parameterType(spreadArgPos + i);
-+            if (src == null || !canConvertArgument(src, dst, 1))
-+                return false;
-+        }
-+        return true;
-+    }
-+
-+
-+    /** Factory method:  Spread selected argument. */
-+    private static MethodHandle makeSpreadArguments(MethodType srcType, MethodHandle target,
-+                                                    Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
-+        // FIXME: Get rid of srcType; derive new arguments from structure of spreadArgType
-+        MethodType targetType = target.type();
-+        assert(canSpreadArguments(srcType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
-+            : "[srcType, targetType, spreadArgType, spreadArgPos, spreadArgCount] = "
-+              + Arrays.asList(srcType, targetType, spreadArgType, spreadArgPos, spreadArgCount);
-+        // dest is not significant; remove?
-+        int dest = T_VOID;
-+        for (int i = 0; i < spreadArgCount; i++) {
-+            Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
-+            if (arg == null)  arg = Object.class;
-+            int dest2 = basicType(arg);
-+            if      (dest == T_VOID)  dest = dest2;
-+            else if (dest != dest2)   dest = T_VOID;
-+            if (dest == T_VOID)  break;
-+            targetType = targetType.changeParameterType(spreadArgPos + i, arg);
-+        }
-+        target = target.asType(targetType);
-+
-+        // Now build a LambdaForm.
-+        MethodType lambdaType = srcType.invokerType();
-+        Name[] names = arguments(spreadArgCount + 2, lambdaType);
-+        int nameCursor = lambdaType.parameterCount();
-+        int[] indexes = new int[targetType.parameterCount()];
-+
-+        for (int i = 0, argIndex = 1; i < targetType.parameterCount() + 1; i++, argIndex++) {
-+            Class<?> src = lambdaType.parameterType(i);
-+            if (i == spreadArgPos) {
-+                // Spread the array.
-+                MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
-+                Name array = names[argIndex];
-+                names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
-+                for (int j = 0; j < spreadArgCount; i++, j++) {
-+                    indexes[i] = nameCursor;
-+                    names[nameCursor++] = new Name(aload, array, j);
-+                }
-+            } else if (i < indexes.length) {
-+                indexes[i] = argIndex;
-+            }
-+        }
-+        assert(nameCursor == names.length-1)
-+                : Arrays.asList(Arrays.asList(names), names.length, nameCursor, srcType, lambdaType, target, spreadArgPos, spreadArgCount)//@@
-+                ;  // leave room for the final call
-+
-+        // Build argument array for the call.
-+        Name[] targetArgs = new Name[targetType.parameterCount()];
-+        for (int i = 0; i < targetType.parameterCount(); i++) {
-+            int idx = indexes[i];
-+            targetArgs[i] = names[idx];
-+        }
-+        names[names.length - 1] = new Name(target, (Object[]) targetArgs);
-+
-+        LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names);
-+        return new SimpleMethodHandle(srcType, form);
-+    }
-+
-+    static void checkSpreadArgument(Object av, int n) {
-+        if (av == null) {
-+            if (n == 0)  return;
-+        } else if (av instanceof Object[]) {
-+            int len = ((Object[])av).length;
-+            if (len == n)  return;
-+        } else {
-+            int len = java.lang.reflect.Array.getLength(av);
-+            if (len == n)  return;
-+        }
-+        // fall through to error:
-+        throw newIllegalArgumentException("Array is not of length "+n);
-+    }
-+
-+    private static final NamedFunction NF_checkSpreadArgument;
-+    static {
-+        try {
-+            NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
-+                    .getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
-+            NF_checkSpreadArgument.resolve();
-+        } catch (ReflectiveOperationException ex) {
-+            throw new InternalError(ex);
-+        }
-     }
- 
-     static MethodHandle collectArguments(MethodHandle target,
-@@ -671,32 +1077,102 @@
-         assert(collectType != void.class);  // else use foldArguments
-         if (collectType != type.parameterType(collectArg))
-             target = target.asType(type.changeParameterType(collectArg, collectType));
--        MethodType newType = type
-+        MethodType srcType = type
-                 .dropParameterTypes(collectArg, collectArg+1)
-                 .insertParameterTypes(collectArg, collector.type().parameterArray());
--        return collectArguments(target, newType, collectArg, collector);
-+        return collectArguments(target, srcType, collectArg, collector);
-     }
-     static MethodHandle collectArguments(MethodHandle target,
--                                                MethodType newType,
-+                                                MethodType srcType,
-                                                 int collectArg,
-                                                 MethodHandle collector) {
--        MethodType oldType = target.type();     // (a...,c)=>r
--        //         newType                      // (a..., b...)=>r
-+        MethodType dstType = target.type();     // (a...,c)=>r
-+        //         srcType                      // (a..., b...)=>r
-         MethodType colType = collector.type();  // (b...)=>c
--        //         oldType                      // (a..., b...)=>r
--        assert(newType.parameterCount() == collectArg + colType.parameterCount());
--        assert(oldType.parameterCount() == collectArg + 1);
--        assert(AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false));
--        return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
-+        //         dstType                      // (a..., b...)=>r
-+        assert(srcType.parameterCount() == collectArg + colType.parameterCount());
-+        assert(dstType.parameterCount() == collectArg + 1);
-+        assert(canCollectArguments(dstType, colType, collectArg, false));
-+        return makeCollectArguments(target, collector, collectArg, false);
-+    }
-+
-+    /** Can an adapter collect a series of arguments, replacing them by zero or one results? */
-+    static boolean canCollectArguments(MethodType targetType,
-+                MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) {
-+        int collectArgCount = collectorType.parameterCount();
-+        Class<?> rtype = collectorType.returnType();
-+        assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype)
-+                // [(Object)Object[], (Object[])Object[], 0, 1]
-+                : Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount)
-+                ;
-+        return true;
-+    }
-+
 +    /** Factory method:  Collect or filter selected argument(s). */
 +    static MethodHandle makeCollectArguments(MethodHandle target,
 +                MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
-+        assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs));
-+        MethodType targetType = target.type();
-+        MethodType collectorType = collector.type();
++        MethodType targetType = target.type();          // (a..., c, [b...])=>r
++        MethodType collectorType = collector.type();    // (b...)=>c
 +        int collectArgCount = collectorType.parameterCount();
 +        Class<?> collectValType = collectorType.returnType();
 +        int collectValCount = (collectValType == void.class ? 0 : 1);
-+        MethodType srcType = targetType.dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
-+        if (!retainOriginalArgs) {
++        MethodType srcType = targetType                 // (a..., [b...])=>r
++                .dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
++        if (!retainOriginalArgs) {                      // (a..., b...)=>r
 +            srcType = srcType.insertParameterTypes(collectArgPos, collectorType.parameterList());
-+        } else {
-+            // parameter types at the fold point must be the same
-+            assert(diffParamTypes(srcType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0)
-+                : Arrays.asList(target, collector, collectArgPos, retainOriginalArgs);
-+        }
+         }
+-        MethodHandle res = null;
+-        Exception ex = null;
+-        try {
+-            res = convertArguments(target, newType, oldType, level);
+-        } catch (IllegalArgumentException ex1) {
+-            ex = ex1;
 +        // in  arglist: [0: ...keep1 | cpos: collect...  | cpos+cacount: keep2... ]
 +        // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
 +        // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
@@ -8419,38 +8258,107 @@
 +        targetArgPos += chunk;
 +        if (collectValType != void.class) {
 +            targetArgs[targetArgPos++] = names[collectNamePos];
-+        }
+         }
+-        if (res == null) {
+-            WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target);
+-            wmt.initCause(ex);
+-            throw wmt;
 +        chunk = collectArgCount;
 +        if (retainOriginalArgs) {
 +            System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
 +            targetArgPos += chunk;   // optionally pass on the collected chunk
-+        }
+         }
+-        if (retFilter != null)
+-            res = MethodHandles.filterReturnValue(res, retFilter);
+-        return res;
+-    }
 +        inputArgPos += chunk;
 +        chunk = targetArgs.length - targetArgPos;  // all the rest
 +        System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
 +        assert(inputArgPos + chunk == collectNamePos);  // use of rest of input args also
 +        names[targetNamePos] = new Name(target, (Object[]) targetArgs);
-+
-+        LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names);
-+        return new SimpleMethodHandle(srcType, form);
-     }
- 
-     static MethodHandle filterArgument(MethodHandle target,
-                                        int pos,
-                                        MethodHandle filter) {
+ 
+-    static MethodHandle convertArguments(MethodHandle target,
+-                                                MethodType newType,
+-                                                MethodType oldType,
+-                                                int level) {
+-        assert(oldType.parameterCount() == target.type().parameterCount());
+-        if (newType == oldType)
+-            return target;
+-        if (oldType.parameterCount() != newType.parameterCount())
+-            throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
+-        return AdapterMethodHandle.makePairwiseConvert(newType, target, level);
+-    }
+-
+-    static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) {
+-        MethodType oldType = target.type();
+-        int nargs = oldType.parameterCount();
+-        int keepPosArgs = nargs - arrayLength;
+-        MethodType newType = oldType
+-                .dropParameterTypes(keepPosArgs, nargs)
+-                .insertParameterTypes(keepPosArgs, arrayType);
+-        return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength);
+-    }
+-    // called internally only
+-    static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) {
+-        int arrayLength = target.type().parameterCount() - spreadArgPos;
+-        return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength);
+-    }
+-    static MethodHandle spreadArguments(MethodHandle target,
+-                                               MethodType newType,
+-                                               int spreadArgPos,
+-                                               Class<?> arrayType,
+-                                               int arrayLength) {
+-        // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
+-        MethodType oldType = target.type();
+-        // spread the last argument of newType to oldType
+-        assert(arrayLength == oldType.parameterCount() - spreadArgPos);
+-        assert(newType.parameterType(spreadArgPos) == arrayType);
+-        return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
+-    }
+-
+-    static MethodHandle collectArguments(MethodHandle target,
+-                                                int collectArg,
+-                                                MethodHandle collector) {
+-        MethodType type = target.type();
+-        Class<?> collectType = collector.type().returnType();
+-        assert(collectType != void.class);  // else use foldArguments
+-        if (collectType != type.parameterType(collectArg))
+-            target = target.asType(type.changeParameterType(collectArg, collectType));
+-        MethodType newType = type
+-                .dropParameterTypes(collectArg, collectArg+1)
+-                .insertParameterTypes(collectArg, collector.type().parameterArray());
+-        return collectArguments(target, newType, collectArg, collector);
+-    }
+-    static MethodHandle collectArguments(MethodHandle target,
+-                                                MethodType newType,
+-                                                int collectArg,
+-                                                MethodHandle collector) {
+-        MethodType oldType = target.type();     // (a...,c)=>r
+-        //         newType                      // (a..., b...)=>r
+-        MethodType colType = collector.type();  // (b...)=>c
+-        //         oldType                      // (a..., b...)=>r
+-        assert(newType.parameterCount() == collectArg + colType.parameterCount());
+-        assert(oldType.parameterCount() == collectArg + 1);
+-        assert(AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false));
+-        return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
+-    }
+-
+-    static MethodHandle filterArgument(MethodHandle target,
+-                                       int pos,
+-                                       MethodHandle filter) {
 -        MethodType ttype = target.type();
-         MethodType ftype = filter.type();
-         assert(ftype.parameterCount() == 1);
+-        MethodType ftype = filter.type();
+-        assert(ftype.parameterCount() == 1);
 -        return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
-+        // return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
-+        return makeCollectArguments(target, filter, pos, false);
-     }
- 
-     static MethodHandle foldArguments(MethodHandle target,
-@@ -705,15 +1181,9 @@
-                                       MethodHandle combiner) {
-         MethodType oldType = target.type();
-         MethodType ctype = combiner.type();
+-    }
+-
+-    static MethodHandle foldArguments(MethodHandle target,
+-                                      MethodType newType,
+-                                      int foldPos,
+-                                      MethodHandle combiner) {
+-        MethodType oldType = target.type();
+-        MethodType ctype = combiner.type();
 -        assert(AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true));
 -        return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
 -    }
@@ -8460,13 +8368,12 @@
 -                               MethodType newType, int argnum) {
 -        int drops = newType.parameterCount() - target.type().parameterCount();
 -        return AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops);
-+        assert(canCollectArguments(oldType, ctype, foldPos, true));
-+        // return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
-+        return makeCollectArguments(target, combiner, foldPos, true);
++        LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names);
++        return new SimpleMethodHandle(srcType, form);
      }
  
      static
-@@ -738,47 +1208,42 @@
+@@ -738,47 +533,42 @@
      MethodHandle makeGuardWithTest(MethodHandle test,
                                     MethodHandle target,
                                     MethodHandle fallback) {
@@ -8540,7 +8447,7 @@
          private Object invoke_V(Object... av) throws Throwable {
              try {
                  return target.invokeExact(av);
-@@ -787,6 +1252,7 @@
+@@ -787,6 +577,7 @@
                  return catcher.invokeExact(t, av);
              }
          }
@@ -8548,7 +8455,7 @@
          private Object invoke_L0() throws Throwable {
              try {
                  return target.invokeExact();
-@@ -795,6 +1261,7 @@
+@@ -795,6 +586,7 @@
                  return catcher.invokeExact(t);
              }
          }
@@ -8556,7 +8463,7 @@
          private Object invoke_L1(Object a0) throws Throwable {
              try {
                  return target.invokeExact(a0);
-@@ -803,6 +1270,7 @@
+@@ -803,6 +595,7 @@
                  return catcher.invokeExact(t, a0);
              }
          }
@@ -8564,7 +8471,7 @@
          private Object invoke_L2(Object a0, Object a1) throws Throwable {
              try {
                  return target.invokeExact(a0, a1);
-@@ -811,6 +1279,7 @@
+@@ -811,6 +604,7 @@
                  return catcher.invokeExact(t, a0, a1);
              }
          }
@@ -8572,7 +8479,7 @@
          private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
              try {
                  return target.invokeExact(a0, a1, a2);
-@@ -819,6 +1288,7 @@
+@@ -819,6 +613,7 @@
                  return catcher.invokeExact(t, a0, a1, a2);
              }
          }
@@ -8580,7 +8487,7 @@
          private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
              try {
                  return target.invokeExact(a0, a1, a2, a3);
-@@ -827,6 +1297,7 @@
+@@ -827,6 +622,7 @@
                  return catcher.invokeExact(t, a0, a1, a2, a3);
              }
          }
@@ -8588,7 +8495,7 @@
          private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
              try {
                  return target.invokeExact(a0, a1, a2, a3, a4);
-@@ -835,6 +1306,7 @@
+@@ -835,6 +631,7 @@
                  return catcher.invokeExact(t, a0, a1, a2, a3, a4);
              }
          }
@@ -8596,7 +8503,7 @@
          private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
              try {
                  return target.invokeExact(a0, a1, a2, a3, a4, a5);
-@@ -843,6 +1315,7 @@
+@@ -843,6 +640,7 @@
                  return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5);
              }
          }
@@ -8604,7 +8511,7 @@
          private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
              try {
                  return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
-@@ -851,6 +1324,7 @@
+@@ -851,6 +649,7 @@
                  return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6);
              }
          }
@@ -8612,7 +8519,7 @@
          private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
              try {
                  return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
-@@ -860,7 +1334,7 @@
+@@ -860,7 +659,7 @@
              }
          }
          static MethodHandle[] makeInvokes() {
@@ -8621,28 +8528,36 @@
              MethodHandles.Lookup lookup = IMPL_LOOKUP;
              for (;;) {
                  int nargs = invokes.size();
-@@ -903,37 +1377,60 @@
+@@ -901,39 +700,60 @@
+             MethodType gtype = type.generic();
+             MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
              // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0)
-             MethodHandle gtarget = convertArguments(target, gtype, type, 2);
-             MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 2);
+-            MethodHandle gtarget = convertArguments(target, gtype, type, 2);
+-            MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 2);
 -            MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
+-            if (gtarget == null || gcatcher == null || gguard == null)  return null;
+-            return convertArguments(gguard, type, gtype, 2);
++            MethodHandle gtarget = makePairwiseConvert(target, gtype, 2);
++            MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2);
 +            GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
-             if (gtarget == null || gcatcher == null || gguard == null)  return null;
--            return convertArguments(gguard, type, gtype, 2);
++            if (gtarget == null || gcatcher == null)  throw new InternalError();
 +            MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard);
-+            return convertArguments(ginvoker, type, gtype, 2);
++            return makePairwiseConvert(ginvoker, type, 2);
          } else {
-             MethodType gtype = MethodType.genericMethodType(0, true);
-             MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
-             MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0);
+-            MethodType gtype = MethodType.genericMethodType(0, true);
+-            MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
+-            MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0);
++            MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs);
              catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class));
-             MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1);
+-            MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1);
 -            MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher);
+-            if (gtarget == null || gcatcher == null || gguard == null)  return null;
+-            return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type);
++            MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs);
 +            GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
-             if (gtarget == null || gcatcher == null || gguard == null)  return null;
--            return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type);
++            if (gtarget == null || gcatcher == null)  throw new InternalError();
 +            MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard);
-+            return collectArguments(ginvoker, type, 0, ValueConversions.varargsArray(nargs)).asType(type);
++            return makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
          }
      }
  
@@ -8654,7 +8569,7 @@
 +        if (arity > 1) {
 +            return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1);
 +        }
-+        return makeRetype(type, throwException());
++        return makePairwiseConvert(throwException(), type, 2);
      }
  
      static MethodHandle THROW_EXCEPTION;
@@ -9151,7 +9066,7 @@
 +                                   Object[] appendixResult) {
 +        MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
 +        Class<?> caller = (Class<?>)callerObj;
-+        String name = nameObj.toString();
++        String name = nameObj.toString().intern();
 +        MethodType type = (MethodType)typeObj;
 +        appendixResult[0] = CallSite.makeSite(bootstrapMethod,
 +                                              name,
@@ -9162,7 +9077,7 @@
      }
  
      /**
-@@ -321,71 +307,47 @@
+@@ -321,71 +307,64 @@
      }
  
      /**
@@ -9177,17 +9092,14 @@
       */
 -    static void notifyGenericMethodType(MethodType type) {
 -        type.form().notifyGenericMethodType();
--    }
 +    static MemberName linkMethod(Class<?> callerClass, int refKind,
 +                                 Class<?> defc, String name, Object type,
 +                                 Object[] appendixResult) {
-+        if (TRACE_METHOD_LINKAGE)
-+            System.out.println("linkMethod "+defc.getName()+"."+
-+                               name+type+"/"+Integer.toHexString(refKind));
-+        //assert(refKind == REF_invokeVirtual) : refKind;
-+        if (defc != MethodHandle.class || refKind != REF_invokeVirtual)
-+            throw new LinkageError("no such method "+defc.getName()+"."+name+type);
- 
++        if (!TRACE_METHOD_LINKAGE)
++            return linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
++        return linkMethodTracing(callerClass, refKind, defc, name, type, appendixResult);
+     }
+-
 -    /**
 -     * The JVM wants to raise an exception.  Here's the path.
 -     */
@@ -9216,6 +9128,11 @@
 -                throw new BootstrapMethodError((Throwable) actual);
 -            }
 -            break;
++    static MemberName linkMethodImpl(Class<?> callerClass, int refKind,
++                                     Class<?> defc, String name, Object type,
++                                     Object[] appendixResult) {
++        if (defc != MethodHandle.class || refKind != REF_invokeVirtual)
++            throw new LinkageError("no such method "+defc.getName()+"."+name+type);
 +        switch (name) {
 +        case "invoke":
 +            return Invokers.genericInvokerMethod(callerClass, type, appendixResult);
@@ -9240,8 +9157,22 @@
 -            throw new ClassCastException(message);
 -        default:
 -            throw new InternalError("unexpected code "+code+": "+message);
--        }
 +        throw new UnsupportedOperationException("linkMethod "+name);
++    }
++    // Tracing logic:
++    static MemberName linkMethodTracing(Class<?> callerClass, int refKind,
++                                        Class<?> defc, String name, Object type,
++                                        Object[] appendixResult) {
++        System.out.println("linkMethod "+defc.getName()+"."+
++                           name+type+"/"+Integer.toHexString(refKind));
++        try {
++            MemberName res = linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
++            System.out.println("linkMethod => "+res+" + "+appendixResult[0]);
++            return res;
++        } catch (Throwable ex) {
++            System.out.println("linkMethod => throw "+ex);
++            throw ex;
+         }
      }
  
      /**
@@ -9265,22 +9196,35 @@
 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
-@@ -39,15 +39,24 @@
+@@ -27,6 +27,7 @@
+ 
+ import java.security.AccessController;
+ import java.security.PrivilegedAction;
++import sun.misc.Unsafe;
+ 
+ /**
+  * This class consists exclusively of static names internal to the
+@@ -38,16 +39,30 @@
+ 
      private MethodHandleStatics() { }  // do not instantiate
  
++    static final Unsafe UNSAFE = Unsafe.getUnsafe();
++
      static final boolean DEBUG_METHOD_HANDLE_NAMES;
 +    static final boolean DUMP_CLASS_FILES;
 +    static final boolean TRACE_INTERPRETER;
 +    static final boolean TRACE_METHOD_LINKAGE;
++    static final Integer COMPILE_THRESHOLD;
      static {
 -        final Object[] values = { false };
-+        final Object[] values = { false, false, false, false };
++        final Object[] values = { false, false, false, false, null };
          AccessController.doPrivileged(new PrivilegedAction<Void>() {
                  public Void run() {
                      values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
 +                    values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES");
 +                    values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
 +                    values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
++                    values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
                      return null;
                  }
              });
@@ -9288,10 +9232,11 @@
 +        DUMP_CLASS_FILES          = (Boolean) values[1];
 +        TRACE_INTERPRETER         = (Boolean) values[2];
 +        TRACE_METHOD_LINKAGE      = (Boolean) values[3];
++        COMPILE_THRESHOLD         = (Integer) values[4];
      }
  
      /*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
-@@ -55,7 +64,7 @@
+@@ -55,7 +70,7 @@
              type = target.type();
          MemberName name = null;
          if (target != null)
@@ -9300,7 +9245,7 @@
          if (name == null)
              return "invoke" + type;
          return name.getName() + type;
-@@ -77,20 +86,6 @@
+@@ -77,20 +92,6 @@
          return str + target.type();
      }
  
@@ -9321,7 +9266,7 @@
      // handy shared exception makers (they simplify the common case code)
      /*non-public*/ static RuntimeException newIllegalStateException(String message) {
          return new IllegalStateException(message);
-@@ -110,6 +105,9 @@
+@@ -110,6 +111,9 @@
      /*non-public*/ static Error uncaughtException(Exception ex) {
          throw new InternalError("uncaught exception", ex);
      }
@@ -9701,7 +9646,7 @@
              String message;
              if (m.isConstructor())
                  message = "expected a method, not a constructor";
-@@ -1150,26 +1094,45 @@
+@@ -1150,26 +1094,43 @@
              else if (wantStatic != m.isStatic())
                  message = wantStatic ? "expected a static method" : "expected a non-static method";
              else
@@ -9724,9 +9669,7 @@
 +        void checkAccess(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {
 +            assert(m.referenceKindIsConsistentWith(refKind) &&
 +                   MethodHandleNatives.refKindIsValid(refKind) &&
-+                   (MethodHandleNatives.refKindIsField(refKind) == m.isField()))
-+                : m.toString()+"!="+refKind //@@
-+                ;
++                   (MethodHandleNatives.refKindIsField(refKind) == m.isField()));
              int allowedModes = this.allowedModes;
              if (allowedModes == TRUSTED)  return;
              int mods = m.getModifiers();
@@ -9757,7 +9700,7 @@
              throw m.makeAccessException(accessFailedMessage(refc, m), this);
          }
  
-@@ -1198,7 +1161,8 @@
+@@ -1198,7 +1159,8 @@
  
          private static final boolean ALLOW_NESTMATE_ACCESS = false;
  
@@ -9767,7 +9710,7 @@
              if (allowedModes == TRUSTED)  return;
              if ((allowedModes & PRIVATE) == 0
                  || (specialCaller != lookupClass()
-@@ -1208,7 +1172,7 @@
+@@ -1208,7 +1170,7 @@
                      makeAccessException("no private access for invokespecial", this);
          }
  
@@ -9776,7 +9719,7 @@
              // The accessing class only has the right to use a protected member
              // on itself or a subclass.  Enforce that restriction, from JVMS 5.4.4, etc.
              if (!method.isProtected() || method.isStatic()
-@@ -1217,52 +1181,83 @@
+@@ -1217,52 +1179,82 @@
                  || VerifyAccess.isSamePackage(method.getDeclaringClass(), lookupClass())
                  || (ALLOW_NESTMATE_ACCESS &&
                      VerifyAccess.isSamePackageMember(method.getDeclaringClass(), lookupClass())))
@@ -9841,7 +9784,7 @@
 +        }
 +        private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException {
 +            checkField(refKind, refc, field);
-+            MethodHandle mh = MethodHandleImpl.makeFieldAccessor(refKind, field, refc);
++            MethodHandle mh = DirectMethodHandle.make(refc, field);
 +            boolean doRestrict = (MethodHandleNatives.refKindHasReceiver(refKind) &&
 +                                    restrictProtectedReceiver(field));
 +            if (doRestrict)
@@ -9851,8 +9794,7 @@
 +        private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
 +            assert(ctor.isConstructor());
 +            checkAccess(REF_newInvokeSpecial, refc, ctor);
-+            MethodHandle mh = DirectMethodHandle.make(ctor);
-+            return MethodHandleImpl.makeAllocator(mh).setVarargs(ctor);
++            return DirectMethodHandle.make(ctor).setVarargs(ctor);
          }
  
          /** Hook called from the JVM (via MethodHandleNatives) to link MH constants:
@@ -9892,7 +9834,7 @@
              }
              // oops
              throw new ReflectiveOperationException("bad MethodHandle constant #"+refKind+" "+name+" : "+type);
-@@ -1281,7 +1276,7 @@
+@@ -1281,7 +1273,7 @@
       */
      public static
      MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
@@ -9901,7 +9843,7 @@
      }
  
      /**
-@@ -1295,7 +1290,7 @@
+@@ -1295,7 +1287,7 @@
       */
      public static
      MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
@@ -9910,7 +9852,7 @@
      }
  
      /// method handle invocation (reflective style)
-@@ -1422,78 +1417,12 @@
+@@ -1422,78 +1414,12 @@
          return type.invokers().generalInvoker();
      }
  
@@ -9993,6 +9935,18 @@
  
      /**
       * Produces a method handle which adapts the type of the
+@@ -1541,7 +1467,10 @@
+      */
+     public static
+     MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
+-        return MethodHandleImpl.convertArguments(target, newType, 2);
++        if (!target.type().isCastableTo(newType)) {
++            throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType);
++        }
++        return MethodHandleImpl.makePairwiseConvert(target, newType, 2);
+     }
+ 
+     /**
 @@ -1605,11 +1534,8 @@
       */
      public static
@@ -10007,18 +9961,17 @@
      }
  
      private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
-@@ -1678,8 +1604,8 @@
+@@ -1678,8 +1604,7 @@
          else if (type.isPrimitive())
              return ValueConversions.identity(Wrapper.forPrimitiveType(type));
          else
 -            return AdapterMethodHandle.makeRetypeRaw(
 -                    MethodType.methodType(type, type), ValueConversions.identity());
-+            return MethodHandleImpl.makeRetype(
-+                     MethodType.methodType(type, type), ValueConversions.identity());
++            return MethodHandleImpl.makeReferenceIdentity(type);
      }
  
      /**
-@@ -1725,18 +1651,26 @@
+@@ -1725,18 +1650,26 @@
          MethodHandle result = target;
          for (int i = 0; i < insCount; i++) {
              Object value = values[i];
@@ -10055,7 +10008,7 @@
          }
          return result;
      }
-@@ -1786,16 +1720,17 @@
+@@ -1786,16 +1719,17 @@
      public static
      MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
          MethodType oldType = target.type();  // get NPE
@@ -10078,7 +10031,16 @@
      }
  
      /**
-@@ -2011,10 +1946,8 @@
+@@ -1939,7 +1873,7 @@
+         if (filterType.parameterCount() != 1
+             || filterType.returnType() != targetType.parameterType(pos))
+             throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+-        return MethodHandleImpl.filterArgument(target, pos, filter);
++        return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
+     }
+ 
+     /**
+@@ -2011,10 +1945,7 @@
              throw newIllegalArgumentException("target and filter types do not match", target, filter);
          // result = fold( lambda(retval, arg...) { filter(retval) },
          //                lambda(        arg...) { target(arg...) } )
@@ -10086,12 +10048,22 @@
 -        MethodHandle result = null;
 -        assert(AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false));
 -        return AdapterMethodHandle.makeCollectArguments(filter, target, 0, false);
-+        assert(MethodHandleImpl.canCollectArguments(filterType, targetType, 0, false));
 +        return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
      }
  
      /**
-@@ -2255,6 +2188,8 @@
+@@ -2112,9 +2043,7 @@
+         if (!ok)
+             throw misMatchedTypes("target and combiner types", targetType, combinerType);
+         MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
+-        MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner);
+-        if (res == null)  throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType);
+-        return res;
++        return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
+     }
+ 
+     /**
+@@ -2255,6 +2184,8 @@
       */
      public static
      MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
@@ -10103,7 +10075,15 @@
 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
-@@ -108,6 +108,8 @@
+@@ -33,6 +33,7 @@
+ import java.util.List;
+ import sun.invoke.util.BytecodeDescriptor;
+ import static java.lang.invoke.MethodHandleStatics.*;
++import sun.invoke.util.VerifyType;
+ 
+ /**
+  * A method type represents the arguments and return type accepted and
+@@ -108,6 +109,8 @@
      /*trusted*/ Class<?> rtype() { return rtype; }
      /*trusted*/ Class<?>[] ptypes() { return ptypes; }
  
@@ -10112,7 +10092,7 @@
      private static void checkRtype(Class<?> rtype) {
          rtype.equals(rtype);  // null check
      }
-@@ -127,7 +129,7 @@
+@@ -127,7 +130,7 @@
          checkSlotCount(ptypes.length + slots);
          return slots;
      }
@@ -10121,6 +10101,16 @@
          if ((count & 0xFF) != count)
              throw newIllegalArgumentException("bad parameter count "+count);
      }
+@@ -238,8 +241,7 @@
+             ptypes = NO_PTYPES; trusted = true;
+         }
+         MethodType mt1 = new MethodType(rtype, ptypes);
+-        MethodType mt0;
+-        mt0 = internTable.get(mt1);
++        MethodType mt0 = internTable.get(mt1);
+         if (mt0 != null)
+             return mt0;
+         if (!trusted)
 @@ -248,10 +250,6 @@
          // promote the object to the Real Thing, and reprobe
          MethodTypeForm form = MethodTypeForm.findForm(mt1);
@@ -10132,7 +10122,40 @@
          return internTable.add(mt1);
      }
      private static final MethodType[] objectOnlyTypes = new MethodType[20];
-@@ -465,6 +463,23 @@
+@@ -385,6 +383,32 @@
+         return insertParameterTypes(parameterCount(), ptypesToInsert);
+     }
+ 
++     /**
++     * Finds or creates a method type with modified parameter types.
++     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
++     * @param start  the position (zero-based) of the first replaced parameter type(s)
++     * @param end    the position (zero-based) after the last replaced parameter type(s)
++     * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
++     * @return the same type, except with the selected parameter(s) replaced
++     * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
++     *                                  or if {@code end} is negative or greater than {@code parameterCount()}
++     *                                  or if {@code start} is greater than {@code end}
++     * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
++     *                                  or if the resulting method type would have more than 255 parameter slots
++     * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
++     */
++    /*non-public*/ MethodType replaceParameterTypes(int start, int end, Class<?>... ptypesToInsert) {
++        if (start == end)
++            return insertParameterTypes(start, ptypesToInsert);
++        int len = ptypes.length;
++        if (!(0 <= start && start <= end && end <= len))
++            throw newIndexOutOfBoundsException("start="+start+" end="+end);
++        int ilen = ptypesToInsert.length;
++        if (ilen == 0)
++            return dropParameterTypes(start, end);
++        return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert);
++    }
++
+     /**
+      * Finds or creates a method type with some parameter types omitted.
+      * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+@@ -465,6 +489,23 @@
      }
  
      /**
@@ -10156,6 +10179,79 @@
       * Converts all types, both reference and primitive, to {@code Object}.
       * Convenience method for {@link #genericMethodType(int) genericMethodType}.
       * The expression {@code type.wrap().erase()} produces the same value
+@@ -558,6 +599,11 @@
+         return Collections.unmodifiableList(Arrays.asList(ptypes));
+     }
+ 
++    /*non-public*/ Class<?> lastParameterType() {
++        int len = ptypes.length;
++        return len == 0 ? void.class : ptypes[len-1];
++    }
++
+     /**
+      * Presents the parameter types as an array (a convenience method).
+      * Changes to the array will not result in changes to the type.
+@@ -627,6 +673,26 @@
+ 
+ 
+     /*non-public*/
++    boolean isViewableAs(MethodType newType) {
++        if (!VerifyType.isNullConversion(returnType(), newType.returnType()))
++            return false;
++        int argc = parameterCount();
++        if (argc != newType.parameterCount())
++            return false;
++        for (int i = 0; i < argc; i++) {
++            if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i)))
++                return false;
++        }
++        return true;
++    }
++    /*non-public*/
++    boolean isCastableTo(MethodType newType) {
++        int argc = parameterCount();
++        if (argc != newType.parameterCount())
++            return false;
++        return true;
++    }
++    /*non-public*/
+     boolean isConvertibleTo(MethodType newType) {
+         if (!canConvert(returnType(), newType.returnType()))
+             return false;
+@@ -809,6 +875,10 @@
+         return BytecodeDescriptor.unparse(this);
+     }
+ 
++    /*non-public*/ static String toFieldDescriptorString(Class<?> cls) {
++        return BytecodeDescriptor.unparse(cls);
++    }
++
+     /// Serialization.
+ 
+     /**
+@@ -881,18 +951,17 @@
+         // store them into the implementation-specific final fields.
+         checkRtype(rtype);
+         checkPtypes(ptypes);
+-        unsafe.putObject(this, rtypeOffset, rtype);
+-        unsafe.putObject(this, ptypesOffset, ptypes);
++        UNSAFE.putObject(this, rtypeOffset, rtype);
++        UNSAFE.putObject(this, ptypesOffset, ptypes);
+     }
+ 
+     // Support for resetting final fields while deserializing
+-    private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+     private static final long rtypeOffset, ptypesOffset;
+     static {
+         try {
+-            rtypeOffset = unsafe.objectFieldOffset
++            rtypeOffset = UNSAFE.objectFieldOffset
+                 (MethodType.class.getDeclaredField("rtype"));
+-            ptypesOffset = unsafe.objectFieldOffset
++            ptypesOffset = UNSAFE.objectFieldOffset
+                 (MethodType.class.getDeclaredField("ptypes"));
+         } catch (Exception ex) {
+             throw new Error(ex);
 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
@@ -10167,7 +10263,7 @@
  
  /**
   * Shared information for a group of method types, which differ
-@@ -41,27 +42,69 @@
+@@ -41,27 +42,70 @@
   * No more than half of these are likely to be loaded at once.
   * @author John Rose
   */
@@ -10197,20 +10293,21 @@
 +    final LambdaForm[] lambdaForms;
 +    // Indexes into lambdaForms:
 +    static final int
-+            LF_REF_KIND_FIRST =  0,
-+            LF_REF_KIND_LIMIT =  LF_REF_KIND_FIRST + REF_LIMIT,
-+            // add more variations of DMH invokers as needed...
-+            LF_INVSTATIC_INIT =  0 + LF_REF_KIND_LIMIT,
-+            LF_INTERPRET      =  1 + LF_REF_KIND_LIMIT,
-+            LF_COUNTER        =  2 + LF_REF_KIND_LIMIT,
-+            LF_REINVOKE       =  3 + LF_REF_KIND_LIMIT,
-+            LF_EX_LINKER      =  4 + LF_REF_KIND_LIMIT,
-+            LF_EX_INVOKER     =  5 + LF_REF_KIND_LIMIT,
-+            LF_GEN_LINKER     =  6 + LF_REF_KIND_LIMIT,
-+            LF_GEN_INVOKER    =  7 + LF_REF_KIND_LIMIT,
-+            LF_CS_LINKER      =  8 + LF_REF_KIND_LIMIT,
-+            // add more here as needed...
-+            LF_LIMIT          =  9 + LF_REF_KIND_LIMIT;
++            LF_INVVIRTUAL     =  0,  // DMH invokeVirtual
++            LF_INVSTATIC      =  1,
++            LF_INVSPECIAL     =  2,
++            LF_NEWINVSPECIAL  =  3,
++            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_CS_LINKER      = 13,  // linkToCallSite_CS
++            LF_LIMIT          = 14;
  
      public MethodType erasedType() {
          return erasedType;
@@ -10246,7 +10343,7 @@
      protected MethodTypeForm(MethodType erasedType) {
          this.erasedType = erasedType;
  
-@@ -75,26 +118,41 @@
+@@ -75,26 +119,41 @@
  
          // Walk the argument types, looking for primitives.
          int pac = 0, lac = 0, prc = 0, lrc = 0;
@@ -10292,7 +10389,7 @@
          if (lac != 0) {
              int slot = ptypeCount + lac;
              slotToArgTab = new int[slot+1];
-@@ -102,7 +160,8 @@
+@@ -102,7 +161,8 @@
              argToSlotTab[0] = slot;  // argument "-1" is past end of slots
              for (int i = 0; i < epts.length; i++) {
                  Class<?> pt = epts[i];
@@ -10302,7 +10399,7 @@
                  --slot;
                  slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
                  argToSlotTab[1+i]  = slot;
-@@ -130,164 +189,13 @@
+@@ -130,164 +190,13 @@
          // send a few bits down to the JVM:
          this.vmslots = parameterSlotCount();
  
@@ -10471,7 +10568,7 @@
      private static long pack(int a, int b, int c, int d) {
          assert(((a|b|c|d) & ~0xFFFF) == 0);
          long hw = ((a << 16) | b), lw = ((c << 16) | d);
-@@ -325,11 +233,11 @@
+@@ -325,11 +234,11 @@
      public boolean hasPrimitives() {
          return primCounts != 0;
      }
@@ -10488,7 +10585,7 @@
      public boolean hasLongPrimitives() {
          return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
      }
-@@ -455,18 +363,6 @@
+@@ -455,18 +364,6 @@
          return cs;
      }
  
@@ -10512,12 +10609,14 @@
 copy to src/share/classes/java/lang/invoke/SimpleMethodHandle.java
 --- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java
 +++ b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
-@@ -25,29 +25,40 @@
+@@ -25,29 +25,42 @@
  
  package java.lang.invoke;
  
 +import static java.lang.invoke.LambdaForm.*;
  import static java.lang.invoke.MethodHandleNatives.Constants.*;
++import java.util.logging.Level;
++import java.util.logging.Logger;
  
  /**
 - * The flavor of method handle which emulates invokespecial or invokestatic.
@@ -10548,7 +10647,7 @@
 +    @Override
 +    MethodHandle bindArgument(int pos, char basicType, Object value) {
 +        MethodType type2 = type().dropParameterTypes(pos, pos+1);
-+        LambdaForm form2 = internalForm().bind(basicType, 1+pos, 0);
++        LambdaForm form2 = internalForm().bind(1+pos, BoundMethodHandle.SpeciesData.EMPTY);
 +        return BoundMethodHandle.bindSingle(type2, form2, basicType, value);
      }
 +
@@ -10590,53 +10689,376 @@
 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
-@@ -120,6 +120,7 @@
- 
-     /// Converting references to "raw" values.
-     /// A raw primitive value is always an int or long.
-+    /// FIXME: Get rid of the raw kind of conversions.
- 
-     static int unboxByteRaw(Object x, boolean cast) {
-         return unboxByte(x, cast);
-@@ -174,7 +175,7 @@
+@@ -118,42 +118,15 @@
+         return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
+     }
+ 
+-    /// Converting references to "raw" values.
+-    /// A raw primitive value is always an int or long.
+-
+-    static int unboxByteRaw(Object x, boolean cast) {
+-        return unboxByte(x, cast);
+-    }
+-
+-    static int unboxShortRaw(Object x, boolean cast) {
+-        return unboxShort(x, cast);
+-    }
+-
+-    static int unboxBooleanRaw(Object x, boolean cast) {
+-        return unboxBoolean(x, cast) ? 1 : 0;
+-    }
+-
+-    static int unboxCharacterRaw(Object x, boolean cast) {
+-        return unboxCharacter(x, cast);
+-    }
+-
+-    static int unboxFloatRaw(Object x, boolean cast) {
+-        return Float.floatToIntBits(unboxFloat(x, cast));
+-    }
+-
+-    static long unboxDoubleRaw(Object x, boolean cast) {
+-        return Double.doubleToRawLongBits(unboxDouble(x, cast));
+-    }
+-
+-    private static MethodType unboxType(Wrapper wrap, boolean raw) {
+-        return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), Object.class, boolean.class);
++    private static MethodType unboxType(Wrapper wrap) {
++        return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
+     }
+ 
+     private static final EnumMap<Wrapper, MethodHandle>[]
+-            UNBOX_CONVERSIONS = newWrapperCaches(4);
++            UNBOX_CONVERSIONS = newWrapperCaches(2);
+ 
+-    private static MethodHandle unbox(Wrapper wrap, boolean raw, boolean cast) {
+-        EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)+(raw?2:0)];
++    private static MethodHandle unbox(Wrapper wrap, boolean cast) {
++        EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)];
+         MethodHandle mh = cache.get(wrap);
+         if (mh != null) {
+             return mh;
+@@ -163,19 +136,15 @@
+             case OBJECT:
+                 mh = IDENTITY; break;
+             case VOID:
+-                mh = raw ? ALWAYS_ZERO : IGNORE; break;
+-            case INT: case LONG:
+-                // these guys don't need separate raw channels
+-                if (raw)  mh = unbox(wrap, false, cast);
+-                break;
++                mh = IGNORE; break;
+         }
+         if (mh != null) {
+             cache.put(wrap, mh);
              return mh;
          }
          // look up the method
 -        String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
-+        String name = "unbox" + wrap.wrapperSimpleName() + (raw ? "Raw" : "");
-         MethodType type = unboxType(wrap, raw);
+-        MethodType type = unboxType(wrap, raw);
++        String name = "unbox" + wrap.wrapperSimpleName();
++        MethodType type = unboxType(wrap);
          try {
              mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
-@@ -339,7 +340,7 @@
+         } catch (ReflectiveOperationException ex) {
+@@ -187,32 +156,30 @@
+             return mh;
+         }
+         throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
+-                + (cast ? " (cast)" : "") + (raw ? " (raw)" : ""));
++                + (cast ? " (cast)" : ""));
+     }
+ 
+     public static MethodHandle unboxCast(Wrapper type) {
+-        return unbox(type, false, true);
+-    }
+-
+-    public static MethodHandle unboxRaw(Wrapper type) {
+-        return unbox(type, true, false);
++        return unbox(type, true);
+     }
+ 
+     public static MethodHandle unbox(Class<?> type) {
+-        return unbox(Wrapper.forPrimitiveType(type), false, false);
++        return unbox(Wrapper.forPrimitiveType(type), false);
+     }
+ 
+     public static MethodHandle unboxCast(Class<?> type) {
+-        return unbox(Wrapper.forPrimitiveType(type), false, true);
+-    }
+-
+-    public static MethodHandle unboxRaw(Class<?> type) {
+-        return unbox(Wrapper.forPrimitiveType(type), true, false);
++        return unbox(Wrapper.forPrimitiveType(type), true);
+     }
+ 
+     static private final Integer ZERO_INT = 0, ONE_INT = 1;
+ 
+     /// Primitive conversions
++    /**
++     * Produce a Number which represents the given value {@code x}
++     * according to the primitive type of the given wrapper {@code wrap}.
++     * Caller must invoke intValue, byteValue, longValue (etc.) on the result
++     * to retrieve the desired primitive value.
++     */
+     public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
+         // Maybe merge this code with Wrapper.convert/cast.
+         Number res = null;
+@@ -237,6 +204,27 @@
+         return res;
+     }
+ 
++    /**
++     * The JVM verifier allows boolean, byte, short, or char to widen to int.
++     * Support exactly this conversion, from a boxed value type Boolean,
++     * Byte, Short, Character, or Integer.
++     */
++    public static int widenSubword(Object x) {
++        if (x instanceof Integer)
++            return (int) x;
++        else if (x instanceof Boolean)
++            return fromBoolean((boolean) x);
++        else if (x instanceof Character)
++            return (char) x;
++        else if (x instanceof Short)
++            return (short) x;
++        else if (x instanceof Byte)
++            return (byte) x;
++        else
++            // Fail with a ClassCastException.
++            return (int) x;
++    }
++
+     /// Converting primitives to references
+ 
+     static Integer boxInteger(int x) {
+@@ -271,53 +259,17 @@
+         return x;
+     }
+ 
+-    /// Converting raw primitives to references
+-
+-    static Byte boxByteRaw(int x) {
+-        return boxByte((byte)x);
+-    }
+-
+-    static Short boxShortRaw(int x) {
+-        return boxShort((short)x);
+-    }
+-
+-    static Boolean boxBooleanRaw(int x) {
+-        return boxBoolean(x != 0);
+-    }
+-
+-    static Character boxCharacterRaw(int x) {
+-        return boxCharacter((char)x);
+-    }
+-
+-    static Float boxFloatRaw(int x) {
+-        return boxFloat(Float.intBitsToFloat(x));
+-    }
+-
+-    static Double boxDoubleRaw(long x) {
+-        return boxDouble(Double.longBitsToDouble(x));
+-    }
+-
+-    // a raw void value is (arbitrarily) a garbage int
+-    static Void boxVoidRaw(int x) {
+-        return null;
+-    }
+-
+-    private static MethodType boxType(Wrapper wrap, boolean raw) {
++    private static MethodType boxType(Wrapper wrap) {
+         // be exact, since return casts are hard to compose
+         Class<?> boxType = wrap.wrapperType();
+-        return MethodType.methodType(boxType, rawWrapper(wrap, raw).primitiveType());
+-    }
+-
+-    private static Wrapper rawWrapper(Wrapper wrap, boolean raw) {
+-        if (raw)  return wrap.isDoubleWord() ? Wrapper.LONG : Wrapper.INT;
+-        return wrap;
++        return MethodType.methodType(boxType, wrap.primitiveType());
+     }
+ 
+     private static final EnumMap<Wrapper, MethodHandle>[]
+-            BOX_CONVERSIONS = newWrapperCaches(4);
++            BOX_CONVERSIONS = newWrapperCaches(2);
+ 
+-    private static MethodHandle box(Wrapper wrap, boolean exact, boolean raw) {
+-        EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
++    private static MethodHandle box(Wrapper wrap, boolean exact) {
++        EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)];
+         MethodHandle mh = cache.get(wrap);
+         if (mh != null) {
+             return mh;
+@@ -327,11 +279,7 @@
+             case OBJECT:
+                 mh = IDENTITY; break;
+             case VOID:
+-                if (!raw)  mh = ZERO_OBJECT;
+-                break;
+-            case INT: case LONG:
+-                // these guys don't need separate raw channels
+-                if (raw)  mh = box(wrap, exact, false);
++                mh = ZERO_OBJECT;
+                 break;
+         }
+         if (mh != null) {
+@@ -339,8 +287,8 @@
              return mh;
          }
          // look up the method
 -        String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
-+        String name = "box" + wrap.wrapperSimpleName() + (raw ? "Raw" : "");
-         MethodType type = boxType(wrap, raw);
+-        MethodType type = boxType(wrap, raw);
++        String name = "box" + wrap.wrapperSimpleName();
++        MethodType type = boxType(wrap);
          if (exact) {
              try {
-@@ -383,6 +384,7 @@
-     }
- 
-     /// Kludges for when raw values get accidentally boxed.
-+    /// FIXME: Get rid of the raw kind of conversions.
- 
-     static int unboxRawInteger(Object x) {
-         if (x instanceof Integer)
-@@ -467,7 +469,7 @@
+                 mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+@@ -348,160 +296,26 @@
+                 mh = null;
+             }
+         } else {
+-            mh = box(wrap, !exact, raw).asType(type.erase());
++            mh = box(wrap, !exact).asType(type.erase());
+         }
+         if (mh != null) {
+             cache.put(wrap, mh);
              return mh;
          }
-         // look up the method
+         throw new IllegalArgumentException("cannot find box adapter for "
+-                + wrap + (exact ? " (exact)" : "") + (raw ? " (raw)" : ""));
++                + 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, false);
+-    }
+-
+-    public static MethodHandle boxRaw(Class<?> type) {
+-        boolean exact = false;
+-        // e.g., boxShortRaw(int)Short if exact
+-        // e.g., boxShortRaw(int)Object if !exact
+-        return box(Wrapper.forPrimitiveType(type), exact, true);
++        return box(Wrapper.forPrimitiveType(type), exact);
+     }
+ 
+     public static MethodHandle box(Wrapper type) {
+         boolean exact = false;
+-        return box(type, exact, false);
+-    }
+-
+-    public static MethodHandle boxRaw(Wrapper type) {
+-        boolean exact = false;
+-        return box(type, exact, true);
+-    }
+-
+-    /// Kludges for when raw values get accidentally boxed.
+-
+-    static int unboxRawInteger(Object x) {
+-        if (x instanceof Integer)
+-            return (int) x;
+-        else
+-            return (int) unboxLong(x, false);
+-    }
+-
+-    static Integer reboxRawInteger(Object x) {
+-        if (x instanceof Integer)
+-            return (Integer) x;
+-        else
+-            return (int) unboxLong(x, false);
+-    }
+-
+-    static Byte reboxRawByte(Object x) {
+-        if (x instanceof Byte)  return (Byte) x;
+-        return boxByteRaw(unboxRawInteger(x));
+-    }
+-
+-    static Short reboxRawShort(Object x) {
+-        if (x instanceof Short)  return (Short) x;
+-        return boxShortRaw(unboxRawInteger(x));
+-    }
+-
+-    static Boolean reboxRawBoolean(Object x) {
+-        if (x instanceof Boolean)  return (Boolean) x;
+-        return boxBooleanRaw(unboxRawInteger(x));
+-    }
+-
+-    static Character reboxRawCharacter(Object x) {
+-        if (x instanceof Character)  return (Character) x;
+-        return boxCharacterRaw(unboxRawInteger(x));
+-    }
+-
+-    static Float reboxRawFloat(Object x) {
+-        if (x instanceof Float)  return (Float) x;
+-        return boxFloatRaw(unboxRawInteger(x));
+-    }
+-
+-    static Long reboxRawLong(Object x) {
+-        return (Long) x;  //never a rebox
+-    }
+-
+-    static Double reboxRawDouble(Object x) {
+-        if (x instanceof Double)  return (Double) x;
+-        return boxDoubleRaw(unboxLong(x, true));
+-    }
+-
+-    private static MethodType reboxType(Wrapper wrap) {
+-        Class<?> boxType = wrap.wrapperType();
+-        return MethodType.methodType(boxType, Object.class);
+-    }
+-
+-    private static final EnumMap<Wrapper, MethodHandle>[]
+-            REBOX_CONVERSIONS = newWrapperCaches(1);
+-
+-    /**
+-     * Because we normalize primitive types to reduce the number of signatures,
+-     * primitives are sometimes manipulated under an "erased" type,
+-     * either int (for types other than long/double) or long (for all types).
+-     * When the erased primitive value is then boxed into an Integer or Long,
+-     * the final boxed primitive is sometimes required.  This transformation
+-     * is called a "rebox".  It takes an Integer or Long and produces some
+-     * other boxed value, typed (inexactly) as an Object
+-     */
+-    public static MethodHandle rebox(Wrapper wrap) {
+-        EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[0];
+-        MethodHandle mh = cache.get(wrap);
+-        if (mh != null) {
+-            return mh;
+-        }
+-        // slow path
+-        switch (wrap) {
+-            case OBJECT:
+-                mh = IDENTITY; break;
+-            case VOID:
+-                throw new IllegalArgumentException("cannot rebox a void");
+-        }
+-        if (mh != null) {
+-            cache.put(wrap, mh);
+-            return mh;
+-        }
+-        // look up the method
 -        String name = "reboxRaw" + wrap.simpleName();
-+        String name = "reboxRaw" + wrap.wrapperSimpleName();
-         MethodType type = reboxType(wrap);
-         try {
-             mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
-@@ -486,24 +488,6 @@
-         return rebox(Wrapper.forPrimitiveType(type));
-     }
- 
+-        MethodType type = reboxType(wrap);
+-        try {
+-            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+-            mh = mh.asType(IDENTITY.type());
+-        } catch (ReflectiveOperationException ex) {
+-            mh = null;
+-        }
+-        if (mh != null) {
+-            cache.put(wrap, mh);
+-            return mh;
+-        }
+-        throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
+-    }
+-
+-    public static MethodHandle rebox(Class<?> type) {
+-        return rebox(Wrapper.forPrimitiveType(type));
+-    }
+-
 -    /// Width-changing conversions between int and long.
 -
 -    static long widenInt(int x) {
@@ -10653,12 +11075,11 @@
 -
 -    static Integer narrowBoxedLong(Long x) {
 -        return (int)(long) x;
--    }
--
++        return box(type, exact);
+     }
+ 
      /// Constant functions
- 
-     static void ignore(Object x) {
-@@ -553,7 +537,7 @@
+@@ -553,7 +367,7 @@
              case OBJECT:
              case INT: case LONG: case FLOAT: case DOUBLE:
                  try {
@@ -10667,7 +11088,23 @@
                  } catch (ReflectiveOperationException ex) {
                      mh = null;
                  }
-@@ -657,7 +641,7 @@
+@@ -564,12 +378,9 @@
+             return mh;
+         }
+ 
+-        // use the raw method
+-        Wrapper rawWrap = wrap.rawPrimitive();
+-        if (mh == null && rawWrap != wrap) {
+-            mh = MethodHandles.explicitCastArguments(zeroConstantFunction(rawWrap), type);
+-        }
+-        if (mh != null) {
++        // use zeroInt and cast the result
++        if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
++            mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
+             cache.put(wrap, mh);
+             return mh;
+         }
+@@ -657,7 +468,7 @@
          return t.cast(x);
      }
  
@@ -10676,7 +11113,7 @@
      static {
          try {
              MethodType idType = MethodType.genericMethodType(1);
-@@ -666,8 +650,6 @@
+@@ -666,8 +477,6 @@
              MethodType ignoreType = idType.changeReturnType(void.class);
              MethodType zeroObjectType = MethodType.genericMethodType(0);
              IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
@@ -10685,7 +11122,15 @@
              //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
              CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType);
              ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType);
-@@ -735,8 +717,10 @@
+@@ -723,7 +532,6 @@
+         if (exact) {
+             MethodType xmt = MethodType.methodType(type, Object.class);
+             mh = MethodHandles.explicitCastArguments(mh, xmt);
+-            //mh = AdapterMethodHandle.makeRetypeRaw(IMPL_TOKEN, xmt, mh);
+         }
+         if (cache != null)
+             cache.put(wrap, mh);
+@@ -735,8 +543,10 @@
      }
  
      public static MethodHandle identity(Class<?> type) {
@@ -10698,7 +11143,7 @@
      }
  
      public static MethodHandle identity(Wrapper wrap) {
-@@ -769,95 +753,203 @@
+@@ -769,95 +579,203 @@
          throw new IllegalArgumentException("cannot find identity for " + wrap);
      }
  
@@ -10943,7 +11388,7 @@
      static boolean toBoolean(byte x) {
          // see javadoc for MethodHandles.explicitCastArguments
          return ((x & 1) != 0);
-@@ -868,62 +960,48 @@
+@@ -868,62 +786,48 @@
      }
  
      private static final EnumMap<Wrapper, MethodHandle>[]
@@ -11084,6 +11529,59 @@
       * Test if two classes have the same class loader and package qualifier.
       * @param class1
       * @param class2
+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
+@@ -122,8 +122,6 @@
+         return isNullConversion(recv.returnType(), call.returnType());
+     }
+ 
+-    //TO DO: isRawConversion
+-
+     /**
+      * Determine if the JVM verifier allows a value of type call to be
+      * passed to a formal parameter (or return variable) of type recv.
+@@ -188,40 +186,6 @@
+         return -1;
+     }
+ 
+-    public static int canPassRaw(Class<?> src, Class<?> dst) {
+-        if (dst.isPrimitive()) {
+-            if (dst == void.class)
+-                // As above, return anything to a caller expecting void.
+-                return 1;
+-            if (src == void.class)
+-                // Special permission for raw conversions: allow a void
+-                // to be captured as a garbage int.
+-                // Caller promises that the actual value will be disregarded.
+-                return dst == int.class ? 1 : 0;
+-            if (isNullType(src))
+-                // Special permission for raw conversions: allow a null
+-                // to be reinterpreted as anything.  For objects, it is safe,
+-                // and for primitives you get a garbage value (probably zero).
+-                return 1;
+-            if (!src.isPrimitive())
+-                return 0;
+-            Wrapper sw = Wrapper.forPrimitiveType(src);
+-            Wrapper dw = Wrapper.forPrimitiveType(dst);
+-            if (sw.stackSlots() == dw.stackSlots())
+-                return 1;  // can do a reinterpret-cast on a stacked primitive
+-            if (sw.isSubwordOrInt() && dw == Wrapper.VOID)
+-                return 1;  // can drop an outgoing int value
+-            return 0;
+-        } else if (src.isPrimitive()) {
+-            return 0;
+-        }
+-
+-        // Both references.
+-        if (isNullReferenceConversion(src, dst))
+-            return 1;
+-        return -1;
+-    }
+-
+     public static boolean isSpreadArgType(Class<?> spreadArg) {
+         return spreadArg.isArray();
+     }
 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
@@ -11172,7 +11670,7 @@
          }
          throw new InternalError("bad wrapper");
      }
-@@ -546,7 +557,7 @@
+@@ -546,72 +557,11 @@
              case 'S': return Short.valueOf((short) x);
              case 'B': return Byte.valueOf((byte) x);
              case 'C': return Character.valueOf((char) x);
@@ -11181,7 +11679,72 @@
          }
          throw new InternalError("bad wrapper");
      }
-@@ -620,7 +631,10 @@
+ 
+-    /** Wrap a value (a long or smaller value) in this wrapper's type.
+-     * Does not perform floating point conversion.
+-     * Produces a {@code Long} for {@code OBJECT}, although the exact type
+-     * of the operand is not known.
+-     * Returns null for {@code VOID}.
+-     */
+-    public Object wrapRaw(long x) {
+-        switch (basicTypeChar) {
+-            case 'F':  return Float.valueOf(Float.intBitsToFloat((int)x));
+-            case 'D':  return Double.valueOf(Double.longBitsToDouble(x));
+-            case 'L':  // same as 'J':
+-            case 'J':  return (Long) x;
+-        }
+-        // Other wrapping operations are just the same, given that the
+-        // operand is already promoted to an int.
+-        return wrap((int)x);
+-    }
+-
+-    /** Produce bitwise value which encodes the given wrapped value.
+-     * Does not perform floating point conversion.
+-     * Returns zero for {@code VOID}.
+-     */
+-    public long unwrapRaw(Object x) {
+-        switch (basicTypeChar) {
+-            case 'F':  return Float.floatToRawIntBits((Float) x);
+-            case 'D':  return Double.doubleToRawLongBits((Double) x);
+-
+-            case 'L': throw newIllegalArgumentException("cannot unwrap from sobject type");
+-            case 'V': return 0;
+-            case 'I': return (int)(Integer) x;
+-            case 'J': return (long)(Long) x;
+-            case 'S': return (short)(Short) x;
+-            case 'B': return (byte)(Byte) x;
+-            case 'C': return (char)(Character) x;
+-            case 'Z': return (boolean)(Boolean) x ? 1 : 0;
+-        }
+-        throw new InternalError("bad wrapper");
+-    }
+-
+-    /** Report what primitive type holds this guy's raw value. */
+-    public Class<?> rawPrimitiveType() {
+-        return rawPrimitive().primitiveType();
+-    }
+-
+-    /** Report, as a wrapper, what primitive type holds this guy's raw value.
+-     *  Returns self for INT, LONG, OBJECT; returns LONG for DOUBLE,
+-     *  else returns INT.
+-     */
+-    public Wrapper rawPrimitive() {
+-        switch (basicTypeChar) {
+-            case 'S': case 'B':
+-            case 'C': case 'Z':
+-            case 'V':
+-            case 'F':
+-                return INT;
+-            case 'D':
+-                return LONG;
+-        }
+-        return this;
+-    }
+-
+     private static Number numberValue(Object x) {
+         if (x instanceof Number)     return (Number)x;
+         if (x instanceof Character)  return (int)(Character)x;
+@@ -620,7 +570,10 @@
          return (Number)x;
      }
  
@@ -12187,6 +12750,15 @@
              for (int ins = 0; ins <= nargs; ins++) {
                  if (nargs > 10 && ins > 4 && ins < nargs-4 && ins % 10 != 3)
                      continue;
+@@ -1787,7 +1821,7 @@
+         List<Object> argsToPass = new ArrayList<>(resList);
+         List<Object> argsToInsert = argsToPass.subList(pos, pos + ins);
+         if (verbosity >= 3)
+-            System.out.println("insert: "+argsToInsert+" into "+target);
++            System.out.println("insert: "+argsToInsert+" @"+pos+" into "+target);
+         @SuppressWarnings("cast")  // cast to spread Object... is helpful
+         MethodHandle target2 = MethodHandles.insertArguments(target, pos,
+                 (Object[]/*...*/) argsToInsert.toArray());
 @@ -1950,7 +1984,7 @@
          assertEquals(resList, res2List);
      }
@@ -12708,7 +13280,110 @@
 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
-@@ -281,6 +281,91 @@
+@@ -122,36 +122,6 @@
+     }
+ 
+     @Test
+-    public void testUnboxRaw() throws Throwable {
+-        //System.out.println("unboxRaw");
+-        for (Wrapper w : Wrapper.values()) {
+-            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
+-            //System.out.println(w);
+-            for (int n = -5; n < 10; n++) {
+-                Object box = w.wrap(n);
+-                long expResult = w.unwrapRaw(box);
+-                Object box2 = w.wrapRaw(expResult);
+-                assertEquals(box, box2);
+-                MethodHandle unboxer = ValueConversions.unboxRaw(w.primitiveType());
+-                long result = -1;
+-                switch (w) {
+-                    case INT:     result = (int)  unboxer.invokeExact(box); break;
+-                    case LONG:    result = (long) unboxer.invokeExact(box); break;
+-                    case FLOAT:   result = (int)  unboxer.invokeExact(box); break;
+-                    case DOUBLE:  result = (long) unboxer.invokeExact(box); break;
+-                    case CHAR:    result = (int)  unboxer.invokeExact(box); break;
+-                    case BYTE:    result = (int)  unboxer.invokeExact(box); break;
+-                    case SHORT:   result = (int)  unboxer.invokeExact(box); break;
+-                    case BOOLEAN: result = (int)  unboxer.invokeExact(box); break;
+-                    case VOID:    result = (int)  unboxer.invokeExact(box); break;
+-                }
+-                assertEquals("(w,n,box)="+Arrays.asList(w,n,box),
+-                             expResult, result);
+-            }
+-        }
+-    }
+-
+-    @Test
+     public void testBox() throws Throwable {
+         //System.out.println("box");
+         for (Wrapper w : Wrapper.values()) {
+@@ -180,65 +150,6 @@
+     }
+ 
+     @Test
+-    public void testBoxRaw() throws Throwable {
+-        //System.out.println("boxRaw");
+-        for (Wrapper w : Wrapper.values()) {
+-            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
+-            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
+-            //System.out.println(w);
+-            for (int n = -5; n < 10; n++) {
+-                Object box = w.wrap(n);
+-                long   raw = w.unwrapRaw(box);
+-                Object expResult = box;
+-                MethodHandle boxer = ValueConversions.boxRaw(w.primitiveType());
+-                Object result = null;
+-                switch (w) {
+-                case INT:     result = boxer.invokeExact((int)raw); break;
+-                case LONG:    result = boxer.invokeExact(raw); break;
+-                case FLOAT:   result = boxer.invokeExact((int)raw); break;
+-                case DOUBLE:  result = boxer.invokeExact(raw); break;
+-                case CHAR:    result = boxer.invokeExact((int)raw); break;
+-                case BYTE:    result = boxer.invokeExact((int)raw); break;
+-                case SHORT:   result = boxer.invokeExact((int)raw); break;
+-                case BOOLEAN: result = boxer.invokeExact((int)raw); break;
+-                }
+-                assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+-                             expResult, result);
+-            }
+-        }
+-    }
+-
+-    @Test
+-    public void testReboxRaw() throws Throwable {
+-        //System.out.println("reboxRaw");
+-        for (Wrapper w : Wrapper.values()) {
+-            Wrapper pw = Wrapper.forPrimitiveType(w.rawPrimitiveType());
+-            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
+-            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
+-            //System.out.println(w);
+-            for (int n = -5; n < 10; n++) {
+-                Object box = w.wrap(n);
+-                Object raw = pw.wrap(w.unwrapRaw(box));
+-                Object expResult = box;
+-                MethodHandle boxer = ValueConversions.rebox(w.primitiveType());
+-                Object result = null;
+-                switch (w) {
+-                case INT:     result = boxer.invokeExact(raw); break;
+-                case LONG:    result = boxer.invokeExact(raw); break;
+-                case FLOAT:   result = boxer.invokeExact(raw); break;
+-                case DOUBLE:  result = boxer.invokeExact(raw); break;
+-                case CHAR:    result = boxer.invokeExact(raw); break;
+-                case BYTE:    result = boxer.invokeExact(raw); break;
+-                case SHORT:   result = boxer.invokeExact(raw); break;
+-                case BOOLEAN: result = boxer.invokeExact(raw); break;
+-                }
+-                assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+-                             expResult, result);
+-            }
+-        }
+-    }
+-
+-    @Test
+     public void testCast() throws Throwable {
+         //System.out.println("cast");
+         Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class };
+@@ -281,6 +192,91 @@
      }
  
      @Test
@@ -12800,7 +13475,7 @@
      public void testVarargsArray() throws Throwable {
          //System.out.println("varargsArray");
          final int MIN = START_ARITY;
-@@ -332,7 +417,7 @@
+@@ -332,7 +328,7 @@
      }
  
      private void testTypedVarargsArray(Class<?> arrayType) throws Throwable {
--- a/meth-lazy-7023639.review.patch	Sat Jul 21 16:55:21 2012 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4532 +0,0 @@
-Addendum to meth-lazy-7023639.patch.
-Fixes performance cliff with invokestatic on uninitialized classes.
-* * *
-Addendum to meth-lazy-7023639.patch.
-Adjustments to BMHs.
-
-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
-@@ -25,42 +25,24 @@
- 
- package java.lang.invoke;
- 
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ACC_FINAL;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ACC_PUBLIC;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ACC_STATIC;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ACC_SUPER;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ALOAD;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ARETURN;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.DLOAD;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.FLOAD;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.GETFIELD;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.ILOAD;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.INVOKESPECIAL;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.INVOKESTATIC;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.LLOAD;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.PUTFIELD;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.RETURN;
--import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.V1_6;
--import static java.lang.invoke.LambdaForm.arguments;
-+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.*;
- import static java.lang.invoke.LambdaForm.basicTypes;
- import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
--import static java.lang.invoke.MethodType.methodType;
-+import static java.lang.invoke.MethodHandleStatics.*;
- 
- import java.lang.invoke.LambdaForm.Name;
- import java.lang.invoke.LambdaForm.NamedFunction;
- import java.lang.invoke.MethodHandles.Lookup;
- import java.lang.reflect.Field;
- import java.util.Arrays;
--import java.util.IdentityHashMap;
--import java.util.Map;
-+import java.util.HashMap;
- 
- import sun.invoke.util.ValueConversions;
- import sun.invoke.util.Wrapper;
--import sun.misc.Unsafe;
- 
- import com.sun.xml.internal.ws.org.objectweb.asm.ClassWriter;
- import com.sun.xml.internal.ws.org.objectweb.asm.MethodVisitor;
-+import com.sun.xml.internal.ws.org.objectweb.asm.Type;
- 
- /**
-  * The flavor of method handle which emulates an invoke instruction
-@@ -83,19 +65,17 @@
-         // for some type signatures, there exist pre-defined concrete BMH classes
-         try {
-             switch (xtype) {
--            case 'L': return new BMH_L(type, form, x);
-+            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':
--                assert x instanceof Integer : "expected Integer, found " + x.getClass();
--                return (BoundMethodHandle) Data.get("I").constructor.invokeBasic(type, form, ((Integer) x).intValue());
-+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('I').constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
-             case 'J':
--                assert x instanceof Long;
--                return new BMH_J(type, form, ((Long) x).longValue());
-+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('J').constructor[0].invokeBasic(type, form, (long) x);
-             case 'F':
--                assert x instanceof Float;
--                return (BoundMethodHandle) Data.get("F").constructor.invokeBasic(type, form, ((Float) x).floatValue());
-+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('F').constructor[0].invokeBasic(type, form, (float) x);
-             case 'D':
--                assert x instanceof Double;
--                return (BoundMethodHandle) Data.get("D").constructor.invokeBasic(type, form, ((Double) x).doubleValue());
-+                return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('D').constructor[0].invokeBasic(type, form, (double) x);
-             default : throw new InternalError("unexpected xtype: " + xtype);
-             }
-         } catch (Throwable t) {
-@@ -103,37 +83,29 @@
-         }
-     }
- 
-+    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':
--                assert x instanceof Integer : "expected Integer, found " + x.getClass();
--                return cloneExtendI(type, form, ((Integer) x).intValue());
--            case 'J':
--                assert x instanceof Long;
--                return cloneExtendJ(type, form, ((Long) x).longValue());
--            case 'F':
--                assert x instanceof Float;
--                return cloneExtendF(type, form, ((Float) x).floatValue());
--            case 'D':
--                assert x instanceof Double;
--                return cloneExtendD(type, form, ((Double) x).doubleValue());
--            default : throw new InternalError("unexpected type: " + xtype);
-+            case 'I': return cloneExtendI(type, form, ValueConversions.widenSubword(x));
-+            case 'J': return cloneExtendJ(type, form, (long) x);
-+            case 'F': return cloneExtendF(type, form, (float) x);
-+            case 'D': return cloneExtendD(type, form, (double) x);
-             }
-         } catch (Throwable t) {
-             throw new InternalError(t);
-         }
-+        throw 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(basicType, 1+pos, tcount());
--        if (basicType == 'I' && !(value instanceof Integer)) {
--            // Cf. ValueConversions.unboxInteger
--            value = ValueConversions.primitiveConversion(Wrapper.INT, value, true);
--        }
-+        LambdaForm form = internalForm().bind(1+pos, speciesData());
-         return cloneExtend(type, form, basicType, value);
-     }
- 
-@@ -156,73 +128,48 @@
-          }
-     }
- 
--    public int tcount() {
--        return types().length();
--    }
--
--    protected final Data myData() {
--        return Data.get(types());
-+    static final String EXTENSION_TYPES = "LIJFD";
-+    static final byte INDEX_L = 0, INDEX_I = 1, INDEX_J = 2, INDEX_F = 3, INDEX_D = 4;
-+    static byte extensionIndex(char type) {
-+        int i = EXTENSION_TYPES.indexOf(type);
-+        if (i < 0)  throw new InternalError();
-+        return (byte) i;
-     }
- 
-     /**
--     * Return the type signature of a concrete BMH. Each concrete BMH is required
--     * to implement this method to return its type signature as a constant String
--     * (the {@link #types} field).
-+     * 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.
-      */
--    public abstract String types();
--
--    /**
--     * All subclasses must provide such a value describing their type signature.
--     */
--    public static final String types = "";
--
--    public char type(int i) {
--        return types().charAt(i);
--    }
--
--    /**
--     * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
--     * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
--     * getter.
--     */
--    static Name getterName(Name mhName, char basicType, int i) {
--        Class<?> paramCls = Wrapper.forBasicType(basicType).primitiveType();
--        MethodHandle bmhGetter;
--        try {
--            bmhGetter = Lookup.IMPL_LOOKUP.findVirtual(BoundMethodHandle.class, "arg" + basicType, methodType(paramCls, int.class));
--        } catch (IllegalAccessException | NoSuchMethodException e) {
--            throw new InternalError(e);
--        }
--
--        Name getterName = new Name(bmhGetter, mhName, i);
--        return getterName;
--    }
-+    protected abstract SpeciesData speciesData();
- 
-     @Override
-     final Object internalValues() {
--        Object[] boundValues = new Object[types().length()];
-+        Object[] boundValues = new Object[speciesData().fieldCount()];
-         for (int i = 0; i < boundValues.length; ++i) {
--            try {
--                switch (types().charAt(i)) {
--                case 'L': boundValues[i] = argL(i); break;
--                case 'I': boundValues[i] = argI(i); break;
--                case 'F': boundValues[i] = argF(i); break;
--                case 'D': boundValues[i] = argD(i); break;
--                case 'J': boundValues[i] = argJ(i); break;
--                default : throw new InternalError("unexpected type: " + types().charAt(i));
--                }
--            } catch (Throwable t) {
--                throw new InternalError(t);
--            }
-+            boundValues[i] = arg(i);
-         }
-         return Arrays.asList(boundValues);
-     }
- 
--    public final Object argL(int i) throws Throwable { return          myData().getters[i].invokeBasic(this); }
--    public final int    argI(int i) throws Throwable { return (int)    myData().getters[i].invokeBasic(this); }
--    public final float  argF(int i) throws Throwable { return (float)  myData().getters[i].invokeBasic(this); }
--    public final double argD(int i) throws Throwable { return (double) myData().getters[i].invokeBasic(this); }
--    public final long   argJ(int i) throws Throwable { return (long)   myData().getters[i].invokeBasic(this); }
-+    public final Object arg(int i) {
-+        try {
-+            switch (speciesData().fieldType(i)) {
-+            case 'L': return argL(i);
-+            case 'I': return argI(i);
-+            case 'F': return argF(i);
-+            case 'D': return argD(i);
-+            case 'J': return argJ(i);
-+            }
-+        } catch (Throwable ex) {
-+            throw new InternalError(ex);
-+        }
-+        throw new InternalError("unexpected type: " + speciesData().types+"."+i);
-+    }
-+    public final Object argL(int i) throws Throwable { return          speciesData().getters[i].invokeBasic(this); }
-+    public final int    argI(int i) throws Throwable { return (int)    speciesData().getters[i].invokeBasic(this); }
-+    public final float  argF(int i) throws Throwable { return (float)  speciesData().getters[i].invokeBasic(this); }
-+    public final double argD(int i) throws Throwable { return (double) speciesData().getters[i].invokeBasic(this); }
-+    public final long   argJ(int i) throws Throwable { return (long)   speciesData().getters[i].invokeBasic(this); }
- 
-     //
-     // cloning API
-@@ -235,302 +182,330 @@
-     public abstract BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float  narg) throws Throwable;
-     public abstract BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable;
- 
--    //
--    // getters to close bootstrap loops
--    //
--
--    public long   argJ0() throws Throwable { return argJ(0); }
--    public Object argL1() throws Throwable { return argL(1); }
-+    // The following is a grossly irregular hack:
-+    @Override MethodHandle reinvokerTarget() {
-+        try {
-+            return (MethodHandle) argL(0);
-+        } catch (Throwable ex) {
-+            throw new InternalError(ex);
-+        }
-+    }
- 
-     //
-     // concrete BMH classes required to close bootstrap loops
-     //
- 
--    static final class BMH_L extends BoundMethodHandle {
-+    private  // make it private to force users to access the enclosing class first
-+    static final class Species_L extends BoundMethodHandle {
-         final Object argL0;
--        public BMH_L(MethodType mt, LambdaForm lf, Object argL0) {
-+        public Species_L(MethodType mt, LambdaForm lf, Object argL0) {
-             super(mt, lf);
-             this.argL0 = argL0;
-         }
-+        // The following is a grossly irregular hack:
-+        @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; }
-         @Override
--        public String types() {
--            return types;
-+        public SpeciesData speciesData() {
-+            return SPECIES_DATA;
-         }
--        public static final String types = "L";
-+        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 BMH_L(mt, lf, argL0);
-+            return new Species_L(mt, lf, argL0);
-         }
-         @Override
-         public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
--            return new BMH_LL(mt, lf, argL0, narg);
-+            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) Data.get("LI").constructor.invokeBasic(mt, lf, argL0, narg);
-+            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) Data.get("LJ").constructor.invokeBasic(mt, lf, argL0, narg);
-+            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) Data.get("LF").constructor.invokeBasic(mt, lf, argL0, narg);
-+            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) Data.get("LD").constructor.invokeBasic(mt, lf, argL0, narg);
-+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, narg);
-         }
-     }
- 
--    static final class BMH_LL extends BoundMethodHandle {
-+/*
-+    static final class Species_LL extends BoundMethodHandle {
-         final Object argL0;
-         final Object argL1;
--        public BMH_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
-+        public Species_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
-             super(mt, lf);
-             this.argL0 = argL0;
-             this.argL1 = argL1;
-         }
-         @Override
--        public String types() {
--            return types;
-+        public SpeciesData speciesData() {
-+            return SPECIES_DATA;
-         }
--        public static final String types = "LL";
-+        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 BMH_LL(mt, lf, argL0, argL1);
-+            return new Species_LL(mt, lf, argL0, argL1);
-         }
-         @Override
-         public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("LLL").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
-+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
-         }
-         @Override
-         public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("LLI").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
-+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
-         }
-         @Override
-         public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("LLJ").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
-+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
-         }
-         @Override
-         public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("LLF").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
-+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
-         }
-         @Override
-         public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("LLD").constructor.invokeBasic(mt, lf, argL0, argL1, narg);
-+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
-         }
-     }
- 
--    static final class BMH_JL extends BoundMethodHandle {
-+    static final class Species_JL extends BoundMethodHandle {
-         final long argJ0;
-         final Object argL1;
--        public BMH_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
-+        public Species_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
-             super(mt, lf);
-             this.argJ0 = argJ0;
-             this.argL1 = argL1;
-         }
-         @Override
--        public String types() {
--            return types;
-+        public SpeciesData speciesData() {
-+            return SPECIES_DATA;
-         }
--        public static final String types = "JL";
-+        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 BMH_JL(mt, lf, argJ0, argL1);
-+            return new Species_JL(mt, lf, argJ0, argL1);
-         }
-         @Override
-         public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("JLL").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
-+            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) Data.get("JLI").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
-+            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) Data.get("JLJ").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
-+            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) Data.get("JLF").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
-+            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) Data.get("JLD").constructor.invokeBasic(mt, lf, argJ0, argL1, narg);
-+            return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
-         }
-     }
--
--    static final class BMH_J extends BoundMethodHandle {
--        final long argJ0;
--        public BMH_J(MethodType mt, LambdaForm lf, long argJ0) {
--            super(mt, lf);
--            this.argJ0 = argJ0;
--        }
--        @Override
--        public String types() {
--            return types;
--        }
--        public static final String types = "J";
--        @Override public final long argJ0() { return argJ0; }
--        @Override
--        public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
--            return new BMH_J(mt, lf, argJ0);
--        }
--        @Override
--        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
--            return new BMH_JL(mt, lf, argJ0, narg);
--        }
--        @Override
--        public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("JI").constructor.invokeBasic(mt, lf, argJ0, narg);
--        }
--        @Override
--        public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("JJ").constructor.invokeBasic(mt, lf, argJ0, narg);
--        }
--        @Override
--        public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("JF").constructor.invokeBasic(mt, lf, argJ0, narg);
--        }
--        @Override
--        public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
--            return (BoundMethodHandle) Data.get("JD").constructor.invokeBasic(mt, lf, argJ0, narg);
--        }
--    }
--
--    public static final MethodHandle MH_argJ0;
--    public static final MethodHandle MH_argL1;
--
--    static {
--        final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
--        final Class<?> BMH = BoundMethodHandle.class;
--        try {
--            MH_argJ0 = LOOKUP.findVirtual(BMH, "argJ0", MethodType.methodType(long.class));
--            MH_argL1 = LOOKUP.findVirtual(BMH, "argL1", MethodType.methodType(Object.class));
--        } catch (NoSuchMethodException | IllegalAccessException e) {
--            throw new InternalError(e);
--        }
--    }
-+*/
- 
-     //
--    // reinvocation
--    //
--
--    /** Create a LF which simply reinvokes a target of the given basic type.
--     *  The target MH must have the reinvocation target bound to value #0.
--     */
--    static LambdaForm reinvokerForm(MethodType mtype) {
--        mtype = mtype.basicType();
--        LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
--        if (reinvoker != null)  return reinvoker;
--        MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
--        final int THIS_BMH    = 0;
--        final int ARG_BASE    = 1;
--        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
--        int nameCursor = ARG_LIMIT;
--        final int NEXT_MH     = nameCursor++;
--        final int REINVOKE    = nameCursor++;
--        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
--        names[NEXT_MH] = getterName(names[THIS_BMH], 'L', 0);
--        Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
--        targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
--        names[REINVOKE] = new Name(MH_invokeBasic, targetArgs);
--        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
--    }
--
--    //
--    // BMH meta-data
-+    // BMH species meta-data
-     //
- 
-     /**
-      * Meta-data wrapper for concrete BMH classes.
-      */
--    static class Data {
-+    static class SpeciesData {
-         final String                             types;
-         final Class<? extends BoundMethodHandle> clazz;
--        final MethodHandle                       constructor;
-+        // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
-+        // Therefore, we need a non-final link in the chain.  Use array elements.
-+        final MethodHandle[]                     constructor;
-         final MethodHandle[]                     getters;
-+        final SpeciesData[]                      extensions;
- 
--        Data(String types, Class<? extends BoundMethodHandle> clazz, MethodHandle constructor, MethodHandle[] getters) {
-+        public int fieldCount() {
-+            return types.length();
-+        }
-+        public char fieldType(int i) {
-+            return types.charAt(i);
-+        }
-+
-+        public String toString() {
-+            return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]";
-+        }
-+
-+        /**
-+         * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
-+         * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
-+         * getter.
-+         */
-+        Name getterName(Name mhName, int i) {
-+            MethodHandle mh = getters[i];
-+            assert(mh != null) : this+"."+i;
-+            return new Name(mh, mhName);
-+        }
-+
-+        static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
-+
-+        private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
-             this.types = types;
-             this.clazz = clazz;
--            this.constructor = constructor;
--            this.getters = getters;
-+            if (!INIT_DONE) {
-+                this.constructor = new MethodHandle[1];
-+                this.getters = new MethodHandle[types.length()];
-+            } else {
-+                this.constructor = Factory.makeCtors(clazz, types, null);
-+                this.getters = Factory.makeGetters(clazz, types, null);
-+            }
-+            this.extensions = new SpeciesData[EXTENSION_TYPES.length()];
-         }
- 
--        static Map<String, Data> CACHE = new IdentityHashMap<>();
--
--        static Data make(String types) {
--            final Class<? extends BoundMethodHandle> cbmh = Factory.generateConcreteBMHClass(types);
--            final MethodHandle ctor = Factory.makeCbmhCtor(cbmh, types);
--            final MethodHandle[] getters = Factory.makeGetters(cbmh, types);
--            return new Data(types, cbmh, ctor, getters);
-+        private void initForBootstrap() {
-+            assert(!INIT_DONE);
-+            if (constructor[0] == null) {
-+                Factory.makeCtors(clazz, types, this.constructor);
-+                Factory.makeGetters(clazz, types, this.getters);
-+            }
-         }
- 
--        static Data get(String types) {
--            final String key = types.intern();
--            Data d = CACHE.get(key);
--            if (d == null) {
--                d = make(types);
--                Data e = CACHE.get(key);
--                if (e != null) {
--                    d = e;
--                } else {
--                    CACHE.put(key, d);
--                }
-+        private SpeciesData(String types) {
-+            // Placeholder only.
-+            this.types = types;
-+            this.clazz = null;
-+            this.constructor = null;
-+            this.getters = null;
-+            this.extensions = null;
-+        }
-+        private boolean isPlaceholder() { return clazz == null; }
-+
-+        private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
-+        private static final boolean INIT_DONE;  // set after <clinit> finishes...
-+
-+        SpeciesData extendWithType(char type) {
-+            int i = extensionIndex(type);
-+            SpeciesData d = extensions[i];
-+            if (d != null)  return d;
-+            extensions[i] = d = get(types+type);
-+            return d;
-+        }
-+
-+        SpeciesData extendWithIndex(byte index) {
-+            SpeciesData d = extensions[index];
-+            if (d != null)  return d;
-+            extensions[index] = d = get(types+EXTENSION_TYPES.charAt(index));
-+            return d;
-+        }
-+
-+        private static SpeciesData get(String types) {
-+            // Acquire cache lock for query.
-+            SpeciesData d = lookupCache(types);
-+            if (!d.isPlaceholder())
-+                return d;
-+            Class<? extends BoundMethodHandle> cbmh;
-+            synchronized (d) {
-+                // Use synch. on the placeholder to prevent multiple instantiation of one species.
-+                // Creating this class forces a recursive call to getForClass.
-+                cbmh = Factory.generateConcreteBMHClass(types);
-             }
-+            // Reacquire cache lock.
-+            d = lookupCache(types);
-+            // Class loading must have upgraded the cache.
-+            assert(d != null && !d.isPlaceholder());
-+            return d;
-+        }
-+        static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
-+            // clazz is a new class which is initializing its SPECIES_DATA field
-+            return updateCache(types, new SpeciesData(types, clazz));
-+        }
-+        private static synchronized SpeciesData lookupCache(String types) {
-+            SpeciesData d = CACHE.get(types);
-+            if (d != null)  return d;
-+            d = new SpeciesData(types);
-+            assert(d.isPlaceholder());
-+            CACHE.put(types, d);
-+            return d;
-+        }
-+        private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
-+            SpeciesData d2;
-+            assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
-+            assert(!d.isPlaceholder());
-+            CACHE.put(types, d);
-             return d;
-         }
- 
-         static {
--            // pre-fill the BMH data cache with BMH's inner classes
--            final Class<BoundMethodHandle> BMH = BoundMethodHandle.class;
-+            // 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 : BMH.getDeclaredClasses()) {
--                    if (BMH.isAssignableFrom(c)) {
-+                for (Class<?> c : rootCls.getDeclaredClasses()) {
-+                    if (rootCls.isAssignableFrom(c)) {
-                         final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
--                        final String types = Factory.typesFromConcreteBMHClass(cbmh);
--                        final MethodHandle ctor = Factory.makeCbmhCtor(cbmh, types);
--                        final MethodHandle[] getters = Factory.makeGetters(cbmh, types);
--                        final Data d = new Data(types, cbmh, ctor, getters);
--                        CACHE.put(types.intern(), d);
-+                        SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
-+                        assert(d != null) : cbmh.getName();
-+                        assert(d.clazz == cbmh);
-+                        assert(d == lookupCache(d.types));
-                     }
-                 }
-             } catch (Throwable e) {
-                 throw new InternalError(e);
-             }
-+
-+            for (SpeciesData d : CACHE.values()) {
-+                d.initForBootstrap();
-+            }
-+            // Note:  Do not simplify this, because INIT_DONE must not be
-+            // a compile-time constant during bootstrapping.
-+            INIT_DONE = Boolean.TRUE;
-         }
-     }
- 
-+    static SpeciesData getSpeciesData(String types) {
-+        return SpeciesData.get(types);
-+    }
-+
-     /**
-      * Generation of concrete BMH classes.
-      *
--     * A concrete BMH is fit for binding a number of values adhering to a
-+     * A concrete BMH species is fit for binding a number of values adhering to a
-      * given type pattern. Reference types are erased.
-      *
--     * BMH classes are cached by type pattern.
-+     * BMH species are cached by type pattern.
-      *
--     * A concrete BMH has a number of fields with the concrete (possibly erased) types of
-+     * A BMH species has a number of fields with the concrete (possibly erased) types of
-      * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
-      * which can be included as names in lambda forms.
-      */
-     static class Factory {
- 
--        static final String JLO_SIG = "Ljava/lang/Object;";
--        static final String JLS_SIG = "Ljava/lang/String;";
--        static final String MH = "java/lang/invoke/MethodHandle";
--        static final String MH_SIG = "Ljava/lang/invoke/MethodHandle;";
--        static final String BMH = "java/lang/invoke/BoundMethodHandle";
--        static final String BMH_SIG = "Ljava/lang/invoke/BoundMethodHandle;";
--        static final String DATA = "java/lang/invoke/BoundMethodHandle$Data";
--        static final String DATA_SIG = "Ljava/lang/invoke/BoundMethodHandle$Data;";
-+        static final String JLO_SIG  = "Ljava/lang/Object;";
-+        static final String JLS_SIG  = "Ljava/lang/String;";
-+        static final String JLC_SIG  = "Ljava/lang/Class;";
-+        static final String MH       = "java/lang/invoke/MethodHandle";
-+        static final String MH_SIG   = "L"+MH+";";
-+        static final String BMH      = "java/lang/invoke/BoundMethodHandle";
-+        static final String BMH_SIG  = "L"+BMH+";";
-+        static final String SPECIES_DATA     = "java/lang/invoke/BoundMethodHandle$SpeciesData";
-+        static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
- 
--        static final String BMHDATA_GET_SIG = "(" + JLS_SIG + ")" + DATA_SIG;
--        static final String TYPES_SIG = "()" + JLS_SIG;
--        static final String MYDATA_SIG = "()" + DATA_SIG;
-+        static final String SPECIES_PREFIX_NAME = "Species_";
-+        static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
-+
-+        static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
-+        static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
-+        static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
-+        static final String VOID_SIG   = "()V";
- 
-         static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
- 
-@@ -541,12 +516,12 @@
-         /**
-          * Generate a concrete subclass of BMH for a given combination of bound types.
-          *
--         * A concrete BMH subclass adheres to the following schema:
-+         * A concrete BMH species adheres to the following schema:
-          *
-          * <pre>
--         * class BMH_<<types>> extends BMH {
-+         * class Species_<<types>> extends BoundMethodHandle {
-          *     <<fields>>
--         *     final String dataValueTypes() { return <<types>>; }
-+         *     final SpeciesData speciesData() { return SpeciesData.get("<<types>>"); }
-          * }
-          * </pre>
-          *
-@@ -557,39 +532,40 @@
-          * the type signature, adhering to the naming schema described in the definition of
-          * {@link #makeFieldName()}.
-          *
--         * For example, a concrete BMH class for two reference and one integral bound values
-+         * For example, a concrete BMH species for two reference and one integral bound values
-          * would have the following shape:
-          *
-          * <pre>
--         * final class BMH_LLI extends BMH {
-+         * class BoundMethodHandle { ... private static
-+         * final class Species_LLI extends BoundMethodHandle {
-          *     final Object argL0;
-          *     final Object argL1;
-          *     final int argI2;
--         *     public BMH_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
-+         *     public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
-          *         super(mt, lf);
-          *         this.argL0 = argL0;
-          *         this.argL1 = argL1;
-          *         this.argI2 = argI2;
-          *     }
--         *     public final String dataValueTypes() { return types; }
--         *     public static final String types = "LLI";
-+         *     public final SpeciesData speciesData() { return SPECIES_DATA; }
-+         *     public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
-          *     public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) {
--         *         return myData().constructor.invokeBasic(mt, lf, argL0, argL1, argI2);
-+         *         return SPECIES_DATA.constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2);
-          *     }
-          *     public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) {
--         *         return BMHData.get("LLIL").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-+         *         return SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-          *     }
-          *     public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) {
--         *         return BMHData.get("LLII").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-+         *         return SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-          *     }
-          *     public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) {
--         *         return BMHData.get("LLIJ").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-+         *         return SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-          *     }
-          *     public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) {
--         *         return BMHData.get("LLIF").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-+         *         return SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-          *     }
-          *     public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) {
--         *         return BMHData.get("LLID").constructor.invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-+         *         return SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-          *     }
-          * }
-          * </pre>
-@@ -600,14 +576,13 @@
-         static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
-             final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
- 
--            final String className = "java/lang/invoke/BMH_" + types;
--            final String sourceFile = "BMH_" + types;
--
-+            final String className  = SPECIES_PREFIX_PATH + types;
-+            final String sourceFile = SPECIES_PREFIX_NAME + types;
-             cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
-             cw.visitSource(sourceFile, null);
- 
--            // emit static types field
--            cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "types", JLS_SIG, null, types).visitEnd();
-+            // emit static types and SPECIES_DATA fields
-+            cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd();
- 
-             // emit bound argument fields
-             for (int i = 0; i < types.length(); ++i) {
-@@ -643,10 +618,20 @@
-             mv.visitMaxs(0, 0);
-             mv.visitEnd();
- 
--            // emit implementation of types()
--            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "types", TYPES_SIG, null, null);
-+            // emit implementation of reinvokerTarget()
-+            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null);
-             mv.visitCode();
--            mv.visitLdcInsn(types);
-+            mv.visitVarInsn(ALOAD, 0);
-+            mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG);
-+            mv.visitTypeInsn(CHECKCAST, MH);
-+            mv.visitInsn(ARETURN);
-+            mv.visitMaxs(0, 0);
-+            mv.visitEnd();
-+
-+            // emit implementation of speciesData()
-+            mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
-+            mv.visitCode();
-+            mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
-             mv.visitInsn(ARETURN);
-             mv.visitMaxs(0, 0);
-             mv.visitEnd();
-@@ -654,11 +639,13 @@
-             // emit clone()
-             mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE);
-             mv.visitCode();
--            // return myData().constructor.invokeBasic(mt, lf, argL0, ...)
-+            // return speciesData().constructor[0].invokeBasic(mt, lf, argL0, ...)
-             // obtain constructor
-             mv.visitVarInsn(ALOAD, 0);
--            mv.visitMethodInsn(INVOKESPECIAL, BMH, "myData", MYDATA_SIG);
--            mv.visitFieldInsn(GETFIELD, DATA, "constructor", MH_SIG);
-+            mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
-+            mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
-+            mv.visitInsn(ICONST_0);
-+            mv.visitInsn(AALOAD);
-             // load mt, lf
-             mv.visitVarInsn(ALOAD, 1);
-             mv.visitVarInsn(ALOAD, 2);
-@@ -673,14 +660,18 @@
-             // for each type, emit cloneExtendT()
-             for (Class<?> c : TYPES) {
-                 char t = Wrapper.basicTypeChar(c);
--                String extypes = types + t;
-                 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "cloneExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE);
-                 mv.visitCode();
--                // return BMHData.get(<extypes>).constructor.invokeBasic(mt, lf, argL0, ..., narg)
-+                // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
-                 // obtain constructor
--                mv.visitLdcInsn(extypes);
--                mv.visitMethodInsn(INVOKESTATIC, DATA, "get", BMHDATA_GET_SIG);
--                mv.visitFieldInsn(GETFIELD, DATA, "constructor", MH_SIG);
-+                mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
-+                int iconstInsn = ICONST_0 + extensionIndex(t);
-+                assert(iconstInsn <= ICONST_5);
-+                mv.visitInsn(iconstInsn);
-+                mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG);
-+                mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
-+                mv.visitInsn(ICONST_0);
-+                mv.visitInsn(AALOAD);
-                 // load mt, lf
-                 mv.visitVarInsn(ALOAD, 1);
-                 mv.visitVarInsn(ALOAD, 2);
-@@ -689,19 +680,31 @@
-                 // put narg on stack
-                 mv.visitVarInsn(typeLoadOp(t), 3);
-                 // finally, invoke the constructor and return
--                mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(extypes, false));
-+                mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + t, false));
-                 mv.visitInsn(ARETURN);
-                 mv.visitMaxs(0, 0);
-                 mv.visitEnd();
-             }
- 
-+            // emit class initializer
-+            mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null);
-+            mv.visitCode();
-+            mv.visitLdcInsn(types);
-+            mv.visitLdcInsn(Type.getObjectType(className));
-+            mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG);
-+            mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
-+            mv.visitInsn(RETURN);
-+            mv.visitMaxs(0, 0);
-+            mv.visitEnd();
-+
-             cw.visitEnd();
- 
-             // load class
-             final byte[] classFile = cw.toByteArray();
-             InvokerBytecodeGenerator.maybeDump(className, classFile);
-             Class<? extends BoundMethodHandle> bmhClass =
--                UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
-+                //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
-+                UNSAFE.defineClass(className, classFile, 0, classFile.length).asSubclass(BoundMethodHandle.class);
-             UNSAFE.ensureClassInitialized(bmhClass);
- 
-             return bmhClass;
-@@ -744,24 +747,31 @@
-             }
-         }
- 
--        static MethodHandle[] makeGetters(Class<?> cbmhClass, String types) {
--            MethodHandle[] mhs = new MethodHandle[types.length()];
-+        static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
-+            if (mhs == null)  mhs = new MethodHandle[types.length()];
-             for (int i = 0; i < mhs.length; ++i) {
-                 mhs[i] = makeGetter(cbmhClass, types, i);
-+                assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
-             }
-             return mhs;
-         }
- 
-+        static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
-+            if (mhs == null)  mhs = new MethodHandle[1];
-+            mhs[0] = makeCbmhCtor(cbmh, types);
-+            return mhs;
-+        }
-+
-         //
-         // Auxiliary methods.
-         //
- 
--        static String typesFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
-+        static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
-             try {
--                Field ftypes = cbmh.getDeclaredField("types");
--                return (String) ftypes.get(null);
--            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
--                throw new InternalError(e);
-+                Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
-+                return (SpeciesData) F_SPECIES_DATA.get(null);
-+            } catch (ReflectiveOperationException ex) {
-+                throw new InternalError(ex);
-             }
-         }
- 
-@@ -831,13 +841,12 @@
-             return cmh;
-         }
- 
--        //
--        // Constants.
--        //
--
--        private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
--        private static final Unsafe UNSAFE = Unsafe.getUnsafe();
--
-     }
- 
-+    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;
- }
-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
-@@ -26,7 +26,7 @@
- package java.lang.invoke;
- 
- import sun.invoke.empty.Empty;
--import sun.misc.Unsafe;
-+import static java.lang.invoke.MethodHandleStatics.*;
- import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
- 
- /**
-@@ -233,12 +233,10 @@
-     }
- 
-     // unsafe stuff:
--    private static final Unsafe unsafe = Unsafe.getUnsafe();
-     private static final long TARGET_OFFSET;
--
-     static {
-         try {
--            TARGET_OFFSET = unsafe.objectFieldOffset(CallSite.class.getDeclaredField("target"));
-+            TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
-         } catch (Exception ex) { throw new Error(ex); }
-     }
- 
-@@ -248,7 +246,7 @@
-     }
-     /*package-private*/
-     MethodHandle getTargetVolatile() {
--        return (MethodHandle) unsafe.getObjectVolatile(this, TARGET_OFFSET);
-+        return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
-     }
-     /*package-private*/
-     void setTargetVolatile(MethodHandle newTarget) {
-diff --git a/src/share/classes/java/lang/invoke/CountingMethodHandle.java b/src/share/classes/java/lang/invoke/CountingMethodHandle.java
-deleted file mode 100644
---- a/src/share/classes/java/lang/invoke/CountingMethodHandle.java
-+++ /dev/null
-@@ -1,129 +0,0 @@
--/*
-- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
-- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-- *
-- * This code is free software; you can redistribute it and/or modify it
-- * under the terms of the GNU General Public License version 2 only, as
-- * published by the Free Software Foundation.  Oracle designates this
-- * particular file as subject to the "Classpath" exception as provided
-- * by Oracle in the LICENSE file that accompanied this code.
-- *
-- * This code is distributed in the hope that it will be useful, but WITHOUT
-- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-- * version 2 for more details (a copy is included in the LICENSE file that
-- * accompanied this code).
-- *
-- * You should have received a copy of the GNU General Public License version
-- * 2 along with this work; if not, write to the Free Software Foundation,
-- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-- *
-- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-- * or visit www.oracle.com if you need additional information or have any
-- * questions.
-- */
--
--package java.lang.invoke;
--
--import java.util.Arrays;
--import static java.lang.invoke.LambdaForm.*;
--import static java.lang.invoke.MethodHandles.*;
--import static java.lang.invoke.MethodType.*;
--
--/**
-- * This method handle is used to optionally provide a count of how
-- * many times it was invoked.
-- *
-- * @author never
-- */
--class CountingMethodHandle extends BoundMethodHandle {
--    private MethodHandle target;
--    private int vmcount;
--
--    private CountingMethodHandle(MethodHandle target) {
--        super(target.type(), countingReinvokerForm(target.type().basicType()));
--        this.target = target;
--    }
--
--    /** Wrap the incoming MethodHandle in a CountingMethodHandle if they are enabled */
--    static MethodHandle wrap(MethodHandle mh) {
--        if (MethodHandleNatives.COUNT_GWT) {
--             return new CountingMethodHandle(mh);
--        }
--        return mh;
--    }
--
--    @Override
--    public String types() {
--        return types;
--    }
--
--    public static final String types = "L";
--
--    @ForceInline
--    void vmcountBump() {
--        vmcount += 1;
--    }
--
--    /** Create a LF which reinvokes a target of the given basic type, and also bumps CountingMethodHandle.vmcount. */
--    private static LambdaForm countingReinvokerForm(MethodType mtype) {
--        assert(mtype == mtype.basicType());  // caller resp
--        LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_COUNTER);
--        if (reinvoker != null)  return reinvoker;
--        MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
--        final int THIS_CMH    = 0;
--        final int ARG_BASE    = 1;
--        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
--        int nameCursor = ARG_LIMIT;
--        final int BUMP_COUNT  = nameCursor++;
--        final int NEXT_MH     = nameCursor++;
--        final int REINVOKE    = nameCursor++;
--        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
--        names[BUMP_COUNT] = new Name(CountingMethodHandle.NF_vmcountBump, names[THIS_CMH]);
--        names[NEXT_MH] = BoundMethodHandle.getterName(names[THIS_CMH], 'L', 0);
--        Object[] targetArgs = Arrays.copyOfRange(names, THIS_CMH, ARG_LIMIT, Object[].class);
--        targetArgs[0] = names[NEXT_MH];  // overwrite CMH with next MH
--        names[REINVOKE] = new Name(MH_invokeBasic, targetArgs);
--        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, new LambdaForm("CMH.reinvoke", ARG_LIMIT, names));
--    }
--
--    static final NamedFunction NF_vmcountBump;
--    static {
--        try {
--            NF_vmcountBump = new NamedFunction(Lookup.IMPL_LOOKUP
--                    .findVirtual(CountingMethodHandle.class, "vmcountBump", methodType(void.class)));
--        } catch (ReflectiveOperationException ex) {
--            throw new InternalError(ex);
--        }
--    }
--
--    @Override
--    public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
--        throw new IllegalStateException("NYI");
--    }
--
--    @Override
--    public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
--        throw new IllegalStateException("NYI");
--    }
--
--    @Override
--    public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
--        throw new IllegalStateException("NYI");
--    }
--
--    @Override
--    public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
--        throw new IllegalStateException("NYI");
--    }
--
--    @Override
--    public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
--        throw new IllegalStateException("NYI");
--    }
--
--    @Override
--    public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
--        throw new IllegalStateException("NYI");
--    }
--}
-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
-@@ -31,18 +31,25 @@
- import sun.invoke.util.VerifyAccess;
- import static java.lang.invoke.MethodHandleNatives.Constants.*;
- import static java.lang.invoke.LambdaForm.*;
-+import static java.lang.invoke.MethodTypeForm.*;
-+import static java.lang.invoke.MethodHandleStatics.*;
-+import java.lang.ref.WeakReference;
-+import java.lang.reflect.Field;
-+import sun.invoke.util.ValueConversions;
-+import sun.invoke.util.VerifyType;
-+import sun.invoke.util.Wrapper;
- 
- /**
-- * The flavor of method handle which emulates invokespecial or invokestatic.
-+ * The flavor of method handle which implements a constant reference
-+ * to a class member.
-  * @author jrose
-  */
- class DirectMethodHandle extends MethodHandle {
-     final MemberName member;
- 
-     // Constructors and factory methods in this class *must* be package scoped or private.
--    private DirectMethodHandle(MethodType mtype, MemberName member, LambdaForm form) {
-+    private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member) {
-         super(mtype, form);
--        assert(member.isMethod() || member.isConstructor());
-         if (!member.isResolved())  throw new InternalError();
-         this.member = member;
-     }
-@@ -50,24 +57,54 @@
-     // Factory methods:
- 
-     static DirectMethodHandle make(Class<?> receiver, MemberName member) {
--        MethodType mtype = member.getMethodType();
-+        MethodType mtype = member.getMethodOrFieldType();
-         if (!member.isStatic()) {
--            if (!member.getDeclaringClass().isAssignableFrom(receiver))
-+            if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
-                 throw new InternalError(member.toString());
-             mtype = mtype.insertParameterTypes(0, receiver);
-         }
--        return new DirectMethodHandle(mtype, member, preparedLambdaForm(mtype.basicType(), member));
-+        if (!member.isField()) {
-+            LambdaForm lform = preparedLambdaForm(member);
-+            return new DirectMethodHandle(mtype, lform, member);
-+        } else {
-+            LambdaForm lform = preparedFieldLambdaForm(member);
-+            if (member.isStatic()) {
-+                long offset = MethodHandleNatives.staticFieldOffset(member);
-+                Object base = MethodHandleNatives.staticFieldBase(member);
-+                return new StaticAccessor(mtype, lform, member, base, offset);
-+            } else {
-+                long offset = MethodHandleNatives.objectFieldOffset(member);
-+                assert(offset == (int)offset);
-+                return new Accessor(mtype, lform, member, (int)offset);
-+            }
-+        }
-     }
-     static DirectMethodHandle make(MemberName member) {
-+        if (member.isConstructor())
-+            return makeAllocator(member);
-         return make(member.getDeclaringClass(), member);
-     }
-     static DirectMethodHandle make(Method method) {
-         return make(method.getDeclaringClass(), new MemberName(method));
-     }
-+    static DirectMethodHandle make(Field field) {
-+        return make(field.getDeclaringClass(), new MemberName(field));
-+    }
-+    private static DirectMethodHandle makeAllocator(MemberName ctor) {
-+        assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
-+        Class<?> instanceClass = ctor.getDeclaringClass();
-+        ctor = ctor.asConstructor();
-+        assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
-+        MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
-+        LambdaForm lform = preparedLambdaForm(ctor);
-+        MemberName init = ctor.asSpecial();
-+        assert(init.getMethodType().returnType() == void.class);
-+        return new Constructor(mtype, lform, ctor, init, instanceClass);
-+    }
- 
-     @Override
-     MethodHandle copyWith(MethodType mt, LambdaForm lf) {
--        return new DirectMethodHandle(mt, member, lf);
-+        return new DirectMethodHandle(mt, lf, member);
-     }
- 
-     @Override
-@@ -114,7 +151,7 @@
-                 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(), concrete, preparedLambdaForm(type().basicType(), concrete));
-+                    return new DirectMethodHandle(type(), preparedLambdaForm(concrete), concrete);
-                 break;
-             }
-         }
-@@ -126,58 +163,117 @@
-      * Cache and share this structure among all methods with
-      * the same basicType and refKind.
-      */
--    private static LambdaForm preparedLambdaForm(MethodType mtype, MemberName m) {
--        assert(m.isInvocable()) : "not a method: " + m;
--        assert(mtype == mtype.basicType());
--        assert(m.getInvocationType().basicType() == mtype);
-+    private static LambdaForm preparedLambdaForm(MemberName m) {
-+        assert(m.isInvocable()) : m;  // call preparedFieldLambdaForm instead
-+        MethodType mtype = m.getInvocationType().basicType();
-         assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
--        byte refKind = m.getReferenceKind();
--        int which = MethodTypeForm.LF_REF_KIND_FIRST + refKind;
--        if (refKind == REF_invokeStatic && shouldBeInitialized(m))
--            which = MethodTypeForm.LF_INVSTATIC_INIT;
-+        int which;
-+        switch (m.getReferenceKind()) {
-+        case REF_invokeVirtual:    which = LF_INVVIRTUAL;    break;
-+        case REF_invokeStatic:     which = LF_INVSTATIC;     break;
-+        case REF_invokeSpecial:    which = LF_INVSPECIAL;    break;
-+        case REF_invokeInterface:  which = LF_INVINTERFACE;  break;
-+        case REF_newInvokeSpecial: which = LF_NEWINVSPECIAL; break;
-+        default:  throw new InternalError(m.toString());
-+        }
-+        if (which == LF_INVSTATIC && shouldBeInitialized(m)) {
-+            // precompute the barrier-free version:
-+            preparedLambdaForm(mtype, which);
-+            which = LF_INVSTATIC_INIT;
-+        }
-+        LambdaForm lform = preparedLambdaForm(mtype, which);
-+        maybeCompile(lform, m);
-+        assert(lform.methodType().dropParameterTypes(0, 1)
-+                .equals(m.getInvocationType().basicType()))
-+                : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
-+        return lform;
-+    }
-+
-+    private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
-         LambdaForm lform = mtype.form().cachedLambdaForm(which);
-         if (lform != null)  return lform;
--        lform = makePreparedLambdaForm(mtype, refKind, which);
--        if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
--            // Help along bootstrapping...
--            lform.compileToBytecode();
-+        lform = makePreparedLambdaForm(mtype, which);
-         return mtype.form().setCachedLambdaForm(which, lform);
-     }
- 
--    private static LambdaForm makePreparedLambdaForm(MethodType mtype, byte refKind, int which) {
-+    private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
-+        boolean needsInit = (which == LF_INVSTATIC_INIT);
-+        boolean doesAlloc = (which == LF_NEWINVSPECIAL);
-         String linkerName, lambdaName;
--        switch (refKind) {
--        case REF_invokeVirtual:    linkerName = "linkToVirtual";    lambdaName = "DMH.invokeVirtual";    break;
--        case REF_invokeStatic:     linkerName = "linkToStatic";     lambdaName = "DMH.invokeStatic";     break;
--        case REF_invokeSpecial:    linkerName = "linkToSpecial";    lambdaName = "DMH.invokeSpecial";    break;
--        case REF_invokeInterface:  linkerName = "linkToInterface";  lambdaName = "DMH.invokeInterface";  break;
--        default:  throw new InternalError("refKind="+refKind);
-+        switch (which) {
-+        case LF_INVVIRTUAL:    linkerName = "linkToVirtual";    lambdaName = "DMH.invokeVirtual";    break;
-+        case LF_INVSTATIC:     linkerName = "linkToStatic";     lambdaName = "DMH.invokeStatic";     break;
-+        case LF_INVSTATIC_INIT:linkerName = "linkToStatic";     lambdaName = "DMH.invokeStaticInit"; break;
-+        case LF_INVSPECIAL:    linkerName = "linkToSpecial";    lambdaName = "DMH.invokeSpecial";    break;
-+        case LF_INVINTERFACE:  linkerName = "linkToInterface";  lambdaName = "DMH.invokeInterface";  break;
-+        case LF_NEWINVSPECIAL: linkerName = "linkToSpecial";    lambdaName = "DMH.newInvokeSpecial"; break;
-+        default:  throw new InternalError("which="+which);
-         }
-         MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class);
-+        if (doesAlloc)
-+            mtypeWithArg = mtypeWithArg
-+                    .insertParameterTypes(0, Object.class)  // insert newly allocated obj
-+                    .changeReturnType(void.class);          // <init> returns void
-         MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
-         try {
-             linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
--            assert(linker.isStatic());
-         } catch (ReflectiveOperationException ex) {
-             throw new InternalError(ex);
-         }
--        boolean needEnsureInit = (which == MethodTypeForm.LF_INVSTATIC_INIT);
-         final int DMH_THIS    = 0;
-         final int ARG_BASE    = 1;
-         final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
-         int nameCursor = ARG_LIMIT;
-+        final int NEW_OBJ     = (doesAlloc ? nameCursor++ : -1);
-         final int GET_MEMBER  = nameCursor++;
--        final int ENSURE_INIT = (needEnsureInit ? nameCursor++ : -1);
-         final int LINKER_CALL = nameCursor++;
-         Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
-         assert(names.length == nameCursor);
--        names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
--        if (ENSURE_INIT > 0)
--            names[ENSURE_INIT] = new Name(NF_ensureClassInitialized, names[GET_MEMBER]);
-+        if (doesAlloc) {
-+            // names = { argx,y,z,... new C, init method }
-+            names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
-+            names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
-+        } else if (needsInit) {
-+            names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
-+        } else {
-+            names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
-+        }
-         Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
-         assert(outArgs[outArgs.length-1] == names[GET_MEMBER]);  // look, shifted args!
-+        int result = LambdaForm.LAST_RESULT;
-+        if (doesAlloc) {
-+            assert(outArgs[outArgs.length-2] == names[NEW_OBJ]);  // got to move this one
-+            System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
-+            outArgs[0] = names[NEW_OBJ];
-+            result = NEW_OBJ;
-+        }
-         names[LINKER_CALL] = new Name(linker, outArgs);
--        return new LambdaForm(lambdaName, ARG_LIMIT, names);
-+        lambdaName += "_" + LambdaForm.basicTypeSignature(mtype);
-+        LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
-+        // This is a tricky bit of code.  Don't send it through the LF interpreter.
-+        lform.compileToBytecode();
-+        return lform;
-+    }
-+
-+    private static void maybeCompile(LambdaForm lform, MemberName m) {
-+        if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
-+            // Help along bootstrapping...
-+            lform.compileToBytecode();
-+    }
-+
-+    /** Static wrapper for DirectMethodHandle.internalMemberName. */
-+    @ForceInline
-+    /*non-public*/ static Object internalMemberName(Object mh) {
-+        return ((DirectMethodHandle)mh).member;
-+    }
-+
-+    /** Static wrapper for DirectMethodHandle.internalMemberName.
-+     * This one also forces initialization.
-+     */
-+    /*non-public*/ static Object internalMemberNameEnsureInit(Object mh) {
-+        DirectMethodHandle dmh = (DirectMethodHandle)mh;
-+        dmh.ensureInitialized();
-+        return dmh.member;
-     }
- 
-     /*non-public*/ static
-@@ -193,84 +289,371 @@
-             return false;
-         }
-         Class<?> cls = member.getDeclaringClass();
--        if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
--            return false;
--        //return UNSAFE.shouldBeInitialized(cls);
--        if (MH_shouldBeInitialized == null) {
--            MethodHandleImpl.UNSAFE.ensureClassInitialized(cls);
-+        if (cls == ValueConversions.class ||
-+            cls == MethodHandleImpl.class ||
-+            cls == Invokers.class) {
-+            // These guys have lots of <clinit> DMH creation but we know
-+            // the MHs will not be used until the system is booted.
-             return false;
-         }
--        try {
--            return (boolean) MH_shouldBeInitialized.invokeExact(MethodHandleImpl.UNSAFE, cls);
--        } catch (Throwable ex) {
--            throw new InternalError(ex);
-+        if (VerifyAccess.isSamePackage(MethodHandle.class, cls) ||
-+            VerifyAccess.isSamePackage(ValueConversions.class, cls)) {
-+            // It is a system class.  It is probably in the process of
-+            // being initialized, but we will help it along just to be safe.
-+            if (UNSAFE.shouldBeInitialized(cls)) {
-+                UNSAFE.ensureClassInitialized(cls);
-+            }
-+            return false;
-         }
-+        return UNSAFE.shouldBeInitialized(cls);
-     }
- 
--    /*non-public*/ static
--    @ForceInline
--    void ensureClassInitialized(Object memberObj) {
--        MemberName member = (MemberName) memberObj;
--        EnsureInitialized.INSTANCE.get(member.getDeclaringClass());
--    }
--    private static class EnsureInitialized extends ClassValue<Void> {
-+    private static class EnsureInitialized extends ClassValue<WeakReference<Thread>> {
-         @Override
--        protected Void computeValue(Class<?> type) {
--            MethodHandleImpl.UNSAFE.ensureClassInitialized(type);
-+        protected WeakReference<Thread> computeValue(Class<?> type) {
-+            UNSAFE.ensureClassInitialized(type);
-+            if (UNSAFE.shouldBeInitialized(type))
-+                // If the previous call didn't block, this can happen.
-+                // We are executing inside <clinit>.
-+                return new WeakReference<>(Thread.currentThread());
-             return null;
-         }
-         static final EnsureInitialized INSTANCE = new EnsureInitialized();
-     }
- 
--    /** Static wrapper for DirectMethodHandle.internalMemberName. */
--    /*non-public*/ static
--    Object internalMemberName(Object mh) {
--        return ((DirectMethodHandle)mh).member;
-+    private void ensureInitialized() {
-+        if (checkInitialized(member)) {
-+            // The coast is clear.  Delete the <clinit> barrier.
-+            if (member.isField())
-+                updateForm(preparedFieldLambdaForm(member));
-+            else
-+                updateForm(preparedLambdaForm(member));
-+        }
-+    }
-+    private static boolean checkInitialized(MemberName member) {
-+        Class<?> defc = member.getDeclaringClass();
-+        WeakReference<Thread> ref = EnsureInitialized.INSTANCE.get(defc);
-+        if (ref == null) {
-+            return true;  // the final state
-+        }
-+        Thread clinitThread = ref.get();
-+        // Somebody may still be running defc.<clinit>.
-+        if (clinitThread == Thread.currentThread()) {
-+            // If anybody is running defc.<clinit>, it is this thread.
-+            if (UNSAFE.shouldBeInitialized(defc))
-+                // Yes, we are running it; keep the barrier for now.
-+                return false;
-+        } else {
-+            // We are in a random thread.  Block.
-+            UNSAFE.ensureClassInitialized(defc);
-+        }
-+        assert(!UNSAFE.shouldBeInitialized(defc));
-+        // put it into the final state
-+        EnsureInitialized.INSTANCE.remove(defc);
-+        return true;
-     }
- 
--    private static final NamedFunction NF_internalMemberName;
--    private static final NamedFunction NF_ensureClassInitialized;
--    private static final MethodHandle MH_shouldBeInitialized;
-+    /*non-public*/ static void ensureInitialized(Object mh) {
-+        ((DirectMethodHandle)mh).ensureInitialized();
-+    }
-+
-+    /** This subclass handles constructor references. */
-+    static class Constructor extends DirectMethodHandle {
-+        final MemberName initMethod;
-+        final Class<?>   instanceClass;
-+
-+        private Constructor(MethodType mtype, LambdaForm form, MemberName constructor,
-+                            MemberName initMethod, Class<?> instanceClass) {
-+            super(mtype, form, constructor);
-+            this.initMethod = initMethod;
-+            this.instanceClass = instanceClass;
-+            assert(initMethod.isResolved());
-+        }
-+    }
-+
-+    /*non-public*/ static Object constructorMethod(Object mh) {
-+        Constructor dmh = (Constructor)mh;
-+        return dmh.initMethod;
-+    }
-+
-+    /*non-public*/ static Object allocateInstance(Object mh) throws InstantiationException {
-+        Constructor dmh = (Constructor)mh;
-+        return UNSAFE.allocateInstance(dmh.instanceClass);
-+    }
-+
-+    /** This subclass handles non-static field references. */
-+    static class Accessor extends DirectMethodHandle {
-+        final Class<?> fieldType;
-+        final int      fieldOffset;
-+        private Accessor(MethodType mtype, LambdaForm form, MemberName member,
-+                         int fieldOffset) {
-+            super(mtype, form, member);
-+            this.fieldType   = member.getFieldType();
-+            this.fieldOffset = fieldOffset;
-+        }
-+
-+        @Override Object checkCast(Object obj) {
-+            return fieldType.cast(obj);
-+        }
-+    }
-+
-+    @ForceInline
-+    /*non-public*/ static long fieldOffset(Object accessorObj) {
-+        // Note: We return a long because that is what Unsafe.getObject likes.
-+        // We store a plain int because it is more compact.
-+        return ((Accessor)accessorObj).fieldOffset;
-+    }
-+
-+    @ForceInline
-+    /*non-public*/ static Object checkBase(Object obj) {
-+        // Note that the object's class has already been verified,
-+        // since the parameter type of the Accessor method handle
-+        // is either member.getDeclaringClass or a subclass.
-+        // This was verified in DirectMethodHandle.make.
-+        // Therefore, the only remaining check is for null.
-+        // Since this check is *not* guaranteed by Unsafe.getInt
-+        // and its siblings, we need to make an explicit one here.
-+        obj.getClass();  // maybe throw NPE
-+        return obj;
-+    }
-+
-+    /** This subclass handles static field references. */
-+    static class StaticAccessor extends DirectMethodHandle {
-+        final private Class<?> fieldType;
-+        final private Object   staticBase;
-+        final private long     staticOffset;
-+
-+        private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member,
-+                               Object staticBase, long staticOffset) {
-+            super(mtype, form, member);
-+            this.fieldType    = member.getFieldType();
-+            this.staticBase   = staticBase;
-+            this.staticOffset = staticOffset;
-+        }
-+
-+        @Override Object checkCast(Object obj) {
-+            return fieldType.cast(obj);
-+        }
-+    }
-+
-+    @ForceInline
-+    /*non-public*/ static Object nullCheck(Object obj) {
-+        obj.getClass();
-+        return obj;
-+    }
-+
-+    @ForceInline
-+    /*non-public*/ static Object staticBase(Object accessorObj) {
-+        return ((StaticAccessor)accessorObj).staticBase;
-+    }
-+
-+    @ForceInline
-+    /*non-public*/ static long staticOffset(Object accessorObj) {
-+        return ((StaticAccessor)accessorObj).staticOffset;
-+    }
-+
-+    @ForceInline
-+    /*non-public*/ static Object checkCast(Object mh, Object obj) {
-+        return ((DirectMethodHandle) mh).checkCast(obj);
-+    }
-+
-+    Object checkCast(Object obj) {
-+        return member.getReturnType().cast(obj);
-+    }
-+
-+    // Caching machinery for field accessors:
-+    private static byte
-+            AF_GETFIELD        = 0,
-+            AF_PUTFIELD        = 1,
-+            AF_GETSTATIC       = 2,
-+            AF_PUTSTATIC       = 3,
-+            AF_GETSTATIC_INIT  = 4,
-+            AF_PUTSTATIC_INIT  = 5,
-+            AF_LIMIT           = 6;
-+    // Enumerate the different field kinds using Wrapper,
-+    // with an extra case added for checked references.
-+    private static int
-+            FT_LAST_WRAPPER    = Wrapper.values().length-1,
-+            FT_UNCHECKED_REF   = Wrapper.OBJECT.ordinal(),
-+            FT_CHECKED_REF     = FT_LAST_WRAPPER+1,
-+            FT_LIMIT           = FT_LAST_WRAPPER+2;
-+    private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) {
-+        return ((formOp * FT_LIMIT * 2)
-+                + (isVolatile ? FT_LIMIT : 0)
-+                + ftypeKind);
-+    }
-+    private static final LambdaForm[] ACCESSOR_FORMS
-+            = new LambdaForm[afIndex(AF_LIMIT, false, 0)];
-+    private static int ftypeKind(Class<?> ftype) {
-+        if (ftype.isPrimitive())
-+            return Wrapper.forPrimitiveType(ftype).ordinal();
-+        else if (VerifyType.isNullReferenceConversion(Object.class, ftype))
-+            return FT_UNCHECKED_REF;
-+        else
-+            return FT_CHECKED_REF;
-+    }
-+
-+    /**
-+     * Create a LF which can access the given field.
-+     * Cache and share this structure among all fields with
-+     * the same basicType and refKind.
-+     */
-+    private static LambdaForm preparedFieldLambdaForm(MemberName m) {
-+        Class<?> ftype = m.getFieldType();
-+        boolean isVolatile = m.isVolatile();
-+        byte formOp;
-+        switch (m.getReferenceKind()) {
-+        case REF_getField:      formOp = AF_GETFIELD;    break;
-+        case REF_putField:      formOp = AF_PUTFIELD;    break;
-+        case REF_getStatic:     formOp = AF_GETSTATIC;   break;
-+        case REF_putStatic:     formOp = AF_PUTSTATIC;   break;
-+        default:  throw new InternalError(m.toString());
-+        }
-+        if (shouldBeInitialized(m)) {
-+            // precompute the barrier-free version:
-+            preparedFieldLambdaForm(formOp, isVolatile, ftype);
-+            assert((AF_GETSTATIC_INIT - AF_GETSTATIC) ==
-+                   (AF_PUTSTATIC_INIT - AF_PUTSTATIC));
-+            formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC);
-+        }
-+        LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype);
-+        maybeCompile(lform, m);
-+        assert(lform.methodType().dropParameterTypes(0, 1)
-+                .equals(m.getInvocationType().basicType()))
-+                : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
-+        return lform;
-+    }
-+    private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class<?> ftype) {
-+        int afIndex = afIndex(formOp, isVolatile, ftypeKind(ftype));
-+        LambdaForm lform = ACCESSOR_FORMS[afIndex];
-+        if (lform != null)  return lform;
-+        lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind(ftype));
-+        ACCESSOR_FORMS[afIndex] = lform;  // don't bother with a CAS
-+        return lform;
-+    }
-+
-+    private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
-+        boolean isGetter  = (formOp & 1) == (AF_GETFIELD & 1);
-+        boolean isStatic  = (formOp >= AF_GETSTATIC);
-+        boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
-+        boolean needsCast = (ftypeKind == FT_CHECKED_REF);
-+        Wrapper fw = (needsCast ? Wrapper.OBJECT : Wrapper.values()[ftypeKind]);
-+        Class<?> ft = fw.primitiveType();
-+        assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
-+        String tname  = fw.primitiveSimpleName();
-+        String ctname = Character.toUpperCase(tname.charAt(0)) + tname.substring(1);
-+        if (isVolatile)  ctname += "Volatile";
-+        String getOrPut = (isGetter ? "get" : "put");
-+        String linkerName = (getOrPut + ctname);  // getObject, putIntVolatile, etc.
-+        MethodType linkerType;
-+        if (isGetter)
-+            linkerType = MethodType.methodType(ft, Object.class, long.class);
-+        else
-+            linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
-+        MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual);
-+        try {
-+            linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
-+        } catch (ReflectiveOperationException ex) {
-+            throw new InternalError(ex);
-+        }
-+
-+        // What is the external type of the lambda form?
-+        MethodType mtype;
-+        if (isGetter)
-+            mtype = MethodType.methodType(ft);
-+        else
-+            mtype = MethodType.methodType(void.class, ft);
-+        mtype = mtype.basicType();  // erase short to int, etc.
-+        if (!isStatic)
-+            mtype = mtype.insertParameterTypes(0, Object.class);
-+        final int DMH_THIS  = 0;
-+        final int ARG_BASE  = 1;
-+        final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
-+        // if this is for non-static access, the base pointer is stored at this index:
-+        final int OBJ_BASE  = isStatic ? -1 : ARG_BASE;
-+        // if this is for write access, the value to be written is stored at this index:
-+        final int SET_VALUE  = isGetter ? -1 : ARG_LIMIT - 1;
-+        int nameCursor = ARG_LIMIT;
-+        final int F_HOLDER  = (isStatic ? nameCursor++ : -1);  // static base if any
-+        final int F_OFFSET  = nameCursor++;  // Either static offset or field offset.
-+        final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
-+        final int INIT_BAR  = (needsInit ? nameCursor++ : -1);
-+        final int PRE_CAST  = (needsCast && !isGetter ? nameCursor++ : -1);
-+        final int LINKER_CALL = nameCursor++;
-+        final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
-+        final int RESULT    = nameCursor-1;  // either the call or the cast
-+        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
-+        if (needsInit)
-+            names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
-+        if (needsCast && !isGetter)
-+            names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
-+        Object[] outArgs = new Object[1 + linkerType.parameterCount()];
-+        assert(outArgs.length == (isGetter ? 3 : 4));
-+        outArgs[0] = UNSAFE;
-+        if (isStatic) {
-+            outArgs[1] = names[F_HOLDER]  = new Name(NF_staticBase, names[DMH_THIS]);
-+            outArgs[2] = names[F_OFFSET]  = new Name(NF_staticOffset, names[DMH_THIS]);
-+        } else {
-+            outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
-+            outArgs[2] = names[F_OFFSET]  = new Name(NF_fieldOffset, names[DMH_THIS]);
-+        }
-+        if (!isGetter) {
-+            outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
-+        }
-+        for (Object a : outArgs)  assert(a != null);
-+        names[LINKER_CALL] = new Name(linker, outArgs);
-+        if (needsCast && isGetter)
-+            names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
-+        for (Name n : names)  assert(n != null);
-+        String fieldOrStatic = (isStatic ? "Static" : "Field");
-+        String lambdaName = (linkerName + fieldOrStatic);  // significant only for debugging
-+        if (needsCast)  lambdaName += "Cast";
-+        if (needsInit)  lambdaName += "Init";
-+        return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT);
-+    }
-+
-+    private static final NamedFunction
-+            NF_internalMemberName,
-+            NF_internalMemberNameEnsureInit,
-+            NF_ensureInitialized,
-+            NF_fieldOffset,
-+            NF_checkBase,
-+            NF_staticBase,
-+            NF_staticOffset,
-+            NF_checkCast,
-+            NF_allocateInstance,
-+            NF_constructorMethod;
-     static {
-         try {
--            NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
--                    .getDeclaredMethod("internalMemberName", Object.class));
--            NF_ensureClassInitialized = new NamedFunction(DirectMethodHandle.class
--                    .getDeclaredMethod("ensureClassInitialized", Object.class));
--            MethodHandle mhOrNull = null;
--            try {
--                mhOrNull = make(Unsafe.class.getMethod("shouldBeInitialized", Class.class));
--            } catch (NoSuchMethodException ex) {
--                mhOrNull = null;  // new API call not in down-rev JVM
-+            NamedFunction nfs[] = {
-+                NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("internalMemberName", Object.class)),
-+                NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
-+                NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("ensureInitialized", Object.class)),
-+                NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("fieldOffset", Object.class)),
-+                NF_checkBase = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("checkBase", Object.class)),
-+                NF_staticBase = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("staticBase", Object.class)),
-+                NF_staticOffset = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("staticOffset", Object.class)),
-+                NF_checkCast = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("checkCast", Object.class, Object.class)),
-+                NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("allocateInstance", Object.class)),
-+                NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
-+                    .getDeclaredMethod("constructorMethod", Object.class))
-+            };
-+            for (NamedFunction nf : nfs) {
-+                // Each nf must be statically invocable or we get tied up in our bootstraps.
-+                assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
-+                nf.resolve();
-             }
--            MH_shouldBeInitialized = mhOrNull;
--            NF_internalMemberName.resolve();
--            NF_ensureClassInitialized.resolve();
--            MH_shouldBeInitialized.form.resolve();
--            // bound
-         } catch (ReflectiveOperationException ex) {
-             throw new InternalError(ex);
-         }
-     }
--
--/*
--    // Smoke-test:
--    static void testDirectMethodHandles() throws Throwable {
--        MemberName.Factory lookup = MemberName.getFactory();
--        MethodHandle asList_MH = make(Arrays.class.getMethod("asList", Object[].class));
--        System.out.println("about to call "+asList_MH);
--        Object[] abc = { "a", "bc" };
--        java.util.List<?> lst = (java.util.List<?>) asList_MH.invokeExact(abc);
--        System.out.println("lst="+lst);
--        MethodHandle toString_MH = make(new MemberName(Object.class.getMethod("toString")));
--        String s1 = (String) toString_MH.invokeExact((Object) lst);
--        toString_MH = make(new MemberName(Object.class.getMethod("toString"), true));
--        String s2 = (String) toString_MH.invokeExact((Object) lst);
--        System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString()));
--        MethodHandle toArray_MH = make(new MemberName(java.util.List.class.getMethod("toArray")));
--        Object[] arr = (Object[]) toArray_MH.invokeExact(lst);
--        System.out.println("toArray="+Arrays.toString(arr));
--    }
--    static { try { testDirectMethodHandles(); } catch (Throwable ex) { throw new RuntimeException(ex); } }
--//*/
- }
-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
-@@ -30,7 +30,6 @@
- import java.lang.invoke.MethodHandles.Lookup;
- 
- import sun.invoke.util.Wrapper;
--import sun.misc.Unsafe;
- 
- import java.io.*;
- import java.util.*;
-@@ -82,7 +81,6 @@
-     private MethodVisitor mv;
- 
-     private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
--    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
-     private static final Class<?> HOST_CLASS = LambdaForm.class;
- 
-     private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
-@@ -444,11 +442,7 @@
-         case 'L':
-             if (VerifyType.isNullConversion(Object.class, pclass))
-                 return;
--            // for BMH species, which are not representable as names in class files (anonymous classes!), cast to BMH instead
--            if (BoundMethodHandle.class.isAssignableFrom(pclass)) {
--                final String className = getInternalName(BoundMethodHandle.class);
--                mv.visitTypeInsn(Opcodes.CHECKCAST, className);
--            } else if (isStaticallyNameable(pclass)) {
-+            if (isStaticallyNameable(pclass)) {
-                 mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
-             } else {
-                 mv.visitLdcInsn(constantPlaceholder(pclass));
-@@ -525,7 +519,7 @@
- 
-         // iterate over the form's names, generating bytecode instructions for each
-         // start iterating at the first name following the arguments
--        for (int i = invokerType.parameterCount(); i < lambdaForm.names.length; i++) {
-+        for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
-             Name name = lambdaForm.names[i];
-             MemberName member = name.function.member();
- 
-@@ -595,15 +589,15 @@
- 
-     static private Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
-         // Sample classes from each package we are willing to bind to statically:
--        Object.class,
--        Arrays.class,
--        Unsafe.class
-+        java.lang.Object.class,
-+        java.util.Arrays.class,
-+        sun.misc.Unsafe.class
-         //MethodHandle.class already covered
-     };
- 
--    boolean isStaticallyInvocable(MemberName member) {
-+    static boolean isStaticallyInvocable(MemberName member) {
-         if (member == null)  return false;
--        assert !member.isConstructor();
-+        if (member.isConstructor())  return false;
-         Class<?> cls = member.getDeclaringClass();
-         if (cls.isArray() || cls.isPrimitive())
-             return false;  // FIXME
-@@ -618,7 +612,7 @@
-         return false;
-     }
- 
--    boolean isStaticallyNameable(Class<?> cls) {
-+    static boolean isStaticallyNameable(Class<?> cls) {
-         while (cls.isArray())
-             cls = cls.getComponentType();
-         if (cls.isPrimitive())
-@@ -643,10 +637,9 @@
-      */
-     void emitStaticInvoke(MemberName member, Name name) {
-         assert(member.equals(name.function.member()));
--        assert(member.isMethod());
-+        String cname = getInternalName(member.getDeclaringClass());
-         String mname = member.getName();
--        String mtype = member.getMethodType().toMethodDescriptorString();
--        String cname = getInternalName(member.getDeclaringClass());
-+        String mtype;
-         byte refKind = member.getReferenceKind();
-         if (refKind == REF_invokeSpecial) {
-             // in order to pass the verifier, we need to convert this to invokevirtual in all cases
-@@ -660,22 +653,26 @@
-         }
- 
-         // invocation
-+        if (member.isMethod()) {
-+            mtype = member.getMethodType().toMethodDescriptorString();
-+            mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype);
-+        } else {
-+            mtype = MethodType.toFieldDescriptorString(member.getFieldType());
-+            mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
-+        }
-+    }
-+    int refKindOpcode(byte refKind) {
-         switch (refKind) {
--        case REF_invokeVirtual:
--            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cname, mname, mtype);
--            break;
--        case REF_invokeStatic:
--            mv.visitMethodInsn(Opcodes.INVOKESTATIC,  cname, mname, mtype);
--            break;
--        case REF_invokeSpecial:
--            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, cname, mname, mtype);
--            break;
--        case REF_invokeInterface:
--            mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, cname, mname, mtype);
--            break;
--        default:
--            throw new InternalError(member.toString());
-+        case REF_invokeVirtual:      return Opcodes.INVOKEVIRTUAL;
-+        case REF_invokeStatic:       return Opcodes.INVOKESTATIC;
-+        case REF_invokeSpecial:      return Opcodes.INVOKESPECIAL;
-+        case REF_invokeInterface:    return Opcodes.INVOKEINTERFACE;
-+        case REF_getField:           return Opcodes.GETFIELD;
-+        case REF_putField:           return Opcodes.PUTFIELD;
-+        case REF_getStatic:          return Opcodes.GETSTATIC;
-+        case REF_putStatic:          return Opcodes.PUTSTATIC;
-         }
-+        throw new InternalError("refKind="+refKind);
-     }
- 
-     /**
-@@ -753,6 +750,8 @@
-             Name n = (Name) arg;
-             emitLoadInsn(n.type, n.index());
-             emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
-+        } else if ((arg == null || arg instanceof String) && ptype == 'L') {
-+            emitConst(arg);
-         } else {
-             if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
-                 emitConst(arg);
-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
-@@ -75,7 +75,7 @@
-         if (invoker != null)  return invoker;
-         MethodType mtype = targetType;
-         LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_INVOKER);
--        invoker = new BoundMethodHandle.BMH_L(mtype.invokerType(), lform, mtype);
-+        invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
-         assert(checkInvoker(invoker));
-         exactInvoker = invoker;
-         return invoker;
-@@ -87,7 +87,7 @@
-         MethodType mtype = targetType;
-         prepareForGenericCall(mtype);
-         LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_INVOKER);
--        invoker = new BoundMethodHandle.BMH_L(mtype.invokerType(), lform, mtype);
-+        invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
-         assert(checkInvoker(invoker));
-         generalInvoker = invoker;
-         return invoker;
-@@ -241,7 +241,7 @@
-         assert(names.length == nameCursor);
-         if (MTYPE_ARG >= INARG_LIMIT) {
-             assert(names[MTYPE_ARG] == null);
--            names[MTYPE_ARG] = BoundMethodHandle.getterName(names[THIS_MH], 'L', 0);
-+            names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0);
-             // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM)
-         }
- 
-@@ -320,9 +320,7 @@
-             form.genericInvoker = gamh;
-             return gamh;
-         } catch (Exception ex) {
--            Error err = new InternalError("Exception while resolving inexact invoke", ex);
--            err.initCause(ex);
--            throw err;
-+            throw new InternalError("Exception while resolving inexact invoke", 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
-@@ -36,6 +36,8 @@
- import sun.invoke.util.Wrapper;
- import static java.lang.invoke.MethodHandleStatics.*;
- import static java.lang.invoke.MethodHandleNatives.Constants.*;
-+import java.lang.reflect.Field;
-+import java.util.Objects;
- 
- /**
-  * The symbolic, non-executable form of a method handle's invocation semantics.
-@@ -121,6 +123,7 @@
-     final Name[] names;
-     final String debugName;
-     MemberName vmentry;   // low-level behavior, or null if not yet prepared
-+    private boolean isCompiled;
- 
-     // Caches for common structural transforms:
-     LambdaForm[] bindCache;
-@@ -428,6 +431,9 @@
-      * as a sort of pre-invocation linkage step.)
-      */
-     public void prepare() {
-+        if (COMPILE_THRESHOLD == 0) {
-+            compileToBytecode();
-+        }
-         if (this.vmentry != null) {
-             // already prepared (e.g., a primitive DMH invoker form)
-             return;
-@@ -441,10 +447,14 @@
-     MemberName compileToBytecode() {
-         MethodType invokerType = methodType();
-         assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
-+        if (vmentry != null && isCompiled) {
-+            return vmentry;  // already compiled somehow
-+        }
-         try {
-             vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
-             if (TRACE_INTERPRETER)
-                 traceInterpreter("compileToBytecode", this);
-+            isCompiled = true;
-             return vmentry;
-         } catch (Error | Exception ex) {
-             throw new InternalError(this.toString(), ex);
-@@ -572,7 +582,13 @@
-     }
- 
-     /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
--    private static final int invocationThreshold = 30;
-+    private static final int COMPILE_THRESHOLD;
-+    static {
-+        if (MethodHandleStatics.COMPILE_THRESHOLD != null)
-+            COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD;
-+        else
-+            COMPILE_THRESHOLD = 30;  // default value
-+    }
-     private int invocationCounter = 0;
- 
-     @Hidden
-@@ -580,9 +596,10 @@
-     Object interpretWithArguments(Object... argumentValues) throws Throwable {
-         if (TRACE_INTERPRETER)
-             return interpretWithArgumentsTracing(argumentValues);
--        if (invocationCounter < invocationThreshold) {
-+        if (COMPILE_THRESHOLD != 0 &&
-+            invocationCounter < COMPILE_THRESHOLD) {
-             invocationCounter++;  // benign race
--            if (invocationCounter >= invocationThreshold) {
-+            if (invocationCounter >= COMPILE_THRESHOLD) {
-                 // Replace vmentry with a bytecode version of this LF.
-                 compileToBytecode();
-             }
-@@ -615,10 +632,10 @@
- 
-     Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
-         traceInterpreter("[ interpretWithArguments", this, argumentValues);
--        if (invocationCounter < invocationThreshold) {
-+        if (invocationCounter < COMPILE_THRESHOLD) {
-             int ctr = invocationCounter++;  // benign race
-             traceInterpreter("| invocationCounter", ctr);
--            if (invocationCounter >= invocationThreshold) {
-+            if (invocationCounter >= COMPILE_THRESHOLD) {
-                 compileToBytecode();
-             }
-         }
-@@ -757,17 +774,21 @@
-         return new LambdaForm(debugName, arity2, names2, result2);
-     }
- 
--    LambdaForm bind(char basicType, int namePos, int dataValuePos) {
--        Name dataValueName = BoundMethodHandle.getterName(names[0], basicType, dataValuePos);
--        return bind(names[namePos], dataValueName);
-+    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) {
-+    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) {
-@@ -782,9 +803,32 @@
-         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);
-@@ -934,13 +978,16 @@
-             this.resolvedHandle = resolvedHandle;
-         }
- 
--        // The next 2 constructors are used to break circular dependencies on MH.invokeStatic, etc.
-+        // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc.
-         // Any LambdaForm containing such a member is not interpretable.
-         // This is OK, since all such LFs are prepared with special primitive vmentry points.
-         // And even without the resolvedHandle, the name can still be compiled and optimized.
-         NamedFunction(Method method) {
-             this(new MemberName(method));
-         }
-+        NamedFunction(Field field) {
-+            this(new MemberName(field));
-+        }
-         NamedFunction(MemberName member) {
-             this.member = member;
-             this.resolvedHandle = null;
-@@ -964,6 +1011,13 @@
-             return this.member != null && this.member.equals(that.member);
-         }
- 
-+        @Override
-+        public int hashCode() {
-+            if (member != null)
-+                return member.hashCode();
-+            return super.hashCode();
-+        }
-+
-         // Put the predefined NamedFunction invokers into the table.
-         static void initializeInvokers() {
-             for (MemberName m : MemberName.getFactory().getMethods(NamedFunction.class, false, null, null, null)) {
-@@ -1137,21 +1191,23 @@
-         }
- 
-         MemberName member() {
--            if (member == null) {
--                return resolvedHandle.internalMemberName();
--            }
--            assert(memberIsConsistent());
-+            assert(assertMemberIsConsistent());
-             return member;
-         }
- 
--        boolean memberIsConsistent() {
-+        // Called only from assert.
-+        private boolean assertMemberIsConsistent() {
-             if (resolvedHandle instanceof DirectMethodHandle) {
-                 MemberName m = resolvedHandle.internalMemberName();
--                assert(m.equals(member)) : Arrays.asList(member, m, resolvedHandle);
-+                assert(m.equals(member));
-             }
-             return true;
-         }
- 
-+        Class<?> memberDeclaringClassOrNull() {
-+            return (member == null) ? null : member.getDeclaringClass();
-+        }
-+
-         char returnType() {
-             return basicType(methodType().returnType());
-         }
-@@ -1361,9 +1417,6 @@
-             return true;
-         }
- 
--        public boolean equals(Object x) {
--            return x instanceof Name && equals((Name)x);
--        }
-         /**
-          * Does this Name precede the given binding node in some canonical order?
-          * This predicate is used to order data bindings (via insertion sort)
-@@ -1393,6 +1446,7 @@
-             }
-             return false;
-         }
-+
-         public boolean equals(Name that) {
-             if (this == that)  return true;
-             if (isParam())
-@@ -1404,6 +1458,11 @@
-                 this.function.equals(that.function) &&
-                 Arrays.equals(this.arguments, that.arguments);
-         }
-+        @Override
-+        public boolean equals(Object x) {
-+            return x instanceof Name && equals((Name)x);
-+        }
-+        @Override
-         public int hashCode() {
-             if (isParam())
-                 return index | (type << 8);
-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
-@@ -40,6 +40,7 @@
- import java.util.List;
- import static java.lang.invoke.MethodHandleNatives.Constants.*;
- import static java.lang.invoke.MethodHandleStatics.*;
-+import java.util.Objects;
- 
- /**
-  * A {@code MemberName} is a compact symbolic datum which fully characterizes
-@@ -77,32 +78,6 @@
-     //@Injected int         vmindex;
-     private Object     resolution;  // if null, this guy is resolved
- 
--    @Override
--    public boolean equals(Object other) {
--        if (this == other) return true;
--        if (other == null) return false;
--        if (!(other instanceof MemberName)) return false;
--        MemberName that = (MemberName) other;
--        return this.clazz == that.clazz
--                && this.flags == that.flags
--                && this.name.equals(that.name)
--                && typesEqual(this.getType(), that.getType());
--    }
--
--    private static boolean typesEqual(Object a, Object b) {
--        if (a == null && b == null)
--            return true;
--        if (a == null ^ b == null)
--            return false;
--        if (a.getClass() != b.getClass())
--            return false;
--        if (a instanceof Class<?>)
--            return a == b;
--        if (a instanceof MethodType)
--            return a.equals(b);
--        return false;
--    }
--
-     /** Return the declaring class of this member.
-      *  In the case of a bare name and type, the declaring class will be null.
-      */
-@@ -174,8 +149,10 @@
-      */
-     public MethodType getInvocationType() {
-         MethodType itype = getMethodOrFieldType();
-+        if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
-+            return itype.changeReturnType(clazz);
-         if (!isStatic())
--            itype = itype.insertParameterTypes(0, clazz);
-+            return itype.insertParameterTypes(0, clazz);
-         return itype;
-     }
- 
-@@ -322,6 +299,10 @@
-         assert(getReferenceKind() == oldKind);
-         assert(MethodHandleNatives.refKindIsValid(refKind));
-         flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
-+//        if (isConstructor() && refKind != REF_newInvokeSpecial)
-+//            flags += (IS_METHOD - IS_CONSTRUCTOR);
-+//        else if (refKind == REF_newInvokeSpecial && isMethod())
-+//            flags += (IS_CONSTRUCTOR - IS_METHOD);
-         return this;
-     }
- 
-@@ -508,8 +489,16 @@
-     }
-     public MemberName asSpecial() {
-         switch (getReferenceKind()) {
--        case REF_invokeSpecial:  return this;
--        case REF_invokeVirtual:  return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
-+        case REF_invokeSpecial:     return this;
-+        case REF_invokeVirtual:     return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
-+        case REF_newInvokeSpecial:  return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
-+        }
-+        throw new IllegalArgumentException(this.toString());
-+    }
-+    public MemberName asConstructor() {
-+        switch (getReferenceKind()) {
-+        case REF_invokeSpecial:     return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial);
-+        case REF_newInvokeSpecial:  return this;
-         }
-         throw new IllegalArgumentException(this.toString());
-     }
-@@ -590,22 +579,27 @@
-         return res;
-     }
- 
--    /*
-     @Override
-     public int hashCode() {
--        Object[] elements = { getDeclaringClass(), getName(), getMethodOrFieldType(), getReferenceKind() };
--        return Arrays.hashCode(elements);
-+        return Objects.hash(clazz, flags, name, getType());
-     }
-     @Override
-     public boolean equals(Object that) {
--        return (that instanceof MemberName && this.sameReference((MemberName)that));
-+        return (that instanceof MemberName && this.equals((MemberName)that));
-     }
--    */
--    public boolean sameReference(MemberName that) {
--        return (this.getDeclaringClass().equals(that.getDeclaringClass()) &&
--                this.getName().equals(that.getName()) &&
--                this.getMethodOrFieldType().equals(that.getMethodOrFieldType()) &&
--                this.getReferenceKind() == that.getReferenceKind());
-+
-+    /** Decide if two member names have exactly the same symbolic content.
-+     *  Does not take into account any actual class members, so even if
-+     *  two member names resolve to the same actual member, they may
-+     *  be distinct references.
-+     */
-+    public boolean equals(MemberName that) {
-+        if (this == that)  return true;
-+        if (that == null)  return false;
-+        return this.clazz == that.clazz
-+                && this.flags == that.flags
-+                && Objects.equals(this.name, that.name)
-+                && Objects.equals(this.getType(), that.getType());
-     }
- 
-     // Construction from symbolic parts, for queries:
-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,6 +31,8 @@
- 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,
-@@ -803,7 +805,8 @@
-      */
-     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
-         asSpreaderChecks(arrayType, arrayLength);
--        return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
-+        int spreadArgPos = type.parameterCount() - arrayLength;
-+        return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
-     }
- 
-     private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
-@@ -916,8 +919,12 @@
-      */
-     public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
-         asCollectorChecks(arrayType, arrayLength);
-+        int collectArgPos = type().parameterCount()-1;
-+        MethodHandle target = this;
-+        if (arrayType != type().parameterType(collectArgPos))
-+            target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
-         MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
--        return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
-+        return MethodHandleImpl.makeCollectArguments(target, collector, collectArgPos, false);
-     }
- 
-     // private API: return true if last param exactly matches arrayType
-@@ -1087,7 +1094,6 @@
-         boolean lastMatch = asCollectorChecks(arrayType, 0);
-         if (isVarargsCollector() && lastMatch)
-             return this;
--        //return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
-         return MethodHandleImpl.makeVarargsCollector(this, arrayType);
-     }
- 
-@@ -1236,7 +1242,6 @@
-         if (argc != 0) {
-             Class<?> arrayType = type().parameterType(argc-1);
-             if (arrayType.isArray()) {
--                //return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
-                 return MethodHandleImpl.makeVarargsCollector(this, arrayType);
-             }
-         }
-@@ -1245,9 +1250,9 @@
-     /*non-public*/
-     MethodHandle viewAsType(MethodType newType) {
-         // No actual conversions, just a new view of the same method.
--        MethodHandle mh = MethodHandleImpl.convertArguments(this, newType, type(), 0);
--        if (mh == null)  throw new InternalError();
--        return mh;
-+        if (!type.isViewableAs(newType))
-+            throw new InternalError();
-+        return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
-     }
- 
-     // Decoding
-@@ -1273,7 +1278,7 @@
- 
-     /*non-public*/ MethodHandle convertArguments(MethodType newType) {
-         // Override this if it can be improved.
--        return MethodHandleImpl.convertArguments(this, newType, 1);
-+        return MethodHandleImpl.makePairwiseConvert(this, newType, 1);
-     }
- 
-     /*non-public*/
-@@ -1320,11 +1325,73 @@
-     }
- 
-     /*non-public*/
--    private BoundMethodHandle rebind() {
-+    MethodHandle rebind() {
-         // Bind 'this' into a new invoker, of the known class BMH.
-         MethodType type2 = type();
--        LambdaForm form2 = BoundMethodHandle.reinvokerForm(type2.basicType());
-+        LambdaForm form2 = reinvokerForm(type2.basicType());
-         // form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
--        return new BoundMethodHandle.BMH_L(type2, form2, this);
-+        return BoundMethodHandle.bindSingle(type2, form2, this);
-+    }
-+
-+    /*non-public*/
-+    MethodHandle reinvokerTarget() {
-+        throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this);
-+    }
-+
-+    /** Create a LF which simply reinvokes a target of the given basic type.
-+     *  The target MH must override {@link #reinvokerTarget} to provide the target.
-+     */
-+    static LambdaForm reinvokerForm(MethodType mtype) {
-+        mtype = mtype.basicType();
-+        LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
-+        if (reinvoker != null)  return reinvoker;
-+        MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
-+        final int THIS_BMH    = 0;
-+        final int ARG_BASE    = 1;
-+        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
-+        int nameCursor = ARG_LIMIT;
-+        final int NEXT_MH     = nameCursor++;
-+        final int REINVOKE    = nameCursor++;
-+        LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
-+        names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
-+        Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
-+        targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
-+        names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
-+        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
-+    }
-+
-+    private static final LambdaForm.NamedFunction NF_reinvokerTarget;
-+    static {
-+        try {
-+            NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class
-+                .getDeclaredMethod("reinvokerTarget"));
-+        } catch (ReflectiveOperationException ex) {
-+            throw new InternalError(ex);
-+        }
-+    }
-+
-+    /**
-+     * Replace the old lambda form of this method handle with a new one.
-+     * The new one must be functionally equivalent to the old one.
-+     * Threads may continue running the old form indefinitely,
-+     * but it is likely that the new one will be preferred for new executions.
-+     * Use with discretion.
-+     * @param newForm
-+     */
-+    /*non-public*/
-+    void updateForm(LambdaForm newForm) {
-+        if (form == newForm)  return;
-+        // ISSUE: Should we have a memory fence here?
-+        UNSAFE.putObject(this, FORM_OFFSET, newForm);
-+        this.form.prepare();  // as in MethodHandle.<init>
-+    }
-+
-+    private static final long FORM_OFFSET;
-+    static {
-+        try {
-+            FORM_OFFSET = UNSAFE.objectFieldOffset(MethodHandle.class.getDeclaredField("form"));
-+        } catch (ReflectiveOperationException ex) {
-+            throw new InternalError(ex);
-+        }
-     }
- }
-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
-@@ -26,15 +26,14 @@
- package java.lang.invoke;
- 
- import sun.invoke.util.VerifyType;
-+
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.HashMap;
- import sun.invoke.empty.Empty;
- import sun.invoke.util.ValueConversions;
- import sun.invoke.util.Wrapper;
--import sun.misc.Unsafe;
- import static java.lang.invoke.LambdaForm.*;
--import static java.lang.invoke.MethodHandleNatives.Constants.*;
- import static java.lang.invoke.MethodHandleStatics.*;
- import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
- 
-@@ -50,41 +49,6 @@
-         MemberName.Factory.INSTANCE.getClass();
-     }
- 
--    /** Cache frequently used stuff. */
--    static final Unsafe UNSAFE = Unsafe.getUnsafe();
--
--    static MethodHandle makeAllocator(MethodHandle rawConstructor) {
--        MethodType rawConType = rawConstructor.type();
--        Class<?> allocateClass = rawConType.parameterType(0);
--        MethodType srcType = rawConType.dropParameterTypes(0, 1).changeReturnType(allocateClass);
--        final int THIS_MH     = 0;
--        final int ARG_BASE    = 1;
--        final int ARG_LIMIT   = ARG_BASE + srcType.parameterCount();
--        int nameCursor = ARG_LIMIT;
--        final int NEW_OBJ     = nameCursor++;
--        final int CALL_CTOR   = nameCursor++;
--        Name[] names = arguments(nameCursor - ARG_LIMIT, srcType.invokerType());
--        names[NEW_OBJ] = new Name(AllocatorData.UNSAFE_ALLOCATEINSTANCE, allocateClass);
--        Object[] conArgs = Arrays.copyOfRange(names, ARG_BASE-1, ARG_LIMIT, Object[].class);
--        conArgs[0] = names[NEW_OBJ];
--        names[CALL_CTOR] = new Name(rawConstructor, conArgs);
--        LambdaForm form = new LambdaForm("newInvokeSpecial", ARG_LIMIT, names, NEW_OBJ);
--        return new SimpleMethodHandle(srcType, form);
--    }
--
--    static final class AllocatorData {
--        static final MethodHandle UNSAFE_ALLOCATEINSTANCE;
--        static {
--            try {
--                assert(IMPL_LOOKUP != null) : "bootstrap problem";
--                UNSAFE_ALLOCATEINSTANCE =
--                    IMPL_LOOKUP.bind(UNSAFE, "allocateInstance", MethodType.methodType(Object.class, Class.class));
--            } catch (NoSuchMethodException | IllegalAccessException e) {
--                throw new InternalError(e);
--            }
--        }
--    }
--
-     static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
-         if (!arrayClass.isArray())
-             throw newIllegalArgumentException("not an array: "+arrayClass);
-@@ -185,221 +149,6 @@
-         }
-     }
- 
--    static MethodHandle makeFieldAccessor(byte refKind, MemberName field, Class<?> receiver) {
--        boolean isStatic = !MethodHandleNatives.refKindHasReceiver(refKind);
--        MethodHandle accessor = FieldAccessor.getAccessor(refKind, field, receiver);
--
--        MethodType srcType = accessor.type();
--        MethodType mhType = srcType.dropParameterTypes(isStatic ? 0 : 1, 2);
--        MethodType lambdaType = mhType.insertParameterTypes(0, isStatic ? BoundMethodHandle.BMH_JL.class : BoundMethodHandle.BMH_J.class);
--
--        Name[] names = arguments(3, lambdaType);
--
--        final int _ARG    = 1;                // at this index in the names array, the object to be accessed is stored
--        final int _VALUE  = isStatic ? 1 : 2; // if this is for write access, the value to be written is stored at this index
--        final int _BASE   = names.length - 3; // the slots for _BASE and _NPE are shared
--        final int _NPE    = names.length - 3; // (the NPE check is used in the non-static case only, where _BASE is not required)
--        final int _OFFSET = names.length - 2;
--        final int _RESULT = names.length - 1;
--
--        if (isStatic) {
--            names[_OFFSET] = new Name(BoundMethodHandle.MH_argJ0, names[0]);
--            names[_BASE]   = new Name(BoundMethodHandle.MH_argL1, names[0]);
--        } else {
--            names[_NPE] = new Name(FieldAccessor.OBJECT_GETCLASS, names[_ARG]); // NPE check
--            names[_OFFSET] = new Name(BoundMethodHandle.MH_argJ0, names[0]);
--        }
--
--        Object[] args;
--        if (refKind == REF_putField || refKind == REF_putStatic) {
--            args = new Object[] { names[isStatic ? _BASE : _ARG], names[_OFFSET], names[_VALUE] };
--        } else {
--            args = new Object[] { names[isStatic ? _BASE : _ARG], names[_OFFSET] };
--        }
--        names[_RESULT] = new Name(accessor, args);
--        LambdaForm form = new LambdaForm(MethodHandleNatives.refKindName(refKind), lambdaType.parameterCount(), names);
--
--        BoundMethodHandle mh;
--        if (isStatic) {
--            long offset = MethodHandleNatives.staticFieldOffset(field);
--            Object base = MethodHandleNatives.staticFieldBase(field);
--            mh = new BoundMethodHandle.BMH_JL(mhType, form, offset, base);
--        } else {
--            long offset = MethodHandleNatives.objectFieldOffset(field);
--            mh = new BoundMethodHandle.BMH_J(mhType, form, offset);
--        }
--        return mh;
--    }
--
--    static final class FieldAccessor {
--        static final MethodHandle OBJECT_GETCLASS;
--        static {
--            try {
--                assert(IMPL_LOOKUP != null) : "bootstrap problem";
--                OBJECT_GETCLASS =
--                    IMPL_LOOKUP.findStatic(FieldAccessor.class, "getClass", MethodType.methodType(Object.class, Object.class));
--            } catch (NoSuchMethodException | IllegalAccessException e) {
--                throw new InternalError(e);
--            }
--        }
--
--        /** Static definition of Object.getClass for null-pointer checking. */
--        /*non-public*/ static
--        @ForceInline
--        Object getClass(Object obj) {
--            return obj.getClass();
--        }
--
--        static String name(byte refKind, MemberName field) {
--            String prefix = MethodHandleNatives.refKindIsGetter(refKind) ? "get" : "put";
--            String type   = Wrapper.forBasicType(field.getFieldType()).primitiveSimpleName();
--            String suffix = field.isVolatile() ? "Volatile" : "";
--            return prefix + Character.toUpperCase(type.charAt(0)) + type.substring(1) + suffix;
--        }
--        static MethodType type(byte refKind, MemberName field) {
--            Class<?> fieldClass = field.getFieldType();
--            return type(refKind, Object.class, fieldClass.isPrimitive() ? fieldClass : Object.class);
--        }
--        static MethodType strongType(byte refKind, MemberName field, Class<?> receiver) {
--            Class<?> fieldClass = field.getFieldType();
--            MethodType type;
--            if (MethodHandleNatives.refKindHasReceiver(refKind)) {
--                if (!field.getDeclaringClass().isAssignableFrom(receiver))
--                    throw new InternalError(field.toString());
--                type = type(refKind, receiver,     fieldClass);
--            } else {
--                type = type(refKind, Object.class, fieldClass);
--            }
--            return type.insertParameterTypes(0, Unsafe.class);
--        }
--        static MethodType type(byte refKind, Class<?> declaringClass, Class<?> fieldClass) {
--            if (MethodHandleNatives.refKindIsGetter(refKind))
--                return MethodType.methodType(fieldClass, declaringClass, long.class);
--            else
--                return MethodType.methodType(void.class, declaringClass, long.class, fieldClass);
--        }
--        static MethodHandle getAccessor(byte refKind, MemberName field, Class<?> receiver) {
--            if (!MethodHandleNatives.refKindIsField(refKind))
--                throw newIllegalArgumentException("refKind not a field: " + refKind);
--            String     name = name(refKind, field);
--            MethodType type = type(refKind, field);
--            MethodHandle mh;
--            try {
--                mh = IMPL_LOOKUP.findVirtual(Unsafe.class, name, type);
--            } catch (ReflectiveOperationException ex) {
--                throw uncaughtException(ex);
--            }
--            Class<?> declaringClass = field.getDeclaringClass();
--            Class<?> fieldClass     = field.getFieldType();
--            if ((!fieldClass.isPrimitive() && fieldClass != Object.class) || (MethodHandleNatives.refKindHasReceiver(refKind) && declaringClass != Object.class)) {
--                MethodType strongType = strongType(refKind, field, receiver);
--                mh = convertArguments(mh, strongType, 0);
--            }
--            mh = mh.bindImmediate(0, 'L', UNSAFE); // bind UNSAFE early
--            return mh;
--        }
--    }
--
--    /*non-public*/ static
--    MethodHandle convertArguments(MethodHandle target, MethodType srcType, int level) {
--        MethodType dstType = target.type();
--        if (dstType.equals(srcType))
--            return target;
--        assert(level > 1 || dstType.isConvertibleTo(srcType));
--        MethodHandle retFilter = null;
--        Class<?> oldRT = dstType.returnType();
--        Class<?> newRT = srcType.returnType();
--        if (!VerifyType.isNullConversion(oldRT, newRT)) {
--            if (oldRT == void.class) {
--                Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
--                retFilter = ValueConversions.zeroConstantFunction(wrap);
--            } else {
--                retFilter = MethodHandles.identity(newRT);
--                retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
--            }
--            srcType = srcType.changeReturnType(oldRT);
--        }
--        MethodHandle res = null;
--        Exception ex = null;
--        try {
--            res = convertArguments(target, srcType, dstType, level);
--        } catch (IllegalArgumentException ex1) {
--            ex = ex1;
--        }
--        if (res == null) {
--            WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+srcType+": "+target);
--            wmt.initCause(ex);
--            throw wmt;
--        }
--        if (retFilter != null)
--            res = MethodHandles.filterReturnValue(res, retFilter);
--        return res;
--    }
--
--    static MethodHandle convertArguments(MethodHandle target,
--                                         MethodType srcType,
--                                         MethodType dstType,
--                                         int level) {
--        assert(dstType.parameterCount() == target.type().parameterCount());
--        if (srcType == dstType)
--            return target;
--        if (dstType.parameterCount() != srcType.parameterCount())
--            throw newIllegalArgumentException("mismatched parameter count", dstType, srcType);
--        return makePairwiseConvert(srcType, target, level);
--    }
--
--    /** Can a JVM-level adapter directly implement the proposed
--     *  argument conversions, as if by fixed-arity MethodHandle.asType?
--     */
--    private static boolean canPairwiseConvert(MethodType srcType, MethodType dstType, int level) {
--        // same number of args, of course
--        int len = srcType.parameterCount();
--        if (len != dstType.parameterCount())
--            return false;
--
--        // Check return type.
--        Class<?> exp = srcType.returnType();
--        Class<?> ret = dstType.returnType();
--        if (!VerifyType.isNullConversion(ret, exp)) {
--            if (!canConvertArgument(ret, exp, level))
--                return false;
--        }
--
--        // Check args pairwise.
--        for (int i = 0; i < len; i++) {
--            Class<?> src = srcType.parameterType(i); // source type
--            Class<?> dst = dstType.parameterType(i); // destination type
--            if (!canConvertArgument(src, dst, level))
--                return false;
--        }
--
--        return true;
--    }
--
--    /** Can a JVM-level adapter directly implement the proposed
--     *  argument conversion, as if by fixed-arity MethodHandle.asType?
--     */
--    private static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
--        // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
--        // so we don't need to repeat so much decision making.
--        if (VerifyType.isNullConversion(src, dst)) {
--            return true;
--        // } else if (convOpSupported(OP_COLLECT_ARGS)) {
--        //     // If we can build filters, we can convert anything to anything.
--        //     return true;
--        } else if (src.isPrimitive()) {
--            if (dst.isPrimitive())
--                return canPrimCast(src, dst);
--            else
--                return canBoxArgument(src, dst);
--        } else {
--            if (dst.isPrimitive())
--                return canUnboxArgument(src, dst, level);
--            else
--                return true;  // any two refs can be interconverted
--        }
--    }
--
-     /**
-      * Create a JVM-level adapter method handle to conform the given method
-      * handle to the similar newType, using only pairwise argument conversions.
-@@ -413,13 +162,12 @@
-      *          or the original target if the types are already identical
-      *          or null if the adaptation cannot be made
-      */
--    private static MethodHandle makePairwiseConvert(MethodType srcType, MethodHandle target, int level) {
-+    static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) {
-+        assert(level >= 0 && level <= 2);
-         MethodType dstType = target.type();
--        if (srcType == dstType)  return target;
--
--        if (!canPairwiseConvert(srcType, dstType, level))
--            return null;
--        // (after this point, it is an assertion error to fail to convert)
-+        assert(dstType.parameterCount() == target.type().parameterCount());
-+        if (srcType == dstType)
-+            return target;
- 
-         // Calculate extra arguments (temporaries) required in the names array.
-         // FIXME: Use an ArrayList<Name>.  Some arguments require more than one conversion step.
-@@ -427,27 +175,26 @@
-         for (int i = 0; i < srcType.parameterCount(); i++) {
-             Class<?> src = srcType.parameterType(i);
-             Class<?> dst = dstType.parameterType(i);
--            if (!isTrivialConversion(src, dst, level)) {
-+            if (!VerifyType.isNullConversion(src, dst)) {
-                 extra++;
-             }
-         }
- 
-         Class<?> needReturn = srcType.returnType();
-         Class<?> haveReturn = dstType.returnType();
--        boolean retConv = !isTrivialConversion(haveReturn, needReturn, level);
-+        boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
- 
-         // Now build a LambdaForm.
-         MethodType lambdaType = srcType.invokerType();
-         Name[] names = arguments(extra + 1, lambdaType);
-         int[] indexes = new int[lambdaType.parameterCount()];
- 
--        MethodHandle adapter = target;
-         MethodType midType = dstType;
-         for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) {
-             Class<?> src = srcType.parameterType(i);
-             Class<?> dst = midType.parameterType(i);
- 
--            if (isTrivialConversion(src, dst, level)) {
-+            if (VerifyType.isNullConversion(src, dst)) {
-                 // do nothing: difference is trivial
-                 indexes[i] = argIndex;
-                 continue;
-@@ -457,24 +204,46 @@
-             midType = midType.changeParameterType(i, src);
- 
-             // Tricky case analysis follows.
--            // It parallels canConvertArgument() above.
-             MethodHandle fn = null;
-             if (src.isPrimitive()) {
-                 if (dst.isPrimitive()) {
--                    fn = makePrimCast(src, dst);
-+                    fn = ValueConversions.convertPrimitive(src, dst);
-                 } else {
--                    fn = makeBoxArgument(midType, adapter, i, src);
-+                    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.
--                    fn = makeUnboxArgument(src, dst, level);
-+                    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 = makeCheckCast(midType, adapter, i, dst);
-+                    fn = ValueConversions.cast(dst);
-                 }
-             }
-             names[tmpIndex] = new Name(fn, names[argIndex]);
-@@ -482,9 +251,17 @@
-             tmpIndex++;
-         }
-         if (retConv) {
--            MethodHandle fn = makeReturnConversion(adapter, haveReturn, needReturn);
--            assert(fn != null);
--            target = fn;
-+            MethodHandle adjustReturn;
-+            if (haveReturn == void.class) {
-+                // synthesize a zero value for the given void
-+                Object zero = Wrapper.forBasicType(needReturn).zero();
-+                adjustReturn = MethodHandles.constant(needReturn, zero);
-+            } else {
-+                MethodHandle identity = MethodHandles.identity(needReturn);
-+                MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
-+                adjustReturn = makePairwiseConvert(identity, needConversion, level);
-+            }
-+            target = makeCollectArguments(adjustReturn, target, 0, false);
-         }
- 
-         // Build argument array for the call.
-@@ -498,133 +275,12 @@
-         return new SimpleMethodHandle(srcType, form);
-     }
- 
--    private static boolean isTrivialConversion(Class<?> src, Class<?> dst, int level) {
--        if (src == dst || dst == void.class)  return true;
--        if (!VerifyType.isNullConversion(src, dst))  return false;
--        if (level > 1)  return true;  // explicitCastArguments
--        boolean sp = src.isPrimitive();
--        boolean dp = dst.isPrimitive();
--        if (sp != dp)  return false;
--        if (sp) {
--            // in addition to being a null conversion, forbid boolean->int etc.
--            return Wrapper.forPrimitiveType(dst)
--                    .isConvertibleFrom(Wrapper.forPrimitiveType(src));
--        } else {
--            return dst.isAssignableFrom(src);
--        }
--    }
--
--    private static MethodHandle makeReturnConversion(MethodHandle target, Class<?> haveReturn, Class<?> needReturn) {
--        MethodHandle adjustReturn;
--        if (haveReturn == void.class) {
--            // synthesize a zero value for the given void
--            Object zero = Wrapper.forBasicType(needReturn).zero();
--            adjustReturn = MethodHandles.constant(needReturn, zero);
--        } else {
--            MethodType needConversion = MethodType.methodType(needReturn, haveReturn);
--            adjustReturn = MethodHandles.identity(needReturn).asType(needConversion);
--        }
--        return makeCollectArguments(adjustReturn, target, 0, false);
--    }
--
--    /* Return one plus the position of the first non-trivial difference
--     * between the given types.  This is not a symmetric operation;
--     * we are considering adapting the targetType to adapterType.
--     * Trivial differences are those which could be ignored by the JVM
--     * without subverting the verifier.  Otherwise, adaptable differences
--     * are ones for which we could create an adapter to make the type change.
--     * Return zero if there are no differences (other than trivial ones).
--     * Return 1+N if N is the only adaptable argument difference.
--     * Return the -2-N where N is the first of several adaptable
--     * argument differences.
--     * Return -1 if there there are differences which are not adaptable.
--     */
--    private static int diffTypes(MethodType adapterType,
--                                 MethodType targetType,
--                                 boolean raw) {
--        int diff;
--        diff = diffReturnTypes(adapterType, targetType, raw);
--        if (diff != 0)  return diff;
--        int nargs = adapterType.parameterCount();
--        if (nargs != targetType.parameterCount())
--            return -1;
--        return diffParamTypes(adapterType, 0, targetType, 0, nargs, raw);
--    }
--    private static int diffReturnTypes(MethodType adapterType,
--                                       MethodType targetType,
--                                       boolean raw) {
--        Class<?> src = targetType.returnType();
--        Class<?> dst = adapterType.returnType();
--        if ((!raw
--             ? VerifyType.canPassUnchecked(src, dst)
--             : VerifyType.canPassRaw(src, dst)
--             ) > 0)
--            return 0;  // no significant difference
--        if (raw && !src.isPrimitive() && !dst.isPrimitive())
--            return 0;  // can force a reference return (very carefully!)
--        //if (false)  return 1;  // never adaptable!
--        return -1;  // some significant difference
--    }
--    private static int diffParamTypes(MethodType adapterType, int astart,
--                                      MethodType targetType, int tstart,
--                                      int nargs, boolean raw) {
--        assert(nargs >= 0);
--        int res = 0;
--        for (int i = 0; i < nargs; i++) {
--            Class<?> src  = adapterType.parameterType(astart+i);
--            Class<?> dest = targetType.parameterType(tstart+i);
--            if ((!raw
--                 ? VerifyType.canPassUnchecked(src, dest)
--                 : VerifyType.canPassRaw(src, dest)
--                ) <= 0) {
--                // found a difference; is it the only one so far?
--                if (res != 0)
--                    return -1-res; // return -2-i for prev. i
--                res = 1+i;
--            }
--        }
--        return res;
--    }
--
--    /** Can a retyping adapter (alone) validly convert the target to newType? */
--    // private static boolean canRetypeOnly(MethodType newType, MethodType targetType) {
--    //     return canRetype(newType, targetType, false);
--    // }
--    /** Can a retyping adapter (alone) convert the target to newType?
--     *  It is allowed to widen subword types and void to int, to make bitwise
--     *  conversions between float/int and double/long, and to perform unchecked
--     *  reference conversions on return.  This last feature requires that the
--     *  caller be trusted, and perform explicit cast conversions on return values.
--     */
--    // private static boolean canRetypeRaw(MethodType newType, MethodType targetType) {
--    //     return canRetype(newType, targetType, true);
--    // }
--    // private static boolean canRetype(MethodType newType, MethodType targetType, boolean raw) {
--    //     int diff = diffTypes(newType, targetType, raw);
--    //     // %%% This assert is too strong.  Factor diff into VerifyType and reconcile.
--    //     assert(raw || (diff == 0) == VerifyType.isNullConversion(newType, targetType));
--    //     return diff == 0;
--    // }
--
--    /** Factory method:  Performs no conversions; simply retypes the adapter.
--     *  Allows unchecked argument conversions pairwise, if they are safe.
--     *  Returns null if not possible.
--     */
--    static MethodHandle makeRetype(MethodType srcType, MethodHandle target) {
--        MethodType dstType = target.type();
--        if (dstType == srcType)  return target;
--        // if (!canRetype(srcType, dstType, raw))
--        //     return null;
--        // TO DO:  clone the target guy, whatever he is, with new type.
--        //return AdapterMethodHandle.makeRetype(srcType, target, raw);
--        MethodType lambdaType = srcType.invokerType();
-+    static MethodHandle makeReferenceIdentity(Class<?> refType) {
-+        MethodType lambdaType = MethodType.genericMethodType(1).invokerType();
-         Name[] names = arguments(1, lambdaType);
--
--        Object[] targetArgs = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount(), Object[].class);
--        names[names.length - 1] = new Name(target, targetArgs);
--
--        LambdaForm form = new LambdaForm("asType", lambdaType.parameterCount(), names);
--        return new SimpleMethodHandle(srcType, form);
-+        names[names.length - 1] = new Name(ValueConversions.identity(), names[1]);
-+        LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names);
-+        return new SimpleMethodHandle(MethodType.methodType(refType, refType), form);
-     }
- 
-     static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
-@@ -636,7 +292,7 @@
-         return new AsVarargsCollector(target, target.type(), arrayType);
-     }
- 
--    static class AsVarargsCollector extends BoundMethodHandle {
-+    static class AsVarargsCollector extends MethodHandle {
-         MethodHandle target;
-         final Class<?> arrayType;
-         MethodHandle cache;
-@@ -648,6 +304,8 @@
-             this.cache = target.asCollector(arrayType, 0);
-         }
- 
-+        @Override MethodHandle reinvokerTarget() { return target; }
-+
-         @Override
-         public boolean isVarargsCollector() {
-             return true;
-@@ -659,13 +317,6 @@
-         }
- 
-         @Override
--        public String types() {
--            return types;
--        }
--
--        public static final String types = "L";
--
--        @Override
-         public MethodHandle asType(MethodType newType) {
-             MethodType type = this.type();
-             int collectArg = type.parameterCount() - 1;
-@@ -730,283 +381,22 @@
-         MethodHandle permuteArguments(MethodType newType, int[] reorder) {
-             return asFixedArity().permuteArguments(newType, reorder);
-         }
--
--        @Override
--        public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
--            throw new IllegalStateException("NYI");
--        }
--
--        @Override
--        public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
--            throw new IllegalStateException("NYI");
--        }
--
--        @Override
--        public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
--            throw new IllegalStateException("NYI");
--        }
--
--        @Override
--        public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
--            throw new IllegalStateException("NYI");
--        }
--
--        @Override
--        public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
--            throw new IllegalStateException("NYI");
--        }
--
--        @Override
--        public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
--            throw new IllegalStateException("NYI");
--        }
-     }
- 
--    /** Can a checkcast adapter validly convert the target to srcType?
--     *  The JVM supports all kind of reference casts, even silly ones.
--     */
--    private static boolean canCheckCast(MethodType srcType, MethodType targetType,
--                                        int arg, Class<?> castType) {
--        Class<?> src = srcType.parameterType(arg);
--        Class<?> dst = targetType.parameterType(arg);
--        if (!canCheckCast(src, castType)
--                || !VerifyType.isNullConversion(castType, dst))
--            return false;
--        // int diff = diffTypes(srcType, targetType, false);
--        // return (diff == arg+1) || (diff == 0);  // arg is sole non-trivial diff
--        return true;
--    }
--    /** Can an primitive conversion adapter validly convert src to dst? */
--    private static boolean canCheckCast(Class<?> src, Class<?> dst) {
--        return (!src.isPrimitive() && !dst.isPrimitive());
--    }
-+    /** Factory method:  Spread selected argument. */
-+    static MethodHandle makeSpreadArguments(MethodHandle target,
-+                                            Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
-+        MethodType targetType = target.type();
- 
--    /** Factory method:  Forces a cast at the given argument.
--     *  The castType is the target of the cast, and can be any type
--     *  with a null conversion to the corresponding target parameter.
--     *  Return null if this cannot be done.
--     */
--    private static MethodHandle makeCheckCast(MethodType srcType, MethodHandle target,
--                                              int arg, Class<?> castType) {
--        if (!canCheckCast(srcType, target.type(), arg, castType))
--            return null;
--        return ValueConversions.cast(castType);
--    }
--
--    /** Can an primitive conversion adapter validly convert the target to newType?
--     *  The JVM currently supports all conversions except those between
--     *  floating and integral types.
--     */
--    private static boolean canPrimCast(MethodType newType, MethodType targetType,
--                                       int arg, Class<?> convType) {
--        Class<?> src = newType.parameterType(arg);
--        Class<?> dst = targetType.parameterType(arg);
--        if (!canPrimCast(src, convType)
--                || !VerifyType.isNullConversion(convType, dst))
--            return false;
--        // int diff = diffTypes(newType, targetType, false);
--        // return (diff == arg+1);  // arg is sole non-trivial diff
--        return true;
--    }
--    /** Can an primitive conversion adapter validly convert src to dst? */
--    static boolean canPrimCast(Class<?> src, Class<?> dst) {
--        if (!src.isPrimitive() || !dst.isPrimitive()) {
--            return false;
--        }
--        return true;
--    }
--
--    /** Factory method:  Truncate the given argument with zero or sign extension,
--     *  and/or convert between single and doubleword versions of integer or float.
--     */
--    private static MethodHandle makePrimCast(Class<?> src, Class<?> dst) {
--        if (VerifyType.isNullConversion(src, dst))
--            return ValueConversions.identity(dst);
--        else
--            return ValueConversions.convertPrimitive(src, dst);
--    }
--
--    /** Can an unboxing conversion validly convert src to dst?
--     *  The JVM currently supports all kinds of casting and unboxing.
--     *  The convType is the unboxed type; it can be either a primitive or wrapper.
--     */
--    private static boolean canUnboxArgument(MethodType srcType, MethodType targetType,
--                                            int arg, Class<?> convType, int level) {
--        Class<?> src = srcType.parameterType(arg);
--        Class<?> dst = targetType.parameterType(arg);
--        Class<?> boxType = Wrapper.asWrapperType(convType);
--        convType = Wrapper.asPrimitiveType(convType);
--        if (!canCheckCast(src, boxType)
--                || boxType == convType
--                || !VerifyType.isNullConversion(convType, dst))
--            return false;
--        // int diff = diffTypes(srcType, targetType, false);
--        // return (diff == arg+1);  // arg is sole non-trivial diff
--        return true;
--    }
--    /** Can an primitive unboxing adapter validly convert src to dst? */
--    private static boolean canUnboxArgument(Class<?> src, Class<?> dst, int level) {
--        assert(dst.isPrimitive());
--        // if we have JVM support for boxing, we can also do complex unboxing
--        return true;
--    }
--
--    /** Factory method:  Unbox the given argument.
--     */
--    private static Met