< 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 >