8131129: Attempt to define a duplicate BMH$Species class
authorplevart
Mon, 09 Nov 2015 13:44:58 +0100
changeset 33646 aeed9f07a50c
parent 33645 1af4c728e3e8
child 33647 2665054d3864
8131129: Attempt to define a duplicate BMH$Species class Reviewed-by: mhaupt, redestad, vlivanov
jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Mon Nov 09 13:58:10 2015 +0300
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Mon Nov 09 13:44:58 2015 +0100
@@ -34,14 +34,16 @@
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Field;
 import java.util.Arrays;
-import java.util.HashMap;
+import java.util.function.Function;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
 
+import jdk.internal.org.objectweb.asm.FieldVisitor;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.Wrapper;
 
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
-import jdk.internal.org.objectweb.asm.Type;
 
 /**
  * The flavor of method handle which emulates an invoke instruction
@@ -217,7 +219,7 @@
         /*non-public*/ int fieldCount() {
             return 1;
         }
-        /*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class);
+        /*non-public*/ static final SpeciesData SPECIES_DATA = new SpeciesData("L", Species_L.class);
         /*non-public*/ static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) {
             return new Species_L(mt, lf, argL0);
         }
@@ -335,7 +337,7 @@
 
         static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
 
-        private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
+        SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
             this.typeChars = types;
             this.typeCodes = basicTypes(types);
             this.clazz = clazz;
@@ -355,26 +357,14 @@
             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);
             }
         }
 
-        private SpeciesData(String typeChars) {
-            // Placeholder only.
-            this.typeChars = typeChars;
-            this.typeCodes = basicTypes(typeChars);
-            this.clazz = null;
-            this.constructor = null;
-            this.getters = null;
-            this.nominalGetters = null;
-            this.extensions = null;
-        }
-        private boolean isPlaceholder() { return clazz == null; }
-
-        private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
-        static { CACHE.put("", EMPTY); }  // make bootstrap predictable
+        private static final ConcurrentMap<String, SpeciesData> CACHE = new ConcurrentHashMap<>();
         private static final boolean INIT_DONE;  // set after <clinit> finishes...
 
         SpeciesData extendWith(byte type) {
@@ -390,62 +380,52 @@
         }
 
         private static SpeciesData get(String types) {
-            // Acquire cache lock for query.
-            SpeciesData d = lookupCache(types);
-            if (!d.isPlaceholder())
-                return d;
-            synchronized (d) {
-                // Use synch. on the placeholder to prevent multiple instantiation of one species.
-                // Creating this class forces a recursive call to getForClass.
-                if (lookupCache(types).isPlaceholder())
-                    Factory.generateConcreteBMHClass(types);
-            }
-            // Reacquire cache lock.
-            d = lookupCache(types);
-            // Class loading must have upgraded the cache.
-            assert(d != null && !d.isPlaceholder());
-            return d;
-        }
-        static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
-            // clazz is a new class which is initializing its SPECIES_DATA field
-            return updateCache(types, new SpeciesData(types, clazz));
-        }
-        private static synchronized SpeciesData lookupCache(String types) {
-            SpeciesData d = CACHE.get(types);
-            if (d != null)  return d;
-            d = new SpeciesData(types);
-            assert(d.isPlaceholder());
-            CACHE.put(types, d);
-            return d;
-        }
-        private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
-            SpeciesData d2;
-            assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
-            assert(!d.isPlaceholder());
-            CACHE.put(types, d);
-            return d;
+            return CACHE.computeIfAbsent(types, new Function<String, SpeciesData>() {
+                @Override
+                public SpeciesData apply(String types) {
+                    Class<? extends BoundMethodHandle> bmhcl = Factory.getConcreteBMHClass(types);
+                    // SpeciesData instantiation may throw VirtualMachineError because of
+                    // code cache overflow...
+                    SpeciesData speciesData = new SpeciesData(types, bmhcl);
+                    // CHM.computeIfAbsent ensures only one SpeciesData will be set
+                    // successfully on the concrete BMH class if ever
+                    Factory.setSpeciesDataToConcreteBMHClass(bmhcl, speciesData);
+                    // the concrete BMH class is published via SpeciesData instance
+                    // returned here only after it's SPECIES_DATA field is set
+                    return speciesData;
+                }
+            });
         }
 
-        static {
-            // pre-fill the BMH speciesdata cache with BMH's inner classes
-            final Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
+        /**
+         * This is to be called when assertions are enabled. It checks whether SpeciesData for all of the statically
+         * defined species subclasses of BoundMethodHandle has been added to the SpeciesData cache. See below in the
+         * static initializer for
+         */
+        static boolean speciesDataCachePopulated() {
+            Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
             try {
                 for (Class<?> c : rootCls.getDeclaredClasses()) {
                     if (rootCls.isAssignableFrom(c)) {
                         final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
-                        SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
+                        SpeciesData d = Factory.getSpeciesDataFromConcreteBMHClass(cbmh);
                         assert(d != null) : cbmh.getName();
                         assert(d.clazz == cbmh);
-                        assert(d == lookupCache(d.typeChars));
+                        assert(CACHE.get(d.typeChars) == d);
                     }
                 }
             } catch (Throwable e) {
                 throw newInternalError(e);
             }
+            return true;
+        }
 
-            for (SpeciesData d : CACHE.values()) {
-                d.initForBootstrap();
-            }
+        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;
@@ -479,6 +459,7 @@
         static final String BMH_SIG  = "L"+BMH+";";
         static final String SPECIES_DATA     = "java/lang/invoke/BoundMethodHandle$SpeciesData";
         static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
+        static final String STABLE_SIG       = "Ljava/lang/invoke/Stable;";
 
         static final String SPECIES_PREFIX_NAME = "Species_";
         static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
@@ -493,6 +474,26 @@
 
         static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
 
+        static final ConcurrentMap<String, Class<? extends BoundMethodHandle>> 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<? extends BoundMethodHandle> getConcreteBMHClass(String types) {
+            // CHM.computeIfAbsent ensures generateConcreteBMHClass is called
+            // only once per key.
+            return CLASS_CACHE.computeIfAbsent(
+                types, new Function<String, Class<? extends BoundMethodHandle>>() {
+                    @Override
+                    public Class<? extends BoundMethodHandle> apply(String types) {
+                        return generateConcreteBMHClass(types);
+                    }
+                });
+        }
+
         /**
          * Generate a concrete subclass of BMH for a given combination of bound types.
          *
@@ -529,7 +530,7 @@
          *     }
          *     final SpeciesData speciesData() { return SPECIES_DATA; }
          *     final int fieldCount() { return 3; }
-         *     static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
+         *     &#64;Stable static SpeciesData SPECIES_DATA; // injected afterwards
          *     static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
          *         return new Species_LLI(mt, lf, argL0, argL1, argI2);
          *     }
@@ -568,7 +569,9 @@
             cw.visitSource(sourceFile, null);
 
             // emit static types and SPECIES_DATA fields
-            cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd();
+            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) {
@@ -694,17 +697,6 @@
                 mv.visitEnd();
             }
 
-            // emit class initializer
-            mv = cw.visitMethod(NOT_ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null);
-            mv.visitCode();
-            mv.visitLdcInsn(types);
-            mv.visitLdcInsn(Type.getObjectType(className));
-            mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG, false);
-            mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
-            mv.visitInsn(RETURN);
-            mv.visitMaxs(0, 0);
-            mv.visitEnd();
-
             cw.visitEnd();
 
             // load class
@@ -715,7 +707,6 @@
                 UNSAFE.defineClass(className, classFile, 0, classFile.length,
                                    BoundMethodHandle.class.getClassLoader(), null)
                     .asSubclass(BoundMethodHandle.class);
-            UNSAFE.ensureClassInitialized(bmhClass);
 
             return bmhClass;
         }
@@ -785,7 +776,7 @@
         // Auxiliary methods.
         //
 
-        static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
+        static SpeciesData getSpeciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
             try {
                 Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
                 return (SpeciesData) F_SPECIES_DATA.get(null);
@@ -794,6 +785,16 @@
             }
         }
 
+        static void setSpeciesDataToConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh, SpeciesData speciesData) {
+            try {
+                Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
+                assert F_SPECIES_DATA.getDeclaredAnnotation(Stable.class) != null;
+                F_SPECIES_DATA.set(null, speciesData);
+            } catch (ReflectiveOperationException ex) {
+                throw newInternalError(ex);
+            }
+        }
+
         /**
          * Field names in concrete BMHs adhere to this pattern:
          * arg + type + index