--- old/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java 2017-07-17 19:03:54.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java 2017-07-17 19:03:53.000000000 -0700 @@ -25,25 +25,18 @@ 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.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; 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.MethodHandleStatics.newInternalError; +import static java.lang.invoke.MethodHandleStatics.uncaughtException; /** * The flavor of method handle which emulates an invoke instruction @@ -56,7 +49,7 @@ /*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) { super(type, form); - assert(speciesData() == speciesData(form)); + assert(speciesData() == formSpeciesData(form)); } // @@ -70,13 +63,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 +91,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 +108,6 @@ BoundMethodHandle bindArgumentD(int pos, double value) { return editor().bindArgumentD(this, pos, value); } - @Override BoundMethodHandle rebind() { if (!tooComplex()) { @@ -137,28 +130,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 BMHSpecies} 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 BMHSpecies speciesData(); - /*non-public*/ static SpeciesData speciesData(LambdaForm form) { + /*non-public*/ static BMHSpecies formSpeciesData(LambdaForm form) { Object c = form.names[0].constraint; - if (c instanceof SpeciesData) - return (SpeciesData) c; + if (c instanceof BMHSpecies) { + return (BMHSpecies) 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 +161,7 @@ @Override final String internalValues() { - int count = speciesData().fieldCount(); + int count = fieldCount(); if (count == 1) { return "[" + arg(0) + "]"; } @@ -180,17 +174,17 @@ /*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); + switch (BasicType.basicType(speciesData().fieldTypes().get(i))) { + 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); } // @@ -216,14 +210,11 @@ this.argL0 = argL0; } @Override - /*non-public*/ SpeciesData speciesData() { - return SPECIES_DATA; + /*non-public*/ BMHSpecies speciesData() { + 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 + BMHSpecies BMH_SPECIES; /*non-public*/ static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) { return new Species_L(mt, lf, argL0); } @@ -234,7 +225,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 +233,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 +241,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 +249,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 +257,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 +268,184 @@ // 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 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*/ 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 fieldHolder() { - return clazz; - } - public String toString() { - return "SpeciesData<"+fieldSignature()+">"; - } - - /** - * 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; - } + /*non-public*/ + static final class BMHSpecies extends ClassSpecializer.SpeciesData { + // This array is filled in lazily, as new species come into being over time. + @Stable final private BMHSpecies[] extensions = new BMHSpecies[ARG_TYPE_LIMIT]; - NamedFunction[] getterFunctions() { - return nominalGetters; + public BMHSpecies(Specializer outer, String key) { + outer.super(key); } - MethodHandle[] getterHandles() { return getters; } - - MethodHandle constructor() { - return constructor[0]; + @Override + protected List> deriveFieldTypes(String key) { + ArrayList> types = new ArrayList<>(key.length()); + for (int i = 0; i < key.length(); i++) { + types.add(basicType(key.charAt(i)).basicTypeClass()); + } + return types; } - static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); - - SpeciesData(String types, Class 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()]; - } 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); - } + @Override + protected String deriveTypeString() { + // (If/when we have to add nominal types, just inherit the more complex default.) + return key(); } - private static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); - private static final boolean INIT_DONE; // set after finishes... - - SpeciesData extendWith(byte type) { - return extendWith(BasicType.basicType(type)); - } - - 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() { - @Override - public SpeciesData apply(String types) { - Class 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; - } - }); + @Override + protected MethodHandle deriveTransformHelper(Method transform, int whichtm) { + if (whichtm == Specializer.TN_COPY_NO_EXTEND) + return factory(); + else if (whichtm < ARG_TYPE_LIMIT) + return extendWith((byte)whichtm).factory(); + else + throw newInternalError("bad transform"); } - /** - * 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 rootCls = BoundMethodHandle.class; - for (Class c : rootCls.getDeclaredClasses()) { - if (rootCls.isAssignableFrom(c)) { - final Class 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); - } + @Override + protected List deriveTransformHelperArguments(Method transform, int whichtm, List args, List 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; + } + private boolean verifyTHAargs(Method transform, int whichtm, List args, List fields) { + assert(transform == Specializer.BMH_TRANSFORMS.get(whichtm)); + assert(args.size() == transform.getParameterCount()); + assert(fields.size() == this.fieldCount()); + final int MH_AND_LF = 2; + if (whichtm == Specializer.TN_COPY_NO_EXTEND) { + assert(transform.getParameterCount() == MH_AND_LF); + } else if (whichtm < ARG_TYPE_LIMIT) { + assert(transform.getParameterCount() == MH_AND_LF+1); + final BasicType type = basicType((byte) whichtm); + assert(transform.getParameterTypes()[MH_AND_LF] == type.basicTypeClass()); + } else { + return false; } return true; } + //private boolean verfiyTHA(); - 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*/ BMHSpecies extendWith(byte typeNum) { + BMHSpecies 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); - } - - /** - * 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 { - - 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 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; - - 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 static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;"; - - private static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" }; - - private static final ConcurrentMap> CLASS_CACHE = new ConcurrentHashMap<>(); - - /** - * 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 getConcreteBMHClass(String types) { - // CHM.computeIfAbsent ensures generateConcreteBMHClass is called - // only once per key. - return CLASS_CACHE.computeIfAbsent( - types, new Function>() { - @Override - public Class 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); - } - } - }); - } - - /** - * Generate a concrete subclass of BMH for a given combination of bound types. - * - * A concrete BMH species adheres to the following schema: - * - *
-         * class Species_[[types]] extends BoundMethodHandle {
-         *     [[fields]]
-         *     final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); }
-         * }
-         * 
- * - * 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: - * - *
-         * 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; }
-         *     @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);
-         *     }
-         * }
-         * 
- * - * @param types the type signature, wherein reference types are erased to 'L' - * @return the generated concrete BMH class - */ - static Class generateConcreteBMHClass(String shortTypes, - String types) { - final String className = speciesInternalClassName(shortTypes); - byte[] classFile = generateConcreteBMHClassBytes(shortTypes, types, className); - - // load class - InvokerBytecodeGenerator.maybeDump(className, classFile); - Class 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, "", 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, "", 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, "", 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, "", 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); - } - } - - 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)); - } - } + /*non-public*/ + static final Specializer SPECIALIZER = new Specializer(); - 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); - } + /*non-public*/ + static final class Specializer extends ClassSpecializer { + private Specializer() { + super( // Reified type parameters: + BoundMethodHandle.class, String.class, BMHSpecies.class, + // Principal constructor type: + MethodType.methodType(void.class, MethodType.class, LambdaForm.class), + // Required linkage between class and species: + reflectMethod(BoundMethodHandle.class, "speciesData"), + "BMH_SPECIES", + BMH_TRANSFORMS); } - 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; + @Override + protected String topSpeciesKey() { + return ""; } - static MethodHandle[] makeCtors(Class 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; + @Override + protected String addFieldTypeToKey(String key, Class type) { + BasicType bt = basicType(type); + if (type != bt.basicTypeClass()) throw newInternalError("must be basic-type"); + Objects.requireNonNull(key); + return key + bt.basicTypeChar(); } - 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; + @Override + protected List> predeclaredSpeciesCode() { + List> result = new ArrayList<>(); + // pick up all nested classes (Species_L): + result.addAll(super.predeclaredSpeciesCode()); + // pick up the special nullary version: + result.add(SimpleMethodHandle.class); + return result; } - // - // Auxiliary methods. - // - - static SpeciesData getSpeciesDataFromConcreteBMHClass(Class cbmh) { - try { - Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA"); - return (SpeciesData) F_SPECIES_DATA.get(null); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } + @Override + protected BMHSpecies newSpeciesData(String key) { + return new BMHSpecies(this, key); } - static void setSpeciesDataToConcreteBMHClass(Class 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); - } + static final List BMH_TRANSFORMS; + static final int TN_COPY_NO_EXTEND = V_TYPE_NUM; + static { + ArrayList result = new ArrayList<>(); + final Class BMH = BoundMethodHandle.class; + Class[] params = { MethodType.class, LambdaForm.class, null }; + // for each basic type T, note the transform BMH::copyWithExtendT(MT, LF, T) + for (BasicType type : BasicType.ARG_TYPES) { + final String tname = "copyWithExtend" + type.basicTypeChar(); + params[params.length-1] = type.basicTypeClass(); + result.add(ClassSpecializer.reflectMethod(BMH, tname, params)); + } + // at the end add BMH::copyWith(MT, LF) + assert(result.size() == TN_COPY_NO_EXTEND); + result.add(ClassSpecializer.reflectMethod(BMH, "copyWith", params[0], params[1])); + // as it happens, there is one transform per BasicType including V_TYPE + assert(result.size() == TYPE_LIMIT); + BMH_TRANSFORMS = List.of(result.toArray(new Method[result.size()])); } /** - * Field names in concrete BMHs adhere to this pattern: - * arg + type + index - * where type is a single character (L, I, J, F, D). + * 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. */ - 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))); + class Factory extends ClassSpecializer.Factory { + @Override + protected String chooseFieldName(Class type, int index) { + return "arg" + super.chooseFieldName(type, index); } - 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(); + @Override protected String loadSpeciesKeyFromPredefinedClass(Class speciesClass) { + if (speciesClass == SimpleMethodHandle.class) return ""; + assert(speciesClass.getEnclosingClass() == BoundMethodHandle.class); + String name = speciesClass.getSimpleName(); + assert(name.startsWith("Species_")); + final String key = name.substring(name.indexOf('_') + 1); + return key; } - return MethodType.makeImpl(BoundMethodHandle.class, ptypes, true); } - static MethodHandle makeCbmhCtor(Class cbmh, String types) { - try { - return LOOKUP.findStatic(cbmh, "make", makeConstructorType(types)); - } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) { - throw newInternalError(e); - } + @Override + protected Factory makeFactory() { + return new Factory(); } - } - - 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) { + private static final BMHSpecies[] SPECIES_DATA_CACHE = new BMHSpecies[6]; + private static BMHSpecies checkCache(int size, String types) { int idx = size - 1; - SpeciesData data = SPECIES_DATA_CACHE[idx]; + BMHSpecies data = SPECIES_DATA_CACHE[idx]; if (data != null) return data; - SPECIES_DATA_CACHE[idx] = data = getSpeciesData(types); + SPECIES_DATA_CACHE[idx] = data = SPECIALIZER.findSpecies(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 BMHSpecies speciesData_L() { return checkCache(1, "L"); } + static BMHSpecies speciesData_LL() { return checkCache(2, "LL"); } + static BMHSpecies speciesData_LLL() { return checkCache(3, "LLL"); } + static BMHSpecies speciesData_LLLL() { return checkCache(4, "LLLL"); } + static BMHSpecies speciesData_LLLLL() { return checkCache(5, "LLLLL"); } } --- old/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java 2017-07-17 19:03:56.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java 2017-07-17 19:03:55.000000000 -0700 @@ -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 java.util.ArrayList; -import java.util.HashSet; import sun.invoke.util.Wrapper; -import static java.lang.invoke.MethodHandleNatives.Constants.*; +import java.util.ArrayList; +import java.util.HashSet; +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.formSpeciesData(reinvoker).key(); assert(speciesSig.equals("L")); names.add(reinvoker.kind.defaultLambdaName + "_" + speciesSig); @@ -205,6 +204,7 @@ DelegatingMethodHandle.NF_getTarget); } + @SuppressWarnings({"rawtypes", "unchecked"}) static Map.Entry generateConcreteBMHClassBytes( final String types) { for (char c : types.toCharArray()) { @@ -213,12 +213,11 @@ + "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.BMHSpecies 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, code); } } --- old/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2017-07-17 19:03:58.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2017-07-17 19:03:57.000000000 -0700 @@ -183,7 +183,7 @@ new java.security.PrivilegedAction<>() { public Void run() { try { - String dumpName = className; + String dumpName = className.replace('.','/'); //dumpName = dumpName.replace('/', '-'); File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class"); System.out.println("dump: " + dumpFile); @@ -630,7 +630,7 @@ String name = form.kind.methodName; switch (form.kind) { case BOUND_REINVOKER: { - name = name + "_" + BoundMethodHandle.speciesData(form).fieldSignature(); + name = name + "_" + BoundMethodHandle.formSpeciesData(form).key(); return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class); } case DELEGATE: return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class); --- old/src/java.base/share/classes/java/lang/invoke/Invokers.java 2017-07-17 19:03:59.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/Invokers.java 2017-07-17 19:03:59.000000000 -0700 @@ -301,7 +301,7 @@ : Arrays.asList(mtype, customized, which, nameCursor, names.length); if (MTYPE_ARG >= INARG_LIMIT) { assert(names[MTYPE_ARG] == null); - BoundMethodHandle.SpeciesData speciesData = BoundMethodHandle.speciesData_L(); + BoundMethodHandle.BMHSpecies speciesData = BoundMethodHandle.speciesData_L(); names[THIS_MH] = names[THIS_MH].withConstraint(speciesData); NamedFunction getter = speciesData.getterFunction(0); names[MTYPE_ARG] = new Name(getter, names[THIS_MH]); @@ -413,7 +413,7 @@ names[ARG_BASE + i] = argument(ARG_BASE + i, BasicType.basicType(mtype.parameterType(i))); } - BoundMethodHandle.SpeciesData speciesData = BoundMethodHandle.speciesData_L(); + BoundMethodHandle.BMHSpecies speciesData = BoundMethodHandle.speciesData_L(); names[THIS_MH] = names[THIS_MH].withConstraint(speciesData); NamedFunction getter = speciesData.getterFunction(0); --- old/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2017-07-17 19:04:01.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2017-07-17 19:04:01.000000000 -0700 @@ -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); --- old/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java 2017-07-17 19:04:03.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java 2017-07-17 19:04:03.000000000 -0700 @@ -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 oldFns, List newFns, Object... forArguments) { assert(inTrans()); - if (oldFns.length == 0) return this; + if (oldFns.size() == 0) 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; --- old/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java 2017-07-17 19:04:05.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java 2017-07-17 19:04:05.000000000 -0700 @@ -380,11 +380,11 @@ /// Editing methods for method handles. These need to have fast paths. - private BoundMethodHandle.SpeciesData oldSpeciesData() { - return BoundMethodHandle.speciesData(lambdaForm); + private BoundMethodHandle.BMHSpecies oldSpeciesData() { + return BoundMethodHandle.formSpeciesData(lambdaForm); } - private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) { - return oldSpeciesData().extendWith(type); + private BoundMethodHandle.BMHSpecies newSpeciesData(BasicType type) { + return oldSpeciesData().extendWith((byte) type.ordinal()); } BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) { @@ -446,8 +446,8 @@ LambdaFormBuffer buf = buffer(); buf.startEdit(); - BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); - BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos)); + BoundMethodHandle.BMHSpecies oldData = oldSpeciesData(); + BoundMethodHandle.BMHSpecies newData = newSpeciesData(lambdaForm.parameterType(pos)); Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values Name newBaseAddress; NamedFunction getter = newData.getterFunction(oldData.fieldCount()); @@ -461,7 +461,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); @@ -653,8 +653,8 @@ assert(combinerType == combinerType.basicType()); assert(combinerType.returnType() != void.class || dropResult); - BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); - BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); + BoundMethodHandle.BMHSpecies oldData = oldSpeciesData(); + BoundMethodHandle.BMHSpecies newData = newSpeciesData(L_TYPE); // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. @@ -716,8 +716,8 @@ assert(combinerType == combinerType.basicType()); assert(combinerType.returnType() != void.class || dropResult); - BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); - BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); + BoundMethodHandle.BMHSpecies oldData = oldSpeciesData(); + BoundMethodHandle.BMHSpecies newData = newSpeciesData(L_TYPE); // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. @@ -786,8 +786,8 @@ else callFilter = new Name(constantZero(newType)); } else { - BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); - BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); + BoundMethodHandle.BMHSpecies oldData = oldSpeciesData(); + BoundMethodHandle.BMHSpecies newData = newSpeciesData(L_TYPE); // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2017-07-17 19:04:07.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2017-07-17 19:04:07.000000000 -0700 @@ -727,11 +727,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) { @@ -915,7 +915,7 @@ MethodType lambdaType = basicType.invokerType(); Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); - BoundMethodHandle.SpeciesData data = + BoundMethodHandle.BMHSpecies data = (GET_COUNTERS != -1) ? BoundMethodHandle.speciesData_LLLL() : BoundMethodHandle.speciesData_LLL(); names[THIS_MH] = names[THIS_MH].withConstraint(data); @@ -992,7 +992,7 @@ Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); - BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); + BoundMethodHandle.BMHSpecies data = BoundMethodHandle.speciesData_LLLLL(); names[THIS_MH] = names[THIS_MH].withConstraint(data); names[GET_TARGET] = new Name(data.getterFunction(0), names[THIS_MH]); names[GET_CLASS] = new Name(data.getterFunction(1), names[THIS_MH]); @@ -1037,10 +1037,10 @@ MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType); MethodHandle unboxResult = unboxResultHandle(type.returnType()); - BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); + BoundMethodHandle.BMHSpecies 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); @@ -1821,10 +1821,10 @@ LoopClauses clauseData = new LoopClauses(new MethodHandle[][]{toArray(init), toArray(step), toArray(pred), toArray(fini)}); - BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL(); + BoundMethodHandle.BMHSpecies 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); @@ -1884,7 +1884,7 @@ if (lform == null) { Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); - BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL(); + BoundMethodHandle.BMHSpecies data = BoundMethodHandle.speciesData_LLL(); names[THIS_MH] = names[THIS_MH].withConstraint(data); names[GET_CLAUSE_DATA] = new Name(data.getterFunction(0), names[THIS_MH]); names[GET_COLLECT_ARGS] = new Name(data.getterFunction(1), names[THIS_MH]); @@ -2064,10 +2064,10 @@ MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType); MethodHandle unboxResult = unboxResultHandle(rtype); - BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL(); + BoundMethodHandle.BMHSpecies 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); @@ -2120,7 +2120,7 @@ Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); - BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL(); + BoundMethodHandle.BMHSpecies data = BoundMethodHandle.speciesData_LLLL(); names[THIS_MH] = names[THIS_MH].withConstraint(data); names[GET_TARGET] = new Name(data.getterFunction(0), names[THIS_MH]); names[GET_CLEANUP] = new Name(data.getterFunction(1), names[THIS_MH]); --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java 2017-07-17 19:04:09.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java 2017-07-17 19:04:09.000000000 -0700 @@ -65,6 +65,21 @@ static native void setCallSiteTargetNormal(CallSite site, MethodHandle target); static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target); + private static boolean MSOK; + static boolean moduleSystemInitialized() { + if (MSOK) return true; + //if (java.lang.reflect.Layer.boot() == null) return false; + try { + Class C_Layer = Class.forName("java.lang.reflect.Layer"); + if (C_Layer.getDeclaredMethod("boot").invoke(null) == null) + return false; + } catch (ReflectiveOperationException ex) { + return false; + } + MSOK = true; + return true; + } + /** Represents a context to track nmethod dependencies on CallSite instance target. */ static class CallSiteContext implements Runnable { //@Injected JVM_nmethodBucket* vmdependencies; --- old/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java 2017-07-17 19:04:11.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java 2017-07-17 19:04:11.000000000 -0700 @@ -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.*; @@ -41,10 +43,10 @@ return new SimpleMethodHandle(type, form); } - /*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY; + /*non-public*/ @Stable static BMHSpecies BMH_SPECIES; - /*non-public*/ public SpeciesData speciesData() { - return SPECIES_DATA; + /*non-public*/ public BMHSpecies speciesData() { + return BMH_SPECIES; } @Override @@ -58,18 +60,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 +74,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 +82,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 +90,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); } --- /dev/null 2017-07-17 19:04:13.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java 2017-07-17 19:04:13.000000000 -0700 @@ -0,0 +1,1056 @@ +/* + * 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.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.MethodHandleStatics.*; +import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +/** + * Class specialization code. + * @param top class under which species classes are created. + * @param key which identifies individual specializations + */ +/*non-public*/ +abstract class ClassSpecializer.SpeciesData> { + private final Class topClass; + private final Class keyType; + private final Class metaType; + private final Method sdAccessor; + private final String sdFieldName; + private final List transformMethods; + private final MethodType baseConstructorType; + private final S topSpecies; + private final ConcurrentMap cache = new ConcurrentHashMap<>(); + private final Factory factory; + private @Stable boolean topClassIsSuper; + + /** Return the top type mirror, for type {@code T} */ + public final Class topClass() { return topClass; } + + /** Return the key type mirror, for type {@code K} */ + public final Class keyType() { return keyType; } + + /** Return the species metadata type mirror, for type {@code S} */ + public final Class 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 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 + * @param sdFieldName + */ + protected ClassSpecializer(Class topClass, Class keyType, Class metaType, + MethodType baseConstructorType, + Method sdAccessor, String sdFieldName, + List transformMethods) { + this.topClass = topClass; + this.keyType = keyType; + this.metaType = metaType; + this.sdAccessor = sdAccessor; + if (sdAccessor.getReturnType() != metaType) + throw newInternalError("accessor must return meta-type: "+sdAccessor); + this.transformMethods = List.of(transformMethods.toArray(new Method[transformMethods.size()])); + this.sdFieldName = sdFieldName; + this.baseConstructorType = baseConstructorType.changeReturnType(void.class); + this.factory = makeFactory(); + K tsk = topSpeciesKey(); + S topSpecies = null; + // Pre-fill the TopClass species-data cache with topSpecies and all TopClass's inner subclasses. + for (Class c : predeclaredSpeciesCode()) { + final Class speciesCode = c.asSubclass(topClass); + K key = factory.loadSpeciesKeyFromPredefinedClass(speciesCode); + S speciesData = newSpeciesData(key); + factory.linkSpeciesDataToCode(speciesData, speciesCode); + factory.linkCodeToSpeciesData(speciesCode, speciesData, true); + cache.put(key, speciesData); + if (tsk != null && tsk.equals(speciesData.key())) topSpecies = speciesData; + } + 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 Method reflectMethod(Class defc, String name, Class... ptypes) { + try { + return defc.getDeclaredMethod(name, ptypes); + } catch (NoSuchMethodException ex) { + throw newIAE(defc.getName()+"."+name+MethodType.methodType(void.class, ptypes), ex); + } + } + + protected static Constructor reflectConstructor(Class 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) { + if (TRACE_RESOLVE) { + System.out.println("[SPECIES_RESOLVE] findSpecies " + key); + } + S speciesData = cache.computeIfAbsent(key, new Function<>() { + @Override + public S apply(K key1) { + return factory.loadSpecies(newSpeciesData(key1), Factory.USE_CLASS_CACHE); + } + }); + if (TRACE_RESOLVE) { + System.out.println("[SPECIES_RESOLVE] findSpecies " + key + " => " + speciesData); + } + // 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. + 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. + @Stable private final K key; + @Stable private final List> fieldTypes; + @Stable private Class speciesCode; + @Stable private Field sdField; + @Stable private MethodHandle[] factories = new MethodHandle[0]; + @Stable private MethodHandle[] getters; + @Stable private LambdaForm.NamedFunction[] nominalGetters; + @Stable private final MethodHandle[] transformHelpers = new MethodHandle[transformMethods.size()]; + + protected SpeciesData(K key) { + this.key = keyType.cast(Objects.requireNonNull(key)); + List> types = deriveFieldTypes(key); + int arity = types.size(); + this.fieldTypes = List.of(types.toArray(new Class[arity])); + } + + public final K key() { + return key; + } + protected final List> fieldTypes() { + return fieldTypes; + } + protected final int fieldCount() { return fieldTypes.size(); } + protected ClassSpecializer outer() { return ClassSpecializer.this; } + protected final boolean isResolved() { + return speciesCode != null && factories != null && factories[0] != null; + } + @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 SpeciesData)) return false; + @SuppressWarnings("rawtypes") + SpeciesData that = (SpeciesData) obj; + return this.outer() == that.outer() && this.key.equals(that.key); + } + + /** Throws NPE if this species is not yet resolved. */ + protected final Class 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 Objects.requireNonNull(getters[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[i]; + assert(nf.memberDeclaringClassOrNull() == speciesCode()); + assert(nf.returnType() == BasicType.basicType(fieldTypes.get(i))); + return nf; + } + + protected List getterFunctions() { + return List.of(nominalGetters); + } + + protected List getters() { + return List.of(getters); + } + + protected MethodHandle factory() { + return Objects.requireNonNull(factories[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); + if (!mt.erase().equals(mh.type().erase())) //FIXME: remove after testing + throw newInternalError("types differ too much: "+mt+" != "+mh.type()); + mh = mh.asType(mt); + return transformHelpers[whichtm] = mh; + } + + private final MethodType transformHelperType(int whichtm) { + Method tm = transformMethods().get(whichtm); + ArrayList> args = new ArrayList<>(); + ArrayList> fields = new ArrayList<>(); + Collections.addAll(args, tm.getParameterTypes()); + fields.addAll(fieldTypes()); + List> 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> 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 produces 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(Method 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 the common element type of the various lists + * @return a new list + */ + protected abstract List deriveTransformHelperArguments(Method transform, int whichtm, + List args, List 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 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> 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 deriveSuperClass() { + final Class topc = topClass(); + if (!topClassIsSuper) { + try { + final Constructor 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 + } + + /** Given a key, add an argument of a basic type to the key, if possible. + * @param key key to start with + * @param type type to add + * @return a new key with the extra argument + */ + protected abstract K addFieldTypeToKey(K key, Class type); + + /** Returns a method handle which operates on {@code topClass} + * and returns a {@code SpeciesData}. + * The method handle type is {@code (T)S}, which means the + * method handle takes the top-type for this specializer + * and returns the species class subtype. + * @return a method handle that extracts a species data + */ + protected final MethodHandle speciesHandle() { + throw new AssertionError("NYI"); + } + + /** Given a species class return the {@code speciesData}. */ + protected final S species(T instance) { + // FIXME: use the virtual method on the instance + throw new AssertionError("NYI"); + } + + /** + * Hook for implementations to report statically generated species classes. + * Use with care. + */ + protected List> predeclaredSpeciesCode() { + ArrayList> results = new ArrayList<>(); + for (Class c : topClass().getDeclaredClasses()) { + if (topClass().isAssignableFrom(c)) { + results.add(c.asSubclass(topClass())); + } + } + return results; + } + + /** 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 + */ + private 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) { + System.out.println("[SPECIES_RESOLVE] " + className + " (salvage)"); + } + } catch (Error ex) { + if (TRACE_RESOLVE) { + System.out.println("[SPECIES_RESOLVE] " + className + " (Error) " + ex.getMessage()); + } + } + final Class 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); + // 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; + } + } + final S result = factory.loadSpeciesDataFromCode(speciesCode); + if (result == null || result != speciesData && !result.key().equals(speciesData.key()) || !result.isResolved()) + throw newInternalError("bad species class linkage for "+className+": "+result+" != "+speciesData); + return result; + } + + // Extra CHM introduced by JDK-8131129: Attempt to define a duplicate BMH$Species class + // FIXME: Figure out if we still need it, now that species bootstrap doesn't go through + private static final boolean USE_CLASS_CACHE = false; + private final ConcurrentMap> classCache = new ConcurrentHashMap<>(); + private S loadSpecies(S speciesData, boolean useClassCache) { + if (useClassCache) { + // CHM.computeIfAbsent ensures generateConcreteSpeciesCode is called + // only once per key. + final Class speciesCode = classCache.computeIfAbsent( + speciesData.key(), + new Function>() { + @Override + public Class apply(K key) { + return loadSpecies(speciesData).speciesCode(); + } + }); + return factory.loadSpeciesDataFromCode(speciesCode); + } + return loadSpecies(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: + * + *
+         * class Species_[[types]] extends [[T]] {
+         *     final [[S]] speciesData() { return ... }
+         *     static [[T]] make([[fields]]) { return ... }
+         *     [[fields]]
+         *     final [[T]] transform([[args]]) { return ... }
+         * }
+         * 
+ * + * 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 reference and one integral bound values + * have a shape like the following: + * + *
+         * 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; }
+         *     @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);
+         *     }
+         * }
+         * 
+ * + * @param speciesData what species we are generating + * @return the generated concrete TopClass class + */ + Class generateConcreteSpeciesCode(String className, SpeciesData speciesData) { + byte[] classFile = generateConcreteSpeciesCodeFile(className, speciesData); + + // load class + InvokerBytecodeGenerator.maybeDump(classBCName(className), classFile); + Class speciesCode; + ProtectionDomain pd = null; // FIXME: should it be this? topClass.getProtectionDomain(); + try { + //speciesCode = IMPL_LOOKUP.in(topClass()).defineClass(classFile); + speciesCode = UNSAFE.defineClass(className, classFile, 0, classFile.length, + topClass.getClassLoader(), 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 TRANSFORM_NAMES; // derived from transformMethods + private final List TRANSFORM_TYPES; + private final List TRANSFORM_MODS; + { + // Tear apart transformMethods to get the names, types, and modifiers. + List tns = new ArrayList<>(); + List tts = new ArrayList<>(); + List tms = new ArrayList<>(); + for (Method tm : transformMethods) { + tns.add(tm.getName()); + final MethodType tt = MethodType.methodType(tm.getReturnType(), tm.getParameterTypes()); + 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-publc*/ byte[] generateConcreteSpeciesCodeFile(String className0, 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 vars) { + int n = vars.size(); + return (n == 0 ? this : vars.get(n-1)); + } + List fromTypes(List types) { + Var prev = this; + ArrayList 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> fieldTypes = speciesData.fieldTypes(); + final List 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, + "", methodSig(thisCtorType), null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); // this + + final List ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList()); + for (Var ca : ctorArgs) { + ca.emitVarInstruction(ALOAD, mv); + } + + // super(ca...) + mv.visitMethodInsn(INVOKESPECIAL, superClassName, + "", 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, + "", 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 targs = AFTER_THIS.fromTypes(TTYPE.parameterList()); + List tfields = new ArrayList<>(fields); + // mix them up and load them for the transform helper: + List helperArgs = speciesData.deriveTransformHelperArguments(transformMethods.get(whichtm), whichtm, targs, tfields); + List> 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> 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 MethodHandle[] findGetters(Class speciesCode, List> 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 mhs; + } + + private MethodHandle[] findFactories(Class speciesCode, List> types) { + MethodHandle[] mhs = new MethodHandle[1]; + mhs[0] = findFactory(speciesCode, types); + return mhs; + } + + LambdaForm.NamedFunction[] makeNominalGetters(List> types, MethodHandle[] getters) { + LambdaForm.NamedFunction[] nfs = new LambdaForm.NamedFunction[types.size()]; + for (int i = 0; i < nfs.length; ++i) { + nfs[i] = new LambdaForm.NamedFunction(getters[i]); + } + return nfs; + } + + // + // Auxiliary methods. + // + + protected K loadSpeciesKeyFromPredefinedClass(Class speciesCode) { + throw new UnsupportedOperationException("must override if there are predefined classes"); + } + + protected void linkSpeciesDataToCode(SpeciesData speciesData, Class speciesCode) { + speciesData.speciesCode = speciesCode.asSubclass(topClass); + speciesData.sdField = reflectSDField(speciesCode); + assert(!MethodHandleNatives.moduleSystemInitialized() || + // annotation parser can't create proxy classes until module system is fully initialzed + speciesData.sdField.getDeclaredAnnotation(Stable.class) != null); + final List> 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 speciesCode) { + final Field field = reflectField(speciesCode, sdFieldName); + assert(field.getType() == metaType); + assert(Modifier.isStatic(field.getModifiers())); + return field; + } + + protected S loadSpeciesDataFromCode(Class speciesCode) { + if (speciesCode == topClass()) return topSpecies; + try { + Field sdField = reflectSDField(speciesCode); + UNSAFE.loadFence(); + final S result = metaType.cast(sdField.get(null)); + if (result.outer() != ClassSpecializer.this) throw newInternalError("wrong class"); + return result; + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + + protected void linkCodeToSpeciesData(Class speciesCode, SpeciesData speciesData, boolean salvage) { + // FIXME: remove this special case hack + if (speciesCode == topClass() && speciesData.key().equals(topSpeciesKey())) return; + try { + Field sdField = speciesData.sdField; + assert(sdField.get(null) == null || + salvage && sdField.get(null).equals(speciesData)); + UNSAFE.storeFence(); + sdField.set(null, speciesData); + UNSAFE.storeFence(); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + + /** + * 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 speciesCode, List> 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 JLO = "java/lang/Object"; + private static final String JLO_SIG = "L" + JLO + ";"; + private static final String MH = "java/lang/invoke/MethodHandle"; + private static final String MH_SIG = "L" + MH + ";"; + private static final String V_MH_SIG = "()" + MH_SIG; + private static final String STABLE_SIG = classSig(Stable.class); + private static final String INT_SIG = "()I"; + private static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" }; + private static final List> NO_TYPES = Collections.nCopies(0, null); + static { + assert(JLO_SIG.equals(classSig(Object.class))); + assert(MH.equals(classBCName(MethodHandle.class))); + } + + private static String methodSig(MethodType mt) { + return mt.toMethodDescriptorString(); + } + private static String classSig(Class cls) { + if (cls.isPrimitive() || cls.isArray()) + return MethodType.methodType(cls).toMethodDescriptorString().substring(2); + return classSig(classBCName(cls)); + } + private static String classSig(String bcName) { + assert(bcName.indexOf('.') < 0); + assert(!bcName.endsWith(";")); + assert(!bcName.startsWith("[")); + return "L" + bcName + ";"; + } + private static String classBCName(Class cls) { + return classBCName(className(cls)); + } + private static String classBCName(String str) { + assert(str.indexOf('/') < 0) : str; + return str.replace('.', '/'); + } + private static String className(Class cls) { + assert(!cls.isArray() && !cls.isPrimitive()); + return cls.getName(); + } +} --- /dev/null 2017-07-17 19:04:15.000000000 -0700 +++ new/test/java/lang/invoke/ClassSpecializerTest.java 2017-07-17 19:04:15.000000000 -0700 @@ -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 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 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 2017-07-17 19:04:17.000000000 -0700 +++ new/test/java/lang/invoke/java.base/java/lang/invoke/ClassSpecializerHelper.java 2017-07-17 19:04:16.000000000 -0700 @@ -0,0 +1,148 @@ +/* + * 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.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Helper class, injected into java.lang.invoke, + * that bridges to the private ClassSpecializer mechanism. + */ + +public interface ClassSpecializerHelper { + interface Frob { + Kind kind(); + int label(); + List 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 asList() { + final List getters = kind().getters(); + ArrayList 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.SpeciesData { + public Kind(SpecTest outer, Byte key) { + outer.super(key); + } + + public MethodHandle factory() { + return super.factory(); + } + + public List getters() { + return super.getters(); + } + + private static final List> 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> deriveFieldTypes(Byte key) { + return FIELD_TYPES.subList(0, key); + } + + @Override + protected Class deriveSuperClass() { + return FrobImpl.class; + } + + @Override + protected MethodHandle deriveTransformHelper(Method transform, int whichtm) { + throw new AssertionError(); + } + + @Override + protected List deriveTransformHelperArguments(Method transform, int whichtm, List args, List fields) { + throw new AssertionError(); + } + } + + class SpecTest extends ClassSpecializer { + + public SpecTest() { + super(Frob.class, Byte.class, Kind.class, + MethodType.methodType(void.class, int.class), + reflectMethod(FrobImpl.class, "kind"), "KIND", Arrays.asList()); + } + + @Override + protected Kind newSpeciesData(Byte key) { + return new Kind(this, key); + } + + @Override + protected Byte addFieldTypeToKey(Byte key, Class type) { + throw new AssertionError("NYI addFieldTypeToKey"); + } + + public static Kind kind(int key) { + return (Kind) SPEC_TEST.findSpecies((byte)key); + } + } + + static final SpecTest SPEC_TEST = new SpecTest(); + +} +