From 01ee4241b76e78ca67803c4b083fcedecef1c96c Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Wed, 19 Jun 2024 15:15:30 +0000 Subject: [PATCH] 8294960: Convert java.base/java.lang.invoke package to use the Classfile API to generate lambdas and method handles Co-authored-by: Claes Redestad Reviewed-by: redestad, liach --- .../java/lang/invoke/ClassSpecializer.java | 502 +++--- .../lang/invoke/GenerateJLIClassesHelper.java | 30 +- .../invoke/InnerClassLambdaMetafactory.java | 552 +++---- .../lang/invoke/InvokerBytecodeGenerator.java | 1454 +++++++---------- .../classes/java/lang/invoke/LambdaForm.java | 20 +- .../java/lang/invoke/MethodHandleImpl.java | 68 +- .../java/lang/invoke/MethodHandles.java | 28 +- .../classes/java/lang/invoke/MethodType.java | 20 +- .../invoke/TypeConvertingMethodAdapter.java | 296 ++-- .../classfile/impl/StackMapGenerator.java | 43 +- test/hotspot/jtreg/ProblemList.txt | 1 + 11 files changed, 1298 insertions(+), 1716 deletions(-) diff --git a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java index a5eb681fb8823..cca05a906ff08 100644 --- a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java +++ b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java @@ -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.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 @@ */ /*non-public*/ abstract class ClassSpecializer.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 topClass; private final Class keyType; private final Class metaType; @@ -404,7 +414,7 @@ protected String deriveTypeString() { buf.append(basicType.basicTypeChar()); } else { buf.append('V'); - end.append(classSig(type)); + end.append(type.descriptorString()); } } String typeString; @@ -572,8 +582,9 @@ Class generateConcreteSpeciesCode(String className, ClassSpecialize } // 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 TRANSFORM_NAMES; // derived from transformMethods @@ -595,268 +606,207 @@ Class generateConcreteSpeciesCode(String className, ClassSpecialize 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.SpeciesData speciesData) { - final String className = classBCName(className0); - final String superClassName = classBCName(speciesData.deriveSuperClass()); - - 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); - - 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 vars) { - int n = vars.size(); - return (n == 0 ? this : vars.get(n-1)); - } - List fromTypes(List types) { - Var prev = this; - ArrayList 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; + 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())) + + // emit static types and BMH_SPECIES fields + .withField(sdFieldName, CD_SPECIES_DATA, ACC_STATIC); + + // 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 vars) { + int n = vars.size(); + return (n == 0 ? this : vars.get(n-1)); + } + List fromTypes(List types) { + Var prev = this; + ArrayList 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; } - 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); - - // figure out the field types - final List> fieldTypes = speciesData.fieldTypes(); - final List 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); + 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); + } } - } - // 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, - "", methodSig(thisCtorType), null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); // this - - final List ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList()); - for (Var ca : ctorArgs) { - ca.emitVarInstruction(ALOAD, mv); + 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> fieldTypes = speciesData.fieldTypes(); + final List 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, - "", 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 - { - 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); - } + // figure out the constructor arguments + MethodType superCtorType = ClassSpecializer.this.baseConstructorType(); + MethodType thisCtorType = superCtorType.appendParameterTypes(fieldTypes); - // finally, invoke the constructor and return - mv.visitMethodInsn(INVOKESPECIAL, className, - "", methodSig(thisCtorType), false); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } + // emit constructor + clb.withMethodBody(INIT_NAME, methodDesc(thisCtorType), ACC_PRIVATE, cob -> { + cob.aload(0); // this - // 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 targs = AFTER_THIS.fromTypes(TTYPE.parameterList()); - List tfields = new ArrayList<>(fields); - // mix them up and load them for the transform helper: - List helperArgs = speciesData.deriveTransformHelperArguments(transformMethods.get(whichtm), whichtm, targs, tfields); - List> 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); + final List ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList()); + for (Var ca : ctorArgs) { + ca.emitLoadInstruction(cob); } - } - - // 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(); - } + // 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); + } - 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); - }; - } + cob.return_(); + }); - 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); - } + // emit make() ...factory method wrapping constructor + MethodType ftryType = thisCtorType.changeReturnType(topClass()); + 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 targs = AFTER_THIS.fromTypes(TTYPE.parameterList()); + List tfields = new ArrayList<>(fields); + // mix them up and load them for the transform helper: + List 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); + } + }); + }); + } + }); } // @@ -990,39 +940,25 @@ protected Factory makeFactory() { // 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); } } diff --git a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java index 46eeb67de54c5..1e3f465f97dbd 100644 --- a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java +++ b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java @@ -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.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 @@ static byte[] generateInvokersHolderClassBytes(String className, * 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) { diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index b4afbc098c96e..ce2547710a5cb 100644 --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -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 static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION; +import java.util.function.Consumer; + +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 @@ * @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 = ""; 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 { + @Override + public void accept(FieldBuilder fb) { + fb.withFlags(flags); + } + }; + record MethodBody(Consumer code) implements Consumer { + @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 @@ 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 @@ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, 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 @@ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, // 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 @@ private Class spinInnerClass() throws LambdaConversionException { * is not found */ private Class generateInnerClass() throws LambdaConversionException { - String[] interfaceNames; - String interfaceName = interfaceClass.getName().replace('.', '/'); + List 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 itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1); - itfs.add(interfaceName); + Set 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() { + @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); - - // 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(); - } + generateConstructor(clb); - generateConstructor(); + if (factoryType.parameterCount() == 0 && disableEagerInitialization) { + generateClassInitializer(clb); + } - if (factoryType.parameterCount() == 0 && disableEagerInitialization) { - generateClassInitializer(); - } + // Forward the SAM method + clb.withMethod(interfaceMethodName, + methodDesc(interfaceMethodType), + ACC_PUBLIC, + forwardingMethod(interfaceMethodType)); + + // 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 @@ else if (accidentallySerializable) /** * 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, "", "()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() { + @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() { + @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() { + @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() { + @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() { + @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]); - } + Consumer forwardingMethod(MethodType methodType) { + return new MethodBody(new Consumer() { + @Override + public void accept(CodeBuilder cob) { + if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { + cob.new_(implMethodClassDesc) + .dup(); + } + 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(methodType); + convertArgumentTypes(cob, methodType); - if (useImplMethodHandle) { - MethodType mtype = implInfo.getMethodType(); - if (implKind != MethodHandleInfo.REF_invokeStatic) { - mtype = mtype.insertParameterTypes(0, implClass); + 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()); } - 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()); + // 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); } - } diff --git a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index 4e3ebb3834d91..6d71296c13426 100644 --- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -25,30 +25,38 @@ package java.lang.invoke; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.FieldVisitor; -import jdk.internal.org.objectweb.asm.Label; -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.VerifyAccess; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; +import java.lang.classfile.*; +import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; +import java.lang.classfile.attribute.SourceFileAttribute; +import java.lang.classfile.instruction.SwitchCase; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDesc; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.LambdaForm.BasicType; +import java.lang.invoke.LambdaForm.Name; +import java.lang.invoke.LambdaForm.NamedFunction; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Stream; +import jdk.internal.constant.MethodTypeDescImpl; +import jdk.internal.constant.ReferenceClassDescImpl; -import static java.lang.invoke.LambdaForm.BasicType; -import static java.lang.invoke.LambdaForm.BasicType.*; +import static java.lang.classfile.ClassFile.*; +import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.LambdaForm.*; +import static java.lang.invoke.LambdaForm.BasicType.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; -import static java.lang.invoke.MethodHandles.Lookup.*; +import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; /** * Code generation backend for LambdaForm. @@ -57,32 +65,46 @@ */ class InvokerBytecodeGenerator { /** Define class names for convenience. */ - private static final String MH = "java/lang/invoke/MethodHandle"; - private static final String MHI = "java/lang/invoke/MethodHandleImpl"; - private static final String LF = "java/lang/invoke/LambdaForm"; - private static final String LFN = "java/lang/invoke/LambdaForm$Name"; - private static final String CLS = "java/lang/Class"; - private static final String OBJ = "java/lang/Object"; - private static final String OBJARY = "[Ljava/lang/Object;"; - - private static final String LOOP_CLAUSES = MHI + "$LoopClauses"; - private static final String MHARY2 = "[[L" + MH + ";"; - private static final String MH_SIG = "L" + MH + ";"; - - - private static final String LF_SIG = "L" + LF + ";"; - private static final String LFN_SIG = "L" + LFN + ";"; - private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";"; - private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V"; - private static final String CLASS_PREFIX = LF + "$"; + private static final ClassDesc CD_CasesHolder = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl$CasesHolder;"); + private static final ClassDesc CD_DirectMethodHandle = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/DirectMethodHandle;"); + private static final ClassDesc CD_MethodHandleImpl = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl;"); + private static final ClassDesc CD_LambdaForm = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm;"); + private static final ClassDesc CD_LambdaForm_Name = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm$Name;"); + private static final ClassDesc CD_LoopClauses = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl$LoopClauses;"); + private static final ClassDesc CD_Object_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Object;"); + private static final ClassDesc CD_MethodHandle_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/invoke/MethodHandle;"); + private static final ClassDesc CD_MethodHandle_array2 = ReferenceClassDescImpl.ofValidated("[[Ljava/lang/invoke/MethodHandle;"); + + private static final MethodTypeDesc MTD_boolean_Object = MethodTypeDescImpl.ofValidated(CD_boolean, CD_Object); + private static final MethodTypeDesc MTD_Object_int = MethodTypeDescImpl.ofValidated(CD_Object, CD_int); + private static final MethodTypeDesc MTD_Object_Class = MethodTypeDescImpl.ofValidated(CD_Object, CD_Class); + private static final MethodTypeDesc MTD_Object_Object = MethodTypeDescImpl.ofValidated(CD_Object, CD_Object); + + private static final String CLASS_PREFIX = "java/lang/invoke/LambdaForm$"; private static final String SOURCE_PREFIX = "LambdaForm$"; + // Static builders to avoid lambdas + private static final Consumer STATIC_FINAL_FIELD = new Consumer() { + @Override + public void accept(FieldBuilder fb) { + fb.withFlags(ACC_STATIC | ACC_FINAL); + } + }; + + record MethodBody(Consumer code) implements Consumer { + @Override + public void accept(MethodBuilder mb) { + mb.withCode(code); + } + }; + /** Name of its super class*/ - static final String INVOKER_SUPER_NAME = OBJ; + static final ClassDesc INVOKER_SUPER_DESC = CD_Object; /** Name of new class */ private final String name; private final String className; + private final ClassDesc classDesc; private final LambdaForm lambdaForm; private final String invokerName; @@ -92,15 +114,8 @@ class InvokerBytecodeGenerator { private int[] localsMap; // index private Class[] localClasses; // type - /** ASM bytecode generation. */ - private ClassWriter cw; - private MethodVisitor mv; private final List classData = new ArrayList<>(); - /** Single element internal class name lookup cache. */ - private Class lastClass; - private String lastInternalName; - private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory(); private static final Class HOST_CLASS = LambdaForm.class; private static final MethodHandles.Lookup LOOKUP = lookup(); @@ -126,6 +141,7 @@ private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize, } this.name = name; this.className = CLASS_PREFIX + name; + this.classDesc = ClassDesc.ofInternalName(className); this.lambdaForm = lambdaForm; this.invokerName = invokerName; this.invokerType = invokerType; @@ -188,10 +204,10 @@ private static String makeDumpableClassName(String className) { static class ClassData { final String name; - final String desc; + final ClassDesc desc; final Object value; - ClassData(String name, String desc, Object value) { + ClassData(String name, ClassDesc desc, Object value) { this.name = name; this.desc = desc; this.value = value; @@ -204,15 +220,15 @@ public String toString() { } String classData(Object arg) { - String desc; + ClassDesc desc; if (arg instanceof Class) { - desc = "Ljava/lang/Class;"; + desc = CD_Class; } else if (arg instanceof MethodHandle) { - desc = MH_SIG; + desc = CD_MethodHandle; } else if (arg instanceof LambdaForm) { - desc = LF_SIG; + desc = CD_LambdaForm; } else { - desc = "Ljava/lang/Object;"; + desc = CD_Object; } // unique static variable name @@ -231,16 +247,6 @@ String classData(Object arg) { return name; } - private static String debugString(Object arg) { - if (arg instanceof MethodHandle mh) { - MemberName member = mh.internalMemberName(); - if (member != null) - return member.toString(); - return mh.debugString(); - } - return arg.toString(); - } - /** * Extract the MemberName of a newly-defined method. */ @@ -265,27 +271,25 @@ private static MemberName resolveInvokerMember(Class invokerClass, String nam /** * Set up class file generation. */ - private ClassWriter classFilePrologue() { - final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); - setClassWriter(cw); - cw.visit(CLASSFILE_VERSION, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, - className, null, INVOKER_SUPER_NAME, null); - cw.visitSource(SOURCE_PREFIX + name, null); - return cw; - } - - private void methodPrologue() { - String invokerDesc = invokerType.toMethodDescriptorString(); - mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null); + private byte[] classFileSetup(Consumer config) { + try { + return ClassFile.of().build(classDesc, new Consumer<>() { + @Override + public void accept(ClassBuilder clb) { + clb.withFlags(ACC_FINAL | ACC_SUPER) + .withSuperclass(INVOKER_SUPER_DESC) + .with(SourceFileAttribute.of(clb.constantPool().utf8Entry(SOURCE_PREFIX + name))); + config.accept(clb); + } + }); + } catch (RuntimeException e) { + throw new BytecodeGenerationException(e); + } } - /** - * Tear down class file generation. - */ - private void methodEpilogue() { - mv.visitMaxs(0, 0); - mv.visitEnd(); + private void methodSetup(ClassBuilder clb, Consumer config) { + var invokerDesc = methodDesc(invokerType); + clb.withMethod(invokerName, invokerDesc, ACC_STATIC, config); } /** @@ -318,202 +322,49 @@ private Object classDataValues() { * to initialize the static final fields with the live class data * LambdaForms can't use condy due to bootstrapping issue. */ - static void clinit(ClassWriter cw, String className, List classData) { + static void clinit(ClassBuilder clb, ClassDesc classDesc, List classData) { if (classData.isEmpty()) return; for (ClassData p : classData) { // add the static field - FieldVisitor fv = cw.visitField(Opcodes.ACC_STATIC|Opcodes.ACC_FINAL, p.name, p.desc, null, null); - fv.visitEnd(); - } - - MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null); - mv.visitCode(); - mv.visitLdcInsn(Type.getType("L" + className + ";")); - mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles", - "classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false); - if (classData.size() == 1) { - ClassData p = classData.get(0); - mv.visitTypeInsn(Opcodes.CHECKCAST, p.desc.substring(1, p.desc.length()-1)); - mv.visitFieldInsn(Opcodes.PUTSTATIC, className, p.name, p.desc); - } else { - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List"); - mv.visitVarInsn(Opcodes.ASTORE, 0); - int index = 0; - for (ClassData p : classData) { - // initialize the static field - mv.visitVarInsn(Opcodes.ALOAD, 0); - emitIconstInsn(mv, index++); - mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List", - "get", "(I)Ljava/lang/Object;", true); - mv.visitTypeInsn(Opcodes.CHECKCAST, p.desc.substring(1, p.desc.length()-1)); - mv.visitFieldInsn(Opcodes.PUTSTATIC, className, p.name, p.desc); - } - } - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(2, 1); - mv.visitEnd(); - } - - /* - * Low-level emit helpers. - */ - private void emitConst(Object con) { - if (con == null) { - mv.visitInsn(Opcodes.ACONST_NULL); - return; - } - if (con instanceof Integer) { - emitIconstInsn((int) con); - return; - } - if (con instanceof Byte) { - emitIconstInsn((byte)con); - return; - } - if (con instanceof Short) { - emitIconstInsn((short)con); - return; - } - if (con instanceof Character) { - emitIconstInsn((char)con); - return; - } - if (con instanceof Long) { - long x = (long) con; - short sx = (short)x; - if (x == sx) { - if (sx >= 0 && sx <= 1) { - mv.visitInsn(Opcodes.LCONST_0 + (int) sx); - } else { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2L); - } - return; - } - } - if (con instanceof Float) { - float x = (float) con; - short sx = (short)x; - if (x == sx) { - if (sx >= 0 && sx <= 2) { - mv.visitInsn(Opcodes.FCONST_0 + (int) sx); + clb.withField(p.name, p.desc, STATIC_FINAL_FIELD); + } + + clb.withMethod(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new MethodBody(new Consumer() { + @Override + public void accept(CodeBuilder cob) { + cob.loadConstant(classDesc) + .invokestatic(CD_MethodHandles, "classData", MTD_Object_Class); + if (classData.size() == 1) { + ClassData p = classData.get(0); + cob.checkcast(p.desc) + .putstatic(classDesc, p.name, p.desc); } else { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2F); - } - return; - } - } - if (con instanceof Double) { - double x = (double) con; - short sx = (short)x; - if (x == sx) { - if (sx >= 0 && sx <= 1) { - mv.visitInsn(Opcodes.DCONST_0 + (int) sx); - } else { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2D); + cob.checkcast(CD_List) + .astore(0); + int index = 0; + var listGet = cob.constantPool().interfaceMethodRefEntry(CD_List, "get", MTD_Object_int); + for (ClassData p : classData) { + // initialize the static field + cob.aload(0) + .loadConstant(index++) + .invokeinterface(listGet) + .checkcast(p.desc) + .putstatic(classDesc, p.name, p.desc); + } } - return; + cob.return_(); } - } - if (con instanceof Boolean) { - emitIconstInsn((boolean) con ? 1 : 0); - return; - } - // fall through: - mv.visitLdcInsn(con); - } - - private void emitIconstInsn(final int cst) { - emitIconstInsn(mv, cst); - } - - private static void emitIconstInsn(MethodVisitor mv, 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); - } - } - - /* - * NOTE: These load/store methods use the localsMap to find the correct index! - */ - private void emitLoadInsn(BasicType type, int index) { - int opcode = loadInsnOpcode(type); - mv.visitVarInsn(opcode, localsMap[index]); + })); } - private int loadInsnOpcode(BasicType type) throws InternalError { - return switch (type) { - case I_TYPE -> Opcodes.ILOAD; - case J_TYPE -> Opcodes.LLOAD; - case F_TYPE -> Opcodes.FLOAD; - case D_TYPE -> Opcodes.DLOAD; - case L_TYPE -> Opcodes.ALOAD; - default -> throw new InternalError("unknown type: " + type); - }; - } - private void emitAloadInsn(int index) { - emitLoadInsn(L_TYPE, index); + private void emitLoadInsn(CodeBuilder cob, TypeKind type, int index) { + cob.loadLocal(type, localsMap[index]); } - private void emitStoreInsn(BasicType type, int index) { - int opcode = storeInsnOpcode(type); - mv.visitVarInsn(opcode, localsMap[index]); - } - - private int storeInsnOpcode(BasicType type) throws InternalError { - return switch (type) { - case I_TYPE -> Opcodes.ISTORE; - case J_TYPE -> Opcodes.LSTORE; - case F_TYPE -> Opcodes.FSTORE; - case D_TYPE -> Opcodes.DSTORE; - case L_TYPE -> Opcodes.ASTORE; - default -> throw new InternalError("unknown type: " + type); - }; - } - private void emitAstoreInsn(int index) { - emitStoreInsn(L_TYPE, index); - } - - private byte arrayTypeCode(Wrapper elementType) { - return (byte) switch (elementType) { - case BOOLEAN -> Opcodes.T_BOOLEAN; - case BYTE -> Opcodes.T_BYTE; - case CHAR -> Opcodes.T_CHAR; - case SHORT -> Opcodes.T_SHORT; - case INT -> Opcodes.T_INT; - case LONG -> Opcodes.T_LONG; - case FLOAT -> Opcodes.T_FLOAT; - case DOUBLE -> Opcodes.T_DOUBLE; - case OBJECT -> 0; // in place of Opcodes.T_OBJECT - default -> throw new InternalError(); - }; - } - - private int arrayInsnOpcode(byte tcode, int aaop) throws InternalError { - assert(aaop == Opcodes.AASTORE || aaop == Opcodes.AALOAD); - int xas = switch (tcode) { - case Opcodes.T_BOOLEAN -> Opcodes.BASTORE; - case Opcodes.T_BYTE -> Opcodes.BASTORE; - case Opcodes.T_CHAR -> Opcodes.CASTORE; - case Opcodes.T_SHORT -> Opcodes.SASTORE; - case Opcodes.T_INT -> Opcodes.IASTORE; - case Opcodes.T_LONG -> Opcodes.LASTORE; - case Opcodes.T_FLOAT -> Opcodes.FASTORE; - case Opcodes.T_DOUBLE -> Opcodes.DASTORE; - case 0 -> Opcodes.AASTORE; - default -> throw new InternalError(); - }; - return xas - Opcodes.AASTORE + aaop; + private void emitStoreInsn(CodeBuilder cob, TypeKind type, int index) { + cob.storeLocal(type, localsMap[index]); } /** @@ -521,11 +372,8 @@ private int arrayInsnOpcode(byte tcode, int aaop) throws InternalError { * * @param wrapper primitive type class to box. */ - private void emitBoxing(Wrapper wrapper) { - String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); - String name = "valueOf"; - String desc = "(" + wrapper.basicTypeChar() + ")L" + owner + ";"; - mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false); + private void emitBoxing(CodeBuilder cob, TypeKind tk) { + TypeConvertingMethodAdapter.box(cob, tk); } /** @@ -533,12 +381,15 @@ private void emitBoxing(Wrapper wrapper) { * * @param wrapper wrapper type class to unbox. */ - private void emitUnboxing(Wrapper wrapper) { - String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); - String name = wrapper.primitiveSimpleName() + "Value"; - String desc = "()" + wrapper.basicTypeChar(); - emitReferenceCast(wrapper.wrapperType(), null); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false); + private void emitUnboxing(CodeBuilder cob, TypeKind target) { + switch (target) { + case BooleanType -> emitReferenceCast(cob, Boolean.class, null); + case CharType -> emitReferenceCast(cob, Character.class, null); + case ByteType, DoubleType, FloatType, IntType, LongType, ShortType -> + emitReferenceCast(cob, Number.class, null); + default -> {} + } + TypeConvertingMethodAdapter.unbox(cob, target); } /** @@ -549,7 +400,7 @@ private void emitUnboxing(Wrapper wrapper) { * @param pclass type of value required on stack * @param arg compile-time representation of value on stack (Node, constant) or null if none */ - private void emitImplicitConversion(BasicType ptype, Class pclass, Object arg) { + private void emitImplicitConversion(CodeBuilder cob, BasicType ptype, Class pclass, Object arg) { assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller if (pclass == ptype.basicTypeClass() && ptype != L_TYPE) return; // nothing to do @@ -557,14 +408,14 @@ private void emitImplicitConversion(BasicType ptype, Class pclass, Object arg case L_TYPE: if (VerifyType.isNullConversion(Object.class, pclass, false)) { if (PROFILE_LEVEL > 0) - emitReferenceCast(Object.class, arg); + emitReferenceCast(cob, Object.class, arg); return; } - emitReferenceCast(pclass, arg); + emitReferenceCast(cob, pclass, arg); return; case I_TYPE: if (!VerifyType.isNullConversion(int.class, pclass, false)) - emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass)); + emitPrimCast(cob, ptype.basicTypeKind(), TypeKind.from(pclass)); return; } throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass); @@ -582,7 +433,7 @@ private boolean assertStaticType(Class cls, Name n) { return false; } - private void emitReferenceCast(Class cls, Object arg) { + private void emitReferenceCast(CodeBuilder cob, Class cls, Object arg) { Name writeBack = null; // local to write back result if (arg instanceof Name n) { if (lambdaForm.useCount(n) > 1) { @@ -594,53 +445,23 @@ private void emitReferenceCast(Class cls, Object arg) { } } if (isStaticallyNameable(cls)) { - String sig = getInternalName(cls); - mv.visitTypeInsn(Opcodes.CHECKCAST, sig); + ClassDesc sig = classDesc(cls); + cob.checkcast(sig); } else { - mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(cls), "Ljava/lang/Class;"); - mv.visitInsn(Opcodes.SWAP); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG, false); + cob.getstatic(classDesc, classData(cls), CD_Class) + .swap() + .invokevirtual(CD_Class, "cast", MTD_Object_Object); if (Object[].class.isAssignableFrom(cls)) - mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY); + cob.checkcast(CD_Object_array); else if (PROFILE_LEVEL > 0) - mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ); + cob.checkcast(CD_Object); } if (writeBack != null) { - mv.visitInsn(Opcodes.DUP); - emitAstoreInsn(writeBack.index()); + cob.dup(); + emitStoreInsn(cob, TypeKind.ReferenceType, writeBack.index()); } } - /** - * Emits an actual return instruction conforming to the given return type. - */ - private void emitReturnInsn(BasicType type) { - int opcode = switch (type) { - case I_TYPE -> Opcodes.IRETURN; - case J_TYPE -> Opcodes.LRETURN; - case F_TYPE -> Opcodes.FRETURN; - case D_TYPE -> Opcodes.DRETURN; - case L_TYPE -> Opcodes.ARETURN; - case V_TYPE -> Opcodes.RETURN; - default -> throw new InternalError("unknown return type: " + type); - }; - mv.visitInsn(opcode); - } - - private String getInternalName(Class c) { - if (c == Object.class) return OBJ; - else if (c == Object[].class) return OBJARY; - else if (c == Class.class) return CLS; - else if (c == MethodHandle.class) return MH; - assert(VerifyAccess.ensureTypeVisible(c, Object.class)) : c.getName(); - - if (c == lastClass) { - return lastInternalName; - } - lastClass = c; - return lastInternalName = c.getName().replace('.', '/'); - } - private static MemberName resolveFrom(String name, MethodType type, Class holder) { assert(!UNSAFE.shouldBeInitialized(holder)) : holder + "not initialized"; MemberName member = new MemberName(holder, name, type, REF_invokeStatic); @@ -713,173 +534,151 @@ static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType } /** Generates code to check that actual receiver and LambdaForm matches */ - private boolean checkActualReceiver() { + private boolean checkActualReceiver(CodeBuilder cob) { // Expects MethodHandle on the stack and actual receiver MethodHandle in slot #0 - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.ALOAD, localsMap[0]); - mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "assertSame", LLV_SIG, false); + cob.dup() + .aload(0) + .invokestatic(CD_MethodHandleImpl, "assertSame", MethodTypeDescImpl.ofValidated(CD_void, CD_Object, CD_Object)); return true; } - static String className(String cn) { - assert checkClassName(cn): "Class not found: " + cn; - return cn; - } - - static boolean checkClassName(String cn) { - Type tp = Type.getType(cn); - // additional sanity so only valid "L;" descriptors work - if (tp.getSort() != Type.OBJECT) { - return false; - } - try { - Class c = Class.forName(tp.getClassName(), false, null); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - - static final String DONTINLINE_SIG = className("Ljdk/internal/vm/annotation/DontInline;"); - static final String FORCEINLINE_SIG = className("Ljdk/internal/vm/annotation/ForceInline;"); - static final String HIDDEN_SIG = className("Ljdk/internal/vm/annotation/Hidden;"); - static final String INJECTEDPROFILE_SIG = className("Ljava/lang/invoke/InjectedProfile;"); - static final String LF_COMPILED_SIG = className("Ljava/lang/invoke/LambdaForm$Compiled;"); + static final Annotation DONTINLINE = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljdk/internal/vm/annotation/DontInline;")); + static final Annotation FORCEINLINE = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljdk/internal/vm/annotation/ForceInline;")); + static final Annotation HIDDEN = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljdk/internal/vm/annotation/Hidden;")); + static final Annotation INJECTEDPROFILE = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/InjectedProfile;")); + static final Annotation LF_COMPILED = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm$Compiled;")); /** * Generate an invoker method for the passed {@link LambdaForm}. */ private byte[] generateCustomizedCodeBytes() { - classFilePrologue(); - addMethod(); - clinit(cw, className, classData); - bogusMethod(lambdaForm); - - return toByteArray(); + final byte[] classFile = classFileSetup(new Consumer() { + @Override + public void accept(ClassBuilder clb) { + addMethod(clb); + clinit(clb, classDesc, classData); + bogusMethod(clb, lambdaForm); + } + }); + return classFile; } - void setClassWriter(ClassWriter cw) { - this.cw = cw; - } + void addMethod(ClassBuilder clb) { + methodSetup(clb, new Consumer() { + @Override + public void accept(MethodBuilder mb) { - void addMethod() { - methodPrologue(); + List annotations = new ArrayList<>(3); - // Suppress this method in backtraces displayed to the user. - mv.visitAnnotation(HIDDEN_SIG, true); + // Suppress this method in backtraces displayed to the user. + annotations.add(HIDDEN); - // Mark this method as a compiled LambdaForm - mv.visitAnnotation(LF_COMPILED_SIG, true); + // Mark this method as a compiled LambdaForm + annotations.add(LF_COMPILED); - if (lambdaForm.forceInline) { - // Force inlining of this invoker method. - mv.visitAnnotation(FORCEINLINE_SIG, true); - } else { - mv.visitAnnotation(DONTINLINE_SIG, true); - } + if (lambdaForm.forceInline) { + // Force inlining of this invoker method. + annotations.add(FORCEINLINE); + } else { + annotations.add(DONTINLINE); + } + mb.accept(RuntimeVisibleAnnotationsAttribute.of(annotations)); + + classData(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled. + + mb.withCode(new Consumer() { + @Override + public void accept(CodeBuilder cob) { + if (lambdaForm.customized != null) { + // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute + // receiver MethodHandle (at slot #0) with an embedded constant and use it instead. + // It enables more efficient code generation in some situations, since embedded constants + // are compile-time constants for JIT compiler. + cob.getstatic(classDesc, classData(lambdaForm.customized), CD_MethodHandle) + .checkcast(CD_MethodHandle); + assert(checkActualReceiver(cob)); // expects MethodHandle on top of the stack + cob.astore(0); + } - classData(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled. - - if (lambdaForm.customized != null) { - // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute - // receiver MethodHandle (at slot #0) with an embedded constant and use it instead. - // It enables more efficient code generation in some situations, since embedded constants - // are compile-time constants for JIT compiler. - mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(lambdaForm.customized), MH_SIG); - mv.visitTypeInsn(Opcodes.CHECKCAST, MH); - assert(checkActualReceiver()); // expects MethodHandle on top of the stack - mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]); - } + // iterate over the form's names, generating bytecode instructions for each + // start iterating at the first name following the arguments + Name onStack = null; + for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { + Name name = lambdaForm.names[i]; + + emitStoreResult(cob, onStack); + onStack = name; // unless otherwise modified below + MethodHandleImpl.Intrinsic intr = name.function.intrinsicName(); + switch (intr) { + case SELECT_ALTERNATIVE: + assert lambdaForm.isSelectAlternative(i); + if (PROFILE_GWT) { + assert(name.arguments[0] instanceof Name n && + n.refersTo(MethodHandleImpl.class, "profileBoolean")); + mb.with(RuntimeVisibleAnnotationsAttribute.of(List.of(INJECTEDPROFILE))); + } + onStack = emitSelectAlternative(cob, name, lambdaForm.names[i+1]); + i++; // skip MH.invokeBasic of the selectAlternative result + continue; + case GUARD_WITH_CATCH: + assert lambdaForm.isGuardWithCatch(i); + onStack = emitGuardWithCatch(cob, i); + i += 2; // jump to the end of GWC idiom + continue; + case TRY_FINALLY: + assert lambdaForm.isTryFinally(i); + onStack = emitTryFinally(cob, i); + i += 2; // jump to the end of the TF idiom + continue; + case TABLE_SWITCH: + assert lambdaForm.isTableSwitch(i); + int numCases = (Integer) name.function.intrinsicData(); + onStack = emitTableSwitch(cob, i, numCases); + i += 2; // jump to the end of the TS idiom + continue; + case LOOP: + assert lambdaForm.isLoop(i); + onStack = emitLoop(cob, i); + i += 2; // jump to the end of the LOOP idiom + continue; + case ARRAY_LOAD: + emitArrayLoad(cob, name); + continue; + case ARRAY_STORE: + emitArrayStore(cob, name); + continue; + case ARRAY_LENGTH: + emitArrayLength(cob, name); + continue; + case IDENTITY: + assert(name.arguments.length == 1); + emitPushArguments(cob, name, 0); + continue; + case ZERO: + assert(name.arguments.length == 0); + cob.loadConstant((ConstantDesc)name.type.basicTypeWrapper().zero()); + continue; + case NONE: + // no intrinsic associated + break; + default: + throw newInternalError("Unknown intrinsic: "+intr); + } + + MemberName member = name.function.member(); + if (isStaticallyInvocable(member)) { + emitStaticInvoke(cob, member, name); + } else { + emitInvoke(cob, name); + } + } - // iterate over the form's names, generating bytecode instructions for each - // start iterating at the first name following the arguments - Name onStack = null; - for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { - Name name = lambdaForm.names[i]; - - emitStoreResult(onStack); - onStack = name; // unless otherwise modified below - MethodHandleImpl.Intrinsic intr = name.function.intrinsicName(); - switch (intr) { - case SELECT_ALTERNATIVE: - assert lambdaForm.isSelectAlternative(i); - if (PROFILE_GWT) { - assert(name.arguments[0] instanceof Name n && - n.refersTo(MethodHandleImpl.class, "profileBoolean")); - mv.visitAnnotation(INJECTEDPROFILE_SIG, true); + // return statement + emitReturn(cob, onStack); } - onStack = emitSelectAlternative(name, lambdaForm.names[i+1]); - i++; // skip MH.invokeBasic of the selectAlternative result - continue; - case GUARD_WITH_CATCH: - assert lambdaForm.isGuardWithCatch(i); - onStack = emitGuardWithCatch(i); - i += 2; // jump to the end of GWC idiom - continue; - case TRY_FINALLY: - assert lambdaForm.isTryFinally(i); - onStack = emitTryFinally(i); - i += 2; // jump to the end of the TF idiom - continue; - case TABLE_SWITCH: - assert lambdaForm.isTableSwitch(i); - int numCases = (Integer) name.function.intrinsicData(); - onStack = emitTableSwitch(i, numCases); - i += 2; // jump to the end of the TS idiom - continue; - case LOOP: - assert lambdaForm.isLoop(i); - onStack = emitLoop(i); - i += 2; // jump to the end of the LOOP idiom - continue; - case ARRAY_LOAD: - emitArrayLoad(name); - continue; - case ARRAY_STORE: - emitArrayStore(name); - continue; - case ARRAY_LENGTH: - emitArrayLength(name); - continue; - case IDENTITY: - assert(name.arguments.length == 1); - emitPushArguments(name, 0); - continue; - case ZERO: - assert(name.arguments.length == 0); - emitConst(name.type.basicTypeWrapper().zero()); - continue; - case NONE: - // no intrinsic associated - break; - default: - throw newInternalError("Unknown intrinsic: "+intr); - } - - MemberName member = name.function.member(); - if (isStaticallyInvocable(member)) { - emitStaticInvoke(member, name); - } else { - emitInvoke(name); + }); } - } - - // return statement - emitReturn(onStack); - - methodEpilogue(); - } - - /* - * @throws BytecodeGenerationException if something goes wrong when - * generating the byte code - */ - private byte[] toByteArray() { - try { - return cw.toByteArray(); - } catch (RuntimeException e) { - throw new BytecodeGenerationException(e); - } + }); } /** @@ -892,48 +691,60 @@ static final class BytecodeGenerationException extends RuntimeException { } } - void emitArrayLoad(Name name) { emitArrayOp(name, Opcodes.AALOAD); } - void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); } - void emitArrayLength(Name name) { emitArrayOp(name, Opcodes.ARRAYLENGTH); } + void emitArrayLoad(CodeBuilder cob, Name name) { + Class elementType = name.function.methodType().parameterType(0).getComponentType(); + assert elementType != null; + emitPushArguments(cob, name, 0); + if (elementType.isPrimitive()) { + cob.arrayLoad(TypeKind.from(elementType)); + } else { + cob.aaload(); + } + } - void emitArrayOp(Name name, int arrayOpcode) { - assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE || arrayOpcode == Opcodes.ARRAYLENGTH; + void emitArrayStore(CodeBuilder cob, Name name) { Class elementType = name.function.methodType().parameterType(0).getComponentType(); assert elementType != null; - emitPushArguments(name, 0); - if (arrayOpcode != Opcodes.ARRAYLENGTH && elementType.isPrimitive()) { - Wrapper w = Wrapper.forPrimitiveType(elementType); - arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode); + emitPushArguments(cob, name, 0); + if (elementType.isPrimitive()) { + cob.arrayStore(TypeKind.from(elementType)); + } else { + cob.aastore(); } - mv.visitInsn(arrayOpcode); + } + + void emitArrayLength(CodeBuilder cob, Name name) { + assert name.function.methodType().parameterType(0).isArray(); + emitPushArguments(cob, name, 0); + cob.arraylength(); } /** * Emit an invoke for the given name. */ - void emitInvoke(Name name) { + void emitInvoke(CodeBuilder cob, Name name) { assert(!name.isLinkerMethodInvoke()); // should use the static path for these if (true) { // push receiver MethodHandle target = name.function.resolvedHandle(); assert(target != null) : name.exprString(); - mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(target), MH_SIG); - emitReferenceCast(MethodHandle.class, target); + cob.getstatic(classDesc, classData(target), CD_MethodHandle); + emitReferenceCast(cob, MethodHandle.class, target); } else { // load receiver - emitAloadInsn(0); - emitReferenceCast(MethodHandle.class, null); - mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG); - mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG); + cob.aload(0); + emitReferenceCast(cob, MethodHandle.class, null); + cob.getfield(CD_MethodHandle, "form", CD_LambdaForm) + .getfield(CD_LambdaForm, "names", CD_LambdaForm_Name); // TODO more to come } // push arguments - emitPushArguments(name, 0); + emitPushArguments(cob, name, 0); // invocation MethodType type = name.function.methodType(); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); } private static final Class[] STATICALLY_INVOCABLE_PACKAGES = { @@ -1020,19 +831,18 @@ static boolean isStaticallyNameable(Class cls) { return false; } - void emitStaticInvoke(Name name) { - emitStaticInvoke(name.function.member(), name); + void emitStaticInvoke(CodeBuilder cob, Name name) { + emitStaticInvoke(cob, name.function.member(), name); } /** * Emit an invoke for the given name, using the MemberName directly. */ - void emitStaticInvoke(MemberName member, Name name) { + void emitStaticInvoke(CodeBuilder cob, MemberName member, Name name) { assert(member.equals(name.function.member())); Class defc = member.getDeclaringClass(); - String cname = getInternalName(defc); + ClassDesc cdesc = classDesc(defc); String mname = member.getName(); - String mtype; byte refKind = member.getReferenceKind(); if (refKind == REF_invokeSpecial) { // in order to pass the verifier, we need to convert this to invokevirtual in all cases @@ -1043,16 +853,16 @@ void emitStaticInvoke(MemberName member, Name name) { assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual)); // push arguments - emitPushArguments(name, 0); + emitPushArguments(cob, name, 0); // invocation if (member.isMethod()) { - mtype = member.getMethodType().toMethodDescriptorString(); - mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype, - member.getDeclaringClass().isInterface()); + var methodTypeDesc = methodDesc(member.getMethodType()); + cob.invoke(refKindOpcode(refKind), cdesc, mname, methodTypeDesc, + member.getDeclaringClass().isInterface()); } else { - mtype = MethodType.toFieldDescriptorString(member.getFieldType()); - mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype); + var fieldTypeDesc = classDesc(member.getFieldType()); + cob.fieldAccess(refKindOpcode(refKind), cdesc, mname, fieldTypeDesc); } // Issue a type assertion for the result, so we can avoid casts later. if (name.type == L_TYPE) { @@ -1064,16 +874,16 @@ void emitStaticInvoke(MemberName member, Name name) { } } - int refKindOpcode(byte refKind) { + Opcode refKindOpcode(byte refKind) { switch (refKind) { - case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL; - case REF_invokeStatic: return Opcodes.INVOKESTATIC; - case REF_invokeSpecial: return Opcodes.INVOKESPECIAL; - case REF_invokeInterface: return Opcodes.INVOKEINTERFACE; - case REF_getField: return Opcodes.GETFIELD; - case REF_putField: return Opcodes.PUTFIELD; - case REF_getStatic: return Opcodes.GETSTATIC; - case REF_putStatic: return Opcodes.PUTSTATIC; + case REF_invokeVirtual: return Opcode.INVOKEVIRTUAL; + case REF_invokeStatic: return Opcode.INVOKESTATIC; + case REF_invokeSpecial: return Opcode.INVOKESPECIAL; + case REF_invokeInterface: return Opcode.INVOKEINTERFACE; + case REF_getField: return Opcode.GETFIELD; + case REF_putField: return Opcode.PUTFIELD; + case REF_getStatic: return Opcode.GETSTATIC; + case REF_putStatic: return Opcode.PUTSTATIC; } throw new InternalError("refKind="+refKind); } @@ -1089,40 +899,40 @@ int refKindOpcode(byte refKind) { * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} * } */ - private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { + private Name emitSelectAlternative(CodeBuilder cob, Name selectAlternativeName, Name invokeBasicName) { assert isStaticallyInvocable(invokeBasicName); Name receiver = (Name) invokeBasicName.arguments[0]; - Label L_fallback = new Label(); - Label L_done = new Label(); + Label L_fallback = cob.newLabel(); + Label L_done = cob.newLabel(); // load test result - emitPushArgument(selectAlternativeName, 0); + emitPushArgument(cob, selectAlternativeName, 0); // if_icmpne L_fallback - mv.visitJumpInsn(Opcodes.IFEQ, L_fallback); + cob.ifeq(L_fallback); // invoke selectAlternativeName.arguments[1] Class[] preForkClasses = localClasses.clone(); - emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative - emitAstoreInsn(receiver.index()); // store the MH in the receiver slot - emitStaticInvoke(invokeBasicName); + emitPushArgument(cob, selectAlternativeName, 1); // get 2nd argument of selectAlternative + emitStoreInsn(cob, TypeKind.ReferenceType, receiver.index()); // store the MH in the receiver slot + emitStaticInvoke(cob, invokeBasicName); // goto L_done - mv.visitJumpInsn(Opcodes.GOTO, L_done); + cob.goto_w(L_done); // L_fallback: - mv.visitLabel(L_fallback); + cob.labelBinding(L_fallback); // invoke selectAlternativeName.arguments[2] System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); - emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative - emitAstoreInsn(receiver.index()); // store the MH in the receiver slot - emitStaticInvoke(invokeBasicName); + emitPushArgument(cob, selectAlternativeName, 2); // get 3rd argument of selectAlternative + emitStoreInsn(cob, TypeKind.ReferenceType, receiver.index()); // store the MH in the receiver slot + emitStaticInvoke(cob, invokeBasicName); // L_done: - mv.visitLabel(L_done); + cob.labelBinding(L_done); // for now do not bother to merge typestate; just reset to the dominator state System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); @@ -1149,57 +959,57 @@ private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicN * return a3.invokeBasic(ex, a6, a7); * }} */ - private Name emitGuardWithCatch(int pos) { + private Name emitGuardWithCatch(CodeBuilder cob, int pos) { Name args = lambdaForm.names[pos]; Name invoker = lambdaForm.names[pos+1]; Name result = lambdaForm.names[pos+2]; - Label L_startBlock = new Label(); - Label L_endBlock = new Label(); - Label L_handler = new Label(); - Label L_done = new Label(); + Label L_startBlock = cob.newLabel(); + Label L_endBlock = cob.newLabel(); + Label L_handler = cob.newLabel(); + Label L_done = cob.newLabel(); Class returnType = result.function.resolvedHandle().type().returnType(); MethodType type = args.function.resolvedHandle().type() .dropParameterTypes(0,1) .changeReturnType(returnType); - mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable"); + cob.exceptionCatch(L_startBlock, L_endBlock, L_handler, CD_Throwable); // Normal case - mv.visitLabel(L_startBlock); + cob.labelBinding(L_startBlock); // load target - emitPushArgument(invoker, 0); - emitPushArguments(args, 1); // skip 1st argument: method handle - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false); - mv.visitLabel(L_endBlock); - mv.visitJumpInsn(Opcodes.GOTO, L_done); + emitPushArgument(cob, invoker, 0); + emitPushArguments(cob, args, 1); // skip 1st argument: method handle + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); + cob.labelBinding(L_endBlock); + cob.goto_w(L_done); // Exceptional case - mv.visitLabel(L_handler); + cob.labelBinding(L_handler); // Check exception's type - mv.visitInsn(Opcodes.DUP); + cob.dup(); // load exception class - emitPushArgument(invoker, 1); - mv.visitInsn(Opcodes.SWAP); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z", false); - Label L_rethrow = new Label(); - mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow); + emitPushArgument(cob, invoker, 1); + cob.swap(); + cob.invokevirtual(CD_Class, "isInstance", MTD_boolean_Object); + Label L_rethrow = cob.newLabel(); + cob.ifeq(L_rethrow); // Invoke catcher // load catcher - emitPushArgument(invoker, 2); - mv.visitInsn(Opcodes.SWAP); - emitPushArguments(args, 1); // skip 1st argument: method handle + emitPushArgument(cob, invoker, 2); + cob.swap(); + emitPushArguments(cob, args, 1); // skip 1st argument: method handle MethodType catcherType = type.insertParameterTypes(0, Throwable.class); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString(), false); - mv.visitJumpInsn(Opcodes.GOTO, L_done); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(catcherType.basicType())); + cob.goto_w(L_done); - mv.visitLabel(L_rethrow); - mv.visitInsn(Opcodes.ATHROW); + cob.labelBinding(L_rethrow); + cob.athrow(); - mv.visitLabel(L_done); + cob.labelBinding(L_done); return result; } @@ -1264,15 +1074,15 @@ private Name emitGuardWithCatch(int pos) { * } * * = depends on whether the return type takes up 2 stack slots. */ - private Name emitTryFinally(int pos) { + private Name emitTryFinally(CodeBuilder cob, int pos) { Name args = lambdaForm.names[pos]; Name invoker = lambdaForm.names[pos+1]; Name result = lambdaForm.names[pos+2]; - Label lFrom = new Label(); - Label lTo = new Label(); - Label lCatch = new Label(); - Label lDone = new Label(); + Label lFrom = cob.newLabel(); + Label lTo = cob.newLabel(); + Label lCatch = cob.newLabel(); + Label lDone = cob.newLabel(); Class returnType = result.function.resolvedHandle().type().returnType(); BasicType basicReturnType = BasicType.basicType(returnType); @@ -1285,68 +1095,64 @@ private Name emitTryFinally(int pos) { if (isNonVoid) { cleanupType = cleanupType.insertParameterTypes(1, returnType); } - String cleanupDesc = cleanupType.basicType().toMethodDescriptorString(); + MethodTypeDesc cleanupDesc = methodDesc(cleanupType.basicType()); // exception handler table - mv.visitTryCatchBlock(lFrom, lTo, lCatch, "java/lang/Throwable"); + cob.exceptionCatch(lFrom, lTo, lCatch, CD_Throwable); // TRY: - mv.visitLabel(lFrom); - emitPushArgument(invoker, 0); // load target - emitPushArguments(args, 1); // load args (skip 0: method handle) - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false); - mv.visitLabel(lTo); + cob.labelBinding(lFrom); + emitPushArgument(cob, invoker, 0); // load target + emitPushArguments(cob, args, 1); // load args (skip 0: method handle) + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); + cob.labelBinding(lTo); // FINALLY_NORMAL: int index = extendLocalsMap(new Class[]{ returnType }); if (isNonVoid) { - emitStoreInsn(basicReturnType, index); + emitStoreInsn(cob, basicReturnType.basicTypeKind(), index); } - emitPushArgument(invoker, 1); // load cleanup - mv.visitInsn(Opcodes.ACONST_NULL); + emitPushArgument(cob, invoker, 1); // load cleanup + cob.loadConstant(null); if (isNonVoid) { - emitLoadInsn(basicReturnType, index); + emitLoadInsn(cob, basicReturnType.basicTypeKind(), index); } - emitPushArguments(args, 1); // load args (skip 0: method handle) - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", cleanupDesc, false); - mv.visitJumpInsn(Opcodes.GOTO, lDone); + emitPushArguments(cob, args, 1); // load args (skip 0: method handle) + cob.invokevirtual(CD_MethodHandle, "invokeBasic", cleanupDesc); + cob.goto_w(lDone); // CATCH: - mv.visitLabel(lCatch); - mv.visitInsn(Opcodes.DUP); + cob.labelBinding(lCatch); + cob.dup(); // FINALLY_EXCEPTIONAL: - emitPushArgument(invoker, 1); // load cleanup - mv.visitInsn(Opcodes.SWAP); + emitPushArgument(cob, invoker, 1); // load cleanup + cob.swap(); if (isNonVoid) { - emitZero(BasicType.basicType(returnType)); // load default for result + emitZero(cob, BasicType.basicType(returnType)); // load default for result } - emitPushArguments(args, 1); // load args (skip 0: method handle) - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", cleanupDesc, false); + emitPushArguments(cob, args, 1); // load args (skip 0: method handle) + cob.invokevirtual(CD_MethodHandle, "invokeBasic", cleanupDesc); if (isNonVoid) { - emitPopInsn(basicReturnType); + emitPopInsn(cob, basicReturnType); } - mv.visitInsn(Opcodes.ATHROW); + cob.athrow(); // DONE: - mv.visitLabel(lDone); + cob.labelBinding(lDone); return result; } - private void emitPopInsn(BasicType type) { - mv.visitInsn(popInsnOpcode(type)); - } - - private static int popInsnOpcode(BasicType type) { - return switch (type) { - case I_TYPE, F_TYPE, L_TYPE -> Opcodes.POP; - case J_TYPE, D_TYPE -> Opcodes.POP2; + private void emitPopInsn(CodeBuilder cob, BasicType type) { + switch (type) { + case I_TYPE, F_TYPE, L_TYPE -> cob.pop(); + case J_TYPE, D_TYPE -> cob.pop2(); default -> throw new InternalError("unknown type: " + type); - }; + } } - private Name emitTableSwitch(int pos, int numCases) { + private Name emitTableSwitch(CodeBuilder cob, int pos, int numCases) { Name args = lambdaForm.names[pos]; Name invoker = lambdaForm.names[pos + 1]; Name result = lambdaForm.names[pos + 2]; @@ -1355,45 +1161,44 @@ private Name emitTableSwitch(int pos, int numCases) { MethodType caseType = args.function.resolvedHandle().type() .dropParameterTypes(0, 1) // drop collector .changeReturnType(returnType); - String caseDescriptor = caseType.basicType().toMethodDescriptorString(); + MethodTypeDesc caseDescriptor = methodDesc(caseType.basicType()); - emitPushArgument(invoker, 2); // push cases - mv.visitFieldInsn(Opcodes.GETFIELD, "java/lang/invoke/MethodHandleImpl$CasesHolder", "cases", - "[Ljava/lang/invoke/MethodHandle;"); + emitPushArgument(cob, invoker, 2); // push cases + cob.getfield(CD_CasesHolder, "cases", CD_MethodHandle_array); int casesLocal = extendLocalsMap(new Class[] { MethodHandle[].class }); - emitStoreInsn(L_TYPE, casesLocal); + emitStoreInsn(cob, TypeKind.ReferenceType, casesLocal); - Label endLabel = new Label(); - Label defaultLabel = new Label(); - Label[] caseLabels = new Label[numCases]; - for (int i = 0; i < caseLabels.length; i++) { - caseLabels[i] = new Label(); + Label endLabel = cob.newLabel(); + Label defaultLabel = cob.newLabel(); + List cases = new ArrayList<>(numCases); + for (int i = 0; i < numCases; i++) { + cases.add(SwitchCase.of(i, cob.newLabel())); } - emitPushArgument(invoker, 0); // push switch input - mv.visitTableSwitchInsn(0, numCases - 1, defaultLabel, caseLabels); + emitPushArgument(cob, invoker, 0); // push switch input + cob.tableswitch(0, numCases - 1, defaultLabel, cases); - mv.visitLabel(defaultLabel); - emitPushArgument(invoker, 1); // push default handle - emitPushArguments(args, 1); // again, skip collector - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", caseDescriptor, false); - mv.visitJumpInsn(Opcodes.GOTO, endLabel); + cob.labelBinding(defaultLabel); + emitPushArgument(cob, invoker, 1); // push default handle + emitPushArguments(cob, args, 1); // again, skip collector + cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor); + cob.goto_(endLabel); for (int i = 0; i < numCases; i++) { - mv.visitLabel(caseLabels[i]); + cob.labelBinding(cases.get(i).target()); // Load the particular case: - emitLoadInsn(L_TYPE, casesLocal); - emitIconstInsn(i); - mv.visitInsn(Opcodes.AALOAD); + emitLoadInsn(cob, TypeKind.ReferenceType, casesLocal); + cob.loadConstant(i); + cob.aaload(); // invoke it: - emitPushArguments(args, 1); // again, skip collector - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", caseDescriptor, false); + emitPushArguments(cob, args, 1); // again, skip collector + cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor); - mv.visitJumpInsn(Opcodes.GOTO, endLabel); + cob.goto_(endLabel); } - mv.visitLabel(endLabel); + cob.labelBinding(endLabel); return result; } @@ -1480,7 +1285,7 @@ private Name emitTableSwitch(int pos, int numCases) { * GOTO DONE // jump beyond end of clauses to return from loop * } */ - private Name emitLoop(int pos) { + private Name emitLoop(CodeBuilder cob, int pos) { Name args = lambdaForm.names[pos]; Name invoker = lambdaForm.names[pos+1]; Name result = lambdaForm.names[pos+2]; @@ -1488,8 +1293,9 @@ private Name emitLoop(int pos) { // extract clause and loop-local state types // find the type info in the loop invocation BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0]; - Class[] loopLocalStateTypes = Stream.of(loopClauseTypes). - filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class[]::new); + Class[] loopLocalStateTypes = Stream.of(loopClauseTypes) + .filter(bt -> bt != BasicType.V_TYPE) + .map(BasicType::basicTypeClass).toArray(Class[]::new); Class[] localTypes = new Class[loopLocalStateTypes.length + 1]; localTypes[0] = MethodHandleImpl.LoopClauses.class; System.arraycopy(loopLocalStateTypes, 0, localTypes, 1, loopLocalStateTypes.length); @@ -1513,61 +1319,61 @@ private Name emitLoop(int pos) { final int preds = 3; final int finis = 4; - Label lLoop = new Label(); - Label lDone = new Label(); + Label lLoop = cob.newLabel(); + Label lDone = cob.newLabel(); Label lNext; // PREINIT: - emitPushArgument(MethodHandleImpl.LoopClauses.class, invoker.arguments[1]); - mv.visitFieldInsn(Opcodes.GETFIELD, LOOP_CLAUSES, "clauses", MHARY2); - emitAstoreInsn(clauseDataIndex); + emitPushArgument(cob, MethodHandleImpl.LoopClauses.class, invoker.arguments[1]); + cob.getfield(CD_LoopClauses, "clauses", CD_MethodHandle_array2); + emitStoreInsn(cob, TypeKind.ReferenceType, clauseDataIndex); // INIT: for (int c = 0, state = 0; c < nClauses; ++c) { MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass()); - emitLoopHandleInvoke(invoker, inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex, + emitLoopHandleInvoke(cob, invoker, inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); if (cInitType.returnType() != void.class) { - emitStoreInsn(BasicType.basicType(cInitType.returnType()), firstLoopStateIndex + state); + emitStoreInsn(cob, BasicType.basicType(cInitType.returnType()).basicTypeKind(), firstLoopStateIndex + state); ++state; } } // LOOP: - mv.visitLabel(lLoop); + cob.labelBinding(lLoop); for (int c = 0, state = 0; c < nClauses; ++c) { - lNext = new Label(); + lNext = cob.newLabel(); MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass()); boolean isVoid = stepType.returnType() == void.class; // invoke loop step - emitLoopHandleInvoke(invoker, steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex, + emitLoopHandleInvoke(cob, invoker, steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); if (!isVoid) { - emitStoreInsn(BasicType.basicType(stepType.returnType()), firstLoopStateIndex + state); + emitStoreInsn(cob, BasicType.basicType(stepType.returnType()).basicTypeKind(), firstLoopStateIndex + state); ++state; } // invoke loop predicate - emitLoopHandleInvoke(invoker, preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex, + emitLoopHandleInvoke(cob, invoker, preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); - mv.visitJumpInsn(Opcodes.IFNE, lNext); + cob.ifne(lNext); // invoke fini - emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, + emitLoopHandleInvoke(cob, invoker, finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); - mv.visitJumpInsn(Opcodes.GOTO, lDone); + cob.goto_w(lDone); // this is the beginning of the next loop clause - mv.visitLabel(lNext); + cob.labelBinding(lNext); } - mv.visitJumpInsn(Opcodes.GOTO, lLoop); + cob.goto_w(lLoop); // DONE: - mv.visitLabel(lDone); + cob.labelBinding(lDone); return result; } @@ -1588,69 +1394,67 @@ private int extendLocalsMap(Class[] types) { return firstSlot; } - private void emitLoopHandleInvoke(Name holder, int handles, int clause, Name args, boolean pushLocalState, + private void emitLoopHandleInvoke(CodeBuilder cob, Name holder, int handles, int clause, Name args, boolean pushLocalState, MethodType type, Class[] loopLocalStateTypes, int clauseDataSlot, int firstLoopStateSlot) { // load handle for clause - emitPushClauseArray(clauseDataSlot, handles); - emitIconstInsn(clause); - mv.visitInsn(Opcodes.AALOAD); + emitPushClauseArray(cob, clauseDataSlot, handles); + cob.loadConstant(clause); + cob.aaload(); // load loop state (preceding the other arguments) if (pushLocalState) { for (int s = 0; s < loopLocalStateTypes.length; ++s) { - emitLoadInsn(BasicType.basicType(loopLocalStateTypes[s]), firstLoopStateSlot + s); + emitLoadInsn(cob, BasicType.basicType(loopLocalStateTypes[s]).basicTypeKind(), firstLoopStateSlot + s); } } // load loop args (skip 0: method handle) - emitPushArguments(args, 1); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.toMethodDescriptorString(), false); + emitPushArguments(cob, args, 1); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type)); } - private void emitPushClauseArray(int clauseDataSlot, int which) { - emitAloadInsn(clauseDataSlot); - emitIconstInsn(which - 1); - mv.visitInsn(Opcodes.AALOAD); + private void emitPushClauseArray(CodeBuilder cob, int clauseDataSlot, int which) { + emitLoadInsn(cob, TypeKind.ReferenceType, clauseDataSlot); + cob.loadConstant(which - 1); + cob.aaload(); } - private void emitZero(BasicType type) { - mv.visitInsn(switch (type) { - case I_TYPE -> Opcodes.ICONST_0; - case J_TYPE -> Opcodes.LCONST_0; - case F_TYPE -> Opcodes.FCONST_0; - case D_TYPE -> Opcodes.DCONST_0; - case L_TYPE -> Opcodes.ACONST_NULL; + private void emitZero(CodeBuilder cob, BasicType type) { + switch (type) { + case I_TYPE -> cob.iconst_0(); + case J_TYPE -> cob.lconst_0(); + case F_TYPE -> cob.fconst_0(); + case D_TYPE -> cob.dconst_0(); + case L_TYPE -> cob.aconst_null(); default -> throw new InternalError("unknown type: " + type); - }); + }; } - private void emitPushArguments(Name args, int start) { + private void emitPushArguments(CodeBuilder cob, Name args, int start) { MethodType type = args.function.methodType(); for (int i = start; i < args.arguments.length; i++) { - emitPushArgument(type.parameterType(i), args.arguments[i]); + emitPushArgument(cob, type.parameterType(i), args.arguments[i]); } } - private void emitPushArgument(Name name, int paramIndex) { + private void emitPushArgument(CodeBuilder cob, Name name, int paramIndex) { Object arg = name.arguments[paramIndex]; Class ptype = name.function.methodType().parameterType(paramIndex); - emitPushArgument(ptype, arg); + emitPushArgument(cob, ptype, arg); } - private void emitPushArgument(Class ptype, Object arg) { + private void emitPushArgument(CodeBuilder cob, Class ptype, Object arg) { BasicType bptype = basicType(ptype); if (arg instanceof Name n) { - emitLoadInsn(n.type, n.index()); - emitImplicitConversion(n.type, ptype, n); - } else if (arg == null && bptype == L_TYPE) { - mv.visitInsn(Opcodes.ACONST_NULL); - } else if (arg instanceof String && bptype == L_TYPE) { - mv.visitLdcInsn(arg); + emitLoadInsn(cob, n.type.basicTypeKind(), n.index()); + emitImplicitConversion(cob, n.type, ptype, n); + } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) { + cob.loadConstant((ConstantDesc)arg); } else { if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) { - emitConst(arg); + cob.loadConstant((ConstantDesc)arg); } else { - mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(arg), "Ljava/lang/Object;"); - emitImplicitConversion(L_TYPE, ptype, arg); + cob.getstatic(classDesc, classData(arg), CD_Object); + emitImplicitConversion(cob, L_TYPE, ptype, arg); } } } @@ -1658,44 +1462,44 @@ private void emitPushArgument(Class ptype, Object arg) { /** * Store the name to its local, if necessary. */ - private void emitStoreResult(Name name) { + private void emitStoreResult(CodeBuilder cob, Name name) { if (name != null && name.type != V_TYPE) { // non-void: actually assign - emitStoreInsn(name.type, name.index()); + emitStoreInsn(cob, name.type.basicTypeKind(), name.index()); } } /** * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type. */ - private void emitReturn(Name onStack) { + private void emitReturn(CodeBuilder cob, Name onStack) { // return statement Class rclass = invokerType.returnType(); BasicType rtype = lambdaForm.returnType(); assert(rtype == basicType(rclass)); // must agree if (rtype == V_TYPE) { // void - mv.visitInsn(Opcodes.RETURN); + cob.return_(); // it doesn't matter what rclass is; the JVM will discard any value } else { LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; // put return value on the stack if it is not already there if (rn != onStack) { - emitLoadInsn(rtype, lambdaForm.result); + emitLoadInsn(cob, rtype.basicTypeKind(), lambdaForm.result); } - emitImplicitConversion(rtype, rclass, rn); + emitImplicitConversion(cob, rtype, rclass, rn); // generate actual return statement - emitReturnInsn(rtype); + cob.return_(rtype.basicTypeKind()); } } /** * Emit a type conversion bytecode casting from "from" to "to". */ - private void emitPrimCast(Wrapper from, Wrapper to) { + private void emitPrimCast(CodeBuilder cob, TypeKind from, TypeKind to) { // Here's how. // - indicates forbidden // <-> indicates implicit @@ -1708,80 +1512,10 @@ private void emitPrimCast(Wrapper from, Wrapper to) { // long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d // float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d // double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <-> - if (from == to) { - // no cast required, should be dead code anyway - return; - } - if (from.isSubwordOrInt()) { - // cast from {byte,short,char,int} to anything - emitI2X(to); - } else { - // cast from {long,float,double} to anything - if (to.isSubwordOrInt()) { - // cast to {byte,short,char,int} - emitX2I(from); - if (to.bitWidth() < 32) { - // targets other than int require another conversion - emitI2X(to); - } - } else { - // cast to {long,float,double} - this is verbose - boolean error = false; - switch (from) { - case LONG -> { - switch (to) { - case FLOAT -> mv.visitInsn(Opcodes.L2F); - case DOUBLE -> mv.visitInsn(Opcodes.L2D); - default -> error = true; - } - } - case FLOAT -> { - switch (to) { - case LONG -> mv.visitInsn(Opcodes.F2L); - case DOUBLE -> mv.visitInsn(Opcodes.F2D); - default -> error = true; - } - } - case DOUBLE -> { - switch (to) { - case LONG -> mv.visitInsn(Opcodes.D2L); - case FLOAT -> mv.visitInsn(Opcodes.D2F); - default -> error = true; - } - } - default -> error = true; - } - if (error) { - throw new IllegalStateException("unhandled prim cast: " + from + "2" + to); - } - } - } - } - - private void emitI2X(Wrapper type) { - switch (type) { - case BYTE: mv.visitInsn(Opcodes.I2B); break; - case SHORT: mv.visitInsn(Opcodes.I2S); break; - case CHAR: mv.visitInsn(Opcodes.I2C); break; - case INT: /* naught */ break; - case LONG: mv.visitInsn(Opcodes.I2L); break; - case FLOAT: mv.visitInsn(Opcodes.I2F); break; - case DOUBLE: mv.visitInsn(Opcodes.I2D); break; - case BOOLEAN: - // For compatibility with ValueConversions and explicitCastArguments: - mv.visitInsn(Opcodes.ICONST_1); - mv.visitInsn(Opcodes.IAND); - break; - default: throw new InternalError("unknown type: " + type); - } - } - - private void emitX2I(Wrapper type) { - switch (type) { - case LONG -> mv.visitInsn(Opcodes.L2I); - case FLOAT -> mv.visitInsn(Opcodes.F2I); - case DOUBLE -> mv.visitInsn(Opcodes.D2I); - default -> throw new InternalError("unknown type: " + type); + if (from != to && from != TypeKind.BooleanType) try { + cob.conversion(from, to); + } catch (IllegalArgumentException e) { + throw new IllegalStateException("unhandled prim cast: " + from + "2" + to); } } @@ -1798,51 +1532,61 @@ static MemberName generateLambdaFormInterpreterEntryPoint(MethodType mt) { } private byte[] generateLambdaFormInterpreterEntryPointBytes() { - classFilePrologue(); - methodPrologue(); - - // Suppress this method in backtraces displayed to the user. - mv.visitAnnotation(HIDDEN_SIG, true); - - // Don't inline the interpreter entry. - mv.visitAnnotation(DONTINLINE_SIG, true); - - // create parameter array - emitIconstInsn(invokerType.parameterCount()); - mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - - // fill parameter array - for (int i = 0; i < invokerType.parameterCount(); i++) { - Class ptype = invokerType.parameterType(i); - mv.visitInsn(Opcodes.DUP); - emitIconstInsn(i); - emitLoadInsn(basicType(ptype), i); - // box if primitive type - if (ptype.isPrimitive()) { - emitBoxing(Wrapper.forPrimitiveType(ptype)); + final byte[] classFile = classFileSetup(new Consumer() { + @Override + public void accept(ClassBuilder clb) { + methodSetup(clb, new Consumer() { + @Override + public void accept(MethodBuilder mb) { + + mb.with(RuntimeVisibleAnnotationsAttribute.of(List.of( + HIDDEN, // Suppress this method in backtraces displayed to the user. + DONTINLINE // Don't inline the interpreter entry. + ))); + + mb.withCode(new Consumer() { + @Override + public void accept(CodeBuilder cob) { + // create parameter array + cob.loadConstant(invokerType.parameterCount()); + cob.anewarray(CD_Object); + + // fill parameter array + for (int i = 0; i < invokerType.parameterCount(); i++) { + Class ptype = invokerType.parameterType(i); + cob.dup(); + cob.loadConstant(i); + emitLoadInsn(cob, basicType(ptype).basicTypeKind(), i); + // box if primitive type + if (ptype.isPrimitive()) { + emitBoxing(cob, TypeKind.from(ptype)); + } + cob.aastore(); + } + // invoke + cob.aload(0); + cob.getfield(CD_MethodHandle, "form", CD_LambdaForm); + cob.swap(); // swap form and array; avoid local variable + cob.invokevirtual(CD_LambdaForm, "interpretWithArguments", MethodTypeDescImpl.ofValidated(CD_Object, CD_Object_array)); + + // maybe unbox + Class rtype = invokerType.returnType(); + TypeKind rtypeK = TypeKind.from(rtype); + if (rtype.isPrimitive() && rtype != void.class) { + emitUnboxing(cob, rtypeK); + } + + // return statement + cob.return_(rtypeK); + } + }); + } + }); + clinit(clb, classDesc, classData); + bogusMethod(clb, invokerType); } - mv.visitInsn(Opcodes.AASTORE); - } - // invoke - emitAloadInsn(0); - mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;"); - mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;", false); - - // maybe unbox - Class rtype = invokerType.returnType(); - if (rtype.isPrimitive() && rtype != void.class) { - emitUnboxing(Wrapper.forPrimitiveType(rtype)); - } - - // return statement - emitReturnInsn(basicType(rtype)); - - methodEpilogue(); - clinit(cw, className, classData); - bogusMethod(invokerType); - - return cw.toByteArray(); + }); + return classFile; } /** @@ -1857,73 +1601,101 @@ static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) { private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) { MethodType dstType = typeForm.erasedType(); - classFilePrologue(); - methodPrologue(); - - // Suppress this method in backtraces displayed to the user. - mv.visitAnnotation(HIDDEN_SIG, true); - - // Force inlining of this invoker method. - mv.visitAnnotation(FORCEINLINE_SIG, true); - - // Load receiver - emitAloadInsn(0); - - // Load arguments from array - for (int i = 0; i < dstType.parameterCount(); i++) { - emitAloadInsn(1); - emitIconstInsn(i); - mv.visitInsn(Opcodes.AALOAD); - - // Maybe unbox - Class dptype = dstType.parameterType(i); - if (dptype.isPrimitive()) { - Wrapper dstWrapper = Wrapper.forBasicType(dptype); - Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper; // narrow subword from int - emitUnboxing(srcWrapper); - emitPrimCast(srcWrapper, dstWrapper); + final byte[] classFile = classFileSetup(new Consumer() { + @Override + public void accept(ClassBuilder clb) { + methodSetup(clb, new Consumer() { + @Override + public void accept(MethodBuilder mb) { + + mb.with(RuntimeVisibleAnnotationsAttribute.of(List.of( + HIDDEN, // Suppress this method in backtraces displayed to the user. + FORCEINLINE // Force inlining of this invoker method. + ))); + + mb.withCode(new Consumer() { + @Override + public void accept(CodeBuilder cob) { + // Load receiver + cob.aload(0); + + // Load arguments from array + for (int i = 0; i < dstType.parameterCount(); i++) { + cob.aload(1); + cob.loadConstant(i); + cob.aaload(); + + // Maybe unbox + Class dptype = dstType.parameterType(i); + if (dptype.isPrimitive()) { + TypeKind dstTK = TypeKind.from(dptype); + TypeKind srcTK = dstTK.asLoadable(); + emitUnboxing(cob, srcTK); + emitPrimCast(cob, srcTK, dstTK); + } + } + + // Invoke + MethodTypeDesc targetDesc = methodDesc(dstType.basicType()); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", targetDesc); + + // Box primitive types + Class rtype = dstType.returnType(); + if (rtype != void.class && rtype.isPrimitive()) { + TypeKind srcTK = TypeKind.from(rtype); + TypeKind dstTK = srcTK.asLoadable(); + // boolean casts not allowed + emitPrimCast(cob, srcTK, dstTK); + emitBoxing(cob, dstTK); + } + + // If the return type is void we return a null reference. + if (rtype == void.class) { + cob.aconst_null(); + } + cob.areturn(); // NOTE: NamedFunction invokers always return a reference value. + } + }); + } + }); + clinit(clb, classDesc, classData); + bogusMethod(clb, dstType); } - } - - // Invoke - String targetDesc = dstType.basicType().toMethodDescriptorString(); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc, false); - - // Box primitive types - Class rtype = dstType.returnType(); - if (rtype != void.class && rtype.isPrimitive()) { - Wrapper srcWrapper = Wrapper.forBasicType(rtype); - Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int - // boolean casts not allowed - emitPrimCast(srcWrapper, dstWrapper); - emitBoxing(dstWrapper); - } - - // If the return type is void we return a null reference. - if (rtype == void.class) { - mv.visitInsn(Opcodes.ACONST_NULL); - } - emitReturnInsn(L_TYPE); // NOTE: NamedFunction invokers always return a reference value. - - methodEpilogue(); - clinit(cw, className, classData); - bogusMethod(dstType); - - return cw.toByteArray(); + }); + return classFile; } /** * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool * for debugging purposes. */ - private void bogusMethod(Object os) { + private void bogusMethod(ClassBuilder clb, Object os) { if (dumper().isEnabled()) { - mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null); - mv.visitLdcInsn(os.toString()); - mv.visitInsn(Opcodes.POP); - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); + clb.withMethod("dummy", MTD_void, ACC_STATIC, new MethodBody(new Consumer() { + @Override + public void accept(CodeBuilder cob) { + cob.loadConstant(os.toString()); + cob.pop(); + cob.return_(); + } + })); + } + } + + static ClassDesc classDesc(Class cls) { +// assert(VerifyAccess.isTypeVisible(cls, Object.class)) : cls.getName(); + return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() + : cls == MethodHandle.class ? CD_MethodHandle + : cls == DirectMethodHandle.class ? CD_DirectMethodHandle + : cls == Object.class ? CD_Object + : 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); } } diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java index 823f527b6c43a..09ea5df4a235f 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -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 @@ enum BasicType { 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 basicTypeClass() { Wrapper basicTypeWrapper() { return btWrapper; } + TypeKind basicTypeKind() { + return btKind; + } int basicTypeSlots() { return btWrapper.stackSlots(); } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index e79c8463d30b2..57446c9b2fdf0 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -27,8 +27,9 @@ 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.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.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 @@ static MethodHandle bindCaller(MethodHandle mh, Class hostClass) { // 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 @@ private static boolean checkCallerClass(Class expected) { /** 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 @@ private static byte[] generateInvokerTemplate() { // } // } // } - 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())); } } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 9ac8e42a2f0c2..0cb77f632b334 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -28,9 +28,6 @@ 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.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.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 @@ private static ClassFile readClassFile(byte[] bytes) { 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); diff --git a/src/java.base/share/classes/java/lang/invoke/MethodType.java b/src/java.base/share/classes/java/lang/invoke/MethodType.java index 29a338261e905..faf8302206ede 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodType.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java @@ -1291,15 +1291,21 @@ static String toFieldDescriptorString(Class cls) { */ @Override public Optional 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. diff --git a/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java b/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java index e49094073c3ca..e35271dc8b42c 100644 --- a/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java +++ b/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java @@ -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); - } - - private static final int NUM_WRAPPERS = Wrapper.COUNT; - - 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; - } - } - - // wideningOpcodes[][] will be NOP-initialized by default - assert(Opcodes.NOP == 0); +class TypeConvertingMethodAdapter { - 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); + private static class BoxHolder { + private static final ConstantPoolBuilder CP = ConstantPoolBuilder.of(); - 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 box(ClassDesc primitive, ClassDesc target) { + return CP.methodRefEntry(target, "valueOf", MethodTypeDescImpl.ofValidated(target, primitive)); } - } - /** - * 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; + 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 MethodRefEntry unbox(ClassDesc owner, String methodName, ClassDesc primitiveTarget) { + return CP.methodRefEntry(owner, methodName, MethodTypeDescImpl.ofValidated(primitiveTarget)); } - 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; - } - } - private static String wrapperName(Wrapper w) { - return "java/lang/" + w.wrapperSimpleName(); + 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); } - private static String unboxMethod(Wrapper w) { - return w.primitiveSimpleName() + "Value"; + 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 static String boxingDescriptor(Wrapper w) { - return "(" + w.basicTypeChar() + ")L" + wrapperName(w) + ";"; + static void boxIfTypePrimitive(CodeBuilder cob, TypeKind tk) { + box(cob, tk); } - 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 @@ private Wrapper toWrapper(String desc) { * @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 @@ void convertType(Class arg, Class target, Class functional) { 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()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index ddb14b2d26a5a..0f1f6fb69de26 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -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.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 @@ private static record Type(int tag, ClassDesc sym, int bci) { //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 @@ Type mergeComponentFrom(Type from, ClassHierarchyImpl context) { } } - 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) { diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index a4ff429bd00c3..c7c76c8fa3a2e 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -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