blob: 9cdfb74354444f685850c72f3455f380a058db19 [file] [log] [blame]
Søren Gjesse8e1222c2024-04-23 15:20:54 +02001// 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.
4package switchpatternmatching;
5
Clément Béraf48bfff2024-05-15 11:22:09 +02006import static org.junit.Assert.assertTrue;
Søren Gjesse8e1222c2024-04-23 15:20:54 +02007import static org.junit.Assume.assumeTrue;
Clément Béra2ab2d972024-05-15 11:32:33 +02008import static switchpatternmatching.SwitchTestHelper.desugarMatchException;
Clément Béraf48bfff2024-05-15 11:22:09 +02009import static switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
Clément Béra2ab2d972024-05-15 11:32:33 +020010import static switchpatternmatching.SwitchTestHelper.matchException;
Søren Gjesse8e1222c2024-04-23 15:20:54 +020011
Clément Béra6592f0e2024-05-13 15:07:46 +020012import com.android.tools.r8.JdkClassFileProvider;
Søren Gjesse8e1222c2024-04-23 15:20:54 +020013import com.android.tools.r8.TestBase;
Clément Béra8ceb3752024-05-06 08:35:31 +020014import com.android.tools.r8.TestBuilder;
Søren Gjesse8e1222c2024-04-23 15:20:54 +020015import com.android.tools.r8.TestParameters;
16import com.android.tools.r8.TestParametersCollection;
17import com.android.tools.r8.TestRuntime.CfVm;
18import com.android.tools.r8.ToolHelper;
Søren Gjesse8e1222c2024-04-23 15:20:54 +020019import com.android.tools.r8.utils.StringUtils;
20import com.android.tools.r8.utils.codeinspector.CodeInspector;
Clément Béra8ceb3752024-05-06 08:35:31 +020021import org.junit.Assume;
Søren Gjesse8e1222c2024-04-23 15:20:54 +020022import org.junit.Test;
23import org.junit.runner.RunWith;
24import org.junit.runners.Parameterized;
25import org.junit.runners.Parameterized.Parameter;
26import org.junit.runners.Parameterized.Parameters;
27
28@RunWith(Parameterized.class)
29public class EnumSwitchTest extends TestBase {
30
31 @Parameter public TestParameters parameters;
32
33 @Parameters(name = "{0}")
34 public static TestParametersCollection data() {
Clément Béra2ab2d972024-05-15 11:32:33 +020035 return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
Søren Gjesse8e1222c2024-04-23 15:20:54 +020036 }
37
Clément Béra8ceb3752024-05-06 08:35:31 +020038 public static String EXPECTED_OUTPUT =
39 StringUtils.lines("null", "E1", "E2", "E3", "E4", "a C", "class %s");
Søren Gjesse8e1222c2024-04-23 15:20:54 +020040
41 @Test
42 public void testJvm() throws Exception {
43 assumeTrue(parameters.isCfRuntime());
44 CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
Clément Béraf48bfff2024-05-15 11:22:09 +020045 assertTrue(
46 hasJdk21TypeSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("enumSwitch")));
Søren Gjesse8e1222c2024-04-23 15:20:54 +020047
48 parameters.assumeJvmTestParameters();
49 testForJvm(parameters)
Clément Béra8ceb3752024-05-06 08:35:31 +020050 .apply(this::addModifiedProgramClasses)
Søren Gjesse8e1222c2024-04-23 15:20:54 +020051 .run(parameters.getRuntime(), Main.class)
52 .applyIf(
53 parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21),
Clément Béra2ab2d972024-05-15 11:32:33 +020054 r -> r.assertSuccessWithOutput(String.format(EXPECTED_OUTPUT, matchException())),
Søren Gjesse8e1222c2024-04-23 15:20:54 +020055 r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
56 }
57
Clément Béra8ceb3752024-05-06 08:35:31 +020058 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 Gjesse8e1222c2024-04-23 15:20:54 +020088 @Test
89 public void testD8() throws Exception {
Clément Béra2ab2d972024-05-15 11:32:33 +020090 testForD8(parameters.getBackend())
Clément Béra8ceb3752024-05-06 08:35:31 +020091 .apply(this::addModifiedProgramClasses)
92 .setMinApi(parameters)
93 .run(parameters.getRuntime(), Main.class)
Clément Béra2ab2d972024-05-15 11:32:33 +020094 .assertSuccessWithOutput(String.format(EXPECTED_OUTPUT, desugarMatchException()));
Søren Gjesse8e1222c2024-04-23 15:20:54 +020095 }
96
97 @Test
98 public void testR8() throws Exception {
Clément Béra2ab2d972024-05-15 11:32:33 +020099 parameters.assumeR8TestParameters();
Clément Béra6592f0e2024-05-13 15:07:46 +0200100 Assume.assumeTrue(
101 parameters.isDexRuntime()
102 || (parameters.isCfRuntime()
103 && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21)));
Clément Béra8ceb3752024-05-06 08:35:31 +0200104 testForR8(parameters.getBackend())
105 .apply(this::addModifiedProgramClasses)
Clément Béra6592f0e2024-05-13 15:07:46 +0200106 .applyIf(
107 parameters.isCfRuntime(),
108 b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
Clément Béra8ceb3752024-05-06 08:35:31 +0200109 .setMinApi(parameters)
110 .addKeepMainRule(Main.class)
111 .run(parameters.getRuntime(), Main.class)
Clément Béra2ab2d972024-05-15 11:32:33 +0200112 .assertSuccessWithOutput(String.format(EXPECTED_OUTPUT, matchException(parameters)));
Søren Gjesse8e1222c2024-04-23 15:20:54 +0200113 }
114
Clément Béra8ceb3752024-05-06 08:35:31 +0200115 // D is added to the list of permitted subclasses to reproduce the MatchException.
Søren Gjesse8e1222c2024-04-23 15:20:54 +0200116 sealed interface I permits E, C {}
117
Clément Béra8ceb3752024-05-06 08:35:31 +0200118 interface FakeI {}
119
Søren Gjesse8e1222c2024-04-23 15:20:54 +0200120 public enum E implements I {
121 E1,
122 E2,
123 E3,
124 E4
125 }
126
Clément Béra8ceb3752024-05-06 08:35:31 +0200127 // Replaced with I.
128 static final class D implements FakeI {}
129
Søren Gjesse8e1222c2024-04-23 15:20:54 +0200130 static final class C implements I {}
131
132 static class Main {
Clément Bérae2438dd2024-04-30 12:38:46 +0200133
Søren Gjesse8e1222c2024-04-23 15:20:54 +0200134 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éra8ceb3752024-05-06 08:35:31 +0200165 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 Gjesse8e1222c2024-04-23 15:20:54 +0200175 }
176 }
177}