Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +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 | |
Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 6 | import static org.junit.Assert.assertTrue; |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 7 | import static org.junit.Assume.assumeTrue; |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 8 | import static switchpatternmatching.SwitchTestHelper.desugarMatchException; |
Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 9 | import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 10 | import static switchpatternmatching.SwitchTestHelper.matchException; |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 11 | |
Clément Béra | 6592f0e | 2024-05-13 15:07:46 +0200 | [diff] [blame] | 12 | import com.android.tools.r8.JdkClassFileProvider; |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 13 | import com.android.tools.r8.TestBase; |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 14 | import com.android.tools.r8.TestBuilder; |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 15 | import com.android.tools.r8.TestParameters; |
| 16 | import com.android.tools.r8.TestParametersCollection; |
| 17 | import com.android.tools.r8.TestRuntime.CfVm; |
| 18 | import com.android.tools.r8.ToolHelper; |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 19 | import com.android.tools.r8.utils.StringUtils; |
| 20 | import com.android.tools.r8.utils.codeinspector.CodeInspector; |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 21 | import org.junit.Assume; |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 22 | import org.junit.Test; |
| 23 | import org.junit.runner.RunWith; |
| 24 | import org.junit.runners.Parameterized; |
| 25 | import org.junit.runners.Parameterized.Parameter; |
| 26 | import org.junit.runners.Parameterized.Parameters; |
| 27 | |
| 28 | @RunWith(Parameterized.class) |
| 29 | public class EnumSwitchTest extends TestBase { |
| 30 | |
| 31 | @Parameter public TestParameters parameters; |
| 32 | |
| 33 | @Parameters(name = "{0}") |
| 34 | public static TestParametersCollection data() { |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 35 | return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 36 | } |
| 37 | |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 38 | public static String EXPECTED_OUTPUT = |
| 39 | StringUtils.lines("null", "E1", "E2", "E3", "E4", "a C", "class %s"); |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 40 | |
| 41 | @Test |
| 42 | public void testJvm() throws Exception { |
| 43 | assumeTrue(parameters.isCfRuntime()); |
| 44 | CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class)); |
Clément Béra | f48bfff | 2024-05-15 11:22:09 +0200 | [diff] [blame] | 45 | assertTrue( |
| 46 | hasJdk21TypeSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("enumSwitch"))); |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 47 | |
| 48 | parameters.assumeJvmTestParameters(); |
| 49 | testForJvm(parameters) |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 50 | .apply(this::addModifiedProgramClasses) |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 51 | .run(parameters.getRuntime(), Main.class) |
| 52 | .applyIf( |
| 53 | parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21), |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 54 | r -> r.assertSuccessWithOutput(String.format(EXPECTED_OUTPUT, matchException())), |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 55 | r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class)); |
| 56 | } |
| 57 | |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 58 | private <T extends TestBuilder<?, T>> void addModifiedProgramClasses( |
| 59 | TestBuilder<?, T> testBuilder) throws Exception { |
| 60 | testBuilder |
| 61 | .addStrippedOuter(getClass()) |
| 62 | .addProgramClasses(FakeI.class, E.class, C.class) |
| 63 | .addProgramClassFileData( |
| 64 | transformer(I.class) |
| 65 | .setPermittedSubclasses(I.class, E.class, C.class, D.class) |
| 66 | .transform()) |
| 67 | .addProgramClassFileData(transformer(D.class).setImplements(I.class).transform()) |
| 68 | .addProgramClassFileData( |
| 69 | transformer(Main.class) |
| 70 | .transformTypeInsnInMethod( |
| 71 | "getD", |
| 72 | (opcode, type, visitor) -> |
| 73 | visitor.visitTypeInsn(opcode, "switchpatternmatching/EnumSwitchTest$D")) |
| 74 | .transformMethodInsnInMethod( |
| 75 | "getD", |
| 76 | (opcode, owner, name, descriptor, isInterface, visitor) -> { |
| 77 | assert name.equals("<init>"); |
| 78 | visitor.visitMethodInsn( |
| 79 | opcode, |
| 80 | "switchpatternmatching/EnumSwitchTest$D", |
| 81 | name, |
| 82 | descriptor, |
| 83 | isInterface); |
| 84 | }) |
| 85 | .transform()); |
| 86 | } |
| 87 | |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 88 | @Test |
| 89 | public void testD8() throws Exception { |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 90 | testForD8(parameters.getBackend()) |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 91 | .apply(this::addModifiedProgramClasses) |
| 92 | .setMinApi(parameters) |
| 93 | .run(parameters.getRuntime(), Main.class) |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 94 | .assertSuccessWithOutput(String.format(EXPECTED_OUTPUT, desugarMatchException())); |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | @Test |
| 98 | public void testR8() throws Exception { |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 99 | parameters.assumeR8TestParameters(); |
Clément Béra | 6592f0e | 2024-05-13 15:07:46 +0200 | [diff] [blame] | 100 | Assume.assumeTrue( |
| 101 | parameters.isDexRuntime() |
| 102 | || (parameters.isCfRuntime() |
| 103 | && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21))); |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 104 | testForR8(parameters.getBackend()) |
| 105 | .apply(this::addModifiedProgramClasses) |
Clément Béra | 6592f0e | 2024-05-13 15:07:46 +0200 | [diff] [blame] | 106 | .applyIf( |
| 107 | parameters.isCfRuntime(), |
| 108 | b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk())) |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 109 | .setMinApi(parameters) |
| 110 | .addKeepMainRule(Main.class) |
| 111 | .run(parameters.getRuntime(), Main.class) |
Clément Béra | 2ab2d97 | 2024-05-15 11:32:33 +0200 | [diff] [blame] | 112 | .assertSuccessWithOutput(String.format(EXPECTED_OUTPUT, matchException(parameters))); |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 113 | } |
| 114 | |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 115 | // D is added to the list of permitted subclasses to reproduce the MatchException. |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 116 | sealed interface I permits E, C {} |
| 117 | |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 118 | interface FakeI {} |
| 119 | |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 120 | public enum E implements I { |
| 121 | E1, |
| 122 | E2, |
| 123 | E3, |
| 124 | E4 |
| 125 | } |
| 126 | |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 127 | // Replaced with I. |
| 128 | static final class D implements FakeI {} |
| 129 | |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 130 | static final class C implements I {} |
| 131 | |
| 132 | static class Main { |
Clément Béra | e2438dd | 2024-04-30 12:38:46 +0200 | [diff] [blame] | 133 | |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 134 | static void enumSwitch(I i) { |
| 135 | switch (i) { |
| 136 | case E.E1 -> { |
| 137 | System.out.println("E1"); |
| 138 | } |
| 139 | case E.E2 -> { |
| 140 | System.out.println("E2"); |
| 141 | } |
| 142 | case E.E3 -> { |
| 143 | System.out.println("E3"); |
| 144 | } |
| 145 | case E.E4 -> { |
| 146 | System.out.println("E4"); |
| 147 | } |
| 148 | case C c -> { |
| 149 | System.out.println("a C"); |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | public static void main(String[] args) { |
| 155 | try { |
| 156 | enumSwitch(null); |
| 157 | } catch (NullPointerException e) { |
| 158 | System.out.println("null"); |
| 159 | } |
| 160 | enumSwitch(E.E1); |
| 161 | enumSwitch(E.E2); |
| 162 | enumSwitch(E.E3); |
| 163 | enumSwitch(E.E4); |
| 164 | enumSwitch(new C()); |
Clément Béra | 8ceb375 | 2024-05-06 08:35:31 +0200 | [diff] [blame] | 165 | try { |
| 166 | enumSwitch(getD()); |
| 167 | } catch (Throwable t) { |
| 168 | System.out.println(t.getClass()); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | public static I getD() { |
| 173 | // Replaced by new D(); |
| 174 | return new C(); |
Søren Gjesse | 8e1222c | 2024-04-23 15:20:54 +0200 | [diff] [blame] | 175 | } |
| 176 | } |
| 177 | } |