changeset 47753:a2008587c13f jdk-10+32

8184777: Factor out species generation logic from BoundMethodHandle Reviewed-by: vlivanov Contributed-by: john.r.rose@oracle.com, claes.redestad@oracle.com
author redestad
date Thu, 16 Nov 2017 00:58:50 +0100
parents e0041b182e31
children 13e39ca700d0 8fd3320d312c
files src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java src/java.base/share/classes/java/lang/invoke/LambdaForm.java src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java test/jdk/java/lang/invoke/ClassSpecializerTest.java test/jdk/java/lang/invoke/java.base/java/lang/invoke/ClassSpecializerHelper.java test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java
diffstat 13 files changed, 1543 insertions(+), 648 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Thu Nov 16 00:58:50 2017 +0100
@@ -25,25 +25,21 @@
 
 package java.lang.invoke;
 
-import jdk.internal.loader.BootLoader;
-import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.internal.org.objectweb.asm.FieldVisitor;
-import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.internal.vm.annotation.Stable;
 import sun.invoke.util.ValueConversions;
-import sun.invoke.util.Wrapper;
 
-import java.lang.invoke.LambdaForm.NamedFunction;
-import java.lang.invoke.MethodHandles.Lookup;
-import java.lang.reflect.Field;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.function.Function;
+import java.util.ArrayList;
+import java.util.List;
 
 import static java.lang.invoke.LambdaForm.BasicType;
 import static java.lang.invoke.LambdaForm.BasicType.*;
-import static java.lang.invoke.MethodHandleStatics.*;
-import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import static java.lang.invoke.LambdaForm.BasicType.V_TYPE_NUM;
+import static java.lang.invoke.LambdaForm.BasicType.V_TYPE_NUM;
+import static java.lang.invoke.LambdaForm.BasicType.V_TYPE_NUM;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandleStatics.newInternalError;
+import static java.lang.invoke.MethodHandleStatics.uncaughtException;
 
 /**
  * The flavor of method handle which emulates an invoke instruction
@@ -56,7 +52,7 @@
 
     /*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) {
         super(type, form);
-        assert(speciesData() == speciesData(form));
+        assert(speciesData() == speciesDataFor(form));
     }
 
     //
@@ -70,13 +66,13 @@
             case L_TYPE:
                 return bindSingle(type, form, x);  // Use known fast path.
             case I_TYPE:
-                return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor().invokeBasic(type, form, ValueConversions.widenSubword(x));
+                return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(I_TYPE_NUM).factory().invokeBasic(type, form, ValueConversions.widenSubword(x));
             case J_TYPE:
-                return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor().invokeBasic(type, form, (long) x);
+                return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(J_TYPE_NUM).factory().invokeBasic(type, form, (long) x);
             case F_TYPE:
-                return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor().invokeBasic(type, form, (float) x);
+                return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(F_TYPE_NUM).factory().invokeBasic(type, form, (float) x);
             case D_TYPE:
-                return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor().invokeBasic(type, form, (double) x);
+                return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(D_TYPE_NUM).factory().invokeBasic(type, form, (double) x);
             default : throw newInternalError("unexpected xtype: " + xtype);
             }
         } catch (Throwable t) {
@@ -98,6 +94,7 @@
     BoundMethodHandle bindArgumentL(int pos, Object value) {
         return editor().bindArgumentL(this, pos, value);
     }
+
     /*non-public*/
     BoundMethodHandle bindArgumentI(int pos, int value) {
         return editor().bindArgumentI(this, pos, value);
@@ -114,7 +111,6 @@
     BoundMethodHandle bindArgumentD(int pos, double value) {
         return editor().bindArgumentD(this, pos, value);
     }
-
     @Override
     BoundMethodHandle rebind() {
         if (!tooComplex()) {
@@ -137,28 +133,29 @@
     static BoundMethodHandle makeReinvoker(MethodHandle target) {
         LambdaForm form = DelegatingMethodHandle.makeReinvokerForm(
                 target, MethodTypeForm.LF_REBIND,
-                Species_L.SPECIES_DATA, Species_L.SPECIES_DATA.getterFunction(0));
+                Species_L.BMH_SPECIES, Species_L.BMH_SPECIES.getterFunction(0));
         return Species_L.make(target.type(), form, target);
     }
 
     /**
-     * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a
+     * Return the {@link BoundMethodHandle.SpeciesData} instance representing this BMH species. All subclasses must provide a
      * static field containing this value, and they must accordingly implement this method.
      */
-    /*non-public*/ abstract SpeciesData speciesData();
+    /*non-public*/ abstract BoundMethodHandle.SpeciesData speciesData();
 
-    /*non-public*/ static SpeciesData speciesData(LambdaForm form) {
+    /*non-public*/ static BoundMethodHandle.SpeciesData speciesDataFor(LambdaForm form) {
         Object c = form.names[0].constraint;
-        if (c instanceof SpeciesData)
+        if (c instanceof SpeciesData) {
             return (SpeciesData) c;
+        }
         // if there is no BMH constraint, then use the null constraint
-        return SpeciesData.EMPTY;
+        return SPECIALIZER.topSpecies();
     }
 
     /**
      * Return the number of fields in this BMH.  Equivalent to speciesData().fieldCount().
      */
-    /*non-public*/ abstract int fieldCount();
+    /*non-public*/ final int fieldCount() { return speciesData().fieldCount(); }
 
     @Override
     Object internalProperties() {
@@ -167,7 +164,7 @@
 
     @Override
     final String internalValues() {
-        int count = speciesData().fieldCount();
+        int count = fieldCount();
         if (count == 1) {
             return "[" + arg(0) + "]";
         }
@@ -180,17 +177,18 @@
 
     /*non-public*/ final Object arg(int i) {
         try {
-            switch (speciesData().fieldType(i)) {
-            case L_TYPE: return          speciesData().getters[i].invokeBasic(this);
-            case I_TYPE: return (int)    speciesData().getters[i].invokeBasic(this);
-            case J_TYPE: return (long)   speciesData().getters[i].invokeBasic(this);
-            case F_TYPE: return (float)  speciesData().getters[i].invokeBasic(this);
-            case D_TYPE: return (double) speciesData().getters[i].invokeBasic(this);
+            Class<?> fieldType = speciesData().fieldTypes().get(i);
+            switch (BasicType.basicType(fieldType)) {
+                case L_TYPE: return          speciesData().getter(i).invokeBasic(this);
+                case I_TYPE: return (int)    speciesData().getter(i).invokeBasic(this);
+                case J_TYPE: return (long)   speciesData().getter(i).invokeBasic(this);
+                case F_TYPE: return (float)  speciesData().getter(i).invokeBasic(this);
+                case D_TYPE: return (double) speciesData().getter(i).invokeBasic(this);
             }
         } catch (Throwable ex) {
             throw uncaughtException(ex);
         }
-        throw new InternalError("unexpected type: " + speciesData().typeChars+"."+i);
+        throw new InternalError("unexpected type: " + speciesData().key()+"."+i);
     }
 
     //
@@ -210,20 +208,21 @@
 
     private  // make it private to force users to access the enclosing class first
     static final class Species_L extends BoundMethodHandle {
+
         final Object argL0;
+
         private Species_L(MethodType mt, LambdaForm lf, Object argL0) {
             super(mt, lf);
             this.argL0 = argL0;
         }
+
         @Override
         /*non-public*/ SpeciesData speciesData() {
-            return SPECIES_DATA;
+            return BMH_SPECIES;
         }
-        @Override
-        /*non-public*/ int fieldCount() {
-            return 1;
-        }
-        /*non-public*/ static final SpeciesData SPECIES_DATA = new SpeciesData("L", Species_L.class);
+
+        /*non-public*/ static @Stable SpeciesData BMH_SPECIES;
+
         /*non-public*/ static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) {
             return new Species_L(mt, lf, argL0);
         }
@@ -234,7 +233,7 @@
         @Override
         /*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
             try {
-                return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
+                return (BoundMethodHandle) BMH_SPECIES.extendWith(L_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg);
             } catch (Throwable ex) {
                 throw uncaughtException(ex);
             }
@@ -242,7 +241,7 @@
         @Override
         /*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
             try {
-                return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
+                return (BoundMethodHandle) BMH_SPECIES.extendWith(I_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg);
             } catch (Throwable ex) {
                 throw uncaughtException(ex);
             }
@@ -250,7 +249,7 @@
         @Override
         /*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
             try {
-                return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
+                return (BoundMethodHandle) BMH_SPECIES.extendWith(J_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg);
             } catch (Throwable ex) {
                 throw uncaughtException(ex);
             }
@@ -258,7 +257,7 @@
         @Override
         /*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
             try {
-                return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
+                return (BoundMethodHandle) BMH_SPECIES.extendWith(F_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg);
             } catch (Throwable ex) {
                 throw uncaughtException(ex);
             }
@@ -266,7 +265,7 @@
         @Override
         /*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
             try {
-                return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
+                return (BoundMethodHandle) BMH_SPECIES.extendWith(D_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg);
             } catch (Throwable ex) {
                 throw uncaughtException(ex);
             }
@@ -277,601 +276,177 @@
     // BMH species meta-data
     //
 
-    /**
-     * Meta-data wrapper for concrete BMH types.
-     * Each BMH type corresponds to a given sequence of basic field types (LIJFD).
-     * The fields are immutable; their values are fully specified at object construction.
-     * Each BMH type supplies an array of getter functions which may be used in lambda forms.
-     * A BMH is constructed by cloning a shorter BMH and adding one or more new field values.
-     * The shortest possible BMH has zero fields; its class is SimpleMethodHandle.
-     * BMH species are not interrelated by subtyping, even though it would appear that
-     * a shorter BMH could serve as a supertype of a longer one which extends it.
-     */
-    static class SpeciesData {
-        private final String                             typeChars;
-        private final BasicType[]                        typeCodes;
-        private final Class<? extends BoundMethodHandle> clazz;
-        // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
-        // Therefore, we need a non-final link in the chain.  Use array elements.
-        @Stable private final MethodHandle[]             constructor;
-        @Stable private final MethodHandle[]             getters;
-        @Stable private final NamedFunction[]            nominalGetters;
-        @Stable private final SpeciesData[]              extensions;
+    /*non-public*/
+    static final class SpeciesData extends ClassSpecializer<BoundMethodHandle, String, SpeciesData>.SpeciesData {
+        // This array is filled in lazily, as new species come into being over time.
+        @Stable final private SpeciesData[] extensions = new SpeciesData[ARG_TYPE_LIMIT];
 
-        /*non-public*/ int fieldCount() {
-            return typeCodes.length;
-        }
-        /*non-public*/ BasicType fieldType(int i) {
-            return typeCodes[i];
-        }
-        /*non-public*/ char fieldTypeChar(int i) {
-            return typeChars.charAt(i);
-        }
-        String fieldSignature() {
-            return typeChars;
-        }
-        public Class<? extends BoundMethodHandle> fieldHolder() {
-            return clazz;
-        }
-        public String toString() {
-            return "SpeciesData<"+fieldSignature()+">";
+        public SpeciesData(Specializer outer, String key) {
+            outer.super(key);
         }
 
-        /**
-         * 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.
-         */
-        NamedFunction getterFunction(int i) {
-            NamedFunction nf = nominalGetters[i];
-            assert(nf.memberDeclaringClassOrNull() == fieldHolder());
-            assert(nf.returnType() == fieldType(i));
-            return nf;
+        @Override
+        protected String deriveClassName() {
+            String typeString = deriveTypeString();
+            if (typeString.isEmpty()) {
+                return SimpleMethodHandle.class.getName();
+            }
+            return BoundMethodHandle.class.getName() + "$Species_" + typeString;
         }
 
-        NamedFunction[] getterFunctions() {
-            return nominalGetters;
+        @Override
+        protected List<Class<?>> deriveFieldTypes(String key) {
+            ArrayList<Class<?>> types = new ArrayList<>(key.length());
+            for (int i = 0; i < key.length(); i++) {
+                types.add(basicType(key.charAt(i)).basicTypeClass());
+            }
+            return types;
         }
 
-        MethodHandle[] getterHandles() { return getters; }
-
-        MethodHandle constructor() {
-            return constructor[0];
+        @Override
+        protected String deriveTypeString() {
+            // (If/when we have to add nominal types, just inherit the more complex default.)
+            return key();
         }
 
-        static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
-
-        SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
-            this.typeChars = types;
-            this.typeCodes = basicTypes(types);
-            this.clazz = clazz;
-            if (!INIT_DONE) {
-                this.constructor = new MethodHandle[1];  // only one ctor
-                this.getters = new MethodHandle[types.length()];
-                this.nominalGetters = new NamedFunction[types.length()];
+        @Override
+        protected MethodHandle deriveTransformHelper(MemberName transform, int whichtm) {
+            if (whichtm == Specializer.TN_COPY_NO_EXTEND) {
+                return factory();
+            } else if (whichtm < ARG_TYPE_LIMIT) {
+                return extendWith((byte) whichtm).factory();
             } else {
-                this.constructor = Factory.makeCtors(clazz, types, null);
-                this.getters = Factory.makeGetters(clazz, types, null);
-                this.nominalGetters = Factory.makeNominalGetters(types, null, this.getters);
-            }
-            this.extensions = new SpeciesData[ARG_TYPE_LIMIT];
-        }
-
-        private void initForBootstrap() {
-            assert(!INIT_DONE);
-            if (constructor() == null) {
-                String types = typeChars;
-                CACHE.put(types, this);
-                Factory.makeCtors(clazz, types, this.constructor);
-                Factory.makeGetters(clazz, types, this.getters);
-                Factory.makeNominalGetters(types, this.nominalGetters, this.getters);
+                throw newInternalError("bad transform");
             }
         }
 
-        private static final ConcurrentMap<String, SpeciesData> CACHE = new ConcurrentHashMap<>();
-        private static final boolean INIT_DONE;  // set after <clinit> finishes...
-
-        SpeciesData extendWith(byte type) {
-            return extendWith(BasicType.basicType(type));
+        @Override
+        protected <X> List<X> deriveTransformHelperArguments(MemberName transform, int whichtm, List<X> args, List<X> fields) {
+            assert(verifyTHAargs(transform, whichtm, args, fields));
+            // The rule is really simple:  Keep the first two arguments
+            // the same, then put in the fields, then put any other argument.
+            args.addAll(2, fields);
+            return args;
         }
 
-        SpeciesData extendWith(BasicType type) {
-            int ord = type.ordinal();
-            SpeciesData d = extensions[ord];
-            if (d != null)  return d;
-            extensions[ord] = d = get(typeChars+type.basicTypeChar());
-            return d;
-        }
-
-        private static SpeciesData get(String types) {
-            return CACHE.computeIfAbsent(types, new Function<String, SpeciesData>() {
-                @Override
-                public SpeciesData apply(String types) {
-                    Class<? extends BoundMethodHandle> bmhcl = Factory.getConcreteBMHClass(types);
-                    // SpeciesData instantiation may throw VirtualMachineError because of
-                    // code cache overflow...
-                    SpeciesData speciesData = new SpeciesData(types, bmhcl);
-                    // CHM.computeIfAbsent ensures only one SpeciesData will be set
-                    // successfully on the concrete BMH class if ever
-                    Factory.setSpeciesDataToConcreteBMHClass(bmhcl, speciesData);
-                    // the concrete BMH class is published via SpeciesData instance
-                    // returned here only after it's SPECIES_DATA field is set
-                    return speciesData;
-                }
-            });
-        }
-
-        /**
-         * This is to be called when assertions are enabled. It checks whether SpeciesData for all of the statically
-         * defined species subclasses of BoundMethodHandle has been added to the SpeciesData cache. See below in the
-         * static initializer for
-         */
-        static boolean speciesDataCachePopulated() {
-            Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
-            for (Class<?> c : rootCls.getDeclaredClasses()) {
-                if (rootCls.isAssignableFrom(c)) {
-                    final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
-                    SpeciesData d = Factory.getSpeciesDataFromConcreteBMHClass(cbmh);
-                    assert(d != null) : cbmh.getName();
-                    assert(d.clazz == cbmh);
-                    assert(CACHE.get(d.typeChars) == d);
-                }
+        private boolean verifyTHAargs(MemberName transform, int whichtm, List<?> args, List<?> fields) {
+            assert(transform == Specializer.BMH_TRANSFORMS.get(whichtm));
+            assert(args.size() == transform.getMethodType().parameterCount());
+            assert(fields.size() == this.fieldCount());
+            final int MH_AND_LF = 2;
+            if (whichtm == Specializer.TN_COPY_NO_EXTEND) {
+                assert(transform.getMethodType().parameterCount() == MH_AND_LF);
+            } else if (whichtm < ARG_TYPE_LIMIT) {
+                assert(transform.getMethodType().parameterCount() == MH_AND_LF+1);
+                final BasicType type = basicType((byte) whichtm);
+                assert(transform.getParameterTypes()[MH_AND_LF] == type.basicTypeClass());
+            } else {
+                return false;
             }
             return true;
         }
 
-        static {
-            // Pre-fill the BMH species-data cache with EMPTY and all BMH's inner subclasses.
-            EMPTY.initForBootstrap();
-            Species_L.SPECIES_DATA.initForBootstrap();
-            // check that all static SpeciesData instances have been initialized
-            assert speciesDataCachePopulated();
-            // Note:  Do not simplify this, because INIT_DONE must not be
-            // a compile-time constant during bootstrapping.
-            INIT_DONE = Boolean.TRUE;
+        /*non-public*/ SpeciesData extendWith(byte typeNum) {
+            SpeciesData sd = extensions[typeNum];
+            if (sd != null)  return sd;
+            sd = SPECIALIZER.findSpecies(key() + BasicType.basicType(typeNum).basicTypeChar());
+            extensions[typeNum] = sd;
+            return sd;
         }
     }
 
-    static SpeciesData getSpeciesData(String types) {
-        return SpeciesData.get(types);
+    /*non-public*/
+    static final Specializer SPECIALIZER = new Specializer();
+    static {
+        SimpleMethodHandle.BMH_SPECIES = BoundMethodHandle.SPECIALIZER.findSpecies("");
+        Species_L.BMH_SPECIES = BoundMethodHandle.SPECIALIZER.findSpecies("L");
     }
 
-    /**
-     * Generation of concrete BMH classes.
-     *
-     * A concrete BMH species is fit for binding a number of values adhering to a
-     * given type pattern. Reference types are erased.
-     *
-     * BMH species are cached by type pattern.
-     *
-     * A BMH species has a number of fields with the concrete (possibly erased) types of
-     * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
-     * which can be included as names in lambda forms.
-     */
-    static class Factory {
+    /*non-public*/
+    static final class Specializer extends ClassSpecializer<BoundMethodHandle, String, SpeciesData> {
 
-        private static final String JLO_SIG  = "Ljava/lang/Object;";
-        private static final String MH       = "java/lang/invoke/MethodHandle";
-        private static final String MH_SIG   = "L"+MH+";";
-        private static final String BMH      = "java/lang/invoke/BoundMethodHandle";
-        private static final String BMH_NAME = "java.lang.invoke.BoundMethodHandle";
-        private static final String BMH_SIG  = "L"+BMH+";";
-        private static final String SPECIES_DATA     = "java/lang/invoke/BoundMethodHandle$SpeciesData";
-        private static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
-        private static final String STABLE_SIG       = "Ljdk/internal/vm/annotation/Stable;";
+        private static final MemberName SPECIES_DATA_ACCESSOR;
 
-        private static final String SPECIES_PREFIX_NAME = "Species_";
-        private static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
-        private static final String SPECIES_CLASS_PREFIX = BMH_NAME + "$" + SPECIES_PREFIX_NAME;
+        static {
+            try {
+                SPECIES_DATA_ACCESSOR = IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BoundMethodHandle.class,
+                        "speciesData", MethodType.methodType(BoundMethodHandle.SpeciesData.class));
+            } catch (ReflectiveOperationException ex) {
+                throw newInternalError("Bootstrap link error", ex);
+            }
+        }
 
-        private static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
-        private static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
-        private static final String INT_SIG    = "()I";
+        private Specializer() {
+            super(  // Reified type parameters:
+                    BoundMethodHandle.class, String.class, BoundMethodHandle.SpeciesData.class,
+                    // Principal constructor type:
+                    MethodType.methodType(void.class, MethodType.class, LambdaForm.class),
+                    // Required linkage between class and species:
+                    SPECIES_DATA_ACCESSOR,
+                    "BMH_SPECIES",
+                    BMH_TRANSFORMS);
+        }
 
-        private static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
+        @Override
+        protected String topSpeciesKey() {
+            return "";
+        }
 
-        private static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
+        @Override
+        protected BoundMethodHandle.SpeciesData newSpeciesData(String key) {
+            return new BoundMethodHandle.SpeciesData(this, key);
+        }
 
-        private static final ConcurrentMap<String, Class<? extends BoundMethodHandle>> CLASS_CACHE = new ConcurrentHashMap<>();
+        static final List<MemberName> BMH_TRANSFORMS;
+        static final int TN_COPY_NO_EXTEND = V_TYPE_NUM;
+        static {
+            final Class<BoundMethodHandle> BMH = BoundMethodHandle.class;
+            // copyWithExtendLIJFD + copyWith
+            try {
+                BMH_TRANSFORMS = List.of(
+                        IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendL", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, Object.class)),
+                        IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendI", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, int.class)),
+                        IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendJ", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, long.class)),
+                        IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendF", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, float.class)),
+                        IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendD", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, double.class)),
+                        IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWith", MethodType.methodType(BMH, MethodType.class, LambdaForm.class))
+                );
+            } catch (ReflectiveOperationException ex) {
+                throw newInternalError("Failed resolving copyWith methods", ex);
+            }
 
-        /**
-         * Get a concrete subclass of BMH for a given combination of bound types.
-         *
-         * @param types the type signature, wherein reference types are erased to 'L'
-         * @return the concrete BMH class
-         */
-        static Class<? extends BoundMethodHandle> getConcreteBMHClass(String types) {
-            // CHM.computeIfAbsent ensures generateConcreteBMHClass is called
-            // only once per key.
-            return CLASS_CACHE.computeIfAbsent(
-                types, new Function<String, Class<? extends BoundMethodHandle>>() {
-                    @Override
-                    public Class<? extends BoundMethodHandle> apply(String types) {
-                        String shortTypes = LambdaForm.shortenSignature(types);
-                        String className = SPECIES_CLASS_PREFIX + shortTypes;
-                        Class<?> c = BootLoader.loadClassOrNull(className);
-                        if (TRACE_RESOLVE) {
-                            System.out.println("[BMH_RESOLVE] " + shortTypes +
-                                    (c != null ? " (success)" : " (fail)") );
-                        }
-                        if (c != null) {
-                            return c.asSubclass(BoundMethodHandle.class);
-                        } else {
-                            // Not pregenerated, generate the class
-                            return generateConcreteBMHClass(shortTypes, types);
-                        }
-                    }
-                });
+            // as it happens, there is one transform per BasicType including V_TYPE
+            assert(BMH_TRANSFORMS.size() == TYPE_LIMIT);
         }
 
         /**
-         * Generate a concrete subclass of BMH for a given combination of bound types.
+         * Generation of concrete BMH classes.
          *
-         * A concrete BMH species adheres to the following schema:
+         * A concrete BMH species is fit for binding a number of values adhering to a
+         * given type pattern. Reference types are erased.
          *
-         * <pre>
-         * class Species_[[types]] extends BoundMethodHandle {
-         *     [[fields]]
-         *     final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); }
-         * }
-         * </pre>
+         * BMH species are cached by type pattern.
          *
-         * The {@code [[types]]} signature is precisely the string that is passed to this
-         * method.
-         *
-         * The {@code [[fields]]} section consists of one field definition per character in
-         * the type signature, adhering to the naming schema described in the definition of
-         * {@link #makeFieldName}.
-         *
-         * For example, a concrete BMH species for two reference and one integral bound values
-         * would have the following shape:
-         *
-         * <pre>
-         * class BoundMethodHandle { ... private static
-         * final class Species_LLI extends BoundMethodHandle {
-         *     final Object argL0;
-         *     final Object argL1;
-         *     final int argI2;
-         *     private Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
-         *         super(mt, lf);
-         *         this.argL0 = argL0;
-         *         this.argL1 = argL1;
-         *         this.argI2 = argI2;
-         *     }
-         *     final SpeciesData speciesData() { return SPECIES_DATA; }
-         *     final int fieldCount() { return 3; }
-         *     &#64;Stable static SpeciesData SPECIES_DATA; // injected afterwards
-         *     static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
-         *         return new Species_LLI(mt, lf, argL0, argL1, argI2);
-         *     }
-         *     final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
-         *         return new Species_LLI(mt, lf, argL0, argL1, argI2);
-         *     }
-         *     final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
-         *         return SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         *     final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
-         *         return SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         *     final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
-         *         return SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         *     final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
-         *         return SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         *     public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
-         *         return SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         * }
-         * </pre>
-         *
-         * @param types the type signature, wherein reference types are erased to 'L'
-         * @return the generated concrete BMH class
+         * 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<? extends BoundMethodHandle> generateConcreteBMHClass(String shortTypes,
-                String types) {
-            final String className  = speciesInternalClassName(shortTypes);
-            byte[] classFile = generateConcreteBMHClassBytes(shortTypes, types, className);
-
-            // load class
-            InvokerBytecodeGenerator.maybeDump(className, classFile);
-            Class<? extends BoundMethodHandle> bmhClass =
-                UNSAFE.defineClass(className, classFile, 0, classFile.length,
-                                   BoundMethodHandle.class.getClassLoader(), null)
-                    .asSubclass(BoundMethodHandle.class);
-
-            return bmhClass;
-        }
-
-        static String speciesInternalClassName(String shortTypes) {
-            return SPECIES_PREFIX_PATH + shortTypes;
-        }
-
-        static byte[] generateConcreteBMHClassBytes(final String shortTypes,
-                final String types, final String className) {
-            final String sourceFile = SPECIES_PREFIX_NAME + shortTypes;
-
-            final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
-            final int NOT_ACC_PUBLIC = 0;  // not ACC_PUBLIC
-            cw.visit(V1_6, NOT_ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
-            cw.visitSource(sourceFile, null);
-
-            // emit static types and SPECIES_DATA fields
-            FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null);
-            fw.visitAnnotation(STABLE_SIG, true);
-            fw.visitEnd();
-
-            // emit bound argument fields
-            for (int i = 0; i < types.length(); ++i) {
-                final char t = types.charAt(i);
-                final String fieldName = makeFieldName(types, i);
-                final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
-                cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
-            }
-
-            MethodVisitor mv;
-
-            // emit constructor
-            mv = cw.visitMethod(ACC_PRIVATE, "<init>", makeSignature(types, true), null, null);
-            mv.visitCode();
-            mv.visitVarInsn(ALOAD, 0); // this
-            mv.visitVarInsn(ALOAD, 1); // type
-            mv.visitVarInsn(ALOAD, 2); // form
-
-            mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true), false);
-
-            for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
-                // i counts the arguments, j counts corresponding argument slots
-                char t = types.charAt(i);
-                mv.visitVarInsn(ALOAD, 0);
-                mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3
-                mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t));
-                if (t == 'J' || t == 'D') {
-                    ++j; // adjust argument register access
-                }
-            }
-
-            mv.visitInsn(RETURN);
-            mv.visitMaxs(0, 0);
-            mv.visitEnd();
-
-            // emit implementation of speciesData()
-            mv = cw.visitMethod(NOT_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 implementation of fieldCount()
-            mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "fieldCount", INT_SIG, null, null);
-            mv.visitCode();
-            int fc = types.length();
-            if (fc <= (ICONST_5 - ICONST_0)) {
-                mv.visitInsn(ICONST_0 + fc);
-            } else {
-                mv.visitIntInsn(SIPUSH, fc);
-            }
-            mv.visitInsn(IRETURN);
-            mv.visitMaxs(0, 0);
-            mv.visitEnd();
-            // emit make()  ...factory method wrapping constructor
-            mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_STATIC, "make", makeSignature(types, false), null, null);
-            mv.visitCode();
-            // make instance
-            mv.visitTypeInsn(NEW, className);
-            mv.visitInsn(DUP);
-            // load mt, lf
-            mv.visitVarInsn(ALOAD, 0);  // type
-            mv.visitVarInsn(ALOAD, 1);  // form
-            // load factory method arguments
-            for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
-                // i counts the arguments, j counts corresponding argument slots
-                char t = types.charAt(i);
-                mv.visitVarInsn(typeLoadOp(t), j + 2); // parameters start at 3
-                if (t == 'J' || t == 'D') {
-                    ++j; // adjust argument register access
-                }
-            }
-
-            // finally, invoke the constructor and return
-            mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", makeSignature(types, true), false);
-            mv.visitInsn(ARETURN);
-            mv.visitMaxs(0, 0);
-            mv.visitEnd();
-
-            // emit copyWith()
-            mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWith", makeSignature("", false), null, null);
-            mv.visitCode();
-            // make instance
-            mv.visitTypeInsn(NEW, className);
-            mv.visitInsn(DUP);
-            // load mt, lf
-            mv.visitVarInsn(ALOAD, 1);
-            mv.visitVarInsn(ALOAD, 2);
-            // put fields on the stack
-            emitPushFields(types, className, mv);
-            // finally, invoke the constructor and return
-            mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", makeSignature(types, true), false);
-            mv.visitInsn(ARETURN);
-            mv.visitMaxs(0, 0);
-            mv.visitEnd();
-
-            // for each type, emit copyWithExtendT()
-            for (BasicType type : BasicType.ARG_TYPES) {
-                int ord = type.ordinal();
-                char btChar = type.basicTypeChar();
-                mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + btChar, makeSignature(String.valueOf(btChar), false), null, E_THROWABLE);
-                mv.visitCode();
-                // return SPECIES_DATA.extendWith(t).constructor().invokeBasic(mt, lf, argL0, ..., narg)
-                // obtain constructor
-                mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
-                int iconstInsn = ICONST_0 + ord;
-                assert(iconstInsn <= ICONST_5);
-                mv.visitInsn(iconstInsn);
-                mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG, false);
-                mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "constructor", "()" + MH_SIG, false);
-                // load mt, lf
-                mv.visitVarInsn(ALOAD, 1);
-                mv.visitVarInsn(ALOAD, 2);
-                // put fields on the stack
-                emitPushFields(types, className, mv);
-                // put narg on stack
-                mv.visitVarInsn(typeLoadOp(btChar), 3);
-                // finally, invoke the constructor and return
-                mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + btChar, false), false);
-                mv.visitInsn(ARETURN);
-                mv.visitMaxs(0, 0);
-                mv.visitEnd();
-            }
-
-            cw.visitEnd();
-
-            return cw.toByteArray();
-        }
-
-        private static int typeLoadOp(char t) {
-            switch (t) {
-            case 'L': return ALOAD;
-            case 'I': return ILOAD;
-            case 'J': return LLOAD;
-            case 'F': return FLOAD;
-            case 'D': return DLOAD;
-            default : throw newInternalError("unrecognized type " + t);
+        class Factory extends ClassSpecializer<BoundMethodHandle, String, BoundMethodHandle.SpeciesData>.Factory {
+            @Override
+            protected String chooseFieldName(Class<?> type, int index) {
+                return "arg" + super.chooseFieldName(type, index);
             }
         }
 
-        private static void emitPushFields(String types, String className, MethodVisitor mv) {
-            for (int i = 0; i < types.length(); ++i) {
-                char tc = types.charAt(i);
-                mv.visitVarInsn(ALOAD, 0);
-                mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
-            }
+        @Override
+        protected Factory makeFactory() {
+            return new Factory();
         }
+      }
 
-        static String typeSig(char t) {
-            return t == 'L' ? JLO_SIG : String.valueOf(t);
-        }
-
-        //
-        // Getter MH generation.
-        //
-
-        private static MethodHandle makeGetter(Class<?> cbmhClass, String types, int index) {
-            String fieldName = makeFieldName(types, index);
-            Class<?> fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType();
-            try {
-                return LOOKUP.findGetter(cbmhClass, fieldName, fieldType);
-            } catch (NoSuchFieldException | IllegalAccessException e) {
-                throw newInternalError(e);
-            }
-        }
-
-        static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
-            if (mhs == null)  mhs = new MethodHandle[types.length()];
-            for (int i = 0; i < mhs.length; ++i) {
-                mhs[i] = makeGetter(cbmhClass, types, i);
-                assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
-            }
-            return mhs;
-        }
-
-        static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
-            if (mhs == null)  mhs = new MethodHandle[1];
-            if (types.equals(""))  return mhs;  // hack for empty BMH species
-            mhs[0] = makeCbmhCtor(cbmh, types);
-            return mhs;
-        }
-
-        static NamedFunction[] makeNominalGetters(String types, NamedFunction[] nfs, MethodHandle[] getters) {
-            if (nfs == null)  nfs = new NamedFunction[types.length()];
-            for (int i = 0; i < nfs.length; ++i) {
-                nfs[i] = new NamedFunction(getters[i]);
-            }
-            return nfs;
-        }
-
-        //
-        // Auxiliary methods.
-        //
-
-        static SpeciesData getSpeciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
-            try {
-                Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
-                return (SpeciesData) F_SPECIES_DATA.get(null);
-            } catch (ReflectiveOperationException ex) {
-                throw newInternalError(ex);
-            }
-        }
-
-        static void setSpeciesDataToConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh, SpeciesData speciesData) {
-            try {
-                Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
-                // ## FIXME: annotation parser can't create proxy classes until module system is fully initialzed
-                // assert F_SPECIES_DATA.getDeclaredAnnotation(Stable.class) != null;
-                F_SPECIES_DATA.set(null, speciesData);
-            } catch (ReflectiveOperationException ex) {
-                throw newInternalError(ex);
-            }
-        }
-
-        /**
-         * Field names in concrete BMHs adhere to this pattern:
-         * arg + type + index
-         * where type is a single character (L, I, J, F, D).
-         */
-        private static String makeFieldName(String types, int index) {
-            assert index >= 0 && index < types.length();
-            return "arg" + types.charAt(index) + index;
-        }
-
-        private static String makeSignature(String types, boolean ctor) {
-            StringBuilder buf = new StringBuilder(SIG_INCIPIT);
-            int len = types.length();
-            for (int i = 0; i < len; i++) {
-                buf.append(typeSig(types.charAt(i)));
-            }
-            return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
-        }
-
-        private static MethodType makeConstructorType(String types) {
-            int length = types.length();
-            Class<?> ptypes[] = new Class<?>[length + 2];
-            ptypes[0] = MethodType.class;
-            ptypes[1] = LambdaForm.class;
-            for (int i = 0; i < length; i++) {
-                ptypes[i + 2] = BasicType.basicType(types.charAt(i)).basicTypeClass();
-            }
-            return MethodType.makeImpl(BoundMethodHandle.class, ptypes, true);
-        }
-
-        static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
-            try {
-                return LOOKUP.findStatic(cbmh, "make", makeConstructorType(types));
-            } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
-                throw newInternalError(e);
-            }
-        }
-    }
-
-    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;
-
-    private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[6];
-    private static SpeciesData checkCache(int size, String types) {
-        int idx = size - 1;
-        SpeciesData data = SPECIES_DATA_CACHE[idx];
-        if (data != null)  return data;
-        SPECIES_DATA_CACHE[idx] = data = getSpeciesData(types);
-        return data;
-    }
-    static SpeciesData speciesData_L()      { return checkCache(1, "L"); }
-    static SpeciesData speciesData_LL()     { return checkCache(2, "LL"); }
-    static SpeciesData speciesData_LLL()    { return checkCache(3, "LLL"); }
-    static SpeciesData speciesData_LLLL()   { return checkCache(4, "LLLL"); }
-    static SpeciesData speciesData_LLLLL()  { return checkCache(5, "LLLLL"); }
+    static SpeciesData speciesData_L()      { return Species_L.BMH_SPECIES; }
+    static SpeciesData speciesData_LL()     { return SPECIALIZER.findSpecies("LL"); }
+    static SpeciesData speciesData_LLL()    { return SPECIALIZER.findSpecies("LLL"); }
+    static SpeciesData speciesData_LLLL()   { return SPECIALIZER.findSpecies("LLLL"); }
+    static SpeciesData speciesData_LLLLL()  { return SPECIALIZER.findSpecies("LLLLL"); }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java	Thu Nov 16 00:58:50 2017 +0100
@@ -0,0 +1,1030 @@
+/*
+ * Copyright (c) 2017, 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 jdk.internal.loader.BootLoader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.vm.annotation.Stable;
+import sun.invoke.util.BytecodeName;
+
+import java.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
+import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic;
+import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+/**
+ * Class specialization code.
+ * @param <T> top class under which species classes are created.
+ * @param <K> key which identifies individual specializations.
+ * @param <S> species data type.
+ */
+/*non-public*/
+abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesData> {
+    private final Class<T> topClass;
+    private final Class<K> keyType;
+    private final Class<S> metaType;
+    private final MemberName sdAccessor;
+    private final String sdFieldName;
+    private final List<MemberName> transformMethods;
+    private final MethodType baseConstructorType;
+    private final S topSpecies;
+    private final ConcurrentMap<K, S> cache = new ConcurrentHashMap<>();
+    private final Factory factory;
+    private @Stable boolean topClassIsSuper;
+
+    /** Return the top type mirror, for type {@code T} */
+    public final Class<T> topClass() { return topClass; }
+
+    /** Return the key type mirror, for type {@code K} */
+    public final Class<K> keyType() { return keyType; }
+
+    /** Return the species metadata type mirror, for type {@code S} */
+    public final Class<S> metaType() { return metaType; }
+
+    /** Report the leading arguments (if any) required by every species factory.
+     * Every species factory adds its own field types as additional arguments,
+     * but these arguments always come first, in every factory method.
+     */
+    protected MethodType baseConstructorType() { return baseConstructorType; }
+
+    /** Return the trivial species for the null sequence of arguments. */
+    protected final S topSpecies() { return topSpecies; }
+
+    /** Return the list of transform methods originally given at creation of this specializer. */
+    protected final List<MemberName> transformMethods() { return transformMethods; }
+
+    /** Return the factory object used to build and load concrete species code. */
+    protected final Factory factory() { return factory; }
+
+    /**
+     * Constructor for this class specializer.
+     * @param topClass type mirror for T
+     * @param keyType type mirror for K
+     * @param metaType type mirror for S
+     * @param baseConstructorType principal constructor type
+     * @param sdAccessor the method used to get the speciesData
+     * @param sdFieldName the name of the species data field, inject the speciesData object
+     * @param transformMethods optional list of transformMethods
+     */
+    protected ClassSpecializer(Class<T> topClass,
+                               Class<K> keyType,
+                               Class<S> metaType,
+                               MethodType baseConstructorType,
+                               MemberName sdAccessor,
+                               String sdFieldName,
+                               List<MemberName> transformMethods) {
+        this.topClass = topClass;
+        this.keyType = keyType;
+        this.metaType = metaType;
+        this.sdAccessor = sdAccessor;
+        // FIXME: use List.copyOf once 8177290 is in
+        this.transformMethods = List.of(transformMethods.toArray(new MemberName[transformMethods.size()]));
+        this.sdFieldName = sdFieldName;
+        this.baseConstructorType = baseConstructorType.changeReturnType(void.class);
+        this.factory = makeFactory();
+        K tsk = topSpeciesKey();
+        S topSpecies = null;
+        if (tsk != null && topSpecies == null) {
+            // if there is a key, build the top species if needed:
+            topSpecies = findSpecies(tsk);
+        }
+        this.topSpecies = topSpecies;
+    }
+
+    // Utilities for subclass constructors:
+    protected static <T> Constructor<T> reflectConstructor(Class<T> defc, Class<?>... ptypes) {
+        try {
+            return defc.getDeclaredConstructor(ptypes);
+        } catch (NoSuchMethodException ex) {
+            throw newIAE(defc.getName()+"("+MethodType.methodType(void.class, ptypes)+")", ex);
+        }
+    }
+
+    protected static Field reflectField(Class<?> defc, String name) {
+        try {
+            return defc.getDeclaredField(name);
+        } catch (NoSuchFieldException ex) {
+            throw newIAE(defc.getName()+"."+name, ex);
+        }
+    }
+
+    private static RuntimeException newIAE(String message, Throwable cause) {
+        return new IllegalArgumentException(message, cause);
+    }
+
+    public final S findSpecies(K key) {
+        S speciesData = cache.computeIfAbsent(key, new Function<>() {
+            @Override
+            public S apply(K key1) {
+                return factory.loadSpecies(newSpeciesData(key1));
+            }
+        });
+        // Note:  Species instantiation may throw VirtualMachineError because of
+        // code cache overflow.  If this happens the species bytecode may be
+        // loaded but not linked to its species metadata (with MH's etc).
+        // That will cause a throw out of CHM.computeIfAbsent,
+        // which will shut down the caller thread.
+        //
+        // In a latter attempt to get the same species, the already-loaded
+        // class will be present in the system dictionary, causing an
+        // error when the species generator tries to reload it.
+        // We try to detect this case and link the pre-existing code.
+        //
+        // Although it would be better to start fresh by loading a new
+        // copy, we have to salvage the previously loaded but broken code.
+        // (As an alternative, we might spin a new class with a new name,
+        // or use the anonymous class mechanism.)
+        //
+        // In the end, as long as everybody goes through the same CHM,
+        // CHM.computeIfAbsent will ensure only one SpeciesData will be set
+        // successfully on a concrete class if ever.
+        // The concrete class is published via SpeciesData instance
+        // returned here only after the class and species data are linked together.
+        assert(speciesData != null);
+        return speciesData;
+    }
+
+    /**
+     * Meta-data wrapper for concrete subtypes of the top class.
+     * Each concrete subtype corresponds to a given sequence of basic field types (LIJFD).
+     * The fields are immutable; their values are fully specified at object construction.
+     * Each species supplies an array of getter functions which may be used in lambda forms.
+     * A concrete value is always constructed from the full tuple of its field values,
+     * accompanied by the required constructor parameters.
+     * There *may* also be transforms which cloning a species instance and
+     * either replace a constructor parameter or add one or more new field values.
+     * The shortest possible species has zero fields.
+     * Subtypes are not interrelated among themselves by subtyping, even though
+     * it would appear that a shorter species could serve as a supertype of a
+     * longer one which extends it.
+     */
+    public abstract class SpeciesData {
+        // Bootstrapping requires circular relations Class -> SpeciesData -> Class
+        // Therefore, we need non-final links in the chain.  Use @Stable fields.
+        private final K key;
+        private final List<Class<?>> fieldTypes;
+        @Stable private Class<? extends T> speciesCode;
+        @Stable private List<MethodHandle> factories;
+        @Stable private List<MethodHandle> getters;
+        @Stable private List<LambdaForm.NamedFunction> nominalGetters;
+        @Stable private final MethodHandle[] transformHelpers = new MethodHandle[transformMethods.size()];
+
+        protected SpeciesData(K key) {
+            this.key = keyType.cast(Objects.requireNonNull(key));
+            List<Class<?>> types = deriveFieldTypes(key);
+            // TODO: List.copyOf
+            int arity = types.size();
+            this.fieldTypes = List.of(types.toArray(new Class<?>[arity]));
+        }
+
+        public final K key() {
+            return key;
+        }
+
+        protected final List<Class<?>> fieldTypes() {
+            return fieldTypes;
+        }
+
+        protected final int fieldCount() {
+            return fieldTypes.size();
+        }
+
+        protected ClassSpecializer<T,K,S> outer() {
+            return ClassSpecializer.this;
+        }
+
+        protected final boolean isResolved() {
+            return speciesCode != null && factories != null && !factories.isEmpty();
+        }
+
+        @Override public String toString() {
+            return metaType.getSimpleName() + "[" + key.toString() + " => " + (isResolved() ? speciesCode.getSimpleName() : "UNRESOLVED") + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return key.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ClassSpecializer.SpeciesData)) {
+                return false;
+            }
+            @SuppressWarnings("rawtypes")
+            ClassSpecializer.SpeciesData that = (ClassSpecializer.SpeciesData) obj;
+            return this.outer() == that.outer() && this.key.equals(that.key);
+        }
+
+        /** Throws NPE if this species is not yet resolved. */
+        protected final Class<? extends T> speciesCode() {
+            return Objects.requireNonNull(speciesCode);
+        }
+
+        /**
+         * Return a {@link MethodHandle} which can get the indexed field of this species.
+         * The return type is the type of the species field it accesses.
+         * The argument type is the {@code fieldHolder} class of this species.
+         */
+        protected MethodHandle getter(int i) {
+            return getters.get(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.
+         */
+        protected LambdaForm.NamedFunction getterFunction(int i) {
+            LambdaForm.NamedFunction nf = nominalGetters.get(i);
+            assert(nf.memberDeclaringClassOrNull() == speciesCode());
+            assert(nf.returnType() == BasicType.basicType(fieldTypes.get(i)));
+            return nf;
+        }
+
+        protected List<LambdaForm.NamedFunction> getterFunctions() {
+            return nominalGetters;
+        }
+
+        protected List<MethodHandle> getters() {
+            return getters;
+        }
+
+        protected MethodHandle factory() {
+            return factories.get(0);
+        }
+
+        protected MethodHandle transformHelper(int whichtm) {
+            MethodHandle mh = transformHelpers[whichtm];
+            if (mh != null)  return mh;
+            mh = deriveTransformHelper(transformMethods().get(whichtm), whichtm);
+            // Do a little type checking before we start using the MH.
+            // (It will be called with invokeBasic, so this is our only chance.)
+            final MethodType mt = transformHelperType(whichtm);
+            mh = mh.asType(mt);
+            return transformHelpers[whichtm] = mh;
+        }
+
+        private final MethodType transformHelperType(int whichtm) {
+            MemberName tm = transformMethods().get(whichtm);
+            ArrayList<Class<?>> args = new ArrayList<>();
+            ArrayList<Class<?>> fields = new ArrayList<>();
+            Collections.addAll(args, tm.getParameterTypes());
+            fields.addAll(fieldTypes());
+            List<Class<?>> helperArgs = deriveTransformHelperArguments(tm, whichtm, args, fields);
+            return MethodType.methodType(tm.getReturnType(), helperArgs);
+        }
+
+        // Hooks for subclasses:
+
+        /**
+         * Given a key, derive the list of field types, which all instances of this
+         * species must store.
+         */
+        protected abstract List<Class<?>> deriveFieldTypes(K key);
+
+        /**
+         * Given the index of a method in the transforms list, supply a factory
+         * method that takes the arguments of the transform, plus the local fields,
+         * and produce a value of the required type.
+         * You can override this to return null or throw if there are no transforms.
+         * This method exists so that the transforms can be "grown" lazily.
+         * This is necessary if the transform *adds* a field to an instance,
+         * which sometimtes requires the creation, on the fly, of an extended species.
+         * This method is only called once for any particular parameter.
+         * The species caches the result in a private array.
+         *
+         * @param transform the transform being implemented
+         * @param whichtm the index of that transform in the original list of transforms
+         * @return the method handle which creates a new result from a mix of transform
+         * arguments and field values
+         */
+        protected abstract MethodHandle deriveTransformHelper(MemberName transform, int whichtm);
+
+        /**
+         * During code generation, this method is called once per transform to determine
+         * what is the mix of arguments to hand to the transform-helper.  The bytecode
+         * which marshals these arguments is open-coded in the species-specific transform.
+         * The two lists are of opaque objects, which you shouldn't do anything with besides
+         * reordering them into the output list.  (They are both mutable, to make editing
+         * easier.)  The imputed types of the args correspond to the transform's parameter
+         * list, while the imputed types of the fields correspond to the species field types.
+         * After code generation, this method may be called occasionally by error-checking code.
+         *
+         * @param transform the transform being implemented
+         * @param whichtm the index of that transform in the original list of transforms
+         * @param args a list of opaque objects representing the incoming transform arguments
+         * @param fields a list of opaque objects representing the field values of the receiver
+         * @param <X> the common element type of the various lists
+         * @return a new list
+         */
+        protected abstract <X> List<X> deriveTransformHelperArguments(MemberName transform, int whichtm,
+                                                                      List<X> args, List<X> fields);
+
+        /** Given a key, generate the name of the class which implements the species for that key.
+         * This algorithm must be stable.
+         *
+         * @return class name, which by default is {@code outer().topClass().getName() + "$Species_" + deriveTypeString(key)}
+         */
+        protected String deriveClassName() {
+            return outer().topClass().getName() + "$Species_" + deriveTypeString();
+        }
+
+        /**
+         * Default implementation collects basic type characters,
+         * plus possibly type names, if some types don't correspond
+         * to basic types.
+         *
+         * @return a string suitable for use in a class name
+         */
+        protected String deriveTypeString() {
+            List<Class<?>> types = fieldTypes();
+            StringBuilder buf = new StringBuilder();
+            StringBuilder end = new StringBuilder();
+            for (Class<?> type : types) {
+                BasicType basicType = BasicType.basicType(type);
+                if (basicType.basicTypeClass() == type) {
+                    buf.append(basicType.basicTypeChar());
+                } else {
+                    buf.append('V');
+                    end.append(classSig(type));
+                }
+            }
+            String typeString;
+            if (end.length() > 0) {
+                typeString = BytecodeName.toBytecodeName(buf.append("_").append(end).toString());
+            } else {
+                typeString = buf.toString();
+            }
+            return LambdaForm.shortenSignature(typeString);
+        }
+
+        /**
+         * Report what immediate super-class to use for the concrete class of this species.
+         * Normally this is {@code topClass}, but if that is an interface, the factory must override.
+         * The super-class must provide a constructor which takes the {@code baseConstructorType} arguments, if any.
+         * This hook also allows the code generator to use more than one canned supertype for species.
+         *
+         * @return the super-class of the class to be generated
+         */
+        protected Class<? extends T> deriveSuperClass() {
+            final Class<T> topc = topClass();
+            if (!topClassIsSuper) {
+                try {
+                    final Constructor<T> con = reflectConstructor(topc, baseConstructorType().parameterArray());
+                    if (!topc.isInterface() && !Modifier.isPrivate(con.getModifiers())) {
+                        topClassIsSuper = true;
+                    }
+                } catch (Exception|InternalError ex) {
+                    // fall through...
+                }
+                if (!topClassIsSuper) {
+                    throw newInternalError("must override if the top class cannot serve as a super class");
+                }
+            }
+            return topc;
+        }
+    }
+
+    protected abstract S newSpeciesData(K key);
+
+    protected K topSpeciesKey() {
+        return null;  // null means don't report a top species
+    }
+
+    /**
+     * Code generation support for instances.
+     * Subclasses can modify the behavior.
+     */
+    public class Factory {
+        /**
+         * Get a concrete subclass of the top class for a given combination of bound types.
+         *
+         * @param speciesData the species requiring the class, not yet linked
+         * @return a linked version of the same species
+         */
+        S loadSpecies(S speciesData) {
+            String className = speciesData.deriveClassName();
+            assert(className.indexOf('/') < 0) : className;
+            Class<?> salvage = null;
+            try {
+                salvage = BootLoader.loadClassOrNull(className);
+                if (TRACE_RESOLVE && salvage != null) {
+                    // Used by jlink species pregeneration plugin, see
+                    // jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin
+                    System.out.println("[SPECIES_RESOLVE] " + className + " (salvaged)");
+                }
+            } catch (Error ex) {
+                if (TRACE_RESOLVE) {
+                    System.out.println("[SPECIES_FRESOLVE] " + className + " (Error) " + ex.getMessage());
+                }
+            }
+            final Class<? extends T> speciesCode;
+            if (salvage != null) {
+                speciesCode = salvage.asSubclass(topClass());
+                factory.linkSpeciesDataToCode(speciesData, speciesCode);
+                factory.linkCodeToSpeciesData(speciesCode, speciesData, true);
+            } else {
+                // Not pregenerated, generate the class
+                try {
+                    speciesCode = generateConcreteSpeciesCode(className, speciesData);
+                    if (TRACE_RESOLVE) {
+                        // Used by jlink species pregeneration plugin, see
+                        // jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin
+                        System.out.println("[SPECIES_RESOLVE] " + className + " (generated)");
+                    }
+                    // This operation causes a lot of churn:
+                    linkSpeciesDataToCode(speciesData, speciesCode);
+                    // This operation commits the relation, but causes little churn:
+                    linkCodeToSpeciesData(speciesCode, speciesData, false);
+                } catch (Error ex) {
+                    if (TRACE_RESOLVE) {
+                        System.out.println("[SPECIES_RESOLVE] " + className + " (Error #2)" );
+                    }
+                    // We can get here if there is a race condition loading a class.
+                    // Or maybe we are out of resources.  Back out of the CHM.get and retry.
+                    throw ex;
+                }
+            }
+
+            if (!speciesData.isResolved()) {
+                throw newInternalError("bad species class linkage for " + className + ": " + speciesData);
+            }
+            assert(speciesData == factory.loadSpeciesDataFromCode(speciesCode));
+            return speciesData;
+        }
+
+        /**
+         * Generate a concrete subclass of the top class for a given combination of bound types.
+         *
+         * A concrete species subclass roughly matches the following schema:
+         *
+         * <pre>
+         * class Species_[[types]] extends [[T]] {
+         *     final [[S]] speciesData() { return ... }
+         *     static [[T]] make([[fields]]) { return ... }
+         *     [[fields]]
+         *     final [[T]] transform([[args]]) { return ... }
+         * }
+         * </pre>
+         *
+         * The {@code [[types]]} signature is precisely the key for the species.
+         *
+         * The {@code [[fields]]} section consists of one field definition per character in
+         * the type signature, adhering to the naming schema described in the definition of
+         * {@link #chooseFieldName}.
+         *
+         * For example, a concrete species for two references and one integral bound value
+         * has a shape like the following:
+         *
+         * <pre>
+         * class TopClass { ... private static
+         * final class Species_LLI extends TopClass {
+         *     final Object argL0;
+         *     final Object argL1;
+         *     final int argI2;
+         *     private Species_LLI(CT ctarg, ..., Object argL0, Object argL1, int argI2) {
+         *         super(ctarg, ...);
+         *         this.argL0 = argL0;
+         *         this.argL1 = argL1;
+         *         this.argI2 = argI2;
+         *     }
+         *     final SpeciesData speciesData() { return BMH_SPECIES; }
+         *     &#64;Stable static SpeciesData BMH_SPECIES; // injected afterwards
+         *     static TopClass make(CT ctarg, ..., Object argL0, Object argL1, int argI2) {
+         *         return new Species_LLI(ctarg, ..., argL0, argL1, argI2);
+         *     }
+         *     final TopClass copyWith(CT ctarg, ...) {
+         *         return new Species_LLI(ctarg, ..., argL0, argL1, argI2);
+         *     }
+         *     // two transforms, for the sake of illustration:
+         *     final TopClass copyWithExtendL(CT ctarg, ..., Object narg) {
+         *         return BMH_SPECIES.transform(L_TYPE).invokeBasic(ctarg, ..., argL0, argL1, argI2, narg);
+         *     }
+         *     final TopClass copyWithExtendI(CT ctarg, ..., int narg) {
+         *         return BMH_SPECIES.transform(I_TYPE).invokeBasic(ctarg, ..., argL0, argL1, argI2, narg);
+         *     }
+         * }
+         * </pre>
+         *
+         * @param className of the species
+         * @param speciesData what species we are generating
+         * @return the generated concrete TopClass class
+         */
+        Class<? extends T> generateConcreteSpeciesCode(String className, ClassSpecializer<T,K,S>.SpeciesData speciesData) {
+            byte[] classFile = generateConcreteSpeciesCodeFile(className, speciesData);
+
+            // load class
+            InvokerBytecodeGenerator.maybeDump(classBCName(className), classFile);
+            Class<?> speciesCode;
+
+            ClassLoader cl = topClass().getClassLoader();
+            ProtectionDomain pd = null;
+            if (cl != null) {
+                pd = AccessController.doPrivileged(
+                        new PrivilegedAction<>() {
+                            @Override
+                            public ProtectionDomain run() {
+                                return topClass().getProtectionDomain();
+                            }
+                        });
+            }
+            try {
+                speciesCode = UNSAFE.defineClass(className, classFile, 0, classFile.length, cl, pd);
+            } catch (Exception ex) {
+                throw newInternalError(ex);
+            }
+
+            return speciesCode.asSubclass(topClass());
+        }
+
+        // These are named like constants because there is only one per specialization scheme:
+        private final String SPECIES_DATA = classBCName(metaType);
+        private final String SPECIES_DATA_SIG = classSig(SPECIES_DATA);
+        private final String SPECIES_DATA_NAME = sdAccessor.getName();
+        private final int SPECIES_DATA_MODS = sdAccessor.getModifiers();
+        private final List<String> TRANSFORM_NAMES;  // derived from transformMethods
+        private final List<MethodType> TRANSFORM_TYPES;
+        private final List<Integer> TRANSFORM_MODS;
+        {
+            // Tear apart transformMethods to get the names, types, and modifiers.
+            List<String> tns = new ArrayList<>();
+            List<MethodType> tts = new ArrayList<>();
+            List<Integer> tms = new ArrayList<>();
+            for (int i = 0; i < transformMethods.size(); i++) {
+                MemberName tm = transformMethods.get(i);
+                tns.add(tm.getName());
+                final MethodType tt = tm.getMethodType();
+                tts.add(tt);
+                tms.add(tm.getModifiers());
+            }
+            TRANSFORM_NAMES = List.of(tns.toArray(new String[0]));
+            TRANSFORM_TYPES = List.of(tts.toArray(new MethodType[0]));
+            TRANSFORM_MODS = List.of(tms.toArray(new Integer[0]));
+        }
+        private static final int ACC_PPP = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
+
+        /*non-public*/ byte[] generateConcreteSpeciesCodeFile(String className0, ClassSpecializer<T,K,S>.SpeciesData speciesData) {
+            final String className = classBCName(className0);
+            final String superClassName = classBCName(speciesData.deriveSuperClass());
+
+            final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+            final int NOT_ACC_PUBLIC = 0;  // not ACC_PUBLIC
+            cw.visit(V1_6, NOT_ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, superClassName, null);
+
+            final String sourceFile = className.substring(className.lastIndexOf('.')+1);
+            cw.visitSource(sourceFile, null);
+
+            // emit static types and BMH_SPECIES fields
+            FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, sdFieldName, SPECIES_DATA_SIG, null, null);
+            fw.visitAnnotation(STABLE_SIG, true);
+            fw.visitEnd();
+
+            // handy holder for dealing with groups of typed values (ctor arguments and fields)
+            class Var {
+                final int index;
+                final String name;
+                final Class<?> type;
+                final String desc;
+                final BasicType basicType;
+                final int slotIndex;
+                Var(int index, int slotIndex) {
+                    this.index = index;
+                    this.slotIndex = slotIndex;
+                    name = null; type = null; desc = null;
+                    basicType = BasicType.V_TYPE;
+                }
+                Var(String name, Class<?> type, Var prev) {
+                    int slotIndex = prev.nextSlotIndex();
+                    int index = prev.nextIndex();
+                    if (name == null)  name = "x";
+                    if (name.endsWith("#"))
+                        name = name.substring(0, name.length()-1) + index;
+                    assert(!type.equals(void.class));
+                    String desc = classSig(type);
+                    BasicType basicType = BasicType.basicType(type);
+                    this.index = index;
+                    this.name = name;
+                    this.type = type;
+                    this.desc = desc;
+                    this.basicType = basicType;
+                    this.slotIndex = slotIndex;
+                }
+                Var lastOf(List<Var> vars) {
+                    int n = vars.size();
+                    return (n == 0 ? this : vars.get(n-1));
+                }
+                <X> List<Var> fromTypes(List<X> types) {
+                    Var prev = this;
+                    ArrayList<Var> result = new ArrayList<>(types.size());
+                    int i = 0;
+                    for (X x : types) {
+                        String vn = name;
+                        Class<?> vt;
+                        if (x instanceof Class) {
+                            vt = (Class<?>) x;
+                            // make the names friendlier if debugging
+                            assert((vn = vn + "_" + (i++)) != null);
+                        } else {
+                            @SuppressWarnings("unchecked")
+                            Var v = (Var) x;
+                            vn = v.name;
+                            vt = v.type;
+                        }
+                        prev = new Var(vn, vt, prev);
+                        result.add(prev);
+                    }
+                    return result;
+                }
+
+                int slotSize() { return basicType.basicTypeSlots(); }
+                int nextIndex() { return index + (slotSize() == 0 ? 0 : 1); }
+                int nextSlotIndex() { return slotIndex >= 0 ? slotIndex + slotSize() : slotIndex; }
+                boolean isInHeap() { return slotIndex < 0; }
+                void emitVarInstruction(int asmop, MethodVisitor mv) {
+                    if (asmop == ALOAD)
+                        asmop = typeLoadOp(basicType.basicTypeChar());
+                    else
+                        throw new AssertionError("bad op="+asmop+" for desc="+desc);
+                    mv.visitVarInsn(asmop, slotIndex);
+                }
+                public void emitFieldInsn(int asmop, MethodVisitor mv) {
+                    mv.visitFieldInsn(asmop, className, name, desc);
+                }
+            }
+
+            final Var NO_THIS = new Var(0, 0),
+                    AFTER_THIS = new Var(0, 1),
+                    IN_HEAP = new Var(0, -1);
+
+            // figure out the field types
+            final List<Class<?>> fieldTypes = speciesData.fieldTypes();
+            final List<Var> fields = new ArrayList<>(fieldTypes.size());
+            {
+                Var nextF = IN_HEAP;
+                for (Class<?> ft : fieldTypes) {
+                    String fn = chooseFieldName(ft, nextF.nextIndex());
+                    nextF = new Var(fn, ft, nextF);
+                    fields.add(nextF);
+                }
+            }
+
+            // emit bound argument fields
+            for (Var field : fields) {
+                cw.visitField(ACC_FINAL, field.name, field.desc, null, null).visitEnd();
+            }
+
+            MethodVisitor mv;
+
+            // emit implementation of speciesData()
+            mv = cw.visitMethod((SPECIES_DATA_MODS & ACC_PPP) + ACC_FINAL,
+                    SPECIES_DATA_NAME, "()" + SPECIES_DATA_SIG, null, null);
+            mv.visitCode();
+            mv.visitFieldInsn(GETSTATIC, className, sdFieldName, SPECIES_DATA_SIG);
+            mv.visitInsn(ARETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+
+            // figure out the constructor arguments
+            MethodType superCtorType = ClassSpecializer.this.baseConstructorType();
+            MethodType thisCtorType = superCtorType.appendParameterTypes(fieldTypes);
+
+            // emit constructor
+            {
+                mv = cw.visitMethod(ACC_PRIVATE,
+                        "<init>", methodSig(thisCtorType), null, null);
+                mv.visitCode();
+                mv.visitVarInsn(ALOAD, 0); // this
+
+                final List<Var> ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList());
+                for (Var ca : ctorArgs) {
+                    ca.emitVarInstruction(ALOAD, mv);
+                }
+
+                // super(ca...)
+                mv.visitMethodInsn(INVOKESPECIAL, superClassName,
+                        "<init>", methodSig(superCtorType), false);
+
+                // store down fields
+                Var lastFV = AFTER_THIS.lastOf(ctorArgs);
+                for (Var f : fields) {
+                    // this.argL1 = argL1
+                    mv.visitVarInsn(ALOAD, 0);  // this
+                    lastFV = new Var(f.name, f.type, lastFV);
+                    lastFV.emitVarInstruction(ALOAD, mv);
+                    f.emitFieldInsn(PUTFIELD, mv);
+                }
+
+                mv.visitInsn(RETURN);
+                mv.visitMaxs(0, 0);
+                mv.visitEnd();
+            }
+
+            // emit make()  ...factory method wrapping constructor
+            {
+                MethodType ftryType = thisCtorType.changeReturnType(topClass());
+                mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_STATIC,
+                        "make", methodSig(ftryType), null, null);
+                mv.visitCode();
+                // make instance
+                mv.visitTypeInsn(NEW, className);
+                mv.visitInsn(DUP);
+                // load factory method arguments:  ctarg... and arg...
+                for (Var v : NO_THIS.fromTypes(ftryType.parameterList())) {
+                    v.emitVarInstruction(ALOAD, mv);
+                }
+
+                // finally, invoke the constructor and return
+                mv.visitMethodInsn(INVOKESPECIAL, className,
+                        "<init>", methodSig(thisCtorType), false);
+                mv.visitInsn(ARETURN);
+                mv.visitMaxs(0, 0);
+                mv.visitEnd();
+            }
+
+            // For each transform, emit the customized override of the transform method.
+            // This method mixes together some incoming arguments (from the transform's
+            // static type signature) with the field types themselves, and passes
+            // the resulting mish-mosh of values to a method handle produced by
+            // the species itself.  (Typically this method handle is the factory
+            // method of this species or a related one.)
+            for (int whichtm = 0; whichtm < TRANSFORM_NAMES.size(); whichtm++) {
+                final String     TNAME = TRANSFORM_NAMES.get(whichtm);
+                final MethodType TTYPE = TRANSFORM_TYPES.get(whichtm);
+                final int        TMODS = TRANSFORM_MODS.get(whichtm);
+                mv = cw.visitMethod((TMODS & ACC_PPP) | ACC_FINAL,
+                        TNAME, TTYPE.toMethodDescriptorString(), null, E_THROWABLE);
+                mv.visitCode();
+                // return a call to the corresponding "transform helper", something like this:
+                //   MY_SPECIES.transformHelper(whichtm).invokeBasic(ctarg, ..., argL0, ..., xarg)
+                mv.visitFieldInsn(GETSTATIC, className,
+                        sdFieldName, SPECIES_DATA_SIG);
+                emitIntConstant(whichtm, mv);
+                mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA,
+                        "transformHelper", "(I)" + MH_SIG, false);
+
+                List<Var> targs = AFTER_THIS.fromTypes(TTYPE.parameterList());
+                List<Var> tfields = new ArrayList<>(fields);
+                // mix them up and load them for the transform helper:
+                List<Var> helperArgs = speciesData.deriveTransformHelperArguments(transformMethods.get(whichtm), whichtm, targs, tfields);
+                List<Class<?>> helperTypes = new ArrayList<>(helperArgs.size());
+                for (Var ha : helperArgs) {
+                    helperTypes.add(ha.basicType.basicTypeClass());
+                    if (ha.isInHeap()) {
+                        assert(tfields.contains(ha));
+                        mv.visitVarInsn(ALOAD, 0);
+                        ha.emitFieldInsn(GETFIELD, mv);
+                    } else {
+                        assert(targs.contains(ha));
+                        ha.emitVarInstruction(ALOAD, mv);
+                    }
+                }
+
+                // jump into the helper (which is probably a factory method)
+                final Class<?> rtype = TTYPE.returnType();
+                final BasicType rbt = BasicType.basicType(rtype);
+                MethodType invokeBasicType = MethodType.methodType(rbt.basicTypeClass(), helperTypes);
+                mv.visitMethodInsn(INVOKEVIRTUAL, MH,
+                        "invokeBasic", methodSig(invokeBasicType), false);
+                if (rbt == BasicType.L_TYPE) {
+                    mv.visitTypeInsn(CHECKCAST, classBCName(rtype));
+                    mv.visitInsn(ARETURN);
+                } else {
+                    throw newInternalError("NYI: transform of type "+rtype);
+                }
+                mv.visitMaxs(0, 0);
+                mv.visitEnd();
+            }
+
+            cw.visitEnd();
+
+            return cw.toByteArray();
+        }
+
+        private int typeLoadOp(char t) {
+            switch (t) {
+            case 'L': return ALOAD;
+            case 'I': return ILOAD;
+            case 'J': return LLOAD;
+            case 'F': return FLOAD;
+            case 'D': return DLOAD;
+            default : throw newInternalError("unrecognized type " + t);
+            }
+        }
+
+        private void emitIntConstant(int con, MethodVisitor mv) {
+            if (ICONST_M1 - ICONST_0 <= con && con <= ICONST_5 - ICONST_0)
+                mv.visitInsn(ICONST_0 + con);
+            else if (con == (byte) con)
+                mv.visitIntInsn(BIPUSH, con);
+            else if (con == (short) con)
+                mv.visitIntInsn(SIPUSH, con);
+            else {
+                mv.visitLdcInsn(con);
+            }
+
+        }
+
+        //
+        // Getter MH generation.
+        //
+
+        private MethodHandle findGetter(Class<?> speciesCode, List<Class<?>> types, int index) {
+            Class<?> fieldType = types.get(index);
+            String fieldName = chooseFieldName(fieldType, index);
+            try {
+                return IMPL_LOOKUP.findGetter(speciesCode, fieldName, fieldType);
+            } catch (NoSuchFieldException | IllegalAccessException e) {
+                throw newInternalError(e);
+            }
+        }
+
+        private List<MethodHandle> findGetters(Class<?> speciesCode, List<Class<?>> types) {
+            MethodHandle[] mhs = new MethodHandle[types.size()];
+            for (int i = 0; i < mhs.length; ++i) {
+                mhs[i] = findGetter(speciesCode, types, i);
+                assert(mhs[i].internalMemberName().getDeclaringClass() == speciesCode);
+            }
+            return List.of(mhs);
+        }
+
+        private List<MethodHandle> findFactories(Class<? extends T> speciesCode, List<Class<?>> types) {
+            MethodHandle[] mhs = new MethodHandle[1];
+            mhs[0] = findFactory(speciesCode, types);
+            return List.of(mhs);
+        }
+
+        List<LambdaForm.NamedFunction> makeNominalGetters(List<Class<?>> types, List<MethodHandle> getters) {
+            LambdaForm.NamedFunction[] nfs = new LambdaForm.NamedFunction[types.size()];
+            for (int i = 0; i < nfs.length; ++i) {
+                nfs[i] = new LambdaForm.NamedFunction(getters.get(i));
+            }
+            return List.of(nfs);
+        }
+
+        //
+        // Auxiliary methods.
+        //
+
+        protected void linkSpeciesDataToCode(ClassSpecializer<T,K,S>.SpeciesData speciesData, Class<? extends T> speciesCode) {
+            speciesData.speciesCode = speciesCode.asSubclass(topClass);
+            final List<Class<?>> types = speciesData.fieldTypes;
+            speciesData.factories = this.findFactories(speciesCode, types);
+            speciesData.getters = this.findGetters(speciesCode, types);
+            speciesData.nominalGetters = this.makeNominalGetters(types, speciesData.getters);
+        }
+
+        private Field reflectSDField(Class<? extends T> speciesCode) {
+            final Field field = reflectField(speciesCode, sdFieldName);
+            assert(field.getType() == metaType);
+            assert(Modifier.isStatic(field.getModifiers()));
+            return field;
+        }
+
+        private S readSpeciesDataFromCode(Class<? extends T> speciesCode) {
+            try {
+                MemberName sdField = IMPL_LOOKUP.resolveOrFail(REF_getStatic, speciesCode, sdFieldName, metaType);
+                Object base = MethodHandleNatives.staticFieldBase(sdField);
+                long offset = MethodHandleNatives.staticFieldOffset(sdField);
+                UNSAFE.loadFence();
+                return metaType.cast(UNSAFE.getObject(base, offset));
+            } catch (Error err) {
+                throw err;
+            } catch (Exception ex) {
+                throw newInternalError("Failed to load speciesData from speciesCode: " + speciesCode.getName(), ex);
+            } catch (Throwable t) {
+                throw uncaughtException(t);
+            }
+        }
+
+        protected S loadSpeciesDataFromCode(Class<? extends T> speciesCode) {
+            if (speciesCode == topClass()) {
+                return topSpecies;
+            }
+            S result = readSpeciesDataFromCode(speciesCode);
+            if (result.outer() != ClassSpecializer.this) {
+                throw newInternalError("wrong class");
+            }
+            return result;
+        }
+
+        protected void linkCodeToSpeciesData(Class<? extends T> speciesCode, ClassSpecializer<T,K,S>.SpeciesData speciesData, boolean salvage) {
+            try {
+                assert(readSpeciesDataFromCode(speciesCode) == null ||
+                    (salvage && readSpeciesDataFromCode(speciesCode).equals(speciesData)));
+
+                MemberName sdField = IMPL_LOOKUP.resolveOrFail(REF_putStatic, speciesCode, sdFieldName, metaType);
+                Object base = MethodHandleNatives.staticFieldBase(sdField);
+                long offset = MethodHandleNatives.staticFieldOffset(sdField);
+                UNSAFE.storeFence();
+                UNSAFE.putObject(base, offset, speciesData);
+                UNSAFE.storeFence();
+            } catch (Error err) {
+                throw err;
+            } catch (Exception ex) {
+                throw newInternalError("Failed to link speciesData to speciesCode: " + speciesCode.getName(), ex);
+            } catch (Throwable t) {
+                throw uncaughtException(t);
+            }
+        }
+
+        /**
+         * Field names in concrete species classes adhere to this pattern:
+         * type + index, where type is a single character (L, I, J, F, D).
+         * The factory subclass can customize this.
+         * The name is purely cosmetic, since it applies to a private field.
+         */
+        protected String chooseFieldName(Class<?> type, int index) {
+            BasicType bt = BasicType.basicType(type);
+            return "" + bt.basicTypeChar() + index;
+        }
+
+        MethodHandle findFactory(Class<? extends T> speciesCode, List<Class<?>> types) {
+            final MethodType type = baseConstructorType().changeReturnType(topClass()).appendParameterTypes(types);
+            try {
+                return IMPL_LOOKUP.findStatic(speciesCode, "make", type);
+            } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
+                throw newInternalError(e);
+            }
+        }
+    }
+
+    /** Hook that virtualizes the Factory class, allowing subclasses to extend it. */
+    protected Factory makeFactory() {
+        return new Factory();
+    }
+
+
+    // Other misc helpers:
+    private static final String MH = "java/lang/invoke/MethodHandle";
+    private static final String MH_SIG = "L" + MH + ";";
+    private static final String STABLE = "jdk/internal/vm/annotation/Stable";
+    private static final String STABLE_SIG = "L" + STABLE + ";";
+    private static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
+    static {
+        assert(MH_SIG.equals(classSig(MethodHandle.class)));
+        assert(MH.equals(classBCName(MethodHandle.class)));
+    }
+
+    static String methodSig(MethodType mt) {
+        return mt.toMethodDescriptorString();
+    }
+    static String classSig(Class<?> cls) {
+        if (cls.isPrimitive() || cls.isArray())
+            return MethodType.methodType(cls).toMethodDescriptorString().substring(2);
+        return classSig(classBCName(cls));
+    }
+    static String classSig(String bcName) {
+        assert(bcName.indexOf('.') < 0);
+        assert(!bcName.endsWith(";"));
+        assert(!bcName.startsWith("["));
+        return "L" + bcName + ";";
+    }
+    static String classBCName(Class<?> cls) {
+        return classBCName(className(cls));
+    }
+    static String classBCName(String str) {
+        assert(str.indexOf('/') < 0) : str;
+        return str.replace('.', '/');
+    }
+    static String className(Class<?> cls) {
+        assert(!cls.isArray() && !cls.isPrimitive());
+        return cls.getName();
+    }
+}
--- a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Thu Nov 16 00:58:50 2017 +0100
@@ -25,14 +25,14 @@
 
 package java.lang.invoke;
 
-import java.util.Map;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.Opcodes;
+import sun.invoke.util.Wrapper;
+
 import java.util.ArrayList;
 import java.util.HashSet;
-import sun.invoke.util.Wrapper;
-
-import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Helper class to assist the GenerateJLIClassesPlugin to get access to
@@ -118,8 +118,7 @@
                 // require an even more complex naming scheme
                 LambdaForm reinvoker = makeReinvokerFor(methodTypes[i]);
                 forms.add(reinvoker);
-                String speciesSig = BoundMethodHandle
-                        .speciesData(reinvoker).fieldSignature();
+                String speciesSig = BoundMethodHandle.speciesDataFor(reinvoker).key();
                 assert(speciesSig.equals("L"));
                 names.add(reinvoker.kind.defaultLambdaName + "_" + speciesSig);
 
@@ -205,20 +204,19 @@
                 DelegatingMethodHandle.NF_getTarget);
     }
 
-    static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(
-            final String types) {
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(final String types) {
         for (char c : types.toCharArray()) {
             if ("LIJFD".indexOf(c) < 0) {
                 throw new IllegalArgumentException("All characters must "
                         + "correspond to a basic field type: LIJFD");
             }
         }
-        String shortTypes = LambdaForm.shortenSignature(types);
-        final String className =
-                BoundMethodHandle.Factory.speciesInternalClassName(shortTypes);
-        return Map.entry(className,
-                         BoundMethodHandle.Factory.generateConcreteBMHClassBytes(
-                                 shortTypes, types, className));
+        final BoundMethodHandle.SpeciesData species = BoundMethodHandle.SPECIALIZER.findSpecies(types);
+        final String className = species.speciesCode().getName();
+        final ClassSpecializer.Factory factory = BoundMethodHandle.SPECIALIZER.factory();
+        final byte[] code = factory.generateConcreteSpeciesCodeFile(className, species);
+        return Map.entry(className.replace('.', '/'), code);
     }
 
 }
--- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Thu Nov 16 00:58:50 2017 +0100
@@ -183,8 +183,7 @@
             new java.security.PrivilegedAction<>() {
                 public Void run() {
                     try {
-                        String dumpName = className;
-                        //dumpName = dumpName.replace('/', '-');
+                        String dumpName = className.replace('.','/');
                         File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
                         System.out.println("dump: " + dumpFile);
                         dumpFile.getParentFile().mkdirs();
@@ -630,7 +629,7 @@
         String name = form.kind.methodName;
         switch (form.kind) {
             case BOUND_REINVOKER: {
-                name = name + "_" + BoundMethodHandle.speciesData(form).fieldSignature();
+                name = name + "_" + BoundMethodHandle.speciesDataFor(form).key();
                 return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class);
             }
             case DELEGATE:                  return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class);
--- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Thu Nov 16 00:58:50 2017 +0100
@@ -143,12 +143,22 @@
         D_TYPE('D', double.class, Wrapper.DOUBLE),  // all primitive types
         V_TYPE('V', void.class,   Wrapper.VOID);    // not valid in all contexts
 
-        static final BasicType[] ALL_TYPES = BasicType.values();
-        static final BasicType[] ARG_TYPES = Arrays.copyOf(ALL_TYPES, ALL_TYPES.length-1);
+        static final @Stable BasicType[] ALL_TYPES = BasicType.values();
+        static final @Stable BasicType[] ARG_TYPES = Arrays.copyOf(ALL_TYPES, ALL_TYPES.length-1);
 
         static final int ARG_TYPE_LIMIT = ARG_TYPES.length;
         static final int TYPE_LIMIT = ALL_TYPES.length;
 
+        // Derived int constants, which (unlike the enums) can be constant folded.
+        // We can remove them when JDK-8161245 is fixed.
+        static final byte
+                L_TYPE_NUM = (byte) L_TYPE.ordinal(),
+                I_TYPE_NUM = (byte) I_TYPE.ordinal(),
+                J_TYPE_NUM = (byte) J_TYPE.ordinal(),
+                F_TYPE_NUM = (byte) F_TYPE.ordinal(),
+                D_TYPE_NUM = (byte) D_TYPE.ordinal(),
+                V_TYPE_NUM = (byte) V_TYPE.ordinal();
+
         final char btChar;
         final Class<?> btClass;
         final Wrapper btWrapper;
@@ -679,6 +689,9 @@
         Class<?> rtype = signatureReturn(sig).btClass;
         return MethodType.makeImpl(rtype, ptypes, true);
     }
+    static MethodType basicMethodType(MethodType mt) {
+        return signatureType(basicTypeSignature(mt));
+    }
 
     /**
      * Check if i-th name is a call to MethodHandleImpl.selectAlternative.
@@ -1291,14 +1304,28 @@
         assert(sigp == sig.length);
         return String.valueOf(sig);
     }
+
+    /** Hack to make signatures more readable when they show up in method names.
+     * Signature should start with a sequence of uppercase ASCII letters.
+     * Runs of three or more are replaced by a single letter plus a decimal repeat count.
+     * A tail of anything other than uppercase ASCII is passed through unchanged.
+     * @param signature sequence of uppercase ASCII letters with possible repetitions
+     * @return same sequence, with repetitions counted by decimal numerals
+     */
     public static String shortenSignature(String signature) {
-        // Hack to make signatures more readable when they show up in method names.
         final int NO_CHAR = -1, MIN_RUN = 3;
         int c0, c1 = NO_CHAR, c1reps = 0;
         StringBuilder buf = null;
         int len = signature.length();
         if (len < MIN_RUN)  return signature;
         for (int i = 0; i <= len; i++) {
+            if (c1 != NO_CHAR && !('A' <= c1 && c1 <= 'Z')) {
+                // wrong kind of char; bail out here
+                if (buf != null) {
+                    buf.append(signature.substring(i - c1reps, len));
+                }
+                break;
+            }
             // shift in the next char:
             c0 = c1; c1 = (i == len ? NO_CHAR : signature.charAt(i));
             if (c1 == c0) { ++c1reps; continue; }
@@ -1342,7 +1369,7 @@
             this.arguments = that.arguments;
             this.constraint = constraint;
             assert(constraint == null || isParam());  // only params have constraints
-            assert(constraint == null || constraint instanceof BoundMethodHandle.SpeciesData || constraint instanceof Class);
+            assert(constraint == null || constraint instanceof ClassSpecializer.SpeciesData || constraint instanceof Class);
         }
         Name(MethodHandle function, Object... arguments) {
             this(new NamedFunction(function), arguments);
--- a/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java	Thu Nov 16 00:58:50 2017 +0100
@@ -27,6 +27,8 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
+
 import static java.lang.invoke.LambdaForm.*;
 import static java.lang.invoke.LambdaForm.BasicType.*;
 
@@ -325,15 +327,15 @@
      *  whose function is in the corresponding position in newFns.
      *  Only do this if the arguments are exactly equal to the given.
      */
-    LambdaFormBuffer replaceFunctions(NamedFunction[] oldFns, NamedFunction[] newFns,
+    LambdaFormBuffer replaceFunctions(List<NamedFunction> oldFns, List<NamedFunction> newFns,
                                       Object... forArguments) {
         assert(inTrans());
-        if (oldFns.length == 0)  return this;
+        if (oldFns.isEmpty())  return this;
         for (int i = arity; i < length; i++) {
             Name n = names[i];
-            int nfi = indexOf(n.function, oldFns);
+            int nfi = oldFns.indexOf(n.function);
             if (nfi >= 0 && Arrays.equals(n.arguments, forArguments)) {
-                changeName(i, new Name(newFns[nfi], n.arguments));
+                changeName(i, new Name(newFns.get(nfi), n.arguments));
             }
         }
         return this;
--- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Thu Nov 16 00:58:50 2017 +0100
@@ -381,10 +381,11 @@
     /// Editing methods for method handles.  These need to have fast paths.
 
     private BoundMethodHandle.SpeciesData oldSpeciesData() {
-        return BoundMethodHandle.speciesData(lambdaForm);
+        return BoundMethodHandle.speciesDataFor(lambdaForm);
     }
+
     private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) {
-        return oldSpeciesData().extendWith(type);
+        return oldSpeciesData().extendWith((byte) type.ordinal());
     }
 
     BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) {
@@ -461,7 +462,7 @@
             buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress));
         } else {
             // cannot bind the MH arg itself, unless oldData is empty
-            assert(oldData == BoundMethodHandle.SpeciesData.EMPTY);
+            assert(oldData == BoundMethodHandle.SPECIALIZER.topSpecies());
             newBaseAddress = new Name(L_TYPE).withConstraint(newData);
             buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress));
             buf.insertParameter(0, newBaseAddress);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Nov 16 00:58:50 2017 +0100
@@ -776,11 +776,11 @@
             if (PROFILE_GWT) {
                 int[] counts = new int[2];
                 mh = (BoundMethodHandle)
-                        BoundMethodHandle.speciesData_LLLL().constructor().invokeBasic(type, form,
+                        BoundMethodHandle.speciesData_LLLL().factory().invokeBasic(type, form,
                                 (Object) test, (Object) profile(target), (Object) profile(fallback), counts);
             } else {
                 mh = (BoundMethodHandle)
-                        BoundMethodHandle.speciesData_LLL().constructor().invokeBasic(type, form,
+                        BoundMethodHandle.speciesData_LLL().factory().invokeBasic(type, form,
                                 (Object) test, (Object) profile(target), (Object) profile(fallback));
             }
         } catch (Throwable ex) {
@@ -1089,7 +1089,7 @@
         BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL();
         BoundMethodHandle mh;
         try {
-            mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) target, (Object) exType,
+            mh = (BoundMethodHandle) data.factory().invokeBasic(type, form, (Object) target, (Object) exType,
                     (Object) catcher, (Object) collectArgs, (Object) unboxResult);
         } catch (Throwable ex) {
             throw uncaughtException(ex);
@@ -1885,7 +1885,7 @@
         BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
         BoundMethodHandle mh;
         try {
-            mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) clauseData,
+            mh = (BoundMethodHandle) data.factory().invokeBasic(type, form, (Object) clauseData,
                     (Object) collectArgs, (Object) unboxResult);
         } catch (Throwable ex) {
             throw uncaughtException(ex);
@@ -2128,7 +2128,7 @@
         BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL();
         BoundMethodHandle mh;
         try {
-            mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) target, (Object) cleanup,
+            mh = (BoundMethodHandle) data.factory().invokeBasic(type, form, (Object) target, (Object) cleanup,
                     (Object) collectArgs, (Object) unboxResult);
         } catch (Throwable ex) {
             throw uncaughtException(ex);
--- a/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java	Thu Nov 16 00:58:50 2017 +0100
@@ -25,6 +25,8 @@
 
 package java.lang.invoke;
 
+import jdk.internal.vm.annotation.Stable;
+
 import static java.lang.invoke.LambdaForm.BasicType.*;
 import static java.lang.invoke.MethodHandleStatics.*;
 
@@ -33,6 +35,7 @@
  * @author jrose
  */
 final class SimpleMethodHandle extends BoundMethodHandle {
+
     private SimpleMethodHandle(MethodType type, LambdaForm form) {
         super(type, form);
     }
@@ -41,10 +44,11 @@
         return new SimpleMethodHandle(type, form);
     }
 
-    /*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
+    /*non-public*/ static @Stable BoundMethodHandle.SpeciesData BMH_SPECIES;
 
-    /*non-public*/ public SpeciesData speciesData() {
-            return SPECIES_DATA;
+    @Override
+    /*non-public*/ BoundMethodHandle.SpeciesData speciesData() {
+            return BMH_SPECIES;
     }
 
     @Override
@@ -58,18 +62,13 @@
     }
 
     @Override
-    /*non-public*/ public int fieldCount() {
-        return 0;
-    }
-
-    @Override
     /*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
         return BoundMethodHandle.bindSingle(mt, lf, narg); // Use known fast path.
     }
     @Override
     /*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
         try {
-            return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, narg);
+            return (BoundMethodHandle) BMH_SPECIES.extendWith(I_TYPE_NUM).factory().invokeBasic(mt, lf, narg);
         } catch (Throwable ex) {
             throw uncaughtException(ex);
         }
@@ -77,7 +76,7 @@
     @Override
     /*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
         try {
-            return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, narg);
+            return (BoundMethodHandle) BMH_SPECIES.extendWith(J_TYPE_NUM).factory().invokeBasic(mt, lf, narg);
         } catch (Throwable ex) {
             throw uncaughtException(ex);
         }
@@ -85,7 +84,7 @@
     @Override
     /*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
         try {
-            return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, narg);
+            return (BoundMethodHandle) BMH_SPECIES.extendWith(F_TYPE_NUM).factory().invokeBasic(mt, lf, narg);
         } catch (Throwable ex) {
             throw uncaughtException(ex);
         }
@@ -93,7 +92,7 @@
     @Override
     /*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
         try {
-            return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, narg);
+            return (BoundMethodHandle) BMH_SPECIES.extendWith(D_TYPE_NUM).factory().invokeBasic(mt, lf, narg);
         } catch (Throwable ex) {
             throw uncaughtException(ex);
         }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Thu Nov 16 00:58:50 2017 +0100
@@ -242,8 +242,14 @@
         lines.map(line -> line.split(" "))
              .forEach(parts -> {
                 switch (parts[0]) {
-                    case "[BMH_RESOLVE]":
-                        speciesTypes.add(expandSignature(parts[1]));
+                    case "[SPECIES_RESOLVE]":
+                        // Allow for new types of species data classes being resolved here
+                        if (parts.length == 3 && parts[1].startsWith("java.lang.invoke.BoundMethodHandle$Species_")) {
+                            String species = parts[1].substring("java.lang.invoke.BoundMethodHandle$Species_".length());
+                            if (!"L".equals(species)) {
+                                speciesTypes.add(expandSignature(species));
+                            }
+                        }
                         break;
                     case "[LF_RESOLVE]":
                         String methodType = parts[3];
@@ -449,7 +455,7 @@
             "/java.base/" + INVOKERS_HOLDER + ".class";
 
     // Convert LL -> LL, L3 -> LLL
-    private static String expandSignature(String signature) {
+    public static String expandSignature(String signature) {
         StringBuilder sb = new StringBuilder();
         char last = 'X';
         int count = 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/ClassSpecializerTest.java	Thu Nov 16 00:58:50 2017 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * 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.
+ */
+
+/* @test
+ * @summary Smoke-test class specializer, used to create BoundMethodHandle classes
+ * @compile/module=java.base java/lang/invoke/ClassSpecializerHelper.java
+ * @run testng/othervm/timeout=250 -ea -esa ClassSpecializerTest
+ */
+
+// Useful diagnostics to try:
+//   -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true
+//   -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true
+
+
+import org.testng.annotations.*;
+import java.lang.invoke.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static java.lang.invoke.ClassSpecializerHelper.*;
+
+
+public class ClassSpecializerTest {
+    @Test
+    public void testFindSpecies() throws Throwable {
+        System.out.println("testFindSpecies");
+        System.out.println("test = " + SPEC_TEST);
+        ArrayList<Object> args = new ArrayList<>();
+        for (int key = 0; key <= Kind.MAX_KEY; key++) {
+            Kind k = SpecTest.kind(key);
+            System.out.println("k = " + k);
+            MethodHandle mh = k.factory();
+            System.out.println("k.f = " + mh);
+            args.clear();
+            for (Class<?> pt : mh.type().parameterList()) {
+                args.add(coughUpA(pt));
+            }
+            args.set(0, key * 1000 + 42);
+            Frob f = (Frob) mh.invokeWithArguments(args.toArray());
+            assert(f.kind() == k);
+            System.out.println("k.f(...) = " + f.toString());
+            List<Object> l = f.asList();
+            System.out.println("f.l = " + l);
+            args.subList(0,1).clear();  // drop label
+            assert(args.equals(l));
+        }
+    }
+    private static Object coughUpA(Class<?> pt) throws Throwable {
+        if (pt == String.class)  return "foo";
+        if (pt.isArray()) return java.lang.reflect.Array.newInstance(pt.getComponentType(), 2);
+        if (pt == Integer.class)  return 42;
+        if (pt == Double.class)  return 3.14;
+        if (pt.isAssignableFrom(List.class))
+            return Arrays.asList("hello", "world", "from", pt.getSimpleName());
+        return MethodHandles.zero(pt).invoke();
+    }
+    public static void main(String... av) throws Throwable {
+        System.out.println("TEST: ClassSpecializerTest");
+        new ClassSpecializerTest().testFindSpecies();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/java.base/java/lang/invoke/ClassSpecializerHelper.java	Thu Nov 16 00:58:50 2017 +0100
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+
+/**
+ * Helper class, injected into java.lang.invoke,
+ * that bridges to the private ClassSpecializer mechanism.
+ */
+
+public interface ClassSpecializerHelper {
+    interface Frob {
+        Kind kind();
+        int label();
+        List<Object> asList();
+    }
+    abstract class FrobImpl implements Frob {
+        private final int label;
+        public FrobImpl(int label) {
+            this.label = label;
+        }
+        public int label() { return label; }
+        @Override public abstract Kind kind();
+
+        public String toString() {
+            final StringBuilder buf = new StringBuilder();
+            buf.append("Frob[label=").append(label);
+            final Kind k = kind();
+            if (k != null) {
+                for (MethodHandle mh : k.getters()) {
+                    Object x = "?";
+                    try {
+                        x = mh.invoke(this);
+                    } catch (Throwable ex) {
+                        x = "<<"+ex.getMessage()+">>";
+                    }
+                    buf.append(", ").append(x);
+                }
+            }
+            buf.append("]");
+            return buf.toString();
+        }
+
+        public List<Object> asList() {
+            final List<MethodHandle> getters = kind().getters();
+            ArrayList<Object> res = new ArrayList<>(getters.size());
+            for (MethodHandle getter : getters) {
+                try {
+                    res.add(getter.invoke(this));
+                } catch (Throwable ex) {
+                    throw new AssertionError(ex);
+                }
+            }
+            return res;
+        }
+    }
+
+    public static class Kind extends ClassSpecializer<Frob, Byte, Kind>.SpeciesData {
+        public Kind(SpecTest outer, Byte key) {
+            outer.super(key);
+        }
+
+        public MethodHandle factory() {
+            return super.factory();
+        }
+
+        public List<MethodHandle> getters() {
+            return super.getters();
+        }
+
+        private static final List<Class<?>> FIELD_TYPES
+                = Arrays.asList(String.class, float.class, Double.class, boolean.class, Object[].class, Object.class);
+
+        public static int MAX_KEY = FIELD_TYPES.size();
+
+        @Override
+        protected List<Class<?>> deriveFieldTypes(Byte key) {
+            return FIELD_TYPES.subList(0, key);
+        }
+
+        @Override
+        protected Class<? extends Frob> deriveSuperClass() {
+            return FrobImpl.class;
+        }
+
+        @Override
+        protected MethodHandle deriveTransformHelper(MemberName transform, int whichtm) {
+            throw new AssertionError();
+        }
+
+        @Override
+        protected <X> List<X> deriveTransformHelperArguments(MemberName transform, int whichtm, List<X> args, List<X> fields) {
+            throw new AssertionError();
+        }
+    }
+
+    class SpecTest extends ClassSpecializer<Frob, Byte, Kind> {
+        private static final MemberName SPECIES_DATA_ACCESSOR;
+        static {
+            try {
+                SPECIES_DATA_ACCESSOR = MethodHandles.publicLookup()
+                    .resolveOrFail(REF_invokeVirtual, FrobImpl.class, "kind", MethodType.methodType(Kind.class));
+            } catch (ReflectiveOperationException ex) {
+                throw new AssertionError("Bootstrap link error", ex);
+            }
+        }
+
+        public SpecTest() {
+            super(Frob.class, Byte.class, Kind.class,
+                    MethodType.methodType(void.class, int.class),
+                    SPECIES_DATA_ACCESSOR,
+                    "KIND",
+                    Arrays.asList());
+        }
+
+        @Override
+        protected Kind newSpeciesData(Byte key) {
+            return new Kind(this, key);
+        }
+
+        public static Kind kind(int key) {
+            return (Kind) SPEC_TEST.findSpecies((byte)key);
+        }
+    }
+
+    static final SpecTest SPEC_TEST = new SpecTest();
+
+}
+
--- a/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java	Wed Nov 08 10:27:10 2017 -0800
+++ b/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java	Thu Nov 16 00:58:50 2017 +0100
@@ -21,6 +21,8 @@
  * questions.
  */
 
+import java.nio.charset.Charset;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Collection;
 import java.util.List;
@@ -74,11 +76,30 @@
                     classFilesForSpecies(GenerateJLIClassesPlugin.defaultSpecies()),
                     List.of());
 
+        // Check that --generate-jli-classes=@file works as intended
+        Path baseFile = Files.createTempFile("base", "trace");
+        String species = "LLLLLLLLLLLLLLLLLLL";
+        String fileString = "[SPECIES_RESOLVE] java.lang.invoke.BoundMethodHandle$Species_" + species + " (salvaged)\n";
+        Files.write(baseFile, fileString.getBytes(Charset.defaultCharset()));
+        result = JImageGenerator.getJLinkTask()
+                .modulePath(helper.defaultModulePath())
+                .output(helper.createNewImageDir("generate-jli-file"))
+                .option("--generate-jli-classes=@" + baseFile.toString())
+                .addMods("java.base")
+                .call();
+
+        image = result.assertSuccess();
+
+        JImageValidator.validate(
+            image.resolve("lib").resolve("modules"),
+                    classFilesForSpecies(List.of(species)), // species should be in the image
+                    classFilesForSpecies(List.of(species.substring(1)))); // but not it's immediate parent
     }
 
     private static List<String> classFilesForSpecies(Collection<String> species) {
         return species.stream()
-                .map(s -> "/java.base/java/lang/invoke/BoundMethodHandle$Species_" + s + ".class")
+                .map(s -> "/java.base/java/lang/invoke/BoundMethodHandle$Species_"
+                        + GenerateJLIClassesPlugin.expandSignature(s) + ".class")
                 .collect(Collectors.toList());
     }
 }