Skip to content

Commit

Permalink
Fixed switch in the cases of unboxing and widening
Browse files Browse the repository at this point in the history
  • Loading branch information
biboudis committed Dec 11, 2023
1 parent 4cf3c06 commit fe2c5ef
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 132 deletions.
213 changes: 82 additions & 131 deletions src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,39 +145,43 @@ private SwitchBootstraps() {}
*
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if any element in the labels array is null, if the
* invocation type is not not a method type of first parameter of a reference type,
* invocation type is not a method type of first parameter of a reference type,
* second parameter of type {@code int} and with {@code int} as its return type,
* or if {@code labels} contains an element that is not of type {@code String},
* {@code Integer}, {@code Class} or {@code EnumDesc}.
* {@code Integer}, {@code Long}, {@code Float}, {@code Double}, {@code Boolean},
* {@code Class} or {@code EnumDesc}. Additionally, if {@code labels} contains an element
* that is not of type {@code Boolean} when {@code target} is a {@code Boolean.class}.
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/
public static CallSite typeSwitch(MethodHandles.Lookup lookup,
String invocationName,
MethodType invocationType,
Object... labels) {
Class<?> selectorType = invocationType.parameterType(0);
if (invocationType.parameterCount() != 2
|| (!invocationType.returnType().equals(int.class))
|| (invocationType.parameterType(0).isPrimitive() && !previewEnabled)
|| (selectorType.isPrimitive() && !previewEnabled)
|| !invocationType.parameterType(1).equals(int.class))
throw new IllegalArgumentException("Illegal invocation type " + invocationType);
requireNonNull(labels);

labels = labels.clone();
Stream.of(labels).forEach(SwitchBootstraps::verifyLabel);
Stream.of(labels).forEach(l -> verifyLabel(l, selectorType));

MethodHandle target = generateInnerClass(lookup, invocationType.parameterType(0), labels);
MethodHandle target = generateInnerClass(lookup, selectorType, labels);

target = withIndexCheck(target, labels.length);

return new ConstantCallSite(target);
}

private static void verifyLabel(Object label) {
private static void verifyLabel(Object label, Class<?> selectorType) {
if (label == null) {
throw new IllegalArgumentException("null label found");
}
Class<?> labelClass = label.getClass();

if (labelClass != Class.class &&
labelClass != String.class &&
labelClass != Integer.class &&
Expand All @@ -186,6 +190,7 @@ private static void verifyLabel(Object label) {
labelClass != Long.class &&
labelClass != Double.class &&
labelClass != Boolean.class) ||
((selectorType.equals(boolean.class) || selectorType.equals(Boolean.class)) && labelClass != Boolean.class && labelClass != Class.class) ||
!previewEnabled) &&

labelClass != EnumDesc.class) {
Expand Down Expand Up @@ -451,63 +456,65 @@ record Element(Label target, Label next, Object caseLabel) {}
if (unconditionalExactnessCheck(selectorType, classLabel)) {
//nothing - unconditionally use this case
} else if (classLabel.isPrimitive()) {
if (selectorType.equals(Object.class)) {
if (!selectorType.isPrimitive() && !Wrapper.isWrapperNumericOrBooleanType(selectorType)) {
// Object o = ...
// o instanceof Wrapped(float)
cb.aload(0);
cb.instanceof_(Wrapper.forBasicType(classLabel)
.wrapperType()
.describeConstable()
.orElseThrow());
cb.ifeq(next);
} else {
Label notNumber = cb.newLabel();
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Number);
if (selectorType == long.class || selectorType == float.class || selectorType == double.class) {
cb.ifeq(next);
} else {
cb.ifeq(notNumber);
}
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Number);
if (selectorType == long.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"longValue",
MethodTypeDesc.of(ConstantDescs.CD_long));
} else if (selectorType == float.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"floatValue",
MethodTypeDesc.of(ConstantDescs.CD_float));
} else if (selectorType == double.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"doubleValue",
MethodTypeDesc.of(ConstantDescs.CD_double));
} else {
Label compare = cb.newLabel();
cb.invokevirtual(ConstantDescs.CD_Number,
"intValue",
MethodTypeDesc.of(ConstantDescs.CD_int));
cb.goto_(compare);
cb.labelBinding(notNumber);
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Character);
cb.ifeq(next);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Character);
cb.invokevirtual(ConstantDescs.CD_Character,
"charValue",
MethodTypeDesc.of(ConstantDescs.CD_char));
cb.labelBinding(compare);
}

TypePairs typePair = TypePairs.of(selectorType, classLabel);
} else if (!unconditionalExactnessCheck(Wrapper.asPrimitiveType(selectorType), classLabel)) {
// Integer i = ... or int i = ...
// o instanceof float
Label notNumber = cb.newLabel();
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Number);
if (selectorType == long.class || selectorType == float.class || selectorType == double.class) {
cb.ifeq(next);
} else {
cb.ifeq(notNumber);
}
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Number);
if (selectorType == long.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"longValue",
MethodTypeDesc.of(ConstantDescs.CD_long));
} else if (selectorType == float.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"floatValue",
MethodTypeDesc.of(ConstantDescs.CD_float));
} else if (selectorType == double.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"doubleValue",
MethodTypeDesc.of(ConstantDescs.CD_double));
} else {
Label compare = cb.newLabel();
cb.invokevirtual(ConstantDescs.CD_Number,
"intValue",
MethodTypeDesc.of(ConstantDescs.CD_int));
cb.goto_(compare);
cb.labelBinding(notNumber);
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Character);
cb.ifeq(next);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Character);
cb.invokevirtual(ConstantDescs.CD_Character,
"charValue",
MethodTypeDesc.of(ConstantDescs.CD_char));
cb.labelBinding(compare);
}

TypePairs typePair = TypePairs.of(Wrapper.asPrimitiveType(selectorType), classLabel);
String methodName = typePairToName.get(typePair);
cb.invokestatic(ExactConversionsSupport.class.describeConstable().orElseThrow(),
methodName,
MethodTypeDesc.of(ConstantDescs.CD_boolean, typePair.from.describeConstable().orElseThrow()));
cb.ifeq(next);
}
} else if (selectorType.isPrimitive()) {
System.err.println("XXXXXXXXXXXXXXXXXXX");
} else {
Optional<ClassDesc> classLabelConstableOpt = classLabel.describeConstable();
if (classLabelConstableOpt.isPresent()) {
Expand Down Expand Up @@ -556,46 +563,30 @@ record Element(Label target, Label next, Object caseLabel) {}
ConstantDescs.CD_Object));
cb.ifeq(next);
} else if (element.caseLabel() instanceof Integer integerLabel) {
if (selectorType == boolean.class) {
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Boolean);
cb.ifeq(next);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Boolean);
cb.invokevirtual(ConstantDescs.CD_Number,
"booleanValue",
MethodTypeDesc.of(ConstantDescs.CD_boolean));
if (integerLabel.intValue() == 0) {
cb.ifne(next);
} else {
cb.ifeq(next);
}
} else {
Label compare = cb.newLabel();
Label notNumber = cb.newLabel();
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Number);
cb.ifeq(notNumber);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Number);
cb.invokevirtual(ConstantDescs.CD_Number,
"intValue",
MethodTypeDesc.of(ConstantDescs.CD_int));
cb.goto_(compare);
cb.labelBinding(notNumber);
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Character);
cb.ifeq(next);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Character);
cb.invokevirtual(ConstantDescs.CD_Character,
"charValue",
MethodTypeDesc.of(ConstantDescs.CD_char));
cb.labelBinding(compare);

cb.ldc(integerLabel);
cb.if_icmpne(next);
}
Label compare = cb.newLabel();
Label notNumber = cb.newLabel();
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Number);
cb.ifeq(notNumber);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Number);
cb.invokevirtual(ConstantDescs.CD_Number,
"intValue",
MethodTypeDesc.of(ConstantDescs.CD_int));
cb.goto_(compare);
cb.labelBinding(notNumber);
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Character);
cb.ifeq(next);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Character);
cb.invokevirtual(ConstantDescs.CD_Character,
"charValue",
MethodTypeDesc.of(ConstantDescs.CD_char));
cb.labelBinding(compare);

cb.ldc(integerLabel);
cb.if_icmpne(next);
} else if (/*selectorType.isPrimitive() && ??*/
(element.caseLabel() instanceof Long ||
element.caseLabel() instanceof Float ||
Expand Down Expand Up @@ -663,46 +654,6 @@ private static String typeSwitchClassName(Class<?> targetClass) {
return name + "$$TypeSwitch";
}

private static void unbox(CodeBuilder cb/*, Class<?> selectorType*/, Label next) {
// String unboxMethod;
// MethodTypeDesc desc;
// if (selectorType == long.class) {
//
// }
Label compare = cb.newLabel();
Label notNumber = cb.newLabel();
Label notCharacter = cb.newLabel();
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Number);
cb.ifeq(notNumber);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Number);
cb.invokevirtual(ConstantDescs.CD_Number,
"intValue",
MethodTypeDesc.of(ConstantDescs.CD_int));
cb.goto_(compare);
cb.labelBinding(notNumber);
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Character);
cb.ifeq(notCharacter);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Character);
cb.invokevirtual(ConstantDescs.CD_Character,
"charValue",
MethodTypeDesc.of(ConstantDescs.CD_char));
cb.goto_(compare);
cb.labelBinding(notCharacter);
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Boolean);
cb.ifeq(next);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Boolean);
cb.invokevirtual(ConstantDescs.CD_Boolean,
"booleanValue",
MethodTypeDesc.of(ConstantDescs.CD_boolean));
cb.labelBinding(compare);
}

// this method should be in sync with com.sun.tools.javac.code.Types.checkUnconditionallyExactPrimitives
private static boolean unconditionalExactnessCheck(Class<?> selectorType, Class<?> targetType) {
Wrapper selectorWrapper = Wrapper.forBasicType(selectorType);
Expand Down
5 changes: 5 additions & 0 deletions src/java.base/share/classes/sun/invoke/util/Wrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,11 @@ public static boolean isWrapperType(Class<?> type) {
return findWrapperType(type) != null;
}

/** Query: Is the given type a wrapper, such as {@code Integer}, {@code Byte}, etc excluding {@code Void} and {@code Object}? */
public static boolean isWrapperNumericOrBooleanType(Class<?> type) {
return isWrapperType(type) && findWrapperType(type) != VOID && findWrapperType(type) != OBJECT;
}

/** Query: Is the given type a primitive, such as {@code int} or {@code void}? */
public static boolean isPrimitiveType(Class<?> type) {
return type.isPrimitive();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ private void handleSwitch(JCTree tree,
MethodSymbol bsm = rs.resolveInternalMethod(tree.pos(), env, syms.switchBootstrapsType,
bootstrapName, staticArgTypes, List.nil());

Type resolvedSelectorType = enumSelector || primitiveSelector ? seltype : syms.objectType;
Type resolvedSelectorType = seltype;
MethodType indyType = new MethodType(
List.of(resolvedSelectorType, syms.intType),
syms.intType,
Expand Down
29 changes: 29 additions & 0 deletions test/langtools/tools/javac/patterns/PrimitivePatternsSwitch.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ public static void main(String[] args) {
assertEquals(0, testDoubleInEnhancedSwitchStatement(0.0d));
assertEquals(1, testLongInEnhancedSwitchStatement(1l));
assertEquals(0, testLongInEnhancedSwitchStatement(0l));
assertEquals(1, testBooleanInEnhancedSwitchStatement(true));
assertEquals(0, testBooleanInEnhancedSwitchStatement(false));
assertEquals(1, testByteWrapperToIntUnconditionallyExact());
assertEquals(1, testIntegerWrapperToFloat());
assertEquals(-1, testIntegerWrapperToFloatInexact());
}

public static int primitiveSwitch(int i) {
Expand Down Expand Up @@ -537,6 +542,30 @@ public static int testBooleanInEnhancedSwitchStatement(boolean v1) {
return i;
}

public static int testByteWrapperToIntUnconditionallyExact() {
Byte b = Byte.valueOf((byte) 42);
return switch (b) {
case int p -> 1;
};
}

public static int testIntegerWrapperToFloat() {
Integer i = Integer.valueOf(42);
return switch (i) {
case float p -> 1;
default -> -1;
};
}

public static int testIntegerWrapperToFloatInexact() {
Integer i = Integer.valueOf(Integer.MAX_VALUE);
return switch (i) {
case float p -> 1;
default -> -1;
};
}


record R_Integer(Integer x) {}
record R_int(int x) {}
record R_double(double x) {}
Expand Down

0 comments on commit fe2c5ef

Please sign in to comment.