From 862119565db311fe0e02e383fd3493601ed23ea8 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 8 Oct 2025 05:32:51 +0000 Subject: [PATCH] 8363917: SwitchBootstraps.enumSwitch() args not checked as documented Reviewed-by: liach --- .../java/lang/runtime/SwitchBootstraps.java | 37 ++++++++------ .../lang/runtime/SwitchBootstrapsTest.java | 49 +++++++++++++++++-- 2 files changed, 68 insertions(+), 18 deletions(-) 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 f4d82595842..99716baf439 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -165,22 +165,22 @@ public final class SwitchBootstraps { * @param lookup Represents a lookup context with the accessibility * privileges of the caller. When used with {@code invokedynamic}, * this is stacked automatically by the VM. - * @param invocationName unused + * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * a reference type, an {@code int}, and {@code int} as a return type. * @param labels case labels - {@code String} and {@code Integer} constants * and {@code Class} and {@code EnumDesc} instances, in any combination * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null} + * @throws NullPointerException if any argument is {@code null}, unless noted otherwise * @throws IllegalArgumentException if any element in the labels array is null * @throws IllegalArgumentException if the 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, + * second parameter of type {@code int} and with {@code int} as its return type * @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code String}, * {@code Integer}, {@code Long}, {@code Float}, {@code Double}, {@code Boolean}, - * {@code Class} or {@code EnumDesc}. + * {@code Class} or {@code EnumDesc} * @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code Boolean} - * when {@code target} is a {@code Boolean.class}. + * 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 */ @@ -255,29 +255,36 @@ public final class SwitchBootstraps { * enum constant's {@link Enum#name()}. * *

- * If no element in the {@code labels} array matches the target, then - * the method of the call site return the length of the {@code labels} array. + * If for a given {@code target} there is no element in the {@code labels} + * fulfilling one of the above conditions, then the method of the call + * site returns the length of the {@code labels} array. *

* The value of the {@code restart} index must be between {@code 0} (inclusive) and * the length of the {@code labels} array (inclusive), - * both or an {@link IndexOutOfBoundsException} is thrown. + * or an {@link IndexOutOfBoundsException} is thrown. + * + * @apiNote It is permissible for the {@code labels} array to contain {@code String} + * values that do not represent any enum constants at runtime. * * @param lookup Represents a lookup context with the accessibility * privileges of the caller. When used with {@code invokedynamic}, * this is stacked automatically by the VM. - * @param invocationName unused + * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * an enum type, an {@code int}, and {@code int} as a return type. * @param labels case labels - {@code String} constants and {@code Class} instances, * in any combination * @return a {@code CallSite} returning the first matching element as described above * - * @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 a method type whose first parameter type is an enum type, - * second parameter of type {@code int} and whose return type is {@code int}, - * or if {@code labels} contains an element that is not of type {@code String} or - * {@code Class} of the target enum type. + * @throws NullPointerException if any argument is {@code null}, unless noted otherwise + * @throws IllegalArgumentException if any element in the labels array is null + * @throws IllegalArgumentException if any element in the labels array is an empty {@code String} + * @throws IllegalArgumentException if the invocation type is not a method type + * whose first parameter type is an enum type, + * second parameter of type {@code int} and + * whose return type is {@code int} + * @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code String} or + * {@code Class} equal to the target enum type * @jvms 4.4.6 The CONSTANT_NameAndType_info Structure * @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures */ diff --git a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java index a231501894f..8c6132b2815 100644 --- a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java +++ b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -155,23 +155,60 @@ public class SwitchBootstrapsTest { testEnum(E1.B, 0, 0, "B", "C", "A", E1.class); testEnum(E1.B, 1, 3, "B", "C", "A", E1.class); try { - testEnum(E1.B, 1, 3, "B", "C", "A", E2.class); + testEnum(E1.B, 0, -1, E2.class); fail("Didn't get the expected exception."); } catch (IllegalArgumentException ex) { //OK } try { - testEnum(E1.B, 1, 3, "B", "C", "A", String.class); + testEnum(E1.B, 0, -1, String.class); fail("Didn't get the expected exception."); } catch (IllegalArgumentException ex) { //OK } + try { + testEnum(E1.B, 0, -1, 10); + fail("Didn't get the expected exception."); + } catch (IllegalArgumentException ex) { + //OK + } + try { + testEnum(E1.B, 0, -1, new Object()); + fail("Didn't get the expected exception."); + } catch (IllegalArgumentException ex) { + //OK + } + try { + testEnum(E1.B, 0, -1, new Object[] { null }); + fail("Didn't get the expected exception."); + } catch (IllegalArgumentException ex) { + //OK + } + try { + testEnum(E1.B, 0, -1, ""); + fail("Didn't get the expected exception."); + } catch (IllegalArgumentException ex) { + //OK + } + try { + testEnum(E1.B, 0, -1, (Object[]) null); + fail("Didn't get the expected exception."); + } catch (NullPointerException ex) { + //OK + } testEnum(E1.B, 0, 0, "B", "A"); testEnum(E1.A, 0, 1, "B", "A"); testEnum(E1.A, 0, 0, "A", "A", "B"); testEnum(E1.A, 1, 1, "A", "A", "B"); testEnum(E1.A, 2, 3, "A", "A", "B"); testEnum(E1.A, 0, 0); + testEnum(E1.B, 0, 2, "A", "OLD_REMOVED_CONSTANT", "B", E1.class); + testEnum(E1.B, 1, 2, "A", "OLD_REMOVED_CONSTANT", "B", E1.class); + + //null invocation name: + MethodType switchType = MethodType.methodType(int.class, E1.class, int.class); + MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), null, switchType)).dynamicInvoker(); + assertEquals((int) indy.invoke(E1.A, 0), 0); } public void testEnumsWithConstants() throws Throwable { @@ -197,6 +234,9 @@ public class SwitchBootstrapsTest { testEnum(E.class, E.A, 0, 0, "A", "B", "C"); testEnum(E.class, E.B, 0, 1, "A", "B", "C"); testEnum(E.class, E.C, 0, 2, "A", "B", "C"); + testEnum(E.class, E.C, 0, 2, "A", "B"); + testEnum(E.class, E.C, 1, 2, "A", "B"); + testEnum(E.class, E.C, 2, 2, "A", "B"); } public void testWrongSwitchTypes() throws Throwable { @@ -279,6 +319,9 @@ public class SwitchBootstrapsTest { } catch (IllegalArgumentException ex) { //OK } + //null invocationName is OK: + BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), null, switchType, + new Object[] {Object.class}); } private static AtomicBoolean enumInitialized = new AtomicBoolean();