< prev index next >

src/share/classes/java/lang/invoke/BoundMethodHandle.java

Print this page
rev 11243 : 8131129: Attempt to define a duplicate BMH$Species class

*** 32,49 **** import java.lang.invoke.LambdaForm.NamedFunction; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; import java.util.Arrays; ! import java.util.HashMap; 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 * on a predetermined argument. The JVM dispatches to the correct method * when the handle is created, not when it is invoked. --- 32,51 ---- import java.lang.invoke.LambdaForm.NamedFunction; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; import java.util.Arrays; ! 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; /** * The flavor of method handle which emulates an invoke instruction * on a predetermined argument. The JVM dispatches to the correct method * when the handle is created, not when it is invoked.
*** 215,225 **** } @Override /*non-public*/ int fieldCount() { return 1; } ! /*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class); /*non-public*/ static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) { return new Species_L(mt, lf, argL0); } @Override /*non-public*/ final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) { --- 217,227 ---- } @Override /*non-public*/ int fieldCount() { return 1; } ! /*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); } @Override /*non-public*/ final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
*** 333,343 **** return constructor[0]; } static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); ! private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) { this.typeChars = types; this.typeCodes = basicTypes(types); this.clazz = clazz; if (!INIT_DONE) { this.constructor = new MethodHandle[1]; // only one ctor --- 335,345 ---- return constructor[0]; } static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); ! SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) { this.typeChars = types; this.typeCodes = basicTypes(types); this.clazz = clazz; if (!INIT_DONE) { this.constructor = new MethodHandle[1]; // only one ctor
*** 353,382 **** private void initForBootstrap() { assert(!INIT_DONE); if (constructor() == null) { String types = typeChars; 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 boolean INIT_DONE; // set after <clinit> finishes... SpeciesData extendWith(byte type) { return extendWith(BasicType.basicType(type)); } --- 355,372 ---- 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); } } ! private static final ConcurrentMap<String, SpeciesData> CACHE = new ConcurrentHashMap<>(); private static final boolean INIT_DONE; // set after <clinit> finishes... SpeciesData extendWith(byte type) { return extendWith(BasicType.basicType(type)); }
*** 388,453 **** extensions[ord] = d = get(typeChars+type.basicTypeChar()); return d; } 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; } ! static { ! // pre-fill the BMH speciesdata cache with BMH's inner classes ! final 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); assert(d != null) : cbmh.getName(); assert(d.clazz == cbmh); ! assert(d == lookupCache(d.typeChars)); } } } catch (Throwable e) { throw newInternalError(e); } ! ! for (SpeciesData d : CACHE.values()) { ! d.initForBootstrap(); } // Note: Do not simplify this, because INIT_DONE must not be // a compile-time constant during bootstrapping. INIT_DONE = Boolean.TRUE; } } --- 378,433 ---- extensions[ord] = d = get(typeChars+type.basicTypeChar()); return d; } private static SpeciesData get(String types) { ! return CACHE.computeIfAbsent(types, new Function<String, SpeciesData>() { ! @Override ! public SpeciesData apply(String types) { ! Class<? extends BoundMethodHandle> bmhcl = Factory.getConcreteBMHClass(types); ! // SpeciesData instantiation may throw VirtualMachineError because of ! // code cache overflow... ! SpeciesData speciesData = new SpeciesData(types, bmhcl); ! // CHM.computeIfAbsent ensures only one SpeciesData will be set ! // successfully on the concrete BMH class if ever ! Factory.setSpeciesDataToConcreteBMHClass(bmhcl, speciesData); ! // the concrete BMH class is published via SpeciesData instance ! // returned here only after it's SPECIES_DATA field is set ! return speciesData; } ! }); } ! /** ! * This is to be called when assertions are enabled. It checks whether SpeciesData for all of the statically ! * defined species subclasses of BoundMethodHandle has been added to the SpeciesData cache. See below in the ! * static initializer for ! */ ! static boolean speciesDataCachePopulated() { ! Class<BoundMethodHandle> rootCls = BoundMethodHandle.class; try { for (Class<?> c : rootCls.getDeclaredClasses()) { if (rootCls.isAssignableFrom(c)) { final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class); ! SpeciesData d = Factory.getSpeciesDataFromConcreteBMHClass(cbmh); assert(d != null) : cbmh.getName(); assert(d.clazz == cbmh); ! assert(CACHE.get(d.typeChars) == d); } } } catch (Throwable e) { throw newInternalError(e); } ! return true; } + + static { + // Pre-fill the BMH species-data cache with EMPTY and all BMH's inner subclasses. + EMPTY.initForBootstrap(); + Species_L.SPECIES_DATA.initForBootstrap(); + // check that all static SpeciesData instances have been initialized + assert speciesDataCachePopulated(); // Note: Do not simplify this, because INIT_DONE must not be // a compile-time constant during bootstrapping. INIT_DONE = Boolean.TRUE; } }
*** 477,486 **** --- 457,467 ---- static final String MH_SIG = "L"+MH+";"; static final String BMH = "java/lang/invoke/BoundMethodHandle"; static final String BMH_SIG = "L"+BMH+";"; static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData"; static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";"; + static final String STABLE_SIG = "Ljava/lang/invoke/Stable;"; static final String SPECIES_PREFIX_NAME = "Species_"; static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME; static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
*** 491,500 **** --- 472,501 ---- static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;"; 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. * * A concrete BMH species adheres to the following schema: *
*** 527,537 **** * this.argL1 = argL1; * this.argI2 = argI2; * } * final SpeciesData speciesData() { return SPECIES_DATA; } * final int fieldCount() { return 3; } ! * static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class); * 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); --- 528,538 ---- * 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);
*** 566,576 **** 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 ! cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).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); --- 567,579 ---- 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);
*** 692,723 **** mv.visitInsn(ARETURN); mv.visitMaxs(0, 0); 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 final byte[] classFile = cw.toByteArray(); InvokerBytecodeGenerator.maybeDump(className, classFile); Class<? extends BoundMethodHandle> bmhClass = //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class); UNSAFE.defineClass(className, classFile, 0, classFile.length, BoundMethodHandle.class.getClassLoader(), null) .asSubclass(BoundMethodHandle.class); - UNSAFE.ensureClassInitialized(bmhClass); return bmhClass; } private static int typeLoadOp(char t) { --- 695,714 ----
*** 783,801 **** // // Auxiliary methods. // ! static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) { try { Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA"); return (SpeciesData) F_SPECIES_DATA.get(null); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } } /** * Field names in concrete BMHs adhere to this pattern: * arg + type + index * where type is a single character (L, I, J, F, D). */ --- 774,802 ---- // // Auxiliary methods. // ! static SpeciesData getSpeciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) { try { Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA"); return (SpeciesData) F_SPECIES_DATA.get(null); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } } + static void setSpeciesDataToConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh, SpeciesData speciesData) { + try { + Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA"); + assert F_SPECIES_DATA.getDeclaredAnnotation(Stable.class) != null; + F_SPECIES_DATA.set(null, speciesData); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + /** * Field names in concrete BMHs adhere to this pattern: * arg + type + index * where type is a single character (L, I, J, F, D). */
< prev index next >