8178387: Reduce memory churn when creating java.lang.invoke entities
Reviewed-by: psandoz, vlivanov
This commit is contained in:
parent
9ab899d481
commit
864cf0d5ca
@ -827,15 +827,27 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
private static String makeSignature(String types, boolean ctor) {
|
||||
StringBuilder buf = new StringBuilder(SIG_INCIPIT);
|
||||
for (char c : types.toCharArray()) {
|
||||
buf.append(typeSig(c));
|
||||
int len = types.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
buf.append(typeSig(types.charAt(i)));
|
||||
}
|
||||
return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
|
||||
}
|
||||
|
||||
private static MethodType makeConstructorType(String types) {
|
||||
int length = types.length();
|
||||
Class<?> ptypes[] = new Class<?>[length + 2];
|
||||
ptypes[0] = MethodType.class;
|
||||
ptypes[1] = LambdaForm.class;
|
||||
for (int i = 0; i < length; i++) {
|
||||
ptypes[i + 2] = BasicType.basicType(types.charAt(i)).basicTypeClass();
|
||||
}
|
||||
return MethodType.makeImpl(BoundMethodHandle.class, ptypes, true);
|
||||
}
|
||||
|
||||
static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
|
||||
try {
|
||||
return LOOKUP.findStatic(cbmh, "make", MethodType.fromDescriptor(makeSignature(types, false), null));
|
||||
return LOOKUP.findStatic(cbmh, "make", makeConstructorType(types));
|
||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ class InvokerBytecodeGenerator {
|
||||
private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
|
||||
private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
|
||||
private static final String CLASS_PREFIX = LF + "$";
|
||||
private static final String SOURCE_PREFIX = "LambdaForm$";
|
||||
|
||||
/** Name of its super class*/
|
||||
static final String INVOKER_SUPER_NAME = OBJ;
|
||||
@ -80,9 +81,6 @@ class InvokerBytecodeGenerator {
|
||||
/** Name of new class */
|
||||
private final String className;
|
||||
|
||||
/** Name of the source file (for stack trace printing). */
|
||||
private final String sourceFile;
|
||||
|
||||
private final LambdaForm lambdaForm;
|
||||
private final String invokerName;
|
||||
private final MethodType invokerType;
|
||||
@ -109,8 +107,7 @@ class InvokerBytecodeGenerator {
|
||||
if (DUMP_CLASS_FILES) {
|
||||
className = makeDumpableClassName(className);
|
||||
}
|
||||
this.className = CLASS_PREFIX + className;
|
||||
this.sourceFile = "LambdaForm$" + className;
|
||||
this.className = className;
|
||||
this.lambdaForm = lambdaForm;
|
||||
this.invokerName = invokerName;
|
||||
this.invokerType = invokerType;
|
||||
@ -173,6 +170,13 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeDump(final byte[] classFile) {
|
||||
if (DUMP_CLASS_FILES) {
|
||||
maybeDump(CLASS_PREFIX + className, classFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Also used from BoundMethodHandle
|
||||
static void maybeDump(final String className, final byte[] classFile) {
|
||||
if (DUMP_CLASS_FILES) {
|
||||
java.security.AccessController.doPrivileged(
|
||||
@ -306,8 +310,9 @@ class InvokerBytecodeGenerator {
|
||||
private ClassWriter classFilePrologue() {
|
||||
final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
|
||||
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, INVOKER_SUPER_NAME, null);
|
||||
cw.visitSource(sourceFile, null);
|
||||
cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
|
||||
CLASS_PREFIX + className, null, INVOKER_SUPER_NAME, null);
|
||||
cw.visitSource(SOURCE_PREFIX + className, null);
|
||||
return cw;
|
||||
}
|
||||
|
||||
@ -617,12 +622,11 @@ class InvokerBytecodeGenerator {
|
||||
return resolvedMember;
|
||||
}
|
||||
|
||||
private static MemberName lookupPregenerated(LambdaForm form) {
|
||||
private static MemberName lookupPregenerated(LambdaForm form, MethodType invokerType) {
|
||||
if (form.customized != null) {
|
||||
// No pre-generated version for customized LF
|
||||
return null;
|
||||
}
|
||||
MethodType invokerType = form.methodType();
|
||||
String name = form.kind.methodName;
|
||||
switch (form.kind) {
|
||||
case BOUND_REINVOKER: {
|
||||
@ -669,8 +673,10 @@ class InvokerBytecodeGenerator {
|
||||
/**
|
||||
* Generate customized bytecode for a given LambdaForm.
|
||||
*/
|
||||
static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
|
||||
MemberName pregenerated = lookupPregenerated(form);
|
||||
static MemberName generateCustomizedCode(LambdaForm form) {
|
||||
final MethodType invokerType = form.methodType();
|
||||
|
||||
MemberName pregenerated = lookupPregenerated(form, invokerType);
|
||||
if (pregenerated != null) return pregenerated; // pre-generated bytecode
|
||||
|
||||
InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
|
||||
@ -720,7 +726,7 @@ class InvokerBytecodeGenerator {
|
||||
bogusMethod(lambdaForm);
|
||||
|
||||
final byte[] classFile = toByteArray();
|
||||
maybeDump(className, classFile);
|
||||
maybeDump(classFile);
|
||||
return classFile;
|
||||
}
|
||||
|
||||
@ -1761,7 +1767,7 @@ class InvokerBytecodeGenerator {
|
||||
bogusMethod(invokerType);
|
||||
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
maybeDump(className, classFile);
|
||||
maybeDump(classFile);
|
||||
return classFile;
|
||||
}
|
||||
|
||||
@ -1829,7 +1835,7 @@ class InvokerBytecodeGenerator {
|
||||
bogusMethod(dstType);
|
||||
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
maybeDump(className, classFile);
|
||||
maybeDump(classFile);
|
||||
return classFile;
|
||||
}
|
||||
|
||||
|
@ -641,7 +641,7 @@ class LambdaForm {
|
||||
for (int i = 0; i < arity; ++i) {
|
||||
ptypes[i] = parameterType(i).btClass;
|
||||
}
|
||||
return MethodType.methodType(returnType().btClass, ptypes);
|
||||
return MethodType.makeImpl(returnType().btClass, ptypes, true);
|
||||
}
|
||||
|
||||
/** Return ABC_Z, where the ABC are parameter type characters, and Z is the return type character. */
|
||||
@ -677,7 +677,7 @@ class LambdaForm {
|
||||
for (int i = 0; i < ptypes.length; i++)
|
||||
ptypes[i] = basicType(sig.charAt(i)).btClass;
|
||||
Class<?> rtype = signatureReturn(sig).btClass;
|
||||
return MethodType.methodType(rtype, ptypes);
|
||||
return MethodType.makeImpl(rtype, ptypes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -847,10 +847,9 @@ class LambdaForm {
|
||||
if (vmentry != null && isCompiled) {
|
||||
return; // already compiled somehow
|
||||
}
|
||||
MethodType invokerType = methodType();
|
||||
assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
|
||||
assert(vmentry == null || vmentry.getMethodType().basicType().equals(methodType()));
|
||||
try {
|
||||
vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
|
||||
vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this);
|
||||
if (TRACE_INTERPRETER)
|
||||
traceInterpreter("compileToBytecode", this);
|
||||
isCompiled = true;
|
||||
@ -901,10 +900,6 @@ class LambdaForm {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private static boolean returnTypesMatch(String sig, Object[] av, Object res) {
|
||||
MethodHandle mh = (MethodHandle) av[0];
|
||||
return valueMatches(signatureReturn(sig), mh.type().returnType(), res);
|
||||
}
|
||||
private static boolean checkInt(Class<?> type, Object x) {
|
||||
assert(x instanceof Integer);
|
||||
if (type == int.class) return true;
|
||||
@ -1179,7 +1174,6 @@ class LambdaForm {
|
||||
// If we have a cached invoker, call it right away.
|
||||
// NOTE: The invoker always returns a reference value.
|
||||
if (TRACE_INTERPRETER) return invokeWithArgumentsTracing(arguments);
|
||||
assert(checkArgumentTypes(arguments, methodType()));
|
||||
return invoker().invokeBasic(resolvedHandle(), arguments);
|
||||
}
|
||||
|
||||
@ -1197,7 +1191,6 @@ class LambdaForm {
|
||||
traceInterpreter("| resolve", this);
|
||||
resolvedHandle();
|
||||
}
|
||||
assert(checkArgumentTypes(arguments, methodType()));
|
||||
rval = invoker().invokeBasic(resolvedHandle(), arguments);
|
||||
} catch (Throwable ex) {
|
||||
traceInterpreter("] throw =>", ex);
|
||||
@ -1213,23 +1206,6 @@ class LambdaForm {
|
||||
return invoker = computeInvoker(methodType().form());
|
||||
}
|
||||
|
||||
private static boolean checkArgumentTypes(Object[] arguments, MethodType methodType) {
|
||||
if (true) return true; // FIXME
|
||||
MethodType dstType = methodType.form().erasedType();
|
||||
MethodType srcType = dstType.basicType().wrap();
|
||||
Class<?>[] ptypes = new Class<?>[arguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
Object arg = arguments[i];
|
||||
Class<?> ptype = arg == null ? Object.class : arg.getClass();
|
||||
// If the dest. type is a primitive we keep the
|
||||
// argument type.
|
||||
ptypes[i] = dstType.parameterType(i).isPrimitive() ? ptype : Object.class;
|
||||
}
|
||||
MethodType argType = MethodType.methodType(srcType.returnType(), ptypes).wrap();
|
||||
assert(argType.isConvertibleTo(srcType)) : "wrong argument types: cannot convert " + argType + " to " + srcType;
|
||||
return true;
|
||||
}
|
||||
|
||||
MethodType methodType() {
|
||||
if (resolvedHandle != null)
|
||||
return resolvedHandle.type();
|
||||
@ -1725,7 +1701,7 @@ class LambdaForm {
|
||||
boolean isVoid = (type == V_TYPE);
|
||||
Class<?> btClass = type.btClass;
|
||||
MethodType zeType = MethodType.methodType(btClass);
|
||||
MethodType idType = (isVoid) ? zeType : zeType.appendParameterTypes(btClass);
|
||||
MethodType idType = (isVoid) ? zeType : MethodType.methodType(btClass, btClass);
|
||||
|
||||
// Look up symbolic names. It might not be necessary to have these,
|
||||
// but if we need to emit direct references to bytecodes, it helps.
|
||||
|
@ -149,7 +149,7 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
||||
Object[] typeInfo = (Object[]) type;
|
||||
Class<?>[] ptypes = (Class<?>[]) typeInfo[1];
|
||||
Class<?> rtype = (Class<?>) typeInfo[0];
|
||||
MethodType res = MethodType.methodType(rtype, ptypes);
|
||||
MethodType res = MethodType.makeImpl(rtype, ptypes, true);
|
||||
type = res;
|
||||
}
|
||||
// Make sure type is a MethodType for racing threads.
|
||||
|
@ -1674,6 +1674,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
// Local constant functions:
|
||||
|
||||
/* non-public */
|
||||
static final byte NF_checkSpreadArgument = 0,
|
||||
NF_guardWithCatch = 1,
|
||||
NF_throwException = 2,
|
||||
@ -1682,7 +1683,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
NF_profileBoolean = 5,
|
||||
NF_LIMIT = 6;
|
||||
|
||||
/*non-public*/
|
||||
private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT];
|
||||
|
||||
static NamedFunction getFunction(byte func) {
|
||||
|
@ -95,7 +95,7 @@ class MethodType implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 292L; // {rtype, {ptype...}}
|
||||
|
||||
// The rtype and ptypes fields define the structural identity of the method type:
|
||||
private final Class<?> rtype;
|
||||
private final @Stable Class<?> rtype;
|
||||
private final @Stable Class<?>[] ptypes;
|
||||
|
||||
// The remaining fields are caches of various sorts:
|
||||
@ -117,7 +117,8 @@ class MethodType implements java.io.Serializable {
|
||||
|
||||
/**
|
||||
* Construct a temporary unchecked instance of MethodType for use only as a key to the intern table.
|
||||
* Does not check the given parameters for validity, and must be discarded after it is used as a searching key.
|
||||
* Does not check the given parameters for validity, and must discarded (if untrusted) or checked
|
||||
* (if trusted) after it has been used as a searching key.
|
||||
* The parameters are reversed for this constructor, so that it is not accidentally used.
|
||||
*/
|
||||
private MethodType(Class<?>[] ptypes, Class<?> rtype) {
|
||||
@ -181,6 +182,7 @@ class MethodType implements java.io.Serializable {
|
||||
checkSlotCount(ptypes.length + slots);
|
||||
return slots;
|
||||
}
|
||||
|
||||
static {
|
||||
// MAX_JVM_ARITY must be power of 2 minus 1 for following code trick to work:
|
||||
assert((MAX_JVM_ARITY & (MAX_JVM_ARITY+1)) == 0);
|
||||
@ -303,18 +305,26 @@ class MethodType implements java.io.Serializable {
|
||||
*/
|
||||
/*trusted*/ static
|
||||
MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
|
||||
MethodType mt = internTable.get(new MethodType(ptypes, rtype));
|
||||
if (mt != null)
|
||||
return mt;
|
||||
if (ptypes.length == 0) {
|
||||
ptypes = NO_PTYPES; trusted = true;
|
||||
}
|
||||
mt = new MethodType(rtype, ptypes, trusted);
|
||||
MethodType primordialMT = new MethodType(ptypes, rtype);
|
||||
MethodType mt = internTable.get(primordialMT);
|
||||
if (mt != null)
|
||||
return mt;
|
||||
|
||||
// promote the object to the Real Thing, and reprobe
|
||||
if (trusted) {
|
||||
MethodType.checkRtype(rtype);
|
||||
MethodType.checkPtypes(ptypes);
|
||||
mt = primordialMT;
|
||||
} else {
|
||||
mt = new MethodType(rtype, ptypes, false);
|
||||
}
|
||||
mt.form = MethodTypeForm.findForm(mt);
|
||||
return internTable.add(mt);
|
||||
}
|
||||
private static final MethodType[] objectOnlyTypes = new MethodType[20];
|
||||
private static final @Stable MethodType[] objectOnlyTypes = new MethodType[20];
|
||||
|
||||
/**
|
||||
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
|
||||
@ -398,9 +408,14 @@ class MethodType implements java.io.Serializable {
|
||||
checkSlotCount(parameterSlotCount() + ptypesToInsert.length + ins);
|
||||
int ilen = ptypesToInsert.length;
|
||||
if (ilen == 0) return this;
|
||||
Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen);
|
||||
System.arraycopy(nptypes, num, nptypes, num+ilen, len-num);
|
||||
Class<?>[] nptypes = new Class<?>[len + ilen];
|
||||
if (num > 0) {
|
||||
System.arraycopy(ptypes, 0, nptypes, 0, num);
|
||||
}
|
||||
System.arraycopy(ptypesToInsert, 0, nptypes, num, ilen);
|
||||
if (num < len) {
|
||||
System.arraycopy(ptypes, num, nptypes, num+ilen, len-num);
|
||||
}
|
||||
return makeImpl(rtype, nptypes, true);
|
||||
}
|
||||
|
||||
@ -636,11 +651,14 @@ class MethodType implements java.io.Serializable {
|
||||
return form.basicType();
|
||||
}
|
||||
|
||||
private static final @Stable Class<?>[] METHOD_HANDLE_ARRAY
|
||||
= new Class<?>[] { MethodHandle.class };
|
||||
|
||||
/**
|
||||
* @return a version of the original type with MethodHandle prepended as the first argument
|
||||
*/
|
||||
/*non-public*/ MethodType invokerType() {
|
||||
return insertParameterTypes(0, MethodHandle.class);
|
||||
return insertParameterTypes(0, METHOD_HANDLE_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,10 +297,13 @@ public class VerifyAccess {
|
||||
* @param refc the class attempting to make the reference
|
||||
*/
|
||||
public static boolean isTypeVisible(java.lang.invoke.MethodType type, Class<?> refc) {
|
||||
for (int n = -1, max = type.parameterCount(); n < max; n++) {
|
||||
Class<?> ptype = (n < 0 ? type.returnType() : type.parameterType(n));
|
||||
if (!isTypeVisible(ptype, refc))
|
||||
if (!isTypeVisible(type.returnType(), refc)) {
|
||||
return false;
|
||||
}
|
||||
for (int n = 0, max = type.parameterCount(); n < max; n++) {
|
||||
if (!isTypeVisible(type.parameterType(n), refc)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -518,12 +518,6 @@ public enum Wrapper {
|
||||
* If the target type is a primitive, change it to a wrapper.
|
||||
*/
|
||||
static <T> Class<T> forceType(Class<?> type, Class<T> exampleType) {
|
||||
boolean z = (type == exampleType ||
|
||||
type.isPrimitive() && forPrimitiveType(type) == findWrapperType(exampleType) ||
|
||||
exampleType.isPrimitive() && forPrimitiveType(exampleType) == findWrapperType(type) ||
|
||||
type == Object.class && !exampleType.isPrimitive());
|
||||
if (!z)
|
||||
System.out.println(type+" <= "+exampleType);
|
||||
assert(type == exampleType ||
|
||||
type.isPrimitive() && forPrimitiveType(type) == findWrapperType(exampleType) ||
|
||||
exampleType.isPrimitive() && forPrimitiveType(exampleType) == findWrapperType(type) ||
|
||||
|
Loading…
Reference in New Issue
Block a user