diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index 2c31b1d060669..f724d7355b005 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -27,6 +27,8 @@ import java.lang.Enum.EnumDesc; import java.lang.classfile.CodeBuilder; +import java.lang.classfile.attribute.StackMapFrameInfo; +import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; import java.lang.constant.MethodTypeDesc; @@ -48,6 +50,7 @@ import java.lang.classfile.Label; import java.lang.classfile.instruction.SwitchCase; +import jdk.internal.classfile.impl.DirectCodeBuilder; import jdk.internal.constant.ClassOrInterfaceDescImpl; import jdk.internal.constant.ConstantUtils; import jdk.internal.constant.MethodTypeDescImpl; @@ -103,6 +106,13 @@ private SwitchBootstraps() {} private static final MethodType MT_TYPE_SWITCH = MethodType.methodType(int.class, Object.class, int.class); + private static final List TYPE_SWITCH_LOCALS = List.of( + StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_Object), StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER + ); + private static final List TYPE_SWITCH_EXTRA_LOCALS = List.of( + StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_Object), StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER, + StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_BiPredicate), StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_List) + ); private static class StaticHolders { private static final MethodHandle MAPPED_ENUM_SWITCH; @@ -482,8 +492,11 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto int ENUM_CACHE = 2; int EXTRA_CLASS_LABELS = 3; + var locals = enumDescs == null && extraClassLabels == null ? TYPE_SWITCH_LOCALS : TYPE_SWITCH_EXTRA_LOCALS; + return cb -> { // Objects.checkIndex(RESTART_IDX, labelConstants + 1) + var stackMapFrames = new ArrayList(labelConstants.length * 2); cb.iload(RESTART_IDX) .loadConstant(labelConstants.length + 1) .invokestatic(CD_Objects, "checkIndex", CHECK_INDEX_DESCRIPTOR) @@ -494,9 +507,12 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto .iconst_m1() .ireturn() .labelBinding(nonNullLabel); + stackMapFrames.add(StackMapFrameInfo.of(nonNullLabel, locals, List.of())); if (labelConstants.length == 0) { cb.loadConstant(0) - .ireturn(); + .ireturn() + .with(StackMapTableAttribute.of(stackMapFrames)); + DirectCodeBuilder.withMaxs(cb, 2, locals.size()); // checkIndex uses 2 return; } cb.iload(RESTART_IDX); @@ -509,6 +525,7 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto for (int idx = labelConstants.length - 1; idx >= 0; idx--) { Object currentLabel = labelConstants[idx]; Label target = cb.newLabel(); + stackMapFrames.add(StackMapFrameInfo.of(target, locals, List.of())); Label next; if (lastLabel == null) { next = dflt; @@ -541,7 +558,7 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto } else if (!unconditionalExactnessCheck(Wrapper.asPrimitiveType(selectorType), classLabel)) { // Integer i = ... or int i = ... // o instanceof float - Label notNumber = cb.newLabel(); + Label notNumber = cb.newLabel(); // this label may end up unbound cb.aload(SELECTOR_OBJ) .instanceOf(CD_Number); if (selectorType == long.class || selectorType == float.class || selectorType == double.class || @@ -570,8 +587,9 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto "intValue", MethodTypeDesc.of(CD_int)) .goto_(compare) - .labelBinding(notNumber) - .aload(SELECTOR_OBJ) + .labelBinding(notNumber); + stackMapFrames.add(StackMapFrameInfo.of(notNumber, locals, List.of())); + cb.aload(SELECTOR_OBJ) .instanceOf(CD_Character) .ifeq(next) .aload(SELECTOR_OBJ) @@ -580,6 +598,7 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto "charValue", MethodTypeDesc.of(CD_char)) .labelBinding(compare); + stackMapFrames.add(StackMapFrameInfo.of(compare, locals, List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER))); } TypePairs typePair = TypePairs.of(Wrapper.asPrimitiveType(selectorType), classLabel); @@ -648,8 +667,9 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto "intValue", MethodTypeDesc.of(CD_int)) .goto_(compare) - .labelBinding(notNumber) - .aload(SELECTOR_OBJ) + .labelBinding(notNumber); + stackMapFrames.add(StackMapFrameInfo.of(notNumber, locals, List.of())); + cb.aload(SELECTOR_OBJ) .instanceOf(CD_Character) .ifeq(next) .aload(SELECTOR_OBJ) @@ -657,9 +677,9 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto .invokevirtual(CD_Character, "charValue", MethodTypeDesc.of(CD_char)) - .labelBinding(compare) - - .loadConstant(integerLabel) + .labelBinding(compare); + stackMapFrames.add(StackMapFrameInfo.of(compare, locals, List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER))); + cb.loadConstant(integerLabel) .if_icmpne(next); } else if ((caseLabel instanceof Long || caseLabel instanceof Float || @@ -688,9 +708,12 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto cb.loadConstant(idx) .ireturn(); } + stackMapFrames.add(StackMapFrameInfo.of(dflt, locals, List.of())); cb.labelBinding(dflt) .loadConstant(labelConstants.length) - .ireturn(); + .ireturn() + .with(StackMapTableAttribute.of(stackMapFrames)); + DirectCodeBuilder.withMaxs(cb, 3, locals.size()); // enum labels use 3 stack, others use 2 }; } @@ -702,7 +725,7 @@ private static MethodHandle generateTypeSwitch(MethodHandles.Lookup caller, Clas List> enumDescs = addExtraInfo ? new ArrayList<>() : null; List> extraClassLabels = addExtraInfo ? new ArrayList<>() : null; - byte[] classBytes = ClassFile.of().build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())), + byte[] classBytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())), clb -> { clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC) .withMethodBody("typeSwitch", diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 2741d1918caeb..bc563482f79d2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -48,10 +48,8 @@ public final class DirectCodeBuilder extends AbstractDirectBuilder implements TerminalCodeBuilder { private static final CharacterRange[] EMPTY_CHARACTER_RANGE = {}; - private static final DeferredLabel[] EMPTY_LABEL_ARRAY = {}; private static final LocalVariable[] EMPTY_LOCAL_VARIABLE_ARRAY = {}; private static final LocalVariableType[] EMPTY_LOCAL_VARIABLE_TYPE_ARRAY = {}; - private static final AbstractPseudoInstruction.ExceptionCatchImpl[] EMPTY_HANDLER_ARRAY = {}; private static final DeferredLabel[] EMPTY_DEFERRED_LABEL_ARRAY = {}; final List handlers = new ArrayList<>(); @@ -74,6 +72,9 @@ public final class DirectCodeBuilder private DeferredLabel[] deferredLabels = EMPTY_DEFERRED_LABEL_ARRAY; private int deferredLabelsCount = 0; + private int maxStackHint = -1; + private int maxLocalsHint = -1; + /* Locals management lazily computed maxLocal = -1 first time: derive count from methodType descriptor (for new methods) & ACC_STATIC, @@ -173,6 +174,12 @@ public MethodInfo methodInfo() { return methodInfo; } + public static void withMaxs(CodeBuilder cob, int stacks, int locals) { + var dcb = (DirectCodeBuilder) cob; + dcb.maxStackHint = stacks; + dcb.maxLocalsHint = locals; + } + private UnboundAttribute content = null; private void writeExceptionHandlers(BufWriterImpl buf) { @@ -319,6 +326,8 @@ private void writeCounters(boolean codeMatch, BufWriterImpl buf) { if (codeMatch) { var originalAttribute = (CodeImpl) original; buf.writeU2U2(originalAttribute.maxStack(), originalAttribute.maxLocals()); + } else if (maxLocalsHint >= 0 && maxStackHint >= 0) { + buf.writeU2U2(maxStackHint, maxLocalsHint); } else { StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); buf.writeU2U2(cntr.maxStack(), cntr.maxLocals());