Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 1 | // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | package switchpatternmatching; |
| 5 | |
| 6 | import static org.junit.Assert.assertTrue; |
| 7 | import static org.junit.Assume.assumeTrue; |
| 8 | import static switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch; |
| 9 | import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; |
| 10 | |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 11 | import com.android.tools.r8.JdkClassFileProvider; |
Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 12 | import com.android.tools.r8.TestBase; |
| 13 | import com.android.tools.r8.TestBuilder; |
| 14 | import com.android.tools.r8.TestParameters; |
| 15 | import com.android.tools.r8.TestParametersCollection; |
| 16 | import com.android.tools.r8.TestRuntime.CfVm; |
| 17 | import com.android.tools.r8.ToolHelper; |
| 18 | import com.android.tools.r8.utils.StringUtils; |
| 19 | import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| 20 | import org.junit.Assume; |
| 21 | import org.junit.Test; |
| 22 | import org.junit.runner.RunWith; |
| 23 | import org.junit.runners.Parameterized; |
| 24 | import org.junit.runners.Parameterized.Parameter; |
| 25 | import org.junit.runners.Parameterized.Parameters; |
| 26 | |
| 27 | @RunWith(Parameterized.class) |
| 28 | public class EnumLessCasesAtRuntimeSwitchTest extends TestBase { |
| 29 | |
| 30 | @Parameter public TestParameters parameters; |
| 31 | |
| 32 | @Parameters(name = "{0}") |
| 33 | public static TestParametersCollection data() { |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 34 | return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); |
Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 35 | } |
| 36 | |
| 37 | public static String EXPECTED_OUTPUT = |
| 38 | StringUtils.lines("TYPE", "null", "E1", "E3", "E5", "a C", "ENUM", "null", "1", "3", "0"); |
| 39 | |
| 40 | @Test |
| 41 | public void testJvm() throws Exception { |
| 42 | assumeTrue(parameters.isCfRuntime()); |
| 43 | CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class)); |
| 44 | assertTrue( |
| 45 | hasJdk21EnumSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("enumSwitch"))); |
| 46 | assertTrue( |
| 47 | hasJdk21TypeSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("typeSwitch"))); |
| 48 | |
| 49 | parameters.assumeJvmTestParameters(); |
| 50 | testForJvm(parameters) |
| 51 | .apply(this::addModifiedProgramClasses) |
| 52 | .run(parameters.getRuntime(), Main.class) |
| 53 | .applyIf( |
| 54 | parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21), |
| 55 | r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT), |
| 56 | r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class)); |
| 57 | } |
| 58 | |
| 59 | private <T extends TestBuilder<?, T>> void addModifiedProgramClasses( |
| 60 | TestBuilder<?, T> testBuilder) throws Exception { |
| 61 | testBuilder |
| 62 | .addStrippedOuter(getClass()) |
| 63 | .addProgramClasses(FakeI.class, C.class, I.class, Main.class) |
| 64 | .addProgramClassFileData( |
| 65 | transformer(CompileTimeE.class) |
| 66 | .setImplements(FakeI.class) |
| 67 | .setClassDescriptor(RuntimeE.class.descriptorString()) |
| 68 | .transform()) |
| 69 | .addProgramClassFileData( |
| 70 | transformer(RuntimeE.class) |
| 71 | .setImplements(I.class) |
| 72 | .setClassDescriptor(CompileTimeE.class.descriptorString()) |
| 73 | .transform()); |
| 74 | } |
| 75 | |
| 76 | @Test |
| 77 | public void testD8() throws Exception { |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 78 | testForD8(parameters.getBackend()) |
Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 79 | .apply(this::addModifiedProgramClasses) |
| 80 | .setMinApi(parameters) |
| 81 | .run(parameters.getRuntime(), Main.class) |
| 82 | .assertSuccessWithOutput(EXPECTED_OUTPUT); |
| 83 | } |
| 84 | |
| 85 | @Test |
| 86 | public void testR8() throws Exception { |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 87 | parameters.assumeR8TestParameters(); |
| 88 | Assume.assumeTrue( |
| 89 | parameters.isDexRuntime() |
| 90 | || (parameters.isCfRuntime() |
| 91 | && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21))); |
Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 92 | testForR8(parameters.getBackend()) |
| 93 | .apply(this::addModifiedProgramClasses) |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 94 | .applyIf( |
| 95 | parameters.isCfRuntime(), |
| 96 | b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk())) |
Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 97 | .setMinApi(parameters) |
| 98 | .addKeepMainRule(Main.class) |
| 99 | .run(parameters.getRuntime(), Main.class) |
| 100 | .assertSuccessWithOutput(EXPECTED_OUTPUT); |
| 101 | } |
| 102 | |
| 103 | sealed interface I permits CompileTimeE, C {} |
| 104 | |
| 105 | public enum CompileTimeE implements I { |
| 106 | E1, |
| 107 | E2, // Case missing at runtime. |
| 108 | E3, |
| 109 | E4, // Case missing at runtime. |
| 110 | E5 |
| 111 | } |
| 112 | |
| 113 | interface FakeI {} |
| 114 | |
| 115 | public enum RuntimeE implements FakeI { |
| 116 | E1, |
| 117 | E3, |
| 118 | E5 |
| 119 | } |
| 120 | |
| 121 | static final class C implements I {} |
| 122 | |
| 123 | static class Main { |
| 124 | |
| 125 | static void typeSwitch(I i) { |
| 126 | switch (i) { |
| 127 | case CompileTimeE.E1 -> { |
| 128 | System.out.println("E1"); |
| 129 | } |
| 130 | case CompileTimeE.E2 -> { // Case missing at runtime. |
| 131 | System.out.println("E2"); |
| 132 | } |
| 133 | case CompileTimeE.E3 -> { |
| 134 | System.out.println("E3"); |
| 135 | } |
| 136 | case CompileTimeE.E4 -> { // Case missing at runtime. |
| 137 | System.out.println("E4"); |
| 138 | } |
| 139 | case CompileTimeE.E5 -> { |
| 140 | System.out.println("E5"); |
| 141 | } |
| 142 | case C c -> { |
| 143 | System.out.println("a C"); |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | static void enumSwitch(CompileTimeE e) { |
| 149 | switch (e) { |
| 150 | case null -> System.out.println("null"); |
| 151 | case CompileTimeE.E1 -> System.out.println("1"); |
| 152 | case CompileTimeE.E2 -> System.out.println("2"); // Case missing at runtime. |
| 153 | case CompileTimeE t when t == CompileTimeE.E3 -> System.out.println("3"); |
| 154 | case CompileTimeE t when t.name().equals("E4") -> |
| 155 | System.out.println("4"); // Case missing at runtime. |
| 156 | case CompileTimeE t -> System.out.println("0"); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | public static void main(String[] args) { |
| 161 | System.out.println("TYPE"); |
| 162 | try { |
| 163 | typeSwitch(null); |
| 164 | } catch (NullPointerException e) { |
| 165 | System.out.println("null"); |
| 166 | } |
| 167 | typeSwitch(CompileTimeE.E1); |
| 168 | typeSwitch(CompileTimeE.E3); |
| 169 | typeSwitch(CompileTimeE.E5); |
| 170 | typeSwitch(new C()); |
| 171 | |
| 172 | System.out.println("ENUM"); |
| 173 | enumSwitch(null); |
| 174 | enumSwitch(CompileTimeE.E1); |
| 175 | enumSwitch(CompileTimeE.E3); |
| 176 | enumSwitch(CompileTimeE.E5); |
| 177 | } |
| 178 | } |
| 179 | } |