8294960: Convert java.base/java.lang.invoke package to use the Classfile API to generate lambdas and method handles

Co-authored-by: Claes Redestad <redestad@openjdk.org>
Reviewed-by: redestad, liach
This commit is contained in:
Adam Sotona 2024-06-19 15:15:30 +00:00
parent 50bed6c67b
commit 01ee4241b7
11 changed files with 1286 additions and 1704 deletions

View File

@ -25,13 +25,12 @@
package java.lang.invoke;
import jdk.internal.loader.BootLoader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.BytecodeName;
import java.lang.classfile.*;
import java.lang.classfile.attribute.ExceptionsAttribute;
import java.lang.classfile.attribute.SourceFileAttribute;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.LambdaForm.BasicType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@ -42,12 +41,19 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import static java.lang.invoke.LambdaForm.*;
import jdk.internal.constant.MethodTypeDescImpl;
import jdk.internal.constant.ReferenceClassDescImpl;
import jdk.internal.loader.BootLoader;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.BytecodeName;
import sun.invoke.util.Wrapper;
import static java.lang.classfile.ClassFile.*;
import static java.lang.constant.ConstantDescs.*;
import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic;
import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
* Class specialization code.
@ -57,6 +63,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
*/
/*non-public*/
abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesData> {
private static final ClassDesc CD_LambdaForm = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm;");
private static final ClassDesc CD_BoundMethodHandle = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/BoundMethodHandle;");
private final Class<T> topClass;
private final Class<K> keyType;
private final Class<S> metaType;
@ -404,7 +414,7 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
buf.append(basicType.basicTypeChar());
} else {
buf.append('V');
end.append(classSig(type));
end.append(type.descriptorString());
}
}
String typeString;
@ -572,8 +582,9 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
}
// These are named like constants because there is only one per specialization scheme:
private final String SPECIES_DATA = classBCName(metaType);
private final String SPECIES_DATA_SIG = classSig(SPECIES_DATA);
private final ClassDesc CD_SPECIES_DATA = classDesc(metaType);
private final MethodTypeDesc MTD_SPECIES_DATA = MethodTypeDescImpl.ofValidated(CD_SPECIES_DATA);
private final String SPECIES_DATA_NAME = sdAccessor.getName();
private final int SPECIES_DATA_MODS = sdAccessor.getModifiers();
private final List<String> TRANSFORM_NAMES; // derived from transformMethods
@ -595,268 +606,207 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
TRANSFORM_TYPES = List.of(tts.toArray(new MethodType[0]));
TRANSFORM_MODS = List.of(tms.toArray(new Integer[0]));
}
private static final MethodTypeDesc MTD_TRANFORM_HELPER = MethodTypeDescImpl.ofValidated(CD_MethodHandle, CD_int);
private static final int ACC_PPP = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
/*non-public*/
byte[] generateConcreteSpeciesCodeFile(String className0, ClassSpecializer<T,K,S>.SpeciesData speciesData) {
final String className = classBCName(className0);
final String superClassName = classBCName(speciesData.deriveSuperClass());
final ClassDesc classDesc = ClassDesc.of(className0);
final ClassDesc superClassDesc = classDesc(speciesData.deriveSuperClass());
return ClassFile.of().build(classDesc, clb -> {
clb.withFlags(ACC_FINAL | ACC_SUPER)
.withSuperclass(superClassDesc)
.with(SourceFileAttribute.of(classDesc.displayName()))
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
cw.visit(CLASSFILE_VERSION, NOT_ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, superClassName, null);
// emit static types and BMH_SPECIES fields
.withField(sdFieldName, CD_SPECIES_DATA, ACC_STATIC);
final String sourceFile = className.substring(className.lastIndexOf('.')+1);
cw.visitSource(sourceFile, null);
// emit static types and BMH_SPECIES fields
FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, sdFieldName, SPECIES_DATA_SIG, null, null);
fw.visitAnnotation(STABLE_SIG, true);
fw.visitEnd();
// handy holder for dealing with groups of typed values (ctor arguments and fields)
class Var {
final int index;
final String name;
final Class<?> type;
final String desc;
final BasicType basicType;
final int slotIndex;
Var(int index, int slotIndex) {
this.index = index;
this.slotIndex = slotIndex;
name = null; type = null; desc = null;
basicType = BasicType.V_TYPE;
}
Var(String name, Class<?> type, Var prev) {
int slotIndex = prev.nextSlotIndex();
int index = prev.nextIndex();
if (name == null) name = "x";
if (name.endsWith("#"))
name = name.substring(0, name.length()-1) + index;
assert(!type.equals(void.class));
String desc = classSig(type);
BasicType basicType = BasicType.basicType(type);
this.index = index;
this.name = name;
this.type = type;
this.desc = desc;
this.basicType = basicType;
this.slotIndex = slotIndex;
}
Var lastOf(List<Var> vars) {
int n = vars.size();
return (n == 0 ? this : vars.get(n-1));
}
<X> List<Var> fromTypes(List<X> types) {
Var prev = this;
ArrayList<Var> result = new ArrayList<>(types.size());
int i = 0;
for (X x : types) {
String vn = name;
Class<?> vt;
if (x instanceof Class<?> cl) {
vt = cl;
// make the names friendlier if debugging
assert((vn = vn + "_" + (i++)) != null);
} else {
@SuppressWarnings("unchecked")
Var v = (Var) x;
vn = v.name;
vt = v.type;
// handy holder for dealing with groups of typed values (ctor arguments and fields)
class Var {
final int index;
final String name;
final Class<?> type;
final ClassDesc desc;
final BasicType basicType;
final int slotIndex;
Var(int index, int slotIndex) {
this.index = index;
this.slotIndex = slotIndex;
name = null; type = null; desc = null;
basicType = BasicType.V_TYPE;
}
Var(String name, Class<?> type, Var prev) {
int slotIndex = prev.nextSlotIndex();
int index = prev.nextIndex();
if (name == null) name = "x";
if (name.endsWith("#"))
name = name.substring(0, name.length()-1) + index;
assert(!type.equals(void.class));
this.index = index;
this.name = name;
this.type = type;
this.desc = classDesc(type);
this.basicType = BasicType.basicType(type);
this.slotIndex = slotIndex;
}
Var lastOf(List<Var> vars) {
int n = vars.size();
return (n == 0 ? this : vars.get(n-1));
}
<X> List<Var> fromTypes(List<X> types) {
Var prev = this;
ArrayList<Var> result = new ArrayList<>(types.size());
int i = 0;
for (X x : types) {
String vn = name;
Class<?> vt;
if (x instanceof Class<?> cl) {
vt = cl;
// make the names friendlier if debugging
assert((vn = vn + "_" + (i++)) != null);
} else {
@SuppressWarnings("unchecked")
Var v = (Var) x;
vn = v.name;
vt = v.type;
}
prev = new Var(vn, vt, prev);
result.add(prev);
}
prev = new Var(vn, vt, prev);
result.add(prev);
return result;
}
int slotSize() { return basicType.basicTypeSlots(); }
int nextIndex() { return index + (slotSize() == 0 ? 0 : 1); }
int nextSlotIndex() { return slotIndex >= 0 ? slotIndex + slotSize() : slotIndex; }
boolean isInHeap() { return slotIndex < 0; }
void emitLoadInstruction(CodeBuilder cob) {
cob.loadLocal(basicType.btKind, slotIndex);
}
return result;
}
int slotSize() { return basicType.basicTypeSlots(); }
int nextIndex() { return index + (slotSize() == 0 ? 0 : 1); }
int nextSlotIndex() { return slotIndex >= 0 ? slotIndex + slotSize() : slotIndex; }
boolean isInHeap() { return slotIndex < 0; }
void emitVarInstruction(int asmop, MethodVisitor mv) {
if (asmop == ALOAD)
asmop = typeLoadOp(basicType.basicTypeChar());
else
throw new AssertionError("bad op="+asmop+" for desc="+desc);
mv.visitVarInsn(asmop, slotIndex);
}
public void emitFieldInsn(int asmop, MethodVisitor mv) {
mv.visitFieldInsn(asmop, className, name, desc);
}
}
final Var NO_THIS = new Var(0, 0),
AFTER_THIS = new Var(0, 1),
IN_HEAP = new Var(0, -1);
final Var NO_THIS = new Var(0, 0),
AFTER_THIS = new Var(0, 1),
IN_HEAP = new Var(0, -1);
// figure out the field types
final List<Class<?>> fieldTypes = speciesData.fieldTypes();
final List<Var> fields = new ArrayList<>(fieldTypes.size());
{
Var nextF = IN_HEAP;
for (Class<?> ft : fieldTypes) {
String fn = chooseFieldName(ft, nextF.nextIndex());
nextF = new Var(fn, ft, nextF);
fields.add(nextF);
}
}
// emit bound argument fields
for (Var field : fields) {
cw.visitField(ACC_FINAL, field.name, field.desc, null, null).visitEnd();
}
MethodVisitor mv;
// emit implementation of speciesData()
mv = cw.visitMethod((SPECIES_DATA_MODS & ACC_PPP) + ACC_FINAL,
SPECIES_DATA_NAME, "()" + SPECIES_DATA_SIG, null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, className, sdFieldName, SPECIES_DATA_SIG);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// figure out the constructor arguments
MethodType superCtorType = ClassSpecializer.this.baseConstructorType();
MethodType thisCtorType = superCtorType.appendParameterTypes(fieldTypes);
// emit constructor
{
mv = cw.visitMethod(ACC_PRIVATE,
"<init>", methodSig(thisCtorType), null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0); // this
final List<Var> ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList());
for (Var ca : ctorArgs) {
ca.emitVarInstruction(ALOAD, mv);
// figure out the field types
final List<Class<?>> fieldTypes = speciesData.fieldTypes();
final List<Var> fields = new ArrayList<>(fieldTypes.size());
{
Var nextF = IN_HEAP;
for (Class<?> ft : fieldTypes) {
String fn = chooseFieldName(ft, nextF.nextIndex());
nextF = new Var(fn, ft, nextF);
fields.add(nextF);
}
}
// super(ca...)
mv.visitMethodInsn(INVOKESPECIAL, superClassName,
"<init>", methodSig(superCtorType), false);
// store down fields
Var lastFV = AFTER_THIS.lastOf(ctorArgs);
for (Var f : fields) {
// this.argL1 = argL1
mv.visitVarInsn(ALOAD, 0); // this
lastFV = new Var(f.name, f.type, lastFV);
lastFV.emitVarInstruction(ALOAD, mv);
f.emitFieldInsn(PUTFIELD, mv);
// emit bound argument fields
for (Var field : fields) {
clb.withField(field.name, field.desc, ACC_FINAL);
}
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
// emit implementation of speciesData()
clb.withMethodBody(SPECIES_DATA_NAME, MTD_SPECIES_DATA, (SPECIES_DATA_MODS & ACC_PPP) | ACC_FINAL,
cob -> cob.getstatic(classDesc, sdFieldName, CD_SPECIES_DATA)
.areturn());
// emit make() ...factory method wrapping constructor
{
// figure out the constructor arguments
MethodType superCtorType = ClassSpecializer.this.baseConstructorType();
MethodType thisCtorType = superCtorType.appendParameterTypes(fieldTypes);
// emit constructor
clb.withMethodBody(INIT_NAME, methodDesc(thisCtorType), ACC_PRIVATE, cob -> {
cob.aload(0); // this
final List<Var> ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList());
for (Var ca : ctorArgs) {
ca.emitLoadInstruction(cob);
}
// super(ca...)
cob.invokespecial(superClassDesc, INIT_NAME, methodDesc(superCtorType));
// store down fields
Var lastFV = AFTER_THIS.lastOf(ctorArgs);
for (Var f : fields) {
// this.argL1 = argL1
cob.aload(0); // this
lastFV = new Var(f.name, f.type, lastFV);
lastFV.emitLoadInstruction(cob);
cob.putfield(classDesc, f.name, f.desc);
}
cob.return_();
});
// emit make() ...factory method wrapping constructor
MethodType ftryType = thisCtorType.changeReturnType(topClass());
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_STATIC,
"make", methodSig(ftryType), null, null);
mv.visitCode();
// make instance
mv.visitTypeInsn(NEW, className);
mv.visitInsn(DUP);
// load factory method arguments: ctarg... and arg...
for (Var v : NO_THIS.fromTypes(ftryType.parameterList())) {
v.emitVarInstruction(ALOAD, mv);
}
// finally, invoke the constructor and return
mv.visitMethodInsn(INVOKESPECIAL, className,
"<init>", methodSig(thisCtorType), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
// For each transform, emit the customized override of the transform method.
// This method mixes together some incoming arguments (from the transform's
// static type signature) with the field types themselves, and passes
// the resulting mish-mosh of values to a method handle produced by
// the species itself. (Typically this method handle is the factory
// method of this species or a related one.)
for (int whichtm = 0; whichtm < TRANSFORM_NAMES.size(); whichtm++) {
final String TNAME = TRANSFORM_NAMES.get(whichtm);
final MethodType TTYPE = TRANSFORM_TYPES.get(whichtm);
final int TMODS = TRANSFORM_MODS.get(whichtm);
mv = cw.visitMethod((TMODS & ACC_PPP) | ACC_FINAL,
TNAME, TTYPE.toMethodDescriptorString(), null, E_THROWABLE);
mv.visitCode();
// return a call to the corresponding "transform helper", something like this:
// MY_SPECIES.transformHelper(whichtm).invokeBasic(ctarg, ..., argL0, ..., xarg)
mv.visitFieldInsn(GETSTATIC, className,
sdFieldName, SPECIES_DATA_SIG);
emitIntConstant(whichtm, mv);
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA,
"transformHelper", "(I)" + MH_SIG, false);
List<Var> targs = AFTER_THIS.fromTypes(TTYPE.parameterList());
List<Var> tfields = new ArrayList<>(fields);
// mix them up and load them for the transform helper:
List<Var> helperArgs = speciesData.deriveTransformHelperArguments(transformMethods.get(whichtm), whichtm, targs, tfields);
List<Class<?>> helperTypes = new ArrayList<>(helperArgs.size());
for (Var ha : helperArgs) {
helperTypes.add(ha.basicType.basicTypeClass());
if (ha.isInHeap()) {
assert(tfields.contains(ha));
mv.visitVarInsn(ALOAD, 0);
ha.emitFieldInsn(GETFIELD, mv);
} else {
assert(targs.contains(ha));
ha.emitVarInstruction(ALOAD, mv);
clb.withMethodBody("make", methodDesc(ftryType), ACC_STATIC, cob -> {
// make instance
cob.new_(classDesc)
.dup();
// load factory method arguments: ctarg... and arg...
for (Var v : NO_THIS.fromTypes(ftryType.parameterList())) {
v.emitLoadInstruction(cob);
}
// finally, invoke the constructor and return
cob.invokespecial(classDesc, INIT_NAME, methodDesc(thisCtorType))
.areturn();
});
// For each transform, emit the customized override of the transform method.
// This method mixes together some incoming arguments (from the transform's
// static type signature) with the field types themselves, and passes
// the resulting mish-mosh of values to a method handle produced by
// the species itself. (Typically this method handle is the factory
// method of this species or a related one.)
for (int i = 0; i < TRANSFORM_NAMES.size(); i++) {
final int whichtm = i;
final String TNAME = TRANSFORM_NAMES.get(whichtm);
final MethodType TTYPE = TRANSFORM_TYPES.get(whichtm);
final int TMODS = TRANSFORM_MODS.get(whichtm);
clb.withMethod(TNAME, methodDesc(TTYPE), (TMODS & ACC_PPP) | ACC_FINAL, mb -> {
mb.with(ExceptionsAttribute.ofSymbols(CD_Throwable))
.withCode(cob -> {
// return a call to the corresponding "transform helper", something like this:
// MY_SPECIES.transformHelper(whichtm).invokeBasic(ctarg, ..., argL0, ..., xarg)
cob.getstatic(classDesc, sdFieldName, CD_SPECIES_DATA)
.loadConstant(whichtm)
.invokevirtual(CD_SPECIES_DATA, "transformHelper", MTD_TRANFORM_HELPER);
List<Var> targs = AFTER_THIS.fromTypes(TTYPE.parameterList());
List<Var> tfields = new ArrayList<>(fields);
// mix them up and load them for the transform helper:
List<Var> helperArgs = speciesData.deriveTransformHelperArguments(transformMethods.get(whichtm), whichtm, targs, tfields);
ClassDesc[] helperTypes = new ClassDesc[helperArgs.size()];
for (int hi = 0; hi < helperTypes.length; hi++) {
Var ha = helperArgs.get(hi);
helperTypes[hi] = ha.basicType.basicTypeWrapper().basicClassDescriptor();
if (ha.isInHeap()) {
assert(tfields.contains(ha));
cob.aload(0);
cob.getfield(classDesc, ha.name, ha.desc);
} else {
assert(targs.contains(ha));
ha.emitLoadInstruction(cob);
}
}
// jump into the helper (which is probably a factory method)
final Class<?> rtype = TTYPE.returnType();
if (!rtype.isPrimitive()) {
cob.invokevirtual(CD_MethodHandle, "invokeBasic", MethodTypeDescImpl.ofValidated(CD_Object, helperTypes))
.checkcast(classDesc(rtype))
.areturn();
} else {
throw newInternalError("NYI: transform of type "+rtype);
}
});
});
}
// jump into the helper (which is probably a factory method)
final Class<?> rtype = TTYPE.returnType();
final BasicType rbt = BasicType.basicType(rtype);
MethodType invokeBasicType = MethodType.methodType(rbt.basicTypeClass(), helperTypes);
mv.visitMethodInsn(INVOKEVIRTUAL, MH,
"invokeBasic", methodSig(invokeBasicType), false);
if (rbt == BasicType.L_TYPE) {
mv.visitTypeInsn(CHECKCAST, classBCName(rtype));
mv.visitInsn(ARETURN);
} else {
throw newInternalError("NYI: transform of type "+rtype);
}
mv.visitMaxs(0, 0);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
private int typeLoadOp(char t) {
return switch (t) {
case 'L' -> ALOAD;
case 'I' -> ILOAD;
case 'J' -> LLOAD;
case 'F' -> FLOAD;
case 'D' -> DLOAD;
default -> throw newInternalError("unrecognized type " + t);
};
}
private void emitIntConstant(int con, MethodVisitor mv) {
if (ICONST_M1 - ICONST_0 <= con && con <= ICONST_5 - ICONST_0)
mv.visitInsn(ICONST_0 + con);
else if (con == (byte) con)
mv.visitIntInsn(BIPUSH, con);
else if (con == (short) con)
mv.visitIntInsn(SIPUSH, con);
else {
mv.visitLdcInsn(con);
}
});
}
//
@ -990,39 +940,25 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
// Other misc helpers:
private static final String MH = "java/lang/invoke/MethodHandle";
private static final String MH_SIG = "L" + MH + ";";
private static final String STABLE = "jdk/internal/vm/annotation/Stable";
private static final String STABLE_SIG = "L" + STABLE + ";";
private static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
static {
assert(MH_SIG.equals(classSig(MethodHandle.class)));
assert(MH.equals(classBCName(MethodHandle.class)));
}
static String methodSig(MethodType mt) {
return mt.toMethodDescriptorString();
}
static String classSig(Class<?> cls) {
if (cls.isPrimitive() || cls.isArray())
return MethodType.methodType(cls).toMethodDescriptorString().substring(2);
return classSig(classBCName(cls));
}
static String classSig(String bcName) {
assert(bcName.indexOf('.') < 0);
assert(!bcName.endsWith(";"));
assert(!bcName.startsWith("["));
return "L" + bcName + ";";
}
static String classBCName(Class<?> cls) {
return classBCName(className(cls));
}
static String classBCName(String str) {
assert(str.indexOf('/') < 0) : str;
return str.replace('.', '/');
}
static String className(Class<?> cls) {
assert(!cls.isArray() && !cls.isPrimitive());
return cls.getName();
static ClassDesc classDesc(Class<?> cls) {
return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor()
: cls == Object.class ? CD_Object
: cls == MethodType.class ? CD_MethodType
: cls == LambdaForm.class ? CD_LambdaForm
: cls == BoundMethodHandle.class ? CD_BoundMethodHandle
: ReferenceClassDescImpl.ofValidated(cls.descriptorString());
}
static MethodTypeDesc methodDesc(MethodType mt) {
var params = new ClassDesc[mt.parameterCount()];
for (int i = 0; i < params.length; i++) {
params[i] = classDesc(mt.parameterType(i));
}
return MethodTypeDescImpl.ofValidated(classDesc(mt.returnType()), params);
}
}

View File

@ -25,10 +25,11 @@
package java.lang.invoke;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;
import sun.invoke.util.Wrapper;
import java.lang.classfile.ClassFile;
import java.lang.classfile.attribute.SourceFileAttribute;
import java.lang.constant.ClassDesc;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
@ -38,10 +39,10 @@ import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Stream;
import static java.lang.classfile.ClassFile.*;
import static java.lang.invoke.LambdaForm.BasicType.*;
import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
import static java.lang.invoke.MethodTypeForm.*;
import static java.lang.invoke.LambdaForm.Kind.*;
import static java.lang.invoke.MethodTypeForm.*;
/**
* Helper class to assist the GenerateJLIClassesPlugin to get access to
@ -557,19 +558,14 @@ class GenerateJLIClassesHelper {
* a class with a specified name.
*/
private static byte[] generateCodeBytesForLFs(String className, String[] names, LambdaForm[] forms) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
cw.visit(CLASSFILE_VERSION, Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
className, null, InvokerBytecodeGenerator.INVOKER_SUPER_NAME, null);
cw.visitSource(className.substring(className.lastIndexOf('/') + 1), null);
for (int i = 0; i < forms.length; i++) {
InvokerBytecodeGenerator g
= new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType());
g.setClassWriter(cw);
g.addMethod();
}
return cw.toByteArray();
return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> {
clb.withFlags(ACC_PRIVATE | ACC_FINAL | ACC_SUPER)
.withSuperclass(InvokerBytecodeGenerator.INVOKER_SUPER_DESC)
.with(SourceFileAttribute.of(className.substring(className.lastIndexOf('/') + 1)));
for (int i = 0; i < forms.length; i++) {
new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType()).addMethod(clb);
}
});
}
private static LambdaForm makeReinvokerFor(MethodType type) {

View File

@ -26,23 +26,40 @@
package java.lang.invoke;
import jdk.internal.misc.CDS;
import jdk.internal.org.objectweb.asm.*;
import jdk.internal.util.ClassFileDumper;
import sun.invoke.util.BytecodeDescriptor;
import sun.invoke.util.VerifyAccess;
import sun.security.action.GetBooleanAction;
import java.io.Serializable;
import java.lang.constant.ConstantDescs;
import java.lang.classfile.ClassBuilder;
import java.lang.classfile.ClassFile;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.FieldBuilder;
import java.lang.classfile.MethodBuilder;
import java.lang.classfile.Opcode;
import java.lang.classfile.TypeKind;
import java.lang.constant.ClassDesc;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.Modifier;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
import static java.lang.classfile.ClassFile.*;
import java.lang.classfile.attribute.ExceptionsAttribute;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.classfile.constantpool.MethodRefEntry;
import static java.lang.constant.ConstantDescs.*;
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
import static java.lang.invoke.MethodType.methodType;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.constant.ConstantUtils;
import jdk.internal.constant.MethodTypeDescImpl;
import jdk.internal.constant.ReferenceClassDescImpl;
import sun.invoke.util.Wrapper;
/**
* Lambda metafactory implementation which dynamically creates an
@ -51,42 +68,29 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* @see LambdaMetafactory
*/
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
private static final String NAME_CTOR = "<init>";
private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";
//Serialization support
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
private static final String NAME_METHOD_READ_OBJECT = "readObject";
private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
private static final String DESCR_CLASS = "Ljava/lang/Class;";
private static final String DESCR_STRING = "Ljava/lang/String;";
private static final String DESCR_OBJECT = "Ljava/lang/Object;";
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
= "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
+ DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final ClassDesc[] EMPTY_CLASSDESC_ARRAY = ConstantUtils.EMPTY_CLASSDESC;
// Static builders to avoid lambdas
record FieldFlags(int flags) implements Consumer<FieldBuilder> {
@Override
public void accept(FieldBuilder fb) {
fb.withFlags(flags);
}
};
record MethodBody(Consumer<CodeBuilder> code) implements Consumer<MethodBuilder> {
@Override
public void accept(MethodBuilder mb) {
mb.withCode(code);
}
};
// For dumping generated classes to disk, for debugging purposes
private static final ClassFileDumper lambdaProxyClassFileDumper;
private static final boolean disableEagerInitialization;
// condy to load implMethod from class data
private static final ConstantDynamic implMethodCondy;
static {
// To dump the lambda proxy classes, set this system property:
// -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
@ -96,23 +100,18 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
// condy to load implMethod from class data
MethodType classDataMType = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
classDataMType.descriptorString(), false);
implMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm);
}
// See context values in AbstractValidatingLambdaMetafactory
private final String implMethodClassName; // Name of type containing implementation "CC"
private final ClassDesc implMethodClassDesc; // Name of type containing implementation "CC"
private final String implMethodName; // Name of implementation method "impl"
private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
private final MethodTypeDesc implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
private final MethodType constructorType; // Generated class constructor type "(CC)void"
private final ClassWriter cw; // ASM class writer
private final MethodTypeDesc constructorTypeDesc;// Type descriptor for the generated class constructor type "(CC)void"
private final String[] argNames; // Generated names for the constructor arguments
private final String[] argDescs; // Type descriptors for the constructor arguments
private final String lambdaClassName; // Generated name for the generated class "X$$Lambda"
private final ClassDesc[] argDescs; // Type descriptors for the constructor arguments
private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
private final ClassDesc lambdaClassDesc; // Type descriptor for the generated class "X$$Lambda$1"
private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation
/**
@ -168,11 +167,13 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
super(caller, factoryType, interfaceMethodName, interfaceMethodType,
implementation, dynamicMethodType,
isSerializable, altInterfaces, altMethods);
implMethodClassName = implClass.getName().replace('.', '/');
implMethodClassDesc = implClassDesc(implClass);
implMethodName = implInfo.getName();
implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
implMethodDesc = methodDesc(implInfo.getMethodType());
constructorType = factoryType.changeReturnType(Void.TYPE);
constructorTypeDesc = methodDesc(constructorType);
lambdaClassName = lambdaClassName(targetClass);
lambdaClassDesc = ClassDesc.ofInternalName(lambdaClassName);
// If the target class invokes a protected method inherited from a
// superclass in a different package, or does 'invokespecial', the
// lambda class has no access to the resolved method, or does
@ -182,19 +183,19 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
// situation by generating bridges in the target class)
useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) &&
!VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) ||
implKind == H_INVOKESPECIAL ||
implKind == H_INVOKESTATIC && implClass.isHidden();
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
implKind == MethodHandleInfo.REF_invokeSpecial ||
implKind == MethodHandleInfo.REF_invokeStatic && implClass.isHidden();
int parameterCount = factoryType.parameterCount();
if (parameterCount > 0) {
argNames = new String[parameterCount];
argDescs = new String[parameterCount];
argDescs = new ClassDesc[parameterCount];
for (int i = 0; i < parameterCount; i++) {
argNames[i] = "arg$" + (i + 1);
argDescs[i] = BytecodeDescriptor.unparse(factoryType.parameterType(i));
argDescs[i] = classDesc(factoryType.parameterType(i));
}
} else {
argNames = argDescs = EMPTY_STRING_ARRAY;
argNames = EMPTY_STRING_ARRAY;
argDescs = EMPTY_CLASSDESC_ARRAY;
}
}
@ -300,65 +301,63 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* is not found
*/
private Class<?> generateInnerClass() throws LambdaConversionException {
String[] interfaceNames;
String interfaceName = interfaceClass.getName().replace('.', '/');
List<ClassDesc> interfaces;
ClassDesc interfaceDesc = classDesc(interfaceClass);
boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass);
if (altInterfaces.length == 0) {
interfaceNames = new String[]{interfaceName};
interfaces = List.of(interfaceDesc);
} else {
// Assure no duplicate interfaces (ClassFormatError)
Set<String> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1);
itfs.add(interfaceName);
Set<ClassDesc> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1);
itfs.add(interfaceDesc);
for (Class<?> i : altInterfaces) {
itfs.add(i.getName().replace('.', '/'));
itfs.add(classDesc(i));
accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
}
interfaceNames = itfs.toArray(new String[itfs.size()]);
interfaces = List.copyOf(itfs);
}
final boolean finalAccidentallySerializable = accidentallySerializable;
final byte[] classBytes = ClassFile.of().build(lambdaClassDesc, new Consumer<ClassBuilder>() {
@Override
public void accept(ClassBuilder clb) {
clb.withFlags(ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC)
.withInterfaceSymbols(interfaces);
// Generate final fields to be filled in by constructor
for (int i = 0; i < argDescs.length; i++) {
clb.withField(argNames[i], argDescs[i], new FieldFlags(ACC_PRIVATE | ACC_FINAL));
}
cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
lambdaClassName, null,
JAVA_LANG_OBJECT, interfaceNames);
generateConstructor(clb);
// Generate final fields to be filled in by constructor
for (int i = 0; i < argDescs.length; i++) {
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
argNames[i],
argDescs[i],
null, null);
fv.visitEnd();
}
if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
generateClassInitializer(clb);
}
generateConstructor();
// Forward the SAM method
clb.withMethod(interfaceMethodName,
methodDesc(interfaceMethodType),
ACC_PUBLIC,
forwardingMethod(interfaceMethodType));
if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
generateClassInitializer();
}
// Forward the bridges
if (altMethods != null) {
for (MethodType mt : altMethods) {
clb.withMethod(interfaceMethodName,
methodDesc(mt),
ACC_PUBLIC | ACC_BRIDGE,
forwardingMethod(mt));
}
}
// Forward the SAM method
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
interfaceMethodType.toMethodDescriptorString(), null, null);
new ForwardingMethodGenerator(mv).generate(interfaceMethodType);
// Forward the altMethods
if (altMethods != null) {
for (MethodType mt : altMethods) {
mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
mt.toMethodDescriptorString(), null, null);
new ForwardingMethodGenerator(mv).generate(mt);
if (isSerializable)
generateSerializationFriendlyMethods(clb);
else if (finalAccidentallySerializable)
generateSerializationHostileMethods(clb);
}
}
if (isSerializable)
generateSerializationFriendlyMethods();
else if (accidentallySerializable)
generateSerializationHostileMethods();
cw.visitEnd();
});
// Define the generated class in this VM.
final byte[] classBytes = cw.toByteArray();
try {
// this class is linked at the indy callsite; so define a hidden nestmate
var classdata = useImplMethodHandle? implementation : null;
@ -373,237 +372,214 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
* Generate a static field and a static initializer that sets this field to an instance of the lambda
*/
private void generateClassInitializer() {
String lambdaTypeDescriptor = factoryType.returnType().descriptorString();
private void generateClassInitializer(ClassBuilder clb) {
ClassDesc lambdaTypeDescriptor = classDesc(factoryType.returnType());
// Generate the static final field that holds the lambda singleton
FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, null, null);
fv.visitEnd();
clb.withField(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, new FieldFlags(ACC_PRIVATE | ACC_STATIC | ACC_FINAL));
// Instantiate the lambda and store it to the static final field
MethodVisitor clinit = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
clinit.visitCode();
clinit.visitTypeInsn(NEW, lambdaClassName);
clinit.visitInsn(Opcodes.DUP);
assert factoryType.parameterCount() == 0;
clinit.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
clinit.visitFieldInsn(PUTSTATIC, lambdaClassName, LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor);
clinit.visitInsn(RETURN);
clinit.visitMaxs(-1, -1);
clinit.visitEnd();
clb.withMethod(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new MethodBody(new Consumer<CodeBuilder>() {
@Override
public void accept(CodeBuilder cob) {
assert factoryType.parameterCount() == 0;
cob.new_(lambdaClassDesc)
.dup()
.invokespecial(lambdaClassDesc, INIT_NAME, constructorTypeDesc)
.putstatic(lambdaClassDesc, LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor)
.return_();
}
}));
}
/**
* Generate the constructor for the class
*/
private void generateConstructor() {
private void generateConstructor(ClassBuilder clb) {
// Generate constructor
MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
constructorType.toMethodDescriptorString(), null, null);
ctor.visitCode();
ctor.visitVarInsn(ALOAD, 0);
ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
METHOD_DESCRIPTOR_VOID, false);
int parameterCount = factoryType.parameterCount();
for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
ctor.visitVarInsn(ALOAD, 0);
Class<?> argType = factoryType.parameterType(i);
ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
lvIndex += getParameterSize(argType);
ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
}
ctor.visitInsn(RETURN);
// Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
ctor.visitMaxs(-1, -1);
ctor.visitEnd();
clb.withMethod(INIT_NAME, constructorTypeDesc, ACC_PRIVATE,
new MethodBody(new Consumer<CodeBuilder>() {
@Override
public void accept(CodeBuilder cob) {
cob.aload(0)
.invokespecial(CD_Object, INIT_NAME, MTD_void);
int parameterCount = factoryType.parameterCount();
for (int i = 0; i < parameterCount; i++) {
cob.aload(0);
Class<?> argType = factoryType.parameterType(i);
cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i));
cob.putfield(lambdaClassDesc, argNames[i], argDescs[i]);
}
cob.return_();
}
}));
}
private static class SerializationSupport {
// Serialization support
private static final ClassDesc CD_SerializedLambda = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/SerializedLambda;");
private static final ClassDesc CD_ObjectOutputStream = ReferenceClassDescImpl.ofValidated("Ljava/io/ObjectOutputStream;");
private static final ClassDesc CD_ObjectInputStream = ReferenceClassDescImpl.ofValidated("Ljava/io/ObjectInputStream;");
private static final MethodTypeDesc MTD_Object = MethodTypeDescImpl.ofValidated(CD_Object);
private static final MethodTypeDesc MTD_void_ObjectOutputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectOutputStream);
private static final MethodTypeDesc MTD_void_ObjectInputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectInputStream);
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
private static final String NAME_METHOD_READ_OBJECT = "readObject";
private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
static final ClassDesc CD_NotSerializableException = ReferenceClassDescImpl.ofValidated("Ljava/io/NotSerializableException;");
static final MethodTypeDesc MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION = MethodTypeDescImpl.ofValidated(CD_void, CD_String);
static final MethodTypeDesc MTD_CTOR_SERIALIZED_LAMBDA = MethodTypeDescImpl.ofValidated(CD_void,
CD_Class, CD_String, CD_String, CD_String, CD_int, CD_String, CD_String, CD_String, CD_String, ReferenceClassDescImpl.ofValidated("[Ljava/lang/Object;"));
}
/**
* Generate a writeReplace method that supports serialization
*/
private void generateSerializationFriendlyMethods() {
TypeConvertingMethodAdapter mv
= new TypeConvertingMethodAdapter(
cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
null, null));
mv.visitCode();
mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
mv.visitInsn(DUP);
mv.visitLdcInsn(Type.getType(targetClass));
mv.visitLdcInsn(factoryType.returnType().getName().replace('.', '/'));
mv.visitLdcInsn(interfaceMethodName);
mv.visitLdcInsn(interfaceMethodType.toMethodDescriptorString());
mv.visitLdcInsn(implInfo.getReferenceKind());
mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
mv.visitLdcInsn(implInfo.getName());
mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
mv.visitLdcInsn(dynamicMethodType.toMethodDescriptorString());
mv.iconst(argDescs.length);
mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
for (int i = 0; i < argDescs.length; i++) {
mv.visitInsn(DUP);
mv.iconst(i);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
DESCR_CTOR_SERIALIZED_LAMBDA, false);
mv.visitInsn(ARETURN);
// Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
mv.visitMaxs(-1, -1);
mv.visitEnd();
private void generateSerializationFriendlyMethods(ClassBuilder clb) {
clb.withMethod(SerializationSupport.NAME_METHOD_WRITE_REPLACE, SerializationSupport.MTD_Object, ACC_PRIVATE | ACC_FINAL,
new MethodBody(new Consumer<CodeBuilder>() {
@Override
public void accept(CodeBuilder cob) {
cob.new_(SerializationSupport.CD_SerializedLambda)
.dup()
.ldc(classDesc(targetClass))
.ldc(factoryType.returnType().getName().replace('.', '/'))
.ldc(interfaceMethodName)
.ldc(interfaceMethodType.toMethodDescriptorString())
.ldc(implInfo.getReferenceKind())
.ldc(implInfo.getDeclaringClass().getName().replace('.', '/'))
.ldc(implInfo.getName())
.ldc(implInfo.getMethodType().toMethodDescriptorString())
.ldc(dynamicMethodType.toMethodDescriptorString())
.loadConstant(argDescs.length)
.anewarray(CD_Object);
for (int i = 0; i < argDescs.length; i++) {
cob.dup()
.loadConstant(i)
.aload(0)
.getfield(lambdaClassDesc, argNames[i], argDescs[i]);
TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i]));
cob.aastore();
}
cob.invokespecial(SerializationSupport.CD_SerializedLambda, INIT_NAME,
SerializationSupport.MTD_CTOR_SERIALIZED_LAMBDA)
.areturn();
}
}));
}
/**
* Generate a readObject/writeObject method that is hostile to serialization
*/
private void generateSerializationHostileMethods() {
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
null, SER_HOSTILE_EXCEPTIONS);
mv.visitCode();
mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
mv.visitInsn(DUP);
mv.visitLdcInsn("Non-serializable lambda");
mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
mv.visitInsn(ATHROW);
mv.visitMaxs(-1, -1);
mv.visitEnd();
mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
null, SER_HOSTILE_EXCEPTIONS);
mv.visitCode();
mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
mv.visitInsn(DUP);
mv.visitLdcInsn("Non-serializable lambda");
mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
mv.visitInsn(ATHROW);
mv.visitMaxs(-1, -1);
mv.visitEnd();
private void generateSerializationHostileMethods(ClassBuilder clb) {
var hostileMethod = new Consumer<MethodBuilder>() {
@Override
public void accept(MethodBuilder mb) {
ConstantPoolBuilder cp = mb.constantPool();
ClassEntry nseCE = cp.classEntry(SerializationSupport.CD_NotSerializableException);
mb.with(ExceptionsAttribute.of(nseCE))
.withCode(new Consumer<CodeBuilder>() {
@Override
public void accept(CodeBuilder cob) {
cob.new_(nseCE)
.dup()
.ldc("Non-serializable lambda")
.invokespecial(cp.methodRefEntry(nseCE, cp.nameAndTypeEntry(INIT_NAME,
SerializationSupport.MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION)))
.athrow();
}
});
}
};
clb.withMethod(SerializationSupport.NAME_METHOD_WRITE_OBJECT, SerializationSupport.MTD_void_ObjectOutputStream,
ACC_PRIVATE + ACC_FINAL, hostileMethod);
clb.withMethod(SerializationSupport.NAME_METHOD_READ_OBJECT, SerializationSupport.MTD_void_ObjectInputStream,
ACC_PRIVATE + ACC_FINAL, hostileMethod);
}
/**
* This class generates a method body which calls the lambda implementation
* This method generates a method body which calls the lambda implementation
* method, converting arguments, as needed.
*/
private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
ForwardingMethodGenerator(MethodVisitor mv) {
super(mv);
}
void generate(MethodType methodType) {
visitCode();
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
visitTypeInsn(NEW, implMethodClassName);
visitInsn(DUP);
}
if (useImplMethodHandle) {
visitLdcInsn(implMethodCondy);
}
for (int i = 0; i < argNames.length; i++) {
visitVarInsn(ALOAD, 0);
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
}
convertArgumentTypes(methodType);
if (useImplMethodHandle) {
MethodType mtype = implInfo.getMethodType();
if (implKind != MethodHandleInfo.REF_invokeStatic) {
mtype = mtype.insertParameterTypes(0, implClass);
Consumer<MethodBuilder> forwardingMethod(MethodType methodType) {
return new MethodBody(new Consumer<CodeBuilder>() {
@Override
public void accept(CodeBuilder cob) {
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
cob.new_(implMethodClassDesc)
.dup();
}
visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
"invokeExact", mtype.descriptorString(), false);
} else {
// Invoke the method we want to forward to
visitMethodInsn(invocationOpcode(), implMethodClassName,
implMethodName, implMethodDesc,
implClass.isInterface());
if (useImplMethodHandle) {
ConstantPoolBuilder cp = cob.constantPool();
cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA), List.of()),
cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle)));
}
for (int i = 0; i < argNames.length; i++) {
cob.aload(0)
.getfield(lambdaClassDesc, argNames[i], argDescs[i]);
}
convertArgumentTypes(cob, methodType);
if (useImplMethodHandle) {
MethodType mtype = implInfo.getMethodType();
if (implKind != MethodHandleInfo.REF_invokeStatic) {
mtype = mtype.insertParameterTypes(0, implClass);
}
cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(mtype));
} else {
// Invoke the method we want to forward to
cob.invoke(invocationOpcode(), implMethodClassDesc, implMethodName, implMethodDesc, implClass.isInterface());
}
// Convert the return value (if any) and return it
// Note: if adapting from non-void to void, the 'return'
// instruction will pop the unneeded result
Class<?> implReturnClass = implMethodType.returnType();
Class<?> samReturnClass = methodType.returnType();
TypeConvertingMethodAdapter.convertType(cob, implReturnClass, samReturnClass, samReturnClass);
cob.return_(TypeKind.from(samReturnClass));
}
// Convert the return value (if any) and return it
// Note: if adapting from non-void to void, the 'return'
// instruction will pop the unneeded result
Class<?> implReturnClass = implMethodType.returnType();
Class<?> samReturnClass = methodType.returnType();
convertType(implReturnClass, samReturnClass, samReturnClass);
visitInsn(getReturnOpcode(samReturnClass));
// Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
visitMaxs(-1, -1);
visitEnd();
}
});
}
private void convertArgumentTypes(MethodType samType) {
int lvIndex = 0;
int samParametersLength = samType.parameterCount();
int captureArity = factoryType.parameterCount();
for (int i = 0; i < samParametersLength; i++) {
Class<?> argType = samType.parameterType(i);
visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
lvIndex += getParameterSize(argType);
convertType(argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i));
}
}
private int invocationOpcode() throws InternalError {
return switch (implKind) {
case MethodHandleInfo.REF_invokeStatic -> INVOKESTATIC;
case MethodHandleInfo.REF_newInvokeSpecial -> INVOKESPECIAL;
case MethodHandleInfo.REF_invokeVirtual -> INVOKEVIRTUAL;
case MethodHandleInfo.REF_invokeInterface -> INVOKEINTERFACE;
case MethodHandleInfo.REF_invokeSpecial -> INVOKESPECIAL;
default -> throw new InternalError("Unexpected invocation kind: " + implKind);
};
private void convertArgumentTypes(CodeBuilder cob, MethodType samType) {
int samParametersLength = samType.parameterCount();
int captureArity = factoryType.parameterCount();
for (int i = 0; i < samParametersLength; i++) {
Class<?> argType = samType.parameterType(i);
cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i));
TypeConvertingMethodAdapter.convertType(cob, argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i));
}
}
static int getParameterSize(Class<?> c) {
if (c == Void.TYPE) {
return 0;
} else if (c == Long.TYPE || c == Double.TYPE) {
return 2;
}
return 1;
private Opcode invocationOpcode() throws InternalError {
return switch (implKind) {
case MethodHandleInfo.REF_invokeStatic -> Opcode.INVOKESTATIC;
case MethodHandleInfo.REF_newInvokeSpecial -> Opcode.INVOKESPECIAL;
case MethodHandleInfo.REF_invokeVirtual -> Opcode.INVOKEVIRTUAL;
case MethodHandleInfo.REF_invokeInterface -> Opcode.INVOKEINTERFACE;
case MethodHandleInfo.REF_invokeSpecial -> Opcode.INVOKESPECIAL;
default -> throw new InternalError("Unexpected invocation kind: " + implKind);
};
}
static int getLoadOpcode(Class<?> c) {
if(c == Void.TYPE) {
throw new InternalError("Unexpected void type of load opcode");
}
return ILOAD + getOpcodeOffset(c);
static ClassDesc implClassDesc(Class<?> cls) {
return cls.isHidden() ? null : ReferenceClassDescImpl.ofValidated(cls.descriptorString());
}
static int getReturnOpcode(Class<?> c) {
if(c == Void.TYPE) {
return RETURN;
}
return IRETURN + getOpcodeOffset(c);
static ClassDesc classDesc(Class<?> cls) {
return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor()
: ReferenceClassDescImpl.ofValidated(cls.descriptorString());
}
private static int getOpcodeOffset(Class<?> c) {
if (c.isPrimitive()) {
if (c == Long.TYPE) {
return 1;
} else if (c == Float.TYPE) {
return 2;
} else if (c == Double.TYPE) {
return 3;
}
return 0;
} else {
return 4;
static MethodTypeDesc methodDesc(MethodType mt) {
var params = new ClassDesc[mt.parameterCount()];
for (int i = 0; i < params.length; i++) {
params[i] = classDesc(mt.parameterType(i));
}
return MethodTypeDescImpl.ofValidated(classDesc(mt.returnType()), params);
}
}

View File

@ -25,6 +25,7 @@
package java.lang.invoke;
import java.lang.classfile.TypeKind;
import jdk.internal.perf.PerfCounter;
import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.Hidden;
@ -137,12 +138,12 @@ class LambdaForm {
public static final int VOID_RESULT = -1, LAST_RESULT = -2;
enum BasicType {
L_TYPE('L', Object.class, Wrapper.OBJECT), // all reference types
I_TYPE('I', int.class, Wrapper.INT),
J_TYPE('J', long.class, Wrapper.LONG),
F_TYPE('F', float.class, Wrapper.FLOAT),
D_TYPE('D', double.class, Wrapper.DOUBLE), // all primitive types
V_TYPE('V', void.class, Wrapper.VOID); // not valid in all contexts
L_TYPE('L', Object.class, Wrapper.OBJECT, TypeKind.ReferenceType), // all reference types
I_TYPE('I', int.class, Wrapper.INT, TypeKind.IntType),
J_TYPE('J', long.class, Wrapper.LONG, TypeKind.LongType),
F_TYPE('F', float.class, Wrapper.FLOAT, TypeKind.FloatType),
D_TYPE('D', double.class, Wrapper.DOUBLE, TypeKind.DoubleType), // all primitive types
V_TYPE('V', void.class, Wrapper.VOID, TypeKind.VoidType); // not valid in all contexts
static final @Stable BasicType[] ALL_TYPES = BasicType.values();
static final @Stable BasicType[] ARG_TYPES = Arrays.copyOf(ALL_TYPES, ALL_TYPES.length-1);
@ -153,11 +154,13 @@ class LambdaForm {
final char btChar;
final Class<?> btClass;
final Wrapper btWrapper;
final TypeKind btKind;
private BasicType(char btChar, Class<?> btClass, Wrapper wrapper) {
private BasicType(char btChar, Class<?> btClass, Wrapper wrapper, TypeKind typeKind) {
this.btChar = btChar;
this.btClass = btClass;
this.btWrapper = wrapper;
this.btKind = typeKind;
}
char basicTypeChar() {
@ -169,6 +172,9 @@ class LambdaForm {
Wrapper basicTypeWrapper() {
return btWrapper;
}
TypeKind basicTypeKind() {
return btKind;
}
int basicTypeSlots() {
return btWrapper.stackSlots();
}

View File

@ -27,8 +27,9 @@ package java.lang.invoke;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.constant.MethodTypeDescImpl;
import jdk.internal.constant.ReferenceClassDescImpl;
import jdk.internal.foreign.abi.NativeEntryPoint;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.ForceInline;
@ -39,6 +40,8 @@ import sun.invoke.util.ValueConversions;
import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
import java.lang.classfile.ClassFile;
import java.lang.constant.ClassDesc;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
@ -56,13 +59,14 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.lang.classfile.ClassFile.*;
import static java.lang.constant.ConstantDescs.*;
import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.MethodHandleNatives.Constants.MN_CALLER_SENSITIVE;
import static java.lang.invoke.MethodHandleNatives.Constants.MN_HIDDEN_MEMBER;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
* Trusted implementation code for MethodHandle.
@ -1035,8 +1039,10 @@ abstract class MethodHandleImpl {
// Put the whole mess into its own nested class.
// That way we can lazily load the code and set up the constants.
private static class BindCaller {
private static MethodType INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
private static MethodType REFLECT_INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object.class, Object[].class);
private static final ClassDesc CD_Object_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Object;");
private static final MethodType INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
private static final MethodType REFLECT_INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object.class, Object[].class);
static MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
// Code in the boot layer should now be careful while creating method handles or
@ -1250,8 +1256,6 @@ abstract class MethodHandleImpl {
/** Produces byte code for a class that is used as an injected invoker. */
private static byte[] generateInvokerTemplate() {
ClassWriter cw = new ClassWriter(0);
// private static class InjectedInvoker {
// /* this is used to wrap DMH(s) of caller-sensitive methods */
// @Hidden
@ -1265,39 +1269,25 @@ abstract class MethodHandleImpl {
// }
// }
// }
cw.visit(CLASSFILE_VERSION, ACC_PRIVATE | ACC_SUPER, "InjectedInvoker", null, "java/lang/Object", null);
{
var mv = cw.visitMethod(ACC_STATIC, "invoke_V",
"(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
"([Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
cw.visitEnd();
}
{
var mv = cw.visitMethod(ACC_STATIC, "reflect_invoke_V",
"(Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
return cw.toByteArray();
return ClassFile.of().build(ReferenceClassDescImpl.ofValidated("LInjectedInvoker;"), clb -> clb
.withFlags(ACC_PRIVATE | ACC_SUPER)
.withMethodBody(
"invoke_V",
MethodTypeDescImpl.ofValidated(CD_Object, CD_MethodHandle, CD_Object_array),
ACC_STATIC,
cob -> cob.aload(0)
.aload(1)
.invokevirtual(CD_MethodHandle, "invokeExact", MethodTypeDescImpl.ofValidated(CD_Object, CD_Object_array))
.areturn())
.withMethodBody(
"reflect_invoke_V",
MethodTypeDescImpl.ofValidated(CD_Object, CD_MethodHandle, CD_Object, CD_Object_array),
ACC_STATIC,
cob -> cob.aload(0)
.aload(1)
.aload(2)
.invokevirtual(CD_MethodHandle, "invokeExact", MethodTypeDescImpl.ofValidated(CD_Object, CD_Object, CD_Object_array))
.areturn()));
}
}

View File

@ -28,9 +28,6 @@ package java.lang.invoke;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.Reflection;
@ -42,8 +39,10 @@ import sun.invoke.util.Wrapper;
import sun.reflect.misc.ReflectUtil;
import sun.security.util.SecurityConstants;
import java.lang.classfile.ClassModel;
import java.lang.constant.ConstantDescs;
import java.lang.invoke.LambdaForm.BasicType;
import java.lang.invoke.MethodHandleImpl.Intrinsic;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
@ -62,8 +61,8 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import static java.lang.classfile.ClassFile.*;
import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
@ -2288,27 +2287,16 @@ public class MethodHandles {
String name;
int accessFlags;
try {
ClassReader reader = new ClassReader(bytes);
// ClassReader does not check if `this_class` is CONSTANT_Class_info
// workaround to read `this_class` using readConst and validate the value
int thisClass = reader.readUnsignedShort(reader.header + 2);
Object constant = reader.readConst(thisClass, new char[reader.getMaxStringLength()]);
if (!(constant instanceof Type type)) {
throw new ClassFormatError("this_class item: #" + thisClass + " not a CONSTANT_Class_info");
}
if (!type.getDescriptor().startsWith("L")) {
throw new ClassFormatError("this_class item: #" + thisClass + " not a CONSTANT_Class_info");
}
name = type.getInternalName();
accessFlags = reader.readUnsignedShort(reader.header);
} catch (RuntimeException e) {
// ASM exceptions are poorly specified
ClassModel cm = java.lang.classfile.ClassFile.of().parse(bytes);
name = cm.thisClass().asInternalName();
accessFlags = cm.flags().flagsMask();
} catch (IllegalArgumentException e) {
ClassFormatError cfe = new ClassFormatError();
cfe.initCause(e);
throw cfe;
}
// must be a class or interface
if ((accessFlags & Opcodes.ACC_MODULE) != 0) {
if ((accessFlags & ACC_MODULE) != 0) {
throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
}
return new ClassFile(name, accessFlags, bytes);

View File

@ -1291,15 +1291,21 @@ class MethodType
*/
@Override
public Optional<MethodTypeDesc> describeConstable() {
try {
return Optional.of(MethodTypeDesc.of(returnType().describeConstable().orElseThrow(),
Stream.of(parameterArray())
.map(p -> p.describeConstable().orElseThrow())
.toArray(ClassDesc[]::new)));
}
catch (NoSuchElementException e) {
var retDesc = returnType().describeConstable();
if (retDesc.isEmpty())
return Optional.empty();
if (parameterCount() == 0)
return Optional.of(MethodTypeDesc.of(retDesc.get()));
var params = new ClassDesc[parameterCount()];
for (int i = 0; i < params.length; i++) {
var paramDesc = parameterType(i).describeConstable();
if (paramDesc.isEmpty())
return Optional.empty();
params[i] = paramDesc.get();
}
return Optional.of(MethodTypeDesc.of(retDesc.get(), params));
}
//--- Serialization.

View File

@ -25,176 +25,103 @@
package java.lang.invoke;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import sun.invoke.util.BytecodeDescriptor;
import java.lang.constant.ClassDesc;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.TypeKind;
import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.classfile.constantpool.MethodRefEntry;
import jdk.internal.constant.MethodTypeDescImpl;
import jdk.internal.constant.ReferenceClassDescImpl;
import sun.invoke.util.Wrapper;
import static sun.invoke.util.Wrapper.*;
class TypeConvertingMethodAdapter extends MethodVisitor {
import static java.lang.constant.ConstantDescs.*;
TypeConvertingMethodAdapter(MethodVisitor mv) {
super(Opcodes.ASM7, mv);
}
class TypeConvertingMethodAdapter {
private static final int NUM_WRAPPERS = Wrapper.COUNT;
private static class BoxHolder {
private static final ConstantPoolBuilder CP = ConstantPoolBuilder.of();
private static final String NAME_OBJECT = "java/lang/Object";
private static final String WRAPPER_PREFIX = "Ljava/lang/";
// Same for all primitives; name of the boxing method
private static final String NAME_BOX_METHOD = "valueOf";
// Table of opcodes for widening primitive conversions; NOP = no conversion
private static final int[][] wideningOpcodes = new int[NUM_WRAPPERS][NUM_WRAPPERS];
private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16];
// Table of wrappers for primitives, indexed by ASM type sorts
private static final Wrapper[] FROM_TYPE_SORT = new Wrapper[12];
static {
for (Wrapper w : Wrapper.values()) {
if (w.basicTypeChar() != 'L') {
int wi = hashWrapperName(w.wrapperSimpleName());
assert (FROM_WRAPPER_NAME[wi] == null);
FROM_WRAPPER_NAME[wi] = w;
}
private static MethodRefEntry box(ClassDesc primitive, ClassDesc target) {
return CP.methodRefEntry(target, "valueOf", MethodTypeDescImpl.ofValidated(target, primitive));
}
// wideningOpcodes[][] will be NOP-initialized by default
assert(Opcodes.NOP == 0);
private static final MethodRefEntry BOX_BOOLEAN = box(CD_boolean, CD_Boolean),
BOX_BYTE = box(CD_byte, CD_Byte),
BOX_SHORT = box(CD_short, CD_Short),
BOX_CHAR = box(CD_char, CD_Character),
BOX_INT = box(CD_int, CD_Integer),
BOX_LONG = box(CD_long, CD_Long),
BOX_FLOAT = box(CD_float, CD_Float),
BOX_DOUBLE = box(CD_double, CD_Double);
initWidening(LONG, Opcodes.I2L, BYTE, SHORT, INT, CHAR);
initWidening(LONG, Opcodes.F2L, FLOAT);
initWidening(FLOAT, Opcodes.I2F, BYTE, SHORT, INT, CHAR);
initWidening(FLOAT, Opcodes.L2F, LONG);
initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
initWidening(DOUBLE, Opcodes.F2D, FLOAT);
initWidening(DOUBLE, Opcodes.L2D, LONG);
FROM_TYPE_SORT[Type.BYTE] = Wrapper.BYTE;
FROM_TYPE_SORT[Type.SHORT] = Wrapper.SHORT;
FROM_TYPE_SORT[Type.INT] = Wrapper.INT;
FROM_TYPE_SORT[Type.LONG] = Wrapper.LONG;
FROM_TYPE_SORT[Type.CHAR] = Wrapper.CHAR;
FROM_TYPE_SORT[Type.FLOAT] = Wrapper.FLOAT;
FROM_TYPE_SORT[Type.DOUBLE] = Wrapper.DOUBLE;
FROM_TYPE_SORT[Type.BOOLEAN] = Wrapper.BOOLEAN;
}
private static void initWidening(Wrapper to, int opcode, Wrapper... from) {
for (Wrapper f : from) {
wideningOpcodes[f.ordinal()][to.ordinal()] = opcode;
private static MethodRefEntry unbox(ClassDesc owner, String methodName, ClassDesc primitiveTarget) {
return CP.methodRefEntry(owner, methodName, MethodTypeDescImpl.ofValidated(primitiveTarget));
}
private static final MethodRefEntry UNBOX_BOOLEAN = unbox(CD_Boolean, "booleanValue", CD_boolean),
UNBOX_BYTE = unbox(CD_Number, "byteValue", CD_byte),
UNBOX_SHORT = unbox(CD_Number, "shortValue", CD_short),
UNBOX_CHAR = unbox(CD_Character, "charValue", CD_char),
UNBOX_INT = unbox(CD_Number, "intValue", CD_int),
UNBOX_LONG = unbox(CD_Number, "longValue", CD_long),
UNBOX_FLOAT = unbox(CD_Number, "floatValue", CD_float),
UNBOX_DOUBLE = unbox(CD_Number, "doubleValue", CD_double);
}
/**
* Class name to Wrapper hash, derived from Wrapper.hashWrap()
* @param xn
* @return The hash code 0-15
*/
private static int hashWrapperName(String xn) {
if (xn.length() < 3) {
return 0;
}
return (3 * xn.charAt(1) + xn.charAt(2)) % 16;
private static TypeKind primitiveTypeKindFromClass(Class<?> type) {
if (type == int.class) return TypeKind.IntType;
if (type == long.class) return TypeKind.LongType;
if (type == boolean.class) return TypeKind.BooleanType;
if (type == short.class) return TypeKind.ShortType;
if (type == byte.class) return TypeKind.ByteType;
if (type == char.class) return TypeKind.CharType;
if (type == float.class) return TypeKind.FloatType;
if (type == double.class) return TypeKind.DoubleType;
return null;
}
private Wrapper wrapperOrNullFromDescriptor(String desc) {
if (!desc.startsWith(WRAPPER_PREFIX)) {
// Not a class type (array or method), so not a boxed type
// or not in the right package
return null;
}
// Pare it down to the simple class name
String cname = desc.substring(WRAPPER_PREFIX.length(), desc.length() - 1);
// Hash to a Wrapper
Wrapper w = FROM_WRAPPER_NAME[hashWrapperName(cname)];
if (w == null || w.wrapperSimpleName().equals(cname)) {
return w;
} else {
return null;
}
static void boxIfTypePrimitive(CodeBuilder cob, TypeKind tk) {
box(cob, tk);
}
private static String wrapperName(Wrapper w) {
return "java/lang/" + w.wrapperSimpleName();
}
private static String unboxMethod(Wrapper w) {
return w.primitiveSimpleName() + "Value";
}
private static String boxingDescriptor(Wrapper w) {
return "(" + w.basicTypeChar() + ")L" + wrapperName(w) + ";";
}
private static String unboxingDescriptor(Wrapper w) {
return "()" + w.basicTypeChar();
}
void boxIfTypePrimitive(Type t) {
Wrapper w = FROM_TYPE_SORT[t.getSort()];
if (w != null) {
box(w);
}
}
void widen(Wrapper ws, Wrapper wt) {
static void widen(CodeBuilder cob, TypeKind ws, TypeKind wt) {
ws = ws.asLoadable();
wt = wt.asLoadable();
if (ws != wt) {
int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()];
if (opcode != Opcodes.NOP) {
visitInsn(opcode);
}
cob.conversion(ws, wt);
}
}
void box(Wrapper w) {
visitMethodInsn(Opcodes.INVOKESTATIC,
wrapperName(w),
NAME_BOX_METHOD,
boxingDescriptor(w), false);
}
/**
* Convert types by unboxing. The source type is known to be a primitive wrapper.
* @param sname A primitive wrapper corresponding to wrapped reference source type
* @param wt A primitive wrapper being converted to
*/
void unbox(String sname, Wrapper wt) {
visitMethodInsn(Opcodes.INVOKEVIRTUAL,
sname,
unboxMethod(wt),
unboxingDescriptor(wt), false);
}
private String descriptorToName(String desc) {
int last = desc.length() - 1;
if (desc.charAt(0) == 'L' && desc.charAt(last) == ';') {
// In descriptor form
return desc.substring(1, last);
} else {
// Already in internal name form
return desc;
static void box(CodeBuilder cob, TypeKind tk) {
switch (tk) {
case BooleanType -> cob.invokestatic(BoxHolder.BOX_BOOLEAN);
case ByteType -> cob.invokestatic(BoxHolder.BOX_BYTE);
case CharType -> cob.invokestatic(BoxHolder.BOX_CHAR);
case DoubleType -> cob.invokestatic(BoxHolder.BOX_DOUBLE);
case FloatType -> cob.invokestatic(BoxHolder.BOX_FLOAT);
case IntType -> cob.invokestatic(BoxHolder.BOX_INT);
case LongType -> cob.invokestatic(BoxHolder.BOX_LONG);
case ShortType -> cob.invokestatic(BoxHolder.BOX_SHORT);
}
}
void cast(String ds, String dt) {
String ns = descriptorToName(ds);
String nt = descriptorToName(dt);
if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) {
visitTypeInsn(Opcodes.CHECKCAST, nt);
static void unbox(CodeBuilder cob, TypeKind to) {
switch (to) {
case BooleanType -> cob.invokevirtual(BoxHolder.UNBOX_BOOLEAN);
case ByteType -> cob.invokevirtual(BoxHolder.UNBOX_BYTE);
case CharType -> cob.invokevirtual(BoxHolder.UNBOX_CHAR);
case DoubleType -> cob.invokevirtual(BoxHolder.UNBOX_DOUBLE);
case FloatType -> cob.invokevirtual(BoxHolder.UNBOX_FLOAT);
case IntType -> cob.invokevirtual(BoxHolder.UNBOX_INT);
case LongType -> cob.invokevirtual(BoxHolder.UNBOX_LONG);
case ShortType -> cob.invokevirtual(BoxHolder.UNBOX_SHORT);
}
}
private Wrapper toWrapper(String desc) {
char first = desc.charAt(0);
if (first == '[' || first == '(') {
first = 'L';
static void cast(CodeBuilder cob, ClassDesc dt) {
if (!dt.equals(CD_Object)) {
cob.checkcast(dt);
}
return Wrapper.forBasicType(first);
}
/**
@ -204,7 +131,7 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
* @param target
* @param functional
*/
void convertType(Class<?> arg, Class<?> target, Class<?> functional) {
static void convertType(CodeBuilder cob, Class<?> arg, Class<?> target, Class<?> functional) {
if (arg.equals(target) && arg.equals(functional)) {
return;
}
@ -212,84 +139,69 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
return;
}
if (arg.isPrimitive()) {
Wrapper wArg = Wrapper.forPrimitiveType(arg);
if (target.isPrimitive()) {
// Both primitives: widening
widen(wArg, Wrapper.forPrimitiveType(target));
widen(cob, TypeKind.from(arg), TypeKind.from(target));
} else {
// Primitive argument to reference target
String dTarget = BytecodeDescriptor.unparse(target);
Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget);
if (wPrimTarget != null) {
TypeKind wPrimTk = primitiveTypeKindFromClass(target);
if (wPrimTk != null) {
// The target is a boxed primitive type, widen to get there before boxing
widen(wArg, wPrimTarget);
box(wPrimTarget);
widen(cob, TypeKind.from(arg), wPrimTk);
box(cob, wPrimTk);
} else {
// Otherwise, box and cast
box(wArg);
cast(wrapperName(wArg), dTarget);
box(cob, TypeKind.from(arg));
cast(cob, classDesc(target));
}
}
} else {
String dArg = BytecodeDescriptor.unparse(arg);
String dSrc;
if (functional.isPrimitive()) {
dSrc = dArg;
Class<?> src;
if (arg == functional || functional.isPrimitive()) {
src = arg;
} else {
// Cast to convert to possibly more specific type, and generate CCE for invalid arg
dSrc = BytecodeDescriptor.unparse(functional);
cast(dArg, dSrc);
src = functional;
cast(cob, classDesc(functional));
}
String dTarget = BytecodeDescriptor.unparse(target);
if (target.isPrimitive()) {
Wrapper wTarget = toWrapper(dTarget);
// Reference argument to primitive target
Wrapper wps = wrapperOrNullFromDescriptor(dSrc);
TypeKind wps = primitiveTypeKindFromClass(src);
if (wps != null) {
if (wps.isSigned() || wps.isFloating()) {
if (src != Character.class && src != Boolean.class) {
// Boxed number to primitive
unbox(wrapperName(wps), wTarget);
unbox(cob, TypeKind.from(target));
} else {
// Character or Boolean
unbox(wrapperName(wps), wps);
widen(wps, wTarget);
unbox(cob, wps);
widen(cob, wps, TypeKind.from(target));
}
} else {
// Source type is reference type, but not boxed type,
// assume it is super type of target type
String intermediate;
if (wTarget.isSigned() || wTarget.isFloating()) {
// Boxed number to primitive
intermediate = "java/lang/Number";
if (target == char.class) {
cast(cob, CD_Character);
} else if (target == boolean.class) {
cast(cob, CD_Boolean);
} else {
// Character or Boolean
intermediate = wrapperName(wTarget);
// Boxed number to primitive
cast(cob, CD_Number);
}
cast(dSrc, intermediate);
unbox(intermediate, wTarget);
unbox(cob, TypeKind.from(target));
}
} else {
// Both reference types: just case to target type
cast(dSrc, dTarget);
if (src != target) {
cast(cob, classDesc(target));
}
}
}
}
/**
* The following method is copied from
* org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
* and fast Java bytecode manipulation framework.
* Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
*/
void iconst(final int cst) {
if (cst >= -1 && cst <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + cst);
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, cst);
} else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, cst);
} else {
mv.visitLdcInsn(cst);
}
static ClassDesc classDesc(Class<?> cls) {
return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor()
: cls == Object.class ? CD_Object
: cls == String.class ? CD_String
: ReferenceClassDescImpl.ofValidated(cls.descriptorString());
}
}

View File

@ -25,15 +25,19 @@
*/
package jdk.internal.classfile.impl;
import java.lang.classfile.constantpool.InvokeDynamicEntry;
import java.lang.constant.ClassDesc;
import static java.lang.constant.ConstantDescs.*;
import java.lang.constant.MethodTypeDesc;
import java.lang.classfile.Attribute;
import java.lang.classfile.Attributes;
import java.lang.classfile.BufWriter;
import java.lang.classfile.ClassFile;
import java.lang.classfile.Label;
import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.classfile.constantpool.ConstantDynamicEntry;
import java.lang.classfile.constantpool.MemberRefEntry;
import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.classfile.constantpool.InvokeDynamicEntry;
import java.lang.classfile.constantpool.MemberRefEntry;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@ -41,15 +45,10 @@ import java.util.BitSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.lang.classfile.Attribute;
import jdk.internal.constant.ReferenceClassDescImpl;
import static java.lang.classfile.ClassFile.*;
import static jdk.internal.constant.ConstantUtils.binaryNameToDesc;
import java.lang.classfile.BufWriter;
import java.lang.classfile.Label;
import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.classfile.Attributes;
import static java.lang.constant.ConstantDescs.*;
/**
* StackMapGenerator is responsible for stack map frames generation.
@ -1249,14 +1248,14 @@ public final class StackMapGenerator {
//frequently used types to reduce footprint
static final Type OBJECT_TYPE = referenceType(CD_Object),
THROWABLE_TYPE = referenceType(CD_Throwable),
INT_ARRAY_TYPE = referenceType(CD_int.arrayType()),
BOOLEAN_ARRAY_TYPE = referenceType(CD_boolean.arrayType()),
BYTE_ARRAY_TYPE = referenceType(CD_byte.arrayType()),
CHAR_ARRAY_TYPE = referenceType(CD_char.arrayType()),
SHORT_ARRAY_TYPE = referenceType(CD_short.arrayType()),
LONG_ARRAY_TYPE = referenceType(CD_long.arrayType()),
DOUBLE_ARRAY_TYPE = referenceType(CD_double.arrayType()),
FLOAT_ARRAY_TYPE = referenceType(CD_float.arrayType()),
INT_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[I")),
BOOLEAN_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[Z")),
BYTE_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[B")),
CHAR_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[C")),
SHORT_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[S")),
LONG_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[J")),
DOUBLE_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[D")),
FLOAT_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[F")),
STRING_TYPE = referenceType(CD_String),
CLASS_TYPE = referenceType(CD_Class),
METHOD_HANDLE_TYPE = referenceType(CD_MethodHandle),
@ -1321,8 +1320,8 @@ public final class StackMapGenerator {
}
}
private static final ClassDesc CD_Cloneable = binaryNameToDesc("java.lang.Cloneable");
private static final ClassDesc CD_Serializable = binaryNameToDesc("java.io.Serializable");
private static final ClassDesc CD_Cloneable = ReferenceClassDescImpl.ofValidated("Ljava/lang/Cloneable;");
private static final ClassDesc CD_Serializable = ReferenceClassDescImpl.ofValidated("Ljava/io/Serializable;");
private Type mergeReferenceFrom(Type from, ClassHierarchyImpl context) {
if (from == NULL_TYPE) {

View File

@ -101,6 +101,7 @@ runtime/StackGuardPages/TestStackGuardPagesNative.java 8303612 linux-all
runtime/ErrorHandling/TestDwarf.java#checkDecoder 8305489 linux-all
runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le
runtime/cds/appcds/customLoader/HelloCustom_JFR.java 8241075 linux-all,windows-x64
runtime/ClassInitErrors/TestStackOverflowDuringInit.java 8334545 generic-all
applications/jcstress/copy.java 8229852 linux-all