mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8260605: Various java.lang.invoke cleanups
Reviewed-by: mchung
This commit is contained in:
parent
9cd21b687e
commit
63eae8fac2
4 changed files with 118 additions and 246 deletions
|
@ -84,6 +84,7 @@ class InvokerBytecodeGenerator {
|
|||
static final String INVOKER_SUPER_NAME = OBJ;
|
||||
|
||||
/** Name of new class */
|
||||
private final String name;
|
||||
private final String className;
|
||||
|
||||
private final LambdaForm lambdaForm;
|
||||
|
@ -117,16 +118,17 @@ class InvokerBytecodeGenerator {
|
|||
|
||||
/** Main constructor; other constructors delegate to this one. */
|
||||
private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
|
||||
String className, String invokerName, MethodType invokerType) {
|
||||
String name, String invokerName, MethodType invokerType) {
|
||||
int p = invokerName.indexOf('.');
|
||||
if (p > -1) {
|
||||
className = invokerName.substring(0, p);
|
||||
name = invokerName.substring(0, p);
|
||||
invokerName = invokerName.substring(p + 1);
|
||||
}
|
||||
if (DUMP_CLASS_FILES) {
|
||||
className = makeDumpableClassName(className);
|
||||
name = makeDumpableClassName(name);
|
||||
}
|
||||
this.className = className;
|
||||
this.name = name;
|
||||
this.className = CLASS_PREFIX + name;
|
||||
this.lambdaForm = lambdaForm;
|
||||
this.invokerName = invokerName;
|
||||
this.invokerType = invokerType;
|
||||
|
@ -135,9 +137,9 @@ class InvokerBytecodeGenerator {
|
|||
}
|
||||
|
||||
/** For generating LambdaForm interpreter entry points. */
|
||||
private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
|
||||
private InvokerBytecodeGenerator(String name, String invokerName, MethodType invokerType) {
|
||||
this(null, invokerType.parameterCount(),
|
||||
className, invokerName, invokerType);
|
||||
name, invokerName, invokerType);
|
||||
MethodType mt = invokerType.erase();
|
||||
// Create an array to map name indexes to locals indexes.
|
||||
localsMap[0] = 0; // localsMap has at least one element
|
||||
|
@ -149,15 +151,15 @@ class InvokerBytecodeGenerator {
|
|||
}
|
||||
|
||||
/** For generating customized code for a single LambdaForm. */
|
||||
private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
|
||||
this(className, form.lambdaName(), form, invokerType);
|
||||
private InvokerBytecodeGenerator(String name, LambdaForm form, MethodType invokerType) {
|
||||
this(name, form.lambdaName(), form, invokerType);
|
||||
}
|
||||
|
||||
/** For generating customized code for a single LambdaForm. */
|
||||
InvokerBytecodeGenerator(String className, String invokerName,
|
||||
InvokerBytecodeGenerator(String name, String invokerName,
|
||||
LambdaForm form, MethodType invokerType) {
|
||||
this(form, form.names.length,
|
||||
className, invokerName, invokerType);
|
||||
name, invokerName, invokerType);
|
||||
// Create an array to map name indexes to locals indexes.
|
||||
Name[] names = form.names;
|
||||
for (int i = 0, index = 0; i < localsMap.length; i++) {
|
||||
|
@ -195,7 +197,7 @@ class InvokerBytecodeGenerator {
|
|||
|
||||
private void maybeDump(final byte[] classFile) {
|
||||
if (DUMP_CLASS_FILES) {
|
||||
maybeDump(CLASS_PREFIX + className, classFile);
|
||||
maybeDump(className, classFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,7 +233,7 @@ class InvokerBytecodeGenerator {
|
|||
}
|
||||
String sfx = ctr.toString();
|
||||
while (sfx.length() < 3)
|
||||
sfx = "0"+sfx;
|
||||
sfx = "0" + sfx;
|
||||
className += sfx;
|
||||
return className;
|
||||
}
|
||||
|
@ -265,23 +267,38 @@ class InvokerBytecodeGenerator {
|
|||
desc = "Ljava/lang/Object;";
|
||||
}
|
||||
|
||||
Class<?> c = arg.getClass();
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
// unique static variable name
|
||||
String name = "_DATA_" + c.getSimpleName() + "_" + classData.size();
|
||||
String name;
|
||||
if (DUMP_CLASS_FILES) {
|
||||
Class<?> c = arg.getClass();
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
name = "_DATA_" + c.getSimpleName() + "_" + classData.size();
|
||||
} else {
|
||||
name = "_D_" + classData.size();
|
||||
}
|
||||
ClassData cd = new ClassData(name, desc, arg);
|
||||
classData.add(cd);
|
||||
return cd.name();
|
||||
return name;
|
||||
}
|
||||
|
||||
List<Object> classDataValues() {
|
||||
Object[] data = new Object[classData.size()];
|
||||
for (int i = 0; i < classData.size(); i++) {
|
||||
data[i] = classData.get(i).value;
|
||||
}
|
||||
return List.of(data);
|
||||
final List<ClassData> cd = classData;
|
||||
return switch(cd.size()) {
|
||||
case 0 -> List.of();
|
||||
case 1 -> List.of(cd.get(0).value);
|
||||
case 2 -> List.of(cd.get(0).value, cd.get(1).value);
|
||||
case 3 -> List.of(cd.get(0).value, cd.get(1).value, cd.get(2).value);
|
||||
case 4 -> List.of(cd.get(0).value, cd.get(1).value, cd.get(2).value, cd.get(3).value);
|
||||
default -> {
|
||||
Object[] data = new Object[classData.size()];
|
||||
for (int i = 0; i < classData.size(); i++) {
|
||||
data[i] = classData.get(i).value;
|
||||
}
|
||||
yield List.of(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static String debugString(Object arg) {
|
||||
|
@ -295,26 +312,11 @@ class InvokerBytecodeGenerator {
|
|||
return arg.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the number of constant pool entries from a given class file.
|
||||
*
|
||||
* @param classFile the bytes of the class file in question.
|
||||
* @return the number of entries in the constant pool.
|
||||
*/
|
||||
private static int getConstantPoolSize(byte[] classFile) {
|
||||
// The first few bytes:
|
||||
// u4 magic;
|
||||
// u2 minor_version;
|
||||
// u2 major_version;
|
||||
// u2 constant_pool_count;
|
||||
return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the MemberName of a newly-defined method.
|
||||
*/
|
||||
private MemberName loadMethod(byte[] classFile) {
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className(), classFile)
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile)
|
||||
.defineClass(true, classDataValues());
|
||||
return resolveInvokerMember(invokerClass, invokerName, invokerType);
|
||||
}
|
||||
|
@ -339,8 +341,8 @@ class InvokerBytecodeGenerator {
|
|||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
setClassWriter(cw);
|
||||
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);
|
||||
className, null, INVOKER_SUPER_NAME, null);
|
||||
cw.visitSource(SOURCE_PREFIX + name, null);
|
||||
return cw;
|
||||
}
|
||||
|
||||
|
@ -357,14 +359,6 @@ class InvokerBytecodeGenerator {
|
|||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private String className() {
|
||||
return CLASS_PREFIX + className;
|
||||
}
|
||||
|
||||
private void clinit() {
|
||||
clinit(cw, className(), classData);
|
||||
}
|
||||
|
||||
/*
|
||||
* <clinit> to initialize the static final fields with the live class data
|
||||
* LambdaForms can't use condy due to bootstrapping issue.
|
||||
|
@ -647,7 +641,7 @@ class InvokerBytecodeGenerator {
|
|||
String sig = getInternalName(cls);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, sig);
|
||||
} else {
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(cls), "Ljava/lang/Class;");
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(cls), "Ljava/lang/Class;");
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG, false);
|
||||
if (Object[].class.isAssignableFrom(cls))
|
||||
|
@ -665,17 +659,15 @@ class InvokerBytecodeGenerator {
|
|||
* Emits an actual return instruction conforming to the given return type.
|
||||
*/
|
||||
private void emitReturnInsn(BasicType type) {
|
||||
int opcode;
|
||||
switch (type) {
|
||||
case I_TYPE: opcode = Opcodes.IRETURN; break;
|
||||
case J_TYPE: opcode = Opcodes.LRETURN; break;
|
||||
case F_TYPE: opcode = Opcodes.FRETURN; break;
|
||||
case D_TYPE: opcode = Opcodes.DRETURN; break;
|
||||
case L_TYPE: opcode = Opcodes.ARETURN; break;
|
||||
case V_TYPE: opcode = Opcodes.RETURN; break;
|
||||
default:
|
||||
throw new InternalError("unknown return type: " + type);
|
||||
}
|
||||
int opcode = switch (type) {
|
||||
case I_TYPE -> Opcodes.IRETURN;
|
||||
case J_TYPE -> Opcodes.LRETURN;
|
||||
case F_TYPE -> Opcodes.FRETURN;
|
||||
case D_TYPE -> Opcodes.DRETURN;
|
||||
case L_TYPE -> Opcodes.ARETURN;
|
||||
case V_TYPE -> Opcodes.RETURN;
|
||||
default -> throw new InternalError("unknown return type: " + type);
|
||||
};
|
||||
mv.visitInsn(opcode);
|
||||
}
|
||||
|
||||
|
@ -722,7 +714,7 @@ class InvokerBytecodeGenerator {
|
|||
case LINK_TO_CALL_SITE: // fall-through
|
||||
case LINK_TO_TARGET_METHOD: // fall-through
|
||||
case GENERIC_INVOKER: // fall-through
|
||||
case GENERIC_LINKER: return resolveFrom(name, invokerType.basicType(), Invokers.Holder.class);
|
||||
case GENERIC_LINKER: return resolveFrom(name, invokerType, Invokers.Holder.class);
|
||||
case GET_REFERENCE: // fall-through
|
||||
case GET_BOOLEAN: // fall-through
|
||||
case GET_BYTE: // fall-through
|
||||
|
@ -803,7 +795,7 @@ class InvokerBytecodeGenerator {
|
|||
private byte[] generateCustomizedCodeBytes() {
|
||||
classFilePrologue();
|
||||
addMethod();
|
||||
clinit();
|
||||
clinit(cw, className, classData);
|
||||
bogusMethod(lambdaForm);
|
||||
|
||||
final byte[] classFile = toByteArray();
|
||||
|
@ -838,7 +830,7 @@ class InvokerBytecodeGenerator {
|
|||
// receiver MethodHandle (at slot #0) with an embedded constant and use it instead.
|
||||
// It enables more efficient code generation in some situations, since embedded constants
|
||||
// are compile-time constants for JIT compiler.
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(lambdaForm.customized), MH_SIG);
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(lambdaForm.customized), MH_SIG);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
|
||||
assert(checkActualReceiver()); // expects MethodHandle on top of the stack
|
||||
mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]);
|
||||
|
@ -971,7 +963,7 @@ class InvokerBytecodeGenerator {
|
|||
// push receiver
|
||||
MethodHandle target = name.function.resolvedHandle();
|
||||
assert(target != null) : name.exprString();
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(target), MH_SIG);
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(target), MH_SIG);
|
||||
emitReferenceCast(MethodHandle.class, target);
|
||||
} else {
|
||||
// load receiver
|
||||
|
@ -990,7 +982,7 @@ class InvokerBytecodeGenerator {
|
|||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
|
||||
}
|
||||
|
||||
private static Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
|
||||
private static final Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
|
||||
// Sample classes from each package we are willing to bind to statically:
|
||||
java.lang.Object.class,
|
||||
java.util.Arrays.class,
|
||||
|
@ -1134,7 +1126,7 @@ class InvokerBytecodeGenerator {
|
|||
}
|
||||
assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
|
||||
assert(emptyArray.getClass() == rtype); // exact typing
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(emptyArray), "Ljava/lang/Object;");
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(emptyArray), "Ljava/lang/Object;");
|
||||
emitReferenceCast(rtype, emptyArray);
|
||||
return;
|
||||
}
|
||||
|
@ -1691,13 +1683,15 @@ class InvokerBytecodeGenerator {
|
|||
Name n = (Name) arg;
|
||||
emitLoadInsn(n.type, n.index());
|
||||
emitImplicitConversion(n.type, ptype, n);
|
||||
} else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
|
||||
emitConst(arg);
|
||||
} else if (arg == null && bptype == L_TYPE) {
|
||||
mv.visitInsn(Opcodes.ACONST_NULL);
|
||||
} else if (arg instanceof String && bptype == L_TYPE) {
|
||||
mv.visitLdcInsn(arg);
|
||||
} else {
|
||||
if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {
|
||||
emitConst(arg);
|
||||
} else {
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(arg), "Ljava/lang/Object;");
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(arg), "Ljava/lang/Object;");
|
||||
emitImplicitConversion(L_TYPE, ptype, arg);
|
||||
}
|
||||
}
|
||||
|
@ -1828,10 +1822,10 @@ class InvokerBytecodeGenerator {
|
|||
|
||||
private void emitX2I(Wrapper type) {
|
||||
switch (type) {
|
||||
case LONG: mv.visitInsn(Opcodes.L2I); break;
|
||||
case FLOAT: mv.visitInsn(Opcodes.F2I); break;
|
||||
case DOUBLE: mv.visitInsn(Opcodes.D2I); break;
|
||||
default: throw new InternalError("unknown type: " + type);
|
||||
case LONG -> mv.visitInsn(Opcodes.L2I);
|
||||
case FLOAT -> mv.visitInsn(Opcodes.F2I);
|
||||
case DOUBLE -> mv.visitInsn(Opcodes.D2I);
|
||||
default -> throw new InternalError("unknown type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1889,7 +1883,7 @@ class InvokerBytecodeGenerator {
|
|||
emitReturnInsn(basicType(rtype));
|
||||
|
||||
methodEpilogue();
|
||||
clinit();
|
||||
clinit(cw, className, classData);
|
||||
bogusMethod(invokerType);
|
||||
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
|
@ -1958,7 +1952,7 @@ class InvokerBytecodeGenerator {
|
|||
emitReturnInsn(L_TYPE); // NOTE: NamedFunction invokers always return a reference value.
|
||||
|
||||
methodEpilogue();
|
||||
clinit();
|
||||
clinit(cw, className, classData);
|
||||
bogusMethod(dstType);
|
||||
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue