Reproduce missing string switch desugaring when using JDK-23
Code generation of the bootstrap method signature changed.
Bug: b/382880986
Change-Id: Id49898891c75780f4122adb05c652aee50e9ea1c
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
index dbb7dcc..f9d6bc9 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package switchpatternmatching;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.TestBase;
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
index 3846dd4..77a6e53 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
@@ -3,12 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package switchpatternmatching;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.desugarMatchException;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.matchException;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import static switchpatternmatching.SwitchTestHelper.desugarMatchException;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
-import static switchpatternmatching.SwitchTestHelper.matchException;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.TestBase;
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
index 9cdfb74..bd16f68 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
@@ -3,11 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package switchpatternmatching;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.desugarMatchException;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.matchException;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import static switchpatternmatching.SwitchTestHelper.desugarMatchException;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
-import static switchpatternmatching.SwitchTestHelper.matchException;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.TestBase;
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java b/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java
index aa6d288..095620c 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java
@@ -3,9 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package switchpatternmatching;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.TestBase;
diff --git a/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
index 27c3374..150951a 100644
--- a/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/StringSwitchTest.java
@@ -3,9 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package switchpatternmatching;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.TestBase;
@@ -21,8 +21,9 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-import switchpatternmatching.TypeSwitchTest.Main;
+// This test is copied into later JDK tests (currently JDK-23). The reason for the copy is that
+// from JDK-23 the code generation changed. Please update the copy as well if updating this test.
@RunWith(Parameterized.class)
public class StringSwitchTest extends TestBase {
diff --git a/src/test/examplesJava21/switchpatternmatching/TypeSwitchMissingClassTest.java b/src/test/examplesJava21/switchpatternmatching/TypeSwitchMissingClassTest.java
index 82dc0d1..cd2e035 100644
--- a/src/test/examplesJava21/switchpatternmatching/TypeSwitchMissingClassTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/TypeSwitchMissingClassTest.java
@@ -3,9 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package switchpatternmatching;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.TestBase;
diff --git a/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
index 9002b29..19cee5b 100644
--- a/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
@@ -3,9 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package switchpatternmatching;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.TestBase;
@@ -21,7 +21,6 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-import switchpatternmatching.StringSwitchTest.Main;
@RunWith(Parameterized.class)
public class TypeSwitchTest extends TestBase {
diff --git a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchRegress382880986Test.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchRegress382880986Test.java
new file mode 100644
index 0000000..d6c5acb
--- /dev/null
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchRegress382880986Test.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.java23.switchpatternmatching;
+
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.JdkClassFileProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Assume;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StringSwitchRegress382880986Test extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("1", "2", "3");
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+
+ CodeInspector inspector =
+ new CodeInspector(ToolHelper.getClassFileForTestClass(TestClass.class));
+ assertTrue(
+ hasJdk21TypeSwitch(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m")));
+
+ testForJvm(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class, "hello", "goodbye", "")
+ .applyIf(
+ parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClassesAndStrippedOuter(getClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class, "hello", "goodbye", "")
+ // TODO(b/382880986): This should not fail.
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ r ->
+ r.assertFailureWithErrorThatMatches(
+ containsString("Instruction is unrepresentable in DEX")),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+
+ @Test
+ @Ignore("TODO(b/382880986) enable test when fixed.")
+ public void testR8() throws Exception {
+ Assume.assumeTrue(
+ parameters.isDexRuntime()
+ || (parameters.isCfRuntime()
+ && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23)));
+ testForR8(parameters.getBackend())
+ .addInnerClassesAndStrippedOuter(getClass())
+ .applyIf(
+ parameters.isCfRuntime(),
+ b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
+ .setMinApi(parameters)
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class, "hello", "goodbye", "")
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ static class TestClass {
+ static final String hello = "hello";
+
+ static void m(String s) {
+ switch (s) {
+ case hello -> System.out.println(1);
+ case "goodbye" -> {
+ System.out.println(2);
+ }
+ case null, default -> System.out.println(3);
+ }
+ }
+
+ public static void main(String[] args) {
+ m(args[0]);
+ m(args[1]);
+ m(args[2]);
+ }
+ }
+}
diff --git a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchTest.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchTest.java
new file mode 100644
index 0000000..1f0a294
--- /dev/null
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchTest.java
@@ -0,0 +1,148 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.java23.switchpatternmatching;
+
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V6_0_1;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.JdkClassFileProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Assume;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a copy of the same test from JDK-21. The reason for the copy is that from JDK-23 the
+// code generation changed.
+@RunWith(Parameterized.class)
+public class StringSwitchTest extends TestBase {
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public static String EXPECTED_OUTPUT =
+ StringUtils.lines(
+ "null", "y or Y", "y or Y", "n or N", "n or N", "yes", "yes", "no", "no", "unknown");
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
+ assertTrue(
+ hasJdk21TypeSwitch(
+ inspector.clazz(Main.class).uniqueMethodWithOriginalName("stringSwitch")));
+
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addInnerClassesAndStrippedOuter(getClass())
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addInnerClassesAndStrippedOuter(getClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class)
+ // TODO(b/382880986): This should not fail.
+ .applyIf(
+ parameters.isDexRuntime() && parameters.asDexRuntime().getVersion().isEqualTo(V6_0_1),
+ r ->
+ r.assertFailureWithErrorThatMatches(
+ containsString(
+ "Attempt to invoke virtual method 'boolean"
+ + " java.lang.String.equalsIgnoreCase(java.lang.String)' on a null"
+ + " object reference")),
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ r ->
+ r.assertFailureWithErrorThatMatches(
+ containsString("Instruction is unrepresentable in DEX")),
+ parameters.isCfRuntime()
+ && (parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17)
+ && parameters.asCfRuntime().isOlderThan(CfVm.JDK23)),
+ r -> r.assertFailureWithErrorThatThrows(BootstrapMethodError.class),
+ !parameters.isCfRuntime()
+ || parameters.isCfRuntime() && parameters.asCfRuntime().isOlderThan(CfVm.JDK17),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ @Ignore("TODO(b/382880986) enable test when fixed.")
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ Assume.assumeTrue(
+ parameters.isDexRuntime()
+ || (parameters.isCfRuntime()
+ && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21)));
+ testForR8(parameters.getBackend())
+ .addInnerClassesAndStrippedOuter(getClass())
+ .applyIf(
+ parameters.isCfRuntime(),
+ b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
+ .setMinApi(parameters)
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ static class Main {
+ static void stringSwitch(String string) {
+ switch (string) {
+ case null -> {
+ System.out.println("null");
+ }
+ case String s when s.equalsIgnoreCase("YES") -> {
+ System.out.println("yes");
+ }
+ case "y", "Y" -> {
+ System.out.println("y or Y");
+ }
+ case String s when s.equalsIgnoreCase("NO") -> {
+ System.out.println("no");
+ }
+ case "n", "N" -> {
+ System.out.println("n or N");
+ }
+ case String s -> {
+ System.out.println("unknown");
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ stringSwitch(null);
+ stringSwitch("y");
+ stringSwitch("Y");
+ stringSwitch("n");
+ stringSwitch("N");
+ stringSwitch("yes");
+ stringSwitch("YES");
+ stringSwitch("no");
+ stringSwitch("NO");
+ stringSwitch("?");
+ }
+ }
+}
diff --git a/src/test/examplesJava21/switchpatternmatching/SwitchTestHelper.java b/src/test/testbase/java/com/android/tools/r8/desugar/switchpatternmatching/SwitchTestHelper.java
similarity index 96%
rename from src/test/examplesJava21/switchpatternmatching/SwitchTestHelper.java
rename to src/test/testbase/java/com/android/tools/r8/desugar/switchpatternmatching/SwitchTestHelper.java
index 03ad175..d6ffd0f 100644
--- a/src/test/examplesJava21/switchpatternmatching/SwitchTestHelper.java
+++ b/src/test/testbase/java/com/android/tools/r8/desugar/switchpatternmatching/SwitchTestHelper.java
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package switchpatternmatching;
+package com.android.tools.r8.desugar.switchpatternmatching;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;