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; package java.lang.invoke;
import jdk.internal.loader.BootLoader; import java.lang.classfile.*;
import jdk.internal.org.objectweb.asm.ClassWriter; import java.lang.classfile.attribute.ExceptionsAttribute;
import jdk.internal.org.objectweb.asm.FieldVisitor; import java.lang.classfile.attribute.SourceFileAttribute;
import jdk.internal.org.objectweb.asm.MethodVisitor; import java.lang.constant.ClassDesc;
import jdk.internal.vm.annotation.Stable; import java.lang.constant.MethodTypeDesc;
import sun.invoke.util.BytecodeName; import java.lang.invoke.LambdaForm.BasicType;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@ -42,12 +41,19 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; 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_getStatic;
import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic; import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic;
import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/** /**
* Class specialization code. * Class specialization code.
@ -57,6 +63,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
*/ */
/*non-public*/ /*non-public*/
abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesData> { 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<T> topClass;
private final Class<K> keyType; private final Class<K> keyType;
private final Class<S> metaType; 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()); buf.append(basicType.basicTypeChar());
} else { } else {
buf.append('V'); buf.append('V');
end.append(classSig(type)); end.append(type.descriptorString());
} }
} }
String typeString; 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: // 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 String SPECIES_DATA_NAME = sdAccessor.getName();
private final int SPECIES_DATA_MODS = sdAccessor.getModifiers(); private final int SPECIES_DATA_MODS = sdAccessor.getModifiers();
private final List<String> TRANSFORM_NAMES; // derived from transformMethods private final List<String> TRANSFORM_NAMES; // derived from transformMethods
@ -595,31 +606,27 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
TRANSFORM_TYPES = List.of(tts.toArray(new MethodType[0])); TRANSFORM_TYPES = List.of(tts.toArray(new MethodType[0]));
TRANSFORM_MODS = List.of(tms.toArray(new Integer[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; private static final int ACC_PPP = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
/*non-public*/ /*non-public*/
byte[] generateConcreteSpeciesCodeFile(String className0, ClassSpecializer<T,K,S>.SpeciesData speciesData) { byte[] generateConcreteSpeciesCodeFile(String className0, ClassSpecializer<T,K,S>.SpeciesData speciesData) {
final String className = classBCName(className0); final ClassDesc classDesc = ClassDesc.of(className0);
final String superClassName = classBCName(speciesData.deriveSuperClass()); final ClassDesc superClassDesc = classDesc(speciesData.deriveSuperClass());
return ClassFile.of().build(classDesc, clb -> {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); clb.withFlags(ACC_FINAL | ACC_SUPER)
final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC .withSuperclass(superClassDesc)
cw.visit(CLASSFILE_VERSION, NOT_ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, superClassName, null); .with(SourceFileAttribute.of(classDesc.displayName()))
final String sourceFile = className.substring(className.lastIndexOf('.')+1);
cw.visitSource(sourceFile, null);
// emit static types and BMH_SPECIES fields // emit static types and BMH_SPECIES fields
FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, sdFieldName, SPECIES_DATA_SIG, null, null); .withField(sdFieldName, CD_SPECIES_DATA, ACC_STATIC);
fw.visitAnnotation(STABLE_SIG, true);
fw.visitEnd();
// handy holder for dealing with groups of typed values (ctor arguments and fields) // handy holder for dealing with groups of typed values (ctor arguments and fields)
class Var { class Var {
final int index; final int index;
final String name; final String name;
final Class<?> type; final Class<?> type;
final String desc; final ClassDesc desc;
final BasicType basicType; final BasicType basicType;
final int slotIndex; final int slotIndex;
Var(int index, int slotIndex) { Var(int index, int slotIndex) {
@ -635,13 +642,11 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
if (name.endsWith("#")) if (name.endsWith("#"))
name = name.substring(0, name.length()-1) + index; name = name.substring(0, name.length()-1) + index;
assert(!type.equals(void.class)); assert(!type.equals(void.class));
String desc = classSig(type);
BasicType basicType = BasicType.basicType(type);
this.index = index; this.index = index;
this.name = name; this.name = name;
this.type = type; this.type = type;
this.desc = desc; this.desc = classDesc(type);
this.basicType = basicType; this.basicType = BasicType.basicType(type);
this.slotIndex = slotIndex; this.slotIndex = slotIndex;
} }
Var lastOf(List<Var> vars) { Var lastOf(List<Var> vars) {
@ -675,15 +680,8 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
int nextIndex() { return index + (slotSize() == 0 ? 0 : 1); } int nextIndex() { return index + (slotSize() == 0 ? 0 : 1); }
int nextSlotIndex() { return slotIndex >= 0 ? slotIndex + slotSize() : slotIndex; } int nextSlotIndex() { return slotIndex >= 0 ? slotIndex + slotSize() : slotIndex; }
boolean isInHeap() { return slotIndex < 0; } boolean isInHeap() { return slotIndex < 0; }
void emitVarInstruction(int asmop, MethodVisitor mv) { void emitLoadInstruction(CodeBuilder cob) {
if (asmop == ALOAD) cob.loadLocal(basicType.btKind, slotIndex);
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);
} }
} }
@ -705,76 +703,58 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
// emit bound argument fields // emit bound argument fields
for (Var field : fields) { for (Var field : fields) {
cw.visitField(ACC_FINAL, field.name, field.desc, null, null).visitEnd(); clb.withField(field.name, field.desc, ACC_FINAL);
} }
MethodVisitor mv;
// emit implementation of speciesData() // emit implementation of speciesData()
mv = cw.visitMethod((SPECIES_DATA_MODS & ACC_PPP) + ACC_FINAL, clb.withMethodBody(SPECIES_DATA_NAME, MTD_SPECIES_DATA, (SPECIES_DATA_MODS & ACC_PPP) | ACC_FINAL,
SPECIES_DATA_NAME, "()" + SPECIES_DATA_SIG, null, null); cob -> cob.getstatic(classDesc, sdFieldName, CD_SPECIES_DATA)
mv.visitCode(); .areturn());
mv.visitFieldInsn(GETSTATIC, className, sdFieldName, SPECIES_DATA_SIG);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// figure out the constructor arguments // figure out the constructor arguments
MethodType superCtorType = ClassSpecializer.this.baseConstructorType(); MethodType superCtorType = ClassSpecializer.this.baseConstructorType();
MethodType thisCtorType = superCtorType.appendParameterTypes(fieldTypes); MethodType thisCtorType = superCtorType.appendParameterTypes(fieldTypes);
// emit constructor // emit constructor
{ clb.withMethodBody(INIT_NAME, methodDesc(thisCtorType), ACC_PRIVATE, cob -> {
mv = cw.visitMethod(ACC_PRIVATE, cob.aload(0); // this
"<init>", methodSig(thisCtorType), null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0); // this
final List<Var> ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList()); final List<Var> ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList());
for (Var ca : ctorArgs) { for (Var ca : ctorArgs) {
ca.emitVarInstruction(ALOAD, mv); ca.emitLoadInstruction(cob);
} }
// super(ca...) // super(ca...)
mv.visitMethodInsn(INVOKESPECIAL, superClassName, cob.invokespecial(superClassDesc, INIT_NAME, methodDesc(superCtorType));
"<init>", methodSig(superCtorType), false);
// store down fields // store down fields
Var lastFV = AFTER_THIS.lastOf(ctorArgs); Var lastFV = AFTER_THIS.lastOf(ctorArgs);
for (Var f : fields) { for (Var f : fields) {
// this.argL1 = argL1 // this.argL1 = argL1
mv.visitVarInsn(ALOAD, 0); // this cob.aload(0); // this
lastFV = new Var(f.name, f.type, lastFV); lastFV = new Var(f.name, f.type, lastFV);
lastFV.emitVarInstruction(ALOAD, mv); lastFV.emitLoadInstruction(cob);
f.emitFieldInsn(PUTFIELD, mv); cob.putfield(classDesc, f.name, f.desc);
} }
mv.visitInsn(RETURN); cob.return_();
mv.visitMaxs(0, 0); });
mv.visitEnd();
}
// emit make() ...factory method wrapping constructor // emit make() ...factory method wrapping constructor
{
MethodType ftryType = thisCtorType.changeReturnType(topClass()); MethodType ftryType = thisCtorType.changeReturnType(topClass());
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_STATIC, clb.withMethodBody("make", methodDesc(ftryType), ACC_STATIC, cob -> {
"make", methodSig(ftryType), null, null);
mv.visitCode();
// make instance // make instance
mv.visitTypeInsn(NEW, className); cob.new_(classDesc)
mv.visitInsn(DUP); .dup();
// load factory method arguments: ctarg... and arg... // load factory method arguments: ctarg... and arg...
for (Var v : NO_THIS.fromTypes(ftryType.parameterList())) { for (Var v : NO_THIS.fromTypes(ftryType.parameterList())) {
v.emitVarInstruction(ALOAD, mv); v.emitLoadInstruction(cob);
} }
// finally, invoke the constructor and return // finally, invoke the constructor and return
mv.visitMethodInsn(INVOKESPECIAL, className, cob.invokespecial(classDesc, INIT_NAME, methodDesc(thisCtorType))
"<init>", methodSig(thisCtorType), false); .areturn();
mv.visitInsn(ARETURN); });
mv.visitMaxs(0, 0);
mv.visitEnd();
}
// For each transform, emit the customized override of the transform method. // For each transform, emit the customized override of the transform method.
// This method mixes together some incoming arguments (from the transform's // This method mixes together some incoming arguments (from the transform's
@ -782,81 +762,51 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
// the resulting mish-mosh of values to a method handle produced by // the resulting mish-mosh of values to a method handle produced by
// the species itself. (Typically this method handle is the factory // the species itself. (Typically this method handle is the factory
// method of this species or a related one.) // method of this species or a related one.)
for (int whichtm = 0; whichtm < TRANSFORM_NAMES.size(); whichtm++) { for (int i = 0; i < TRANSFORM_NAMES.size(); i++) {
final int whichtm = i;
final String TNAME = TRANSFORM_NAMES.get(whichtm); final String TNAME = TRANSFORM_NAMES.get(whichtm);
final MethodType TTYPE = TRANSFORM_TYPES.get(whichtm); final MethodType TTYPE = TRANSFORM_TYPES.get(whichtm);
final int TMODS = TRANSFORM_MODS.get(whichtm); final int TMODS = TRANSFORM_MODS.get(whichtm);
mv = cw.visitMethod((TMODS & ACC_PPP) | ACC_FINAL, clb.withMethod(TNAME, methodDesc(TTYPE), (TMODS & ACC_PPP) | ACC_FINAL, mb -> {
TNAME, TTYPE.toMethodDescriptorString(), null, E_THROWABLE); mb.with(ExceptionsAttribute.ofSymbols(CD_Throwable))
mv.visitCode(); .withCode(cob -> {
// return a call to the corresponding "transform helper", something like this: // return a call to the corresponding "transform helper", something like this:
// MY_SPECIES.transformHelper(whichtm).invokeBasic(ctarg, ..., argL0, ..., xarg) // MY_SPECIES.transformHelper(whichtm).invokeBasic(ctarg, ..., argL0, ..., xarg)
mv.visitFieldInsn(GETSTATIC, className, cob.getstatic(classDesc, sdFieldName, CD_SPECIES_DATA)
sdFieldName, SPECIES_DATA_SIG); .loadConstant(whichtm)
emitIntConstant(whichtm, mv); .invokevirtual(CD_SPECIES_DATA, "transformHelper", MTD_TRANFORM_HELPER);
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA,
"transformHelper", "(I)" + MH_SIG, false);
List<Var> targs = AFTER_THIS.fromTypes(TTYPE.parameterList()); List<Var> targs = AFTER_THIS.fromTypes(TTYPE.parameterList());
List<Var> tfields = new ArrayList<>(fields); List<Var> tfields = new ArrayList<>(fields);
// mix them up and load them for the transform helper: // mix them up and load them for the transform helper:
List<Var> helperArgs = speciesData.deriveTransformHelperArguments(transformMethods.get(whichtm), whichtm, targs, tfields); List<Var> helperArgs = speciesData.deriveTransformHelperArguments(transformMethods.get(whichtm), whichtm, targs, tfields);
List<Class<?>> helperTypes = new ArrayList<>(helperArgs.size()); ClassDesc[] helperTypes = new ClassDesc[helperArgs.size()];
for (Var ha : helperArgs) { for (int hi = 0; hi < helperTypes.length; hi++) {
helperTypes.add(ha.basicType.basicTypeClass()); Var ha = helperArgs.get(hi);
helperTypes[hi] = ha.basicType.basicTypeWrapper().basicClassDescriptor();
if (ha.isInHeap()) { if (ha.isInHeap()) {
assert(tfields.contains(ha)); assert(tfields.contains(ha));
mv.visitVarInsn(ALOAD, 0); cob.aload(0);
ha.emitFieldInsn(GETFIELD, mv); cob.getfield(classDesc, ha.name, ha.desc);
} else { } else {
assert(targs.contains(ha)); assert(targs.contains(ha));
ha.emitVarInstruction(ALOAD, mv); ha.emitLoadInstruction(cob);
} }
} }
// jump into the helper (which is probably a factory method) // jump into the helper (which is probably a factory method)
final Class<?> rtype = TTYPE.returnType(); final Class<?> rtype = TTYPE.returnType();
final BasicType rbt = BasicType.basicType(rtype); if (!rtype.isPrimitive()) {
MethodType invokeBasicType = MethodType.methodType(rbt.basicTypeClass(), helperTypes); cob.invokevirtual(CD_MethodHandle, "invokeBasic", MethodTypeDescImpl.ofValidated(CD_Object, helperTypes))
mv.visitMethodInsn(INVOKEVIRTUAL, MH, .checkcast(classDesc(rtype))
"invokeBasic", methodSig(invokeBasicType), false); .areturn();
if (rbt == BasicType.L_TYPE) {
mv.visitTypeInsn(CHECKCAST, classBCName(rtype));
mv.visitInsn(ARETURN);
} else { } else {
throw newInternalError("NYI: transform of type "+rtype); 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: // 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) { static String classBCName(String str) {
assert(str.indexOf('/') < 0) : str; assert(str.indexOf('/') < 0) : str;
return str.replace('.', '/'); return str.replace('.', '/');
} }
static String className(Class<?> cls) {
assert(!cls.isArray() && !cls.isPrimitive()); static ClassDesc classDesc(Class<?> cls) {
return cls.getName(); 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; package java.lang.invoke;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;
import sun.invoke.util.Wrapper; 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.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -38,10 +39,10 @@ import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.lang.classfile.ClassFile.*;
import static java.lang.invoke.LambdaForm.BasicType.*; 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.LambdaForm.Kind.*;
import static java.lang.invoke.MethodTypeForm.*;
/** /**
* Helper class to assist the GenerateJLIClassesPlugin to get access to * Helper class to assist the GenerateJLIClassesPlugin to get access to
@ -557,19 +558,14 @@ class GenerateJLIClassesHelper {
* a class with a specified name. * a class with a specified name.
*/ */
private static byte[] generateCodeBytesForLFs(String className, String[] names, LambdaForm[] forms) { private static byte[] generateCodeBytesForLFs(String className, String[] names, LambdaForm[] forms) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> {
cw.visit(CLASSFILE_VERSION, Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, clb.withFlags(ACC_PRIVATE | ACC_FINAL | ACC_SUPER)
className, null, InvokerBytecodeGenerator.INVOKER_SUPER_NAME, null); .withSuperclass(InvokerBytecodeGenerator.INVOKER_SUPER_DESC)
cw.visitSource(className.substring(className.lastIndexOf('/') + 1), null); .with(SourceFileAttribute.of(className.substring(className.lastIndexOf('/') + 1)));
for (int i = 0; i < forms.length; i++) { for (int i = 0; i < forms.length; i++) {
InvokerBytecodeGenerator g new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType()).addMethod(clb);
= new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType());
g.setClassWriter(cw);
g.addMethod();
} }
});
return cw.toByteArray();
} }
private static LambdaForm makeReinvokerFor(MethodType type) { private static LambdaForm makeReinvokerFor(MethodType type) {

View File

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

View File

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

View File

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

View File

@ -28,9 +28,6 @@ package java.lang.invoke;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe; import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM; 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.CallerSensitive;
import jdk.internal.reflect.CallerSensitiveAdapter; import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.Reflection; import jdk.internal.reflect.Reflection;
@ -42,8 +39,10 @@ import sun.invoke.util.Wrapper;
import sun.reflect.misc.ReflectUtil; import sun.reflect.misc.ReflectUtil;
import sun.security.util.SecurityConstants; import sun.security.util.SecurityConstants;
import java.lang.classfile.ClassModel;
import java.lang.constant.ConstantDescs; import java.lang.constant.ConstantDescs;
import java.lang.invoke.LambdaForm.BasicType; import java.lang.invoke.LambdaForm.BasicType;
import java.lang.invoke.MethodHandleImpl.Intrinsic;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Member; import java.lang.reflect.Member;
@ -62,8 +61,8 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream; 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.LambdaForm.BasicType.V_TYPE;
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.UNSAFE; import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException; import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
@ -2288,27 +2287,16 @@ public class MethodHandles {
String name; String name;
int accessFlags; int accessFlags;
try { try {
ClassReader reader = new ClassReader(bytes); ClassModel cm = java.lang.classfile.ClassFile.of().parse(bytes);
// ClassReader does not check if `this_class` is CONSTANT_Class_info name = cm.thisClass().asInternalName();
// workaround to read `this_class` using readConst and validate the value accessFlags = cm.flags().flagsMask();
int thisClass = reader.readUnsignedShort(reader.header + 2); } catch (IllegalArgumentException e) {
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
ClassFormatError cfe = new ClassFormatError(); ClassFormatError cfe = new ClassFormatError();
cfe.initCause(e); cfe.initCause(e);
throw cfe; throw cfe;
} }
// must be a class or interface // 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"); throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
} }
return new ClassFile(name, accessFlags, bytes); return new ClassFile(name, accessFlags, bytes);

View File

@ -1291,15 +1291,21 @@ class MethodType
*/ */
@Override @Override
public Optional<MethodTypeDesc> describeConstable() { public Optional<MethodTypeDesc> describeConstable() {
try { var retDesc = returnType().describeConstable();
return Optional.of(MethodTypeDesc.of(returnType().describeConstable().orElseThrow(), if (retDesc.isEmpty())
Stream.of(parameterArray())
.map(p -> p.describeConstable().orElseThrow())
.toArray(ClassDesc[]::new)));
}
catch (NoSuchElementException e) {
return Optional.empty(); 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. //--- Serialization.

View File

@ -25,176 +25,103 @@
package java.lang.invoke; package java.lang.invoke;
import jdk.internal.org.objectweb.asm.MethodVisitor; import java.lang.constant.ClassDesc;
import jdk.internal.org.objectweb.asm.Opcodes; import java.lang.classfile.CodeBuilder;
import jdk.internal.org.objectweb.asm.Type; import java.lang.classfile.TypeKind;
import sun.invoke.util.BytecodeDescriptor; 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 sun.invoke.util.Wrapper;
import static sun.invoke.util.Wrapper.*;
class TypeConvertingMethodAdapter extends MethodVisitor { import static java.lang.constant.ConstantDescs.*;
TypeConvertingMethodAdapter(MethodVisitor mv) { class TypeConvertingMethodAdapter {
super(Opcodes.ASM7, mv);
private static class BoxHolder {
private static final ConstantPoolBuilder CP = ConstantPoolBuilder.of();
private static MethodRefEntry box(ClassDesc primitive, ClassDesc target) {
return CP.methodRefEntry(target, "valueOf", MethodTypeDescImpl.ofValidated(target, primitive));
} }
private static final int NUM_WRAPPERS = Wrapper.COUNT; 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);
private static final String NAME_OBJECT = "java/lang/Object"; private static MethodRefEntry unbox(ClassDesc owner, String methodName, ClassDesc primitiveTarget) {
private static final String WRAPPER_PREFIX = "Ljava/lang/"; return CP.methodRefEntry(owner, methodName, MethodTypeDescImpl.ofValidated(primitiveTarget));
// 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;
}
} }
// wideningOpcodes[][] will be NOP-initialized by default private static final MethodRefEntry UNBOX_BOOLEAN = unbox(CD_Boolean, "booleanValue", CD_boolean),
assert(Opcodes.NOP == 0); UNBOX_BYTE = unbox(CD_Number, "byteValue", CD_byte),
UNBOX_SHORT = unbox(CD_Number, "shortValue", CD_short),
initWidening(LONG, Opcodes.I2L, BYTE, SHORT, INT, CHAR); UNBOX_CHAR = unbox(CD_Character, "charValue", CD_char),
initWidening(LONG, Opcodes.F2L, FLOAT); UNBOX_INT = unbox(CD_Number, "intValue", CD_int),
initWidening(FLOAT, Opcodes.I2F, BYTE, SHORT, INT, CHAR); UNBOX_LONG = unbox(CD_Number, "longValue", CD_long),
initWidening(FLOAT, Opcodes.L2F, LONG); UNBOX_FLOAT = unbox(CD_Number, "floatValue", CD_float),
initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR); UNBOX_DOUBLE = unbox(CD_Number, "doubleValue", CD_double);
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) { private static TypeKind primitiveTypeKindFromClass(Class<?> type) {
for (Wrapper f : from) { if (type == int.class) return TypeKind.IntType;
wideningOpcodes[f.ordinal()][to.ordinal()] = opcode; 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;
* Class name to Wrapper hash, derived from Wrapper.hashWrap() if (type == float.class) return TypeKind.FloatType;
* @param xn if (type == double.class) return TypeKind.DoubleType;
* @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 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; return null;
} }
static void boxIfTypePrimitive(CodeBuilder cob, TypeKind tk) {
box(cob, tk);
} }
private static String wrapperName(Wrapper w) { static void widen(CodeBuilder cob, TypeKind ws, TypeKind wt) {
return "java/lang/" + w.wrapperSimpleName(); ws = ws.asLoadable();
} wt = wt.asLoadable();
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) {
if (ws != wt) { if (ws != wt) {
int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()]; cob.conversion(ws, wt);
if (opcode != Opcodes.NOP) {
visitInsn(opcode);
}
}
}
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;
} }
} }
void cast(String ds, String dt) { static void box(CodeBuilder cob, TypeKind tk) {
String ns = descriptorToName(ds); switch (tk) {
String nt = descriptorToName(dt); case BooleanType -> cob.invokestatic(BoxHolder.BOX_BOOLEAN);
if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) { case ByteType -> cob.invokestatic(BoxHolder.BOX_BYTE);
visitTypeInsn(Opcodes.CHECKCAST, nt); 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);
} }
} }
private Wrapper toWrapper(String desc) { static void unbox(CodeBuilder cob, TypeKind to) {
char first = desc.charAt(0); switch (to) {
if (first == '[' || first == '(') { case BooleanType -> cob.invokevirtual(BoxHolder.UNBOX_BOOLEAN);
first = 'L'; 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);
}
}
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 target
* @param functional * @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)) { if (arg.equals(target) && arg.equals(functional)) {
return; return;
} }
@ -212,84 +139,69 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
return; return;
} }
if (arg.isPrimitive()) { if (arg.isPrimitive()) {
Wrapper wArg = Wrapper.forPrimitiveType(arg);
if (target.isPrimitive()) { if (target.isPrimitive()) {
// Both primitives: widening // Both primitives: widening
widen(wArg, Wrapper.forPrimitiveType(target)); widen(cob, TypeKind.from(arg), TypeKind.from(target));
} else { } else {
// Primitive argument to reference target // Primitive argument to reference target
String dTarget = BytecodeDescriptor.unparse(target); TypeKind wPrimTk = primitiveTypeKindFromClass(target);
Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget); if (wPrimTk != null) {
if (wPrimTarget != null) {
// The target is a boxed primitive type, widen to get there before boxing // The target is a boxed primitive type, widen to get there before boxing
widen(wArg, wPrimTarget); widen(cob, TypeKind.from(arg), wPrimTk);
box(wPrimTarget); box(cob, wPrimTk);
} else { } else {
// Otherwise, box and cast // Otherwise, box and cast
box(wArg); box(cob, TypeKind.from(arg));
cast(wrapperName(wArg), dTarget); cast(cob, classDesc(target));
} }
} }
} else { } else {
String dArg = BytecodeDescriptor.unparse(arg); Class<?> src;
String dSrc; if (arg == functional || functional.isPrimitive()) {
if (functional.isPrimitive()) { src = arg;
dSrc = dArg;
} else { } else {
// Cast to convert to possibly more specific type, and generate CCE for invalid arg // Cast to convert to possibly more specific type, and generate CCE for invalid arg
dSrc = BytecodeDescriptor.unparse(functional); src = functional;
cast(dArg, dSrc); cast(cob, classDesc(functional));
} }
String dTarget = BytecodeDescriptor.unparse(target);
if (target.isPrimitive()) { if (target.isPrimitive()) {
Wrapper wTarget = toWrapper(dTarget);
// Reference argument to primitive target // Reference argument to primitive target
Wrapper wps = wrapperOrNullFromDescriptor(dSrc); TypeKind wps = primitiveTypeKindFromClass(src);
if (wps != null) { if (wps != null) {
if (wps.isSigned() || wps.isFloating()) { if (src != Character.class && src != Boolean.class) {
// Boxed number to primitive // Boxed number to primitive
unbox(wrapperName(wps), wTarget); unbox(cob, TypeKind.from(target));
} else { } else {
// Character or Boolean // Character or Boolean
unbox(wrapperName(wps), wps); unbox(cob, wps);
widen(wps, wTarget); widen(cob, wps, TypeKind.from(target));
} }
} else { } else {
// Source type is reference type, but not boxed type, // Source type is reference type, but not boxed type,
// assume it is super type of target type // assume it is super type of target type
String intermediate; if (target == char.class) {
if (wTarget.isSigned() || wTarget.isFloating()) { cast(cob, CD_Character);
// Boxed number to primitive } else if (target == boolean.class) {
intermediate = "java/lang/Number"; cast(cob, CD_Boolean);
} else { } else {
// Character or Boolean // Boxed number to primitive
intermediate = wrapperName(wTarget); cast(cob, CD_Number);
} }
cast(dSrc, intermediate); unbox(cob, TypeKind.from(target));
unbox(intermediate, wTarget);
} }
} else { } else {
// Both reference types: just case to target type // Both reference types: just case to target type
cast(dSrc, dTarget); if (src != target) {
cast(cob, classDesc(target));
}
} }
} }
} }
/** static ClassDesc classDesc(Class<?> cls) {
* The following method is copied from return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor()
* org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small : cls == Object.class ? CD_Object
* and fast Java bytecode manipulation framework. : cls == String.class ? CD_String
* Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved. : ReferenceClassDescImpl.ofValidated(cls.descriptorString());
*/
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);
}
} }
} }

View File

@ -25,15 +25,19 @@
*/ */
package jdk.internal.classfile.impl; package jdk.internal.classfile.impl;
import java.lang.classfile.constantpool.InvokeDynamicEntry; import java.lang.classfile.Attribute;
import java.lang.constant.ClassDesc; import java.lang.classfile.Attributes;
import static java.lang.constant.ConstantDescs.*; import java.lang.classfile.BufWriter;
import java.lang.constant.MethodTypeDesc;
import java.lang.classfile.ClassFile; 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.ClassEntry;
import java.lang.classfile.constantpool.ConstantDynamicEntry; import java.lang.classfile.constantpool.ConstantDynamicEntry;
import java.lang.classfile.constantpool.MemberRefEntry;
import java.lang.classfile.constantpool.ConstantPoolBuilder; 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.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -41,15 +45,10 @@ import java.util.BitSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.lang.classfile.Attribute; import jdk.internal.constant.ReferenceClassDescImpl;
import static java.lang.classfile.ClassFile.*; import static java.lang.classfile.ClassFile.*;
import static jdk.internal.constant.ConstantUtils.binaryNameToDesc; import static java.lang.constant.ConstantDescs.*;
import java.lang.classfile.BufWriter;
import java.lang.classfile.Label;
import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.classfile.Attributes;
/** /**
* StackMapGenerator is responsible for stack map frames generation. * StackMapGenerator is responsible for stack map frames generation.
@ -1249,14 +1248,14 @@ public final class StackMapGenerator {
//frequently used types to reduce footprint //frequently used types to reduce footprint
static final Type OBJECT_TYPE = referenceType(CD_Object), static final Type OBJECT_TYPE = referenceType(CD_Object),
THROWABLE_TYPE = referenceType(CD_Throwable), THROWABLE_TYPE = referenceType(CD_Throwable),
INT_ARRAY_TYPE = referenceType(CD_int.arrayType()), INT_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[I")),
BOOLEAN_ARRAY_TYPE = referenceType(CD_boolean.arrayType()), BOOLEAN_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[Z")),
BYTE_ARRAY_TYPE = referenceType(CD_byte.arrayType()), BYTE_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[B")),
CHAR_ARRAY_TYPE = referenceType(CD_char.arrayType()), CHAR_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[C")),
SHORT_ARRAY_TYPE = referenceType(CD_short.arrayType()), SHORT_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[S")),
LONG_ARRAY_TYPE = referenceType(CD_long.arrayType()), LONG_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[J")),
DOUBLE_ARRAY_TYPE = referenceType(CD_double.arrayType()), DOUBLE_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[D")),
FLOAT_ARRAY_TYPE = referenceType(CD_float.arrayType()), FLOAT_ARRAY_TYPE = referenceType(ReferenceClassDescImpl.ofValidated("[F")),
STRING_TYPE = referenceType(CD_String), STRING_TYPE = referenceType(CD_String),
CLASS_TYPE = referenceType(CD_Class), CLASS_TYPE = referenceType(CD_Class),
METHOD_HANDLE_TYPE = referenceType(CD_MethodHandle), 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_Cloneable = ReferenceClassDescImpl.ofValidated("Ljava/lang/Cloneable;");
private static final ClassDesc CD_Serializable = binaryNameToDesc("java.io.Serializable"); private static final ClassDesc CD_Serializable = ReferenceClassDescImpl.ofValidated("Ljava/io/Serializable;");
private Type mergeReferenceFrom(Type from, ClassHierarchyImpl context) { private Type mergeReferenceFrom(Type from, ClassHierarchyImpl context) {
if (from == NULL_TYPE) { 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/TestDwarf.java#checkDecoder 8305489 linux-all
runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le
runtime/cds/appcds/customLoader/HelloCustom_JFR.java 8241075 linux-all,windows-x64 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 applications/jcstress/copy.java 8229852 linux-all