blob: 4153e38f0dd1cb33993c8dd8b8150dbce7b6a132 [file] [log] [blame]
Clément Béra8eca1092021-03-15 14:41:02 +00001// Copyright (c) 2021, 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
Clément Béra0f1f4f72024-09-11 10:21:14 +02005package records;
Clément Béra8eca1092021-03-15 14:41:02 +00006
Ian Zernyef267142022-03-28 12:40:03 +02007import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
8import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
Søren Gjesse12a98922023-09-11 10:24:49 +02009import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
Ian Zernyef267142022-03-28 12:40:03 +020010import static org.hamcrest.MatcherAssert.assertThat;
11import static org.junit.Assert.assertThrows;
12import static org.junit.Assert.assertTrue;
13
14import com.android.tools.r8.CompilationFailedException;
Søren Gjesse12a98922023-09-11 10:24:49 +020015import com.android.tools.r8.D8TestBuilder;
Clément Béra8eca1092021-03-15 14:41:02 +000016import com.android.tools.r8.D8TestCompileResult;
Clément Béra0f1f4f72024-09-11 10:21:14 +020017import com.android.tools.r8.GlobalSyntheticsTestingConsumer;
Clément Béra528ce252022-06-28 15:05:59 +020018import com.android.tools.r8.OutputMode;
Clément Béra8eca1092021-03-15 14:41:02 +000019import com.android.tools.r8.TestBase;
20import com.android.tools.r8.TestParameters;
Ian Zernyef267142022-03-28 12:40:03 +020021import com.android.tools.r8.TestParametersCollection;
22import com.android.tools.r8.errors.DuplicateTypesDiagnostic;
23import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic;
Clément Béra8eca1092021-03-15 14:41:02 +000024import com.android.tools.r8.utils.StringUtils;
Ian Zernyef267142022-03-28 12:40:03 +020025import com.android.tools.r8.utils.codeinspector.CodeInspector;
Clément Béra8eca1092021-03-15 14:41:02 +000026import java.nio.file.Path;
Clément Béra528ce252022-06-28 15:05:59 +020027import org.junit.Assume;
Clément Béra8eca1092021-03-15 14:41:02 +000028import org.junit.Test;
29import org.junit.runner.RunWith;
30import org.junit.runners.Parameterized;
31
32@RunWith(Parameterized.class)
33public class RecordMergeTest extends TestBase {
34
Clément Béra8eca1092021-03-15 14:41:02 +000035 private static final String EXPECTED_RESULT_1 =
Clément Béra0f1f4f72024-09-11 10:21:14 +020036 StringUtils.lines("BobX", "43", "FelixX", "-1", "print", "Bob43", "extra");
Clément Béra8eca1092021-03-15 14:41:02 +000037
Clément Béra8eca1092021-03-15 14:41:02 +000038 private static final String EXPECTED_RESULT_2 =
Christoffer Quist Adamsen2f14e242023-06-29 11:04:22 +020039 StringUtils.lines(
Clément Béra0f1f4f72024-09-11 10:21:14 +020040 "Jane Doe", "42", "true", "true", "true", "false", "false", "false", "false");
Clément Béra8eca1092021-03-15 14:41:02 +000041
42 private final TestParameters parameters;
43
Ian Zernyef267142022-03-28 12:40:03 +020044 public RecordMergeTest(TestParameters parameters) {
Clément Béra8eca1092021-03-15 14:41:02 +000045 this.parameters = parameters;
46 }
47
Ian Zernyef267142022-03-28 12:40:03 +020048 @Parameterized.Parameters(name = "{0}")
49 public static TestParametersCollection data() {
50 return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
51 }
52
53 @Test
Søren Gjesse12a98922023-09-11 10:24:49 +020054 public void testNoGlobalSyntheticsConsumer() throws Exception {
55 D8TestBuilder builder =
56 testForD8(parameters.getBackend())
Clément Béra0f1f4f72024-09-11 10:21:14 +020057 .addStrippedOuter(getClass())
58 .addProgramClassesAndInnerClasses(RecordWithMembers.class)
59 .addClasspathClassesAndInnerClasses(SimpleRecord.class)
Søren Gjesse12a98922023-09-11 10:24:49 +020060 .setMinApi(parameters)
61 .setIntermediate(true);
Clément Bérad8769c22024-08-15 08:43:58 +020062 if (isRecordsFullyDesugaredForD8(parameters)) {
Søren Gjesse12a98922023-09-11 10:24:49 +020063 assertThrows(
64 CompilationFailedException.class,
65 () ->
66 builder.compileWithExpectedDiagnostics(
67 diagnostics ->
68 diagnostics
69 .assertOnlyErrors()
70 .assertErrorsMatch(
71 diagnosticType(MissingGlobalSyntheticsConsumerDiagnostic.class))));
Søren Gjessefb2b4ba2023-11-21 09:11:14 +010072 } else {
73 builder.compile();
Søren Gjesse12a98922023-09-11 10:24:49 +020074 }
Clément Béra8eca1092021-03-15 14:41:02 +000075 }
76
77 @Test
78 public void testMergeDesugaredInputs() throws Exception {
Clément Béra528ce252022-06-28 15:05:59 +020079 testMergeDesugaredInputsDexPerClass(false);
80 }
81
82 @Test
83 public void testMergeDesugaredInputsDexPerClass() throws Exception {
84 Assume.assumeTrue("CF is already run from the other test", parameters.isDexRuntime());
85 testMergeDesugaredInputsDexPerClass(true);
86 }
87
88 private void testMergeDesugaredInputsDexPerClass(boolean filePerClass) throws Exception {
Ian Zernye736dec2022-05-05 20:07:45 +020089 GlobalSyntheticsTestingConsumer globals1 = new GlobalSyntheticsTestingConsumer();
Clément Béra8eca1092021-03-15 14:41:02 +000090 Path output1 =
91 testForD8(parameters.getBackend())
Clément Béra0f1f4f72024-09-11 10:21:14 +020092 .addStrippedOuter(getClass())
93 .addProgramClassesAndInnerClasses(RecordWithMembers.class)
94 .addClasspathClassesAndInnerClasses(SimpleRecord.class)
Christoffer Quist Adamsend4d93602023-02-21 15:28:42 +010095 .setMinApi(parameters)
Ian Zernyef267142022-03-28 12:40:03 +020096 .setIntermediate(true)
Clément Béra528ce252022-06-28 15:05:59 +020097 .applyIf(
98 filePerClass && !parameters.isCfRuntime(),
99 b -> b.setOutputMode(OutputMode.DexFilePerClassFile))
Ian Zernyef267142022-03-28 12:40:03 +0200100 .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals1))
Clément Béra8eca1092021-03-15 14:41:02 +0000101 .compile()
Ian Zernyef267142022-03-28 12:40:03 +0200102 .inspect(this::assertDoesNotHaveRecordTag)
Clément Béra8eca1092021-03-15 14:41:02 +0000103 .writeToZip();
Ian Zernyef267142022-03-28 12:40:03 +0200104
Ian Zernye736dec2022-05-05 20:07:45 +0200105 GlobalSyntheticsTestingConsumer globals2 = new GlobalSyntheticsTestingConsumer();
Clément Béra8eca1092021-03-15 14:41:02 +0000106 Path output2 =
107 testForD8(parameters.getBackend())
Clément Béra0f1f4f72024-09-11 10:21:14 +0200108 .addProgramClassesAndInnerClasses(SimpleRecord.class)
109 .addClasspathClassesAndInnerClasses(getClass())
Christoffer Quist Adamsend4d93602023-02-21 15:28:42 +0100110 .setMinApi(parameters)
Ian Zernyef267142022-03-28 12:40:03 +0200111 .setIntermediate(true)
Clément Béra528ce252022-06-28 15:05:59 +0200112 .applyIf(
113 filePerClass && !parameters.isCfRuntime(),
114 b -> b.setOutputMode(OutputMode.DexFilePerClassFile))
Ian Zernyef267142022-03-28 12:40:03 +0200115 .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals2))
Clément Béra8eca1092021-03-15 14:41:02 +0000116 .compile()
Ian Zernyef267142022-03-28 12:40:03 +0200117 .inspect(this::assertDoesNotHaveRecordTag)
Clément Béra8eca1092021-03-15 14:41:02 +0000118 .writeToZip();
Ian Zernyef267142022-03-28 12:40:03 +0200119
Clément Bérad8769c22024-08-15 08:43:58 +0200120 assertTrue(isRecordsFullyDesugaredForD8(parameters) ^ !globals1.hasGlobals());
121 assertTrue(isRecordsFullyDesugaredForD8(parameters) ^ !globals2.hasGlobals());
Ian Zernyef267142022-03-28 12:40:03 +0200122
Clément Béra8eca1092021-03-15 14:41:02 +0000123 D8TestCompileResult result =
124 testForD8(parameters.getBackend())
125 .addProgramFiles(output1, output2)
Ian Zernye736dec2022-05-05 20:07:45 +0200126 .apply(
127 b ->
128 b.getBuilder()
Clément Béra528ce252022-06-28 15:05:59 +0200129 .addGlobalSyntheticsResourceProviders(globals1.getProviders())
130 .addGlobalSyntheticsResourceProviders(globals2.getProviders()))
Christoffer Quist Adamsend4d93602023-02-21 15:28:42 +0100131 .setMinApi(parameters)
Ian Zernyef267142022-03-28 12:40:03 +0200132 .compile()
133 .inspect(this::assertHasRecordTag);
134
Søren Gjesse12a98922023-09-11 10:24:49 +0200135 result
Clément Béra0f1f4f72024-09-11 10:21:14 +0200136 .run(parameters.getRuntime(), RecordWithMembers.class)
Søren Gjesse12a98922023-09-11 10:24:49 +0200137 .applyIf(
Clément Bérad8769c22024-08-15 08:43:58 +0200138 isRecordsFullyDesugaredForD8(parameters)
Søren Gjessefb2b4ba2023-11-21 09:11:14 +0100139 || runtimeWithRecordsSupport(parameters.getRuntime()),
140 r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1),
141 r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
Søren Gjesse12a98922023-09-11 10:24:49 +0200142 result
Clément Béra0f1f4f72024-09-11 10:21:14 +0200143 .run(parameters.getRuntime(), SimpleRecord.class)
Søren Gjesse12a98922023-09-11 10:24:49 +0200144 .applyIf(
Clément Bérad8769c22024-08-15 08:43:58 +0200145 isRecordsFullyDesugaredForD8(parameters)
Søren Gjessefb2b4ba2023-11-21 09:11:14 +0100146 || runtimeWithRecordsSupport(parameters.getRuntime()),
147 r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2),
148 r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
Clément Béra8eca1092021-03-15 14:41:02 +0000149 }
150
151 @Test
152 public void testMergeDesugaredAndNonDesugaredInputs() throws Exception {
Ian Zernye736dec2022-05-05 20:07:45 +0200153 GlobalSyntheticsTestingConsumer globals1 = new GlobalSyntheticsTestingConsumer();
Clément Béra8eca1092021-03-15 14:41:02 +0000154 Path output1 =
155 testForD8(parameters.getBackend())
Clément Béra0f1f4f72024-09-11 10:21:14 +0200156 .addProgramClassesAndInnerClasses(RecordWithMembers.class)
157 .addClasspathClasses(getClass())
158 .addClasspathClassesAndInnerClasses(SimpleRecord.class)
Christoffer Quist Adamsend4d93602023-02-21 15:28:42 +0100159 .setMinApi(parameters)
Ian Zernyef267142022-03-28 12:40:03 +0200160 .setIntermediate(true)
161 .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals1))
Clément Béra8eca1092021-03-15 14:41:02 +0000162 .compile()
163 .writeToZip();
Ian Zernyef267142022-03-28 12:40:03 +0200164
Clément Béra8eca1092021-03-15 14:41:02 +0000165 D8TestCompileResult result =
166 testForD8(parameters.getBackend())
Clément Béra0f1f4f72024-09-11 10:21:14 +0200167 .addStrippedOuter(getClass())
Clément Béra8eca1092021-03-15 14:41:02 +0000168 .addProgramFiles(output1)
Ian Zernye736dec2022-05-05 20:07:45 +0200169 .apply(
170 b -> b.getBuilder().addGlobalSyntheticsResourceProviders(globals1.getProviders()))
Clément Béra0f1f4f72024-09-11 10:21:14 +0200171 .addProgramClassesAndInnerClasses(SimpleRecord.class)
172 .addClasspathClassesAndInnerClasses(getClass())
Christoffer Quist Adamsend4d93602023-02-21 15:28:42 +0100173 .setMinApi(parameters)
Clément Béra8eca1092021-03-15 14:41:02 +0000174 .compile();
Søren Gjesse12a98922023-09-11 10:24:49 +0200175 result
Clément Béra0f1f4f72024-09-11 10:21:14 +0200176 .run(parameters.getRuntime(), RecordWithMembers.class)
Søren Gjesse12a98922023-09-11 10:24:49 +0200177 .applyIf(
Clément Bérad8769c22024-08-15 08:43:58 +0200178 isRecordsFullyDesugaredForD8(parameters)
Søren Gjessefb2b4ba2023-11-21 09:11:14 +0100179 || runtimeWithRecordsSupport(parameters.getRuntime()),
180 r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1),
181 r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
Søren Gjesse12a98922023-09-11 10:24:49 +0200182 result
Clément Béra0f1f4f72024-09-11 10:21:14 +0200183 .run(parameters.getRuntime(), SimpleRecord.class)
Søren Gjesse12a98922023-09-11 10:24:49 +0200184 .applyIf(
Clément Bérad8769c22024-08-15 08:43:58 +0200185 isRecordsFullyDesugaredForD8(parameters)
Søren Gjessefb2b4ba2023-11-21 09:11:14 +0100186 || runtimeWithRecordsSupport(parameters.getRuntime()),
187 r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2),
188 r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
Clément Béra8eca1092021-03-15 14:41:02 +0000189 }
Ian Zernyef267142022-03-28 12:40:03 +0200190
191 @Test
192 public void testMergeNonIntermediates() throws Exception {
193 Path output1 =
194 testForD8(parameters.getBackend())
Clément Béra0f1f4f72024-09-11 10:21:14 +0200195 .addStrippedOuter(getClass())
196 .addProgramClassesAndInnerClasses(RecordWithMembers.class)
197 .addClasspathClassesAndInnerClasses(SimpleRecord.class)
Christoffer Quist Adamsend4d93602023-02-21 15:28:42 +0100198 .setMinApi(parameters)
Ian Zernyef267142022-03-28 12:40:03 +0200199 .compile()
200 .inspect(this::assertHasRecordTag)
201 .writeToZip();
202
203 Path output2 =
204 testForD8(parameters.getBackend())
Clément Béra0f1f4f72024-09-11 10:21:14 +0200205 .addProgramClassesAndInnerClasses(SimpleRecord.class)
206 .addClasspathClassesAndInnerClasses(getClass())
Christoffer Quist Adamsend4d93602023-02-21 15:28:42 +0100207 .setMinApi(parameters)
Ian Zernyef267142022-03-28 12:40:03 +0200208 .compile()
209 .inspect(this::assertHasRecordTag)
210 .writeToZip();
211
Clément Bérad8769c22024-08-15 08:43:58 +0200212 if (!isRecordsFullyDesugaredForD8(parameters)) {
Søren Gjesse12a98922023-09-11 10:24:49 +0200213 D8TestCompileResult result =
214 testForD8(parameters.getBackend())
215 .addProgramFiles(output1, output2)
216 .setMinApi(parameters)
217 .compile();
218 result
Clément Béra0f1f4f72024-09-11 10:21:14 +0200219 .run(parameters.getRuntime(), RecordWithMembers.class)
Søren Gjesse12a98922023-09-11 10:24:49 +0200220 .applyIf(
Clément Bérad8769c22024-08-15 08:43:58 +0200221 isRecordsFullyDesugaredForD8(parameters)
Søren Gjessefb2b4ba2023-11-21 09:11:14 +0100222 || runtimeWithRecordsSupport(parameters.getRuntime()),
223 r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1),
224 r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
Søren Gjesse12a98922023-09-11 10:24:49 +0200225 result
Clément Béra0f1f4f72024-09-11 10:21:14 +0200226 .run(parameters.getRuntime(), SimpleRecord.class)
Søren Gjesse12a98922023-09-11 10:24:49 +0200227 .applyIf(
Clément Bérad8769c22024-08-15 08:43:58 +0200228 isRecordsFullyDesugaredForD8(parameters)
Søren Gjessefb2b4ba2023-11-21 09:11:14 +0100229 || runtimeWithRecordsSupport(parameters.getRuntime()),
230 r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2),
231 r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
Søren Gjesse12a98922023-09-11 10:24:49 +0200232 } else {
233 assertThrows(
234 CompilationFailedException.class,
235 () ->
236 testForD8(parameters.getBackend())
237 .addProgramFiles(output1, output2)
238 .setMinApi(parameters)
239 .compileWithExpectedDiagnostics(
240 diagnostics ->
241 diagnostics
242 .assertOnlyErrors()
243 .assertErrorsMatch(diagnosticType(DuplicateTypesDiagnostic.class))));
244 }
Ian Zernyef267142022-03-28 12:40:03 +0200245 }
246
247 private void assertHasRecordTag(CodeInspector inspector) {
248 // Note: this should be asserting on record tag.
Søren Gjessefb2b4ba2023-11-21 09:11:14 +0100249 assertThat(
Clément Bérad8769c22024-08-15 08:43:58 +0200250 inspector.clazz("java.lang.Record"), isPresentIf(isRecordsFullyDesugaredForD8(parameters)));
Ian Zernyef267142022-03-28 12:40:03 +0200251 }
252
253 private void assertDoesNotHaveRecordTag(CodeInspector inspector) {
254 // Note: this should be asserting on record tag.
255 assertThat(inspector.clazz("java.lang.Record"), isAbsent());
256 }
Clément Béra0f1f4f72024-09-11 10:21:14 +0200257
258 public class RecordWithMembers {
259 record PersonWithConstructors(String name, int age) {
260
261 public PersonWithConstructors(String name, int age) {
262 this.name = name + "X";
263 this.age = age;
264 }
265
266 public PersonWithConstructors(String name) {
267 this(name, -1);
268 }
269 }
270
271 record PersonWithMethods(String name, int age) {
272 public static void staticPrint() {
273 System.out.println("print");
274 }
275
276 @Override
277 public String toString() {
278 return name + age;
279 }
280 }
281
282 record PersonWithFields(String name, int age) {
283
284 // Extra instance fields are not allowed on records.
285 public static String globalName;
286 }
287
288 public static void main(String[] args) {
289 personWithConstructorTest();
290 personWithMethodsTest();
291 personWithFieldsTest();
292 }
293
294 private static void personWithConstructorTest() {
295 PersonWithConstructors bob = new PersonWithConstructors("Bob", 43);
296 System.out.println(bob.name());
297 System.out.println(bob.age());
298 PersonWithConstructors felix = new PersonWithConstructors("Felix");
299 System.out.println(felix.name());
300 System.out.println(felix.age());
301 }
302
303 private static void personWithMethodsTest() {
304 PersonWithMethods.staticPrint();
305 PersonWithMethods bob = new PersonWithMethods("Bob", 43);
306 System.out.println(bob.toString());
307 }
308
309 private static void personWithFieldsTest() {
310 PersonWithFields.globalName = "extra";
311 System.out.println(PersonWithFields.globalName);
312 }
313 }
314
315 public class SimpleRecord {
316
317 record Person(String name, int age) {}
318
319 public static void main(String[] args) {
320 Person janeDoe = new Person("Jane Doe", 42);
321 System.out.println(janeDoe.name());
322 System.out.println(janeDoe.age());
323
324 // Test equals with self.
325 System.out.println(janeDoe.equals(janeDoe));
326
327 // Test equals with structurally equals Person.
328 Person otherJaneDoe = new Person("Jane Doe", 42);
329 System.out.println(janeDoe.equals(otherJaneDoe));
330 System.out.println(otherJaneDoe.equals(janeDoe));
331
332 // Test equals with not-structually equals Person.
333 Person johnDoe = new Person("John Doe", 42);
334 System.out.println(janeDoe.equals(johnDoe));
335 System.out.println(johnDoe.equals(janeDoe));
336
337 // Test equals with Object and null.
338 System.out.println(janeDoe.equals(new Object()));
339 System.out.println(janeDoe.equals(null));
340 }
341 }
Clément Béra8eca1092021-03-15 14:41:02 +0000342}