Merge commit '7a230461925d65a826100945f01a8b6f86ab3e66' into dev-release Change-Id: Icae05d3e143368fcd9da4475dca8429568a1ae12
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java index 10f0699..cee4f28 100644 --- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java +++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -22,7 +22,6 @@ import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Pair; -import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.structural.StructuralItem; import com.android.tools.r8.utils.structural.StructuralMapping; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -152,12 +151,7 @@ DexString descriptor = annotationType.getDescriptor(); if (descriptor.startsWith(factory.dalvikAnnotationPrefix)) { if (descriptor.startsWith(factory.dalvikAnnotationCodegenCovariantReturnTypePrefix)) { - if (options.processCovariantReturnTypeAnnotations) { - return true; - } - throw options.reporter.fatalError( - new StringDiagnostic( - "Unexpected @CovariantReturnType annotation in non-platform build")); + return options.processCovariantReturnTypeAnnotations; } return descriptor.startsWith(factory.dalvikAnnotationOptimizationPrefix); }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java index adb0992..9ce000f 100644 --- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java +++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -70,6 +70,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -649,14 +650,17 @@ private void dumpAndroidResourcesProvider( AndroidResourceProvider androidResourceProvider, ZipOutputStream out, String name) throws IOException, ResourceException { + Set<String> seen = new HashSet<>(); try (ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream()) { try (ZipOutputStream archiveOutputStream = new ZipOutputStream(archiveByteStream)) { for (AndroidResourceInput androidResource : androidResourceProvider.getAndroidResources()) { - writeToZipStream( - archiveOutputStream, - androidResource.getPath().location(), - androidResource.getByteStream().readAllBytes(), - ZipEntry.DEFLATED); + if (seen.add(androidResource.getPath().location())) { + writeToZipStream( + archiveOutputStream, + androidResource.getPath().location(), + androidResource.getByteStream().readAllBytes(), + ZipEntry.DEFLATED); + } } } writeToZipStream(out, name, archiveByteStream.toByteArray(), ZipEntry.DEFLATED);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java b/src/test/examplesJava17/records/RecordBlogTest.java similarity index 84% rename from src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java rename to src/test/examplesJava17/records/RecordBlogTest.java index 10f10f4..1128f07 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java +++ b/src/test/examplesJava17/records/RecordBlogTest.java
@@ -2,11 +2,12 @@ // 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.desugar.records; +package records; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -26,9 +27,6 @@ @RunWith(Parameterized.class) public class RecordBlogTest extends TestBase { - private static final String RECORD_NAME = "RecordBlog"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); private static final String REFERENCE_OUTPUT_FORMAT = "Person[name=%s, age=42]"; private static final String CLASS = "records.RecordBlog$Person"; private static final Map<String, String> KEEP_RULE_TO_OUTPUT_FORMAT = @@ -71,17 +69,17 @@ public void testReference() throws Exception { assumeTrue(isCfRuntimeWithNativeRecordSupport()); testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) + .addProgramClassesAndInnerClasses(RecordBlog.class) + .run(parameters.getRuntime(), RecordBlog.class) .assertSuccessWithOutput(computeOutput(REFERENCE_OUTPUT_FORMAT)); } @Test public void testD8() throws Exception { testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addProgramClassesAndInnerClasses(RecordBlog.class) .setMinApi(parameters) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordBlog.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), @@ -99,19 +97,19 @@ try { R8FullTestBuilder builder = testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addProgramClassesAndInnerClasses(RecordBlog.class) .setMinApi(parameters) .addKeepRules(kr) - .addKeepMainRule(MAIN_TYPE); + .addKeepMainRule(RecordBlog.class); String res; if (parameters.isCfRuntime()) { res = builder - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .run(parameters.getRuntime(), MAIN_TYPE) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) + .run(parameters.getRuntime(), RecordBlog.class) .getStdOut(); } else { - res = builder.run(parameters.getRuntime(), MAIN_TYPE).getStdOut(); + res = builder.run(parameters.getRuntime(), RecordBlog.class).getStdOut(); } results.put(kr, res); } catch (Exception e) {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordClasspathTest.java b/src/test/examplesJava17/records/RecordClasspathTest.java similarity index 60% rename from src/test/java/com/android/tools/r8/desugar/records/RecordClasspathTest.java rename to src/test/examplesJava17/records/RecordClasspathTest.java index a431cf2..867d632 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordClasspathTest.java +++ b/src/test/examplesJava17/records/RecordClasspathTest.java
@@ -2,21 +2,27 @@ // 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.desugar.records; +package records; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.OutputMode; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.transformers.ClassFileTransformer.FieldPredicate; +import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.io.IOException; +import java.nio.file.Files; import java.util.List; import org.junit.Assume; import org.junit.Test; @@ -30,8 +36,6 @@ @RunWith(Parameterized.class) public class RecordClasspathTest extends TestBase { - private static final String RECORD_NAME_1 = "RecordWithMembers"; - private static final byte[][] PROGRAM_DATA_1 = RecordTestUtils.getProgramData(RECORD_NAME_1); private static final String EXPECTED_RESULT = StringUtils.lines("Hello"); private final TestParameters parameters; @@ -53,8 +57,19 @@ BooleanUtils.values()); } - private byte[][] getClasspathData() { - return stripClasspath ? stripFields(PROGRAM_DATA_1) : PROGRAM_DATA_1; + private byte[][] getClasspathData() throws IOException { + byte[][] programData = + ToolHelper.getClassFilesForInnerClasses(getClass()).stream() + .map( + c -> { + try { + return Files.readAllBytes(c); + } catch (IOException e) { + throw new RuntimeException(e); + } + }) + .toArray(byte[][]::new); + return stripClasspath ? stripFields(programData) : programData; } private byte[][] stripFields(byte[][] programData1) { @@ -80,6 +95,7 @@ public void testD8() throws Exception { testForD8(parameters.getBackend()) .addProgramClasses(TestClass.class) + .addClasspathClasses(getClass()) .addClasspathClassFileData(getClasspathData()) .setMinApi(parameters) .compile() @@ -94,6 +110,7 @@ Assume.assumeFalse(parameters.isCfRuntime()); testForD8(parameters.getBackend()) .addProgramClasses(TestClass.class) + .addClasspathClasses(getClass()) .addClasspathClassFileData(getClasspathData()) .setMinApi(parameters) .setIntermediate(true) @@ -112,12 +129,20 @@ R8FullTestBuilder builder = testForR8(parameters.getBackend()) .addProgramClasses(TestClass.class) + .addClasspathClassFileData( + TestBase.transformer(getClass()) + .removeFields(FieldPredicate.all()) + .removeMethods(MethodPredicate.all()) + .removeAllAnnotations() + .setSuper(descriptor(Object.class)) + .setImplements() + .transform()) .addClasspathClassFileData(getClasspathData()) .setMinApi(parameters) .addKeepMainRule(TestClass.class); if (parameters.isCfRuntime()) { builder - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .compile() .inspect(this::assertNoRecord) .inspect(RecordTestUtils::assertRecordsAreRecords) @@ -140,4 +165,62 @@ System.out.println("Hello"); } } + + public class RecordWithMembers { + + record PersonWithConstructors(String name, int age) { + + public PersonWithConstructors(String name, int age) { + this.name = name + "X"; + this.age = age; + } + + public PersonWithConstructors(String name) { + this(name, -1); + } + } + + record PersonWithMethods(String name, int age) { + public static void staticPrint() { + System.out.println("print"); + } + + @Override + public String toString() { + return name + age; + } + } + + record PersonWithFields(String name, int age) { + + // Extra instance fields are not allowed on records. + public static String globalName; + } + + public static void main(String[] args) { + personWithConstructorTest(); + personWithMethodsTest(); + personWithFieldsTest(); + } + + private static void personWithConstructorTest() { + PersonWithConstructors bob = new PersonWithConstructors("Bob", 43); + System.out.println(bob.name()); + System.out.println(bob.age()); + PersonWithConstructors felix = new PersonWithConstructors("Felix"); + System.out.println(felix.name()); + System.out.println(felix.age()); + } + + private static void personWithMethodsTest() { + PersonWithMethods.staticPrint(); + PersonWithMethods bob = new PersonWithMethods("Bob", 43); + System.out.println(bob.toString()); + } + + private static void personWithFieldsTest() { + PersonWithFields.globalName = "extra"; + System.out.println(PersonWithFields.globalName); + } + } }
diff --git a/src/test/examplesJava17/records/RecordComponentAnnotationsTest.java b/src/test/examplesJava17/records/RecordComponentAnnotationsTest.java new file mode 100644 index 0000000..8aba71c --- /dev/null +++ b/src/test/examplesJava17/records/RecordComponentAnnotationsTest.java
@@ -0,0 +1,476 @@ +// Copyright (c) 2023, 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 records; + +import static com.android.tools.r8.utils.codeinspector.AnnotationMatchers.hasAnnotationTypes; +import static com.android.tools.r8.utils.codeinspector.AnnotationMatchers.hasElements; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestRuntime.CfVm; +import com.android.tools.r8.TestShrinkerBuilder; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.Pair; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.FieldSubject; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.lang.reflect.RecordComponent; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +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 RecordComponentAnnotationsTest extends TestBase { + + private static final String JVM_UNTIL_20_EXPECTED_RESULT = + StringUtils.lines( + "Jane Doe", + "42", + "true", + "2", + "name", + "java.lang.String", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"a\")", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(\"c\")", + "age", + "int", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"x\")", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(\"z\")", + "2", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"x\")", + "@records.RecordComponentAnnotationsTest$AnnotationFieldOnly(\"y\")", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"a\")", + "@records.RecordComponentAnnotationsTest$AnnotationFieldOnly(\"b\")"); + private static final String JVM_FROM_21_EXPECTED_RESULT = + JVM_UNTIL_20_EXPECTED_RESULT.replaceAll( + "records.RecordComponentAnnotationsTest\\$Annotation", + "records.RecordComponentAnnotationsTest.Annotation"); + private static final String ART_EXPECTED_RESULT = + StringUtils.lines( + "Jane Doe", + "42", + "true", + "2", + "name", + "java.lang.String", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=a)", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(value=c)", + "age", + "int", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=x)", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(value=z)", + "2", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=x)", + "@records.RecordComponentAnnotationsTest$AnnotationFieldOnly(value=y)", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=a)", + "@records.RecordComponentAnnotationsTest$AnnotationFieldOnly(value=b)"); + private static final String JVM_EXPECTED_RESULT_R8 = + StringUtils.lines( + "Jane Doe", + "42", + "true", + "2", + "a", + "java.lang.String", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"a\")", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(\"c\")", + "b", + "int", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"x\")", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(\"z\")", + "2", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"a\")", + "@records.RecordComponentAnnotationsTest$AnnotationFieldOnly(\"b\")", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"x\")", + "@records.RecordComponentAnnotationsTest$AnnotationFieldOnly(\"y\")"); + private static final String ART_EXPECTED_RESULT_R8 = + StringUtils.lines( + "Jane Doe", + "42", + "true", + "2", + "a", + "java.lang.String", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=a)", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(value=c)", + "b", + "int", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=x)", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(value=z)", + "2", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=a)", + "@records.RecordComponentAnnotationsTest$AnnotationFieldOnly(value=b)", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=x)", + "@records.RecordComponentAnnotationsTest$AnnotationFieldOnly(value=y)"); + private static final String JVM_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS = + StringUtils.lines( + "Jane Doe", + "42", + "true", + "2", + "a", + "java.lang.String", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"a\")", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(\"c\")", + "b", + "int", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(\"x\")", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(\"z\")", + "2", + "0", + "0"); + private static final String ART_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS = + StringUtils.lines( + "Jane Doe", + "42", + "true", + "2", + "a", + "java.lang.String", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=a)", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(value=c)", + "b", + "int", + "true", + "2", + "@records.RecordComponentAnnotationsTest$Annotation(value=x)", + "@records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly(value=z)", + "2", + "0", + "0"); + private static final String EXPECTED_RESULT_DESUGARED_RECORD_SUPPORT = + StringUtils.lines("Jane Doe", "42", "false"); + private static final String EXPECTED_RESULT_DESUGARED_NO_RECORD_SUPPORT = + StringUtils.lines("Jane Doe", "42", "Class.isRecord not present"); + + @Parameter(0) + public TestParameters parameters; + + @Parameter(1) + public Boolean keepAnnotations; + + // Enable once records are no longer partially desugared on platform. + public boolean recordDesugaringIsOffOnDex = false; + + @Parameters(name = "{0}, keepAnnotations: {1}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters() + .withDexRuntimesAndAllApiLevels() + .withCfRuntimesStartingFromIncluding(CfVm.JDK17) + .withAllApiLevelsAlsoForCf() + .build(), + BooleanUtils.values()); + } + + @Test + public void testJvm() throws Exception { + parameters.assumeJvmTestParameters(); + assumeTrue(keepAnnotations); + testForJvm(parameters) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), RecordWithAnnotations.class) + .assertSuccessWithOutput( + parameters.getRuntime().asCf().getVm().isLessThanOrEqualTo(CfVm.JDK20) + ? JVM_UNTIL_20_EXPECTED_RESULT + : JVM_FROM_21_EXPECTED_RESULT); + } + + @Test + public void testDesugaring() throws Exception { + parameters.assumeDexRuntime(); + assumeTrue(keepAnnotations); + testForDesugaring(parameters) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), RecordWithAnnotations.class) + .applyIf( + parameters.isDexRuntime(), + r -> + r.assertSuccessWithOutput( + runtimeWithRecordsSupport(parameters.getRuntime()) + ? EXPECTED_RESULT_DESUGARED_RECORD_SUPPORT + : EXPECTED_RESULT_DESUGARED_NO_RECORD_SUPPORT), + r -> + r.assertSuccessWithOutput(ART_EXPECTED_RESULT) + .inspect( + inspector -> { + ClassSubject person = + inspector.clazz("records.RecordComponentAnnotationsTest$Person"); + FieldSubject name = person.uniqueFieldWithOriginalName("name"); + assertThat(name, isPresentAndNotRenamed()); + FieldSubject age = person.uniqueFieldWithOriginalName("age"); + assertThat(age, isPresentAndNotRenamed()); + assertEquals(2, person.getFinalRecordComponents().size()); + + assertEquals( + name.getFinalName(), + person.getFinalRecordComponents().get(0).getName()); + assertTrue( + person + .getFinalRecordComponents() + .get(0) + .getType() + .is("java.lang.String")); + assertNull(person.getFinalRecordComponents().get(0).getSignature()); + assertEquals( + 2, person.getFinalRecordComponents().get(0).getAnnotations().size()); + assertThat( + person.getFinalRecordComponents().get(0).getAnnotations(), + hasAnnotationTypes( + inspector.getTypeSubject( + "records.RecordComponentAnnotationsTest$Annotation"), + inspector.getTypeSubject( + "records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly"))); + assertThat( + person.getFinalRecordComponents().get(0).getAnnotations().get(0), + hasElements(new Pair<>("value", "a"))); + assertThat( + person.getFinalRecordComponents().get(0).getAnnotations().get(1), + hasElements(new Pair<>("value", "c"))); + + assertEquals( + age.getFinalName(), + person.getFinalRecordComponents().get(1).getName()); + assertTrue(person.getFinalRecordComponents().get(1).getType().is("int")); + assertNull(person.getFinalRecordComponents().get(1).getSignature()); + assertEquals( + 2, person.getFinalRecordComponents().get(1).getAnnotations().size()); + assertThat( + person.getFinalRecordComponents().get(1).getAnnotations(), + hasAnnotationTypes( + inspector.getTypeSubject( + "records.RecordComponentAnnotationsTest$Annotation"), + inspector.getTypeSubject( + "records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly"))); + assertThat( + person.getFinalRecordComponents().get(1).getAnnotations().get(0), + hasElements(new Pair<>("value", "x"))); + assertThat( + person.getFinalRecordComponents().get(1).getAnnotations().get(1), + hasElements(new Pair<>("value", "z"))); + })); + } + + @Test + public void testR8() throws Exception { + parameters.assumeR8TestParameters(); + testForR8(parameters.getBackend()) + .addInnerClassesAndStrippedOuter(getClass()) + .addLibraryFiles(ToolHelper.getAndroidJar(35)) + .addKeepMainRule(RecordWithAnnotations.class) + .addKeepClassAndMembersRulesWithAllowObfuscation( + "records.RecordComponentAnnotationsTest$Person") + .addKeepClassAndMembersRules( + "records.RecordComponentAnnotationsTest$Annotation", + "records.RecordComponentAnnotationsTest$AnnotationFieldOnly", + "records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly") + .applyIf(keepAnnotations, TestShrinkerBuilder::addKeepRuntimeVisibleAnnotations) + .setMinApi(parameters) + .compile() + .inspect( + inspector -> { + ClassSubject person = + inspector.clazz("records.RecordComponentAnnotationsTest$Person"); + FieldSubject name = person.uniqueFieldWithOriginalName("name"); + FieldSubject age = person.uniqueFieldWithOriginalName("age"); + if (parameters.isCfRuntime()) { + assertEquals(2, person.getFinalRecordComponents().size()); + + assertEquals( + name.getFinalName(), person.getFinalRecordComponents().get(0).getName()); + assertTrue( + person.getFinalRecordComponents().get(0).getType().is("java.lang.String")); + assertNull(person.getFinalRecordComponents().get(0).getSignature()); + assertEquals(2, person.getFinalRecordComponents().get(0).getAnnotations().size()); + assertThat( + person.getFinalRecordComponents().get(0).getAnnotations(), + hasAnnotationTypes( + inspector.getTypeSubject( + "records.RecordComponentAnnotationsTest$Annotation"), + inspector.getTypeSubject( + "records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly"))); + assertThat( + person.getFinalRecordComponents().get(0).getAnnotations().get(0), + hasElements(new Pair<>("value", "a"))); + assertThat( + person.getFinalRecordComponents().get(0).getAnnotations().get(1), + hasElements(new Pair<>("value", "c"))); + + assertEquals( + age.getFinalName(), person.getFinalRecordComponents().get(1).getName()); + assertTrue(person.getFinalRecordComponents().get(1).getType().is("int")); + assertNull(person.getFinalRecordComponents().get(1).getSignature()); + assertEquals(2, person.getFinalRecordComponents().get(1).getAnnotations().size()); + assertThat( + person.getFinalRecordComponents().get(1).getAnnotations(), + hasAnnotationTypes( + inspector.getTypeSubject( + "records.RecordComponentAnnotationsTest$Annotation"), + inspector.getTypeSubject( + "records.RecordComponentAnnotationsTest$AnnotationRecordComponentOnly"))); + assertThat( + person.getFinalRecordComponents().get(1).getAnnotations().get(0), + hasElements(new Pair<>("value", "x"))); + assertThat( + person.getFinalRecordComponents().get(1).getAnnotations().get(1), + hasElements(new Pair<>("value", "z"))); + } else { + assertEquals(0, person.getFinalRecordComponents().size()); + } + }) + .run(parameters.getRuntime(), RecordWithAnnotations.class) + .applyIf( + // TODO(b/274888318): EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS still has component + // annotations. + parameters.isCfRuntime(), + r -> + r.assertSuccessWithOutput( + keepAnnotations + ? JVM_EXPECTED_RESULT_R8 + : JVM_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS), + recordDesugaringIsOffOnDex, + r -> + r.assertSuccessWithOutput( + keepAnnotations + ? ART_EXPECTED_RESULT_R8 + : ART_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS), + r -> + r.assertSuccessWithOutput( + runtimeWithRecordsSupport(parameters.getRuntime()) + ? EXPECTED_RESULT_DESUGARED_RECORD_SUPPORT + : EXPECTED_RESULT_DESUGARED_NO_RECORD_SUPPORT)); + } + + @Target({ElementType.FIELD, ElementType.RECORD_COMPONENT}) + @Retention(RetentionPolicy.RUNTIME) + @interface Annotation { + + String value(); + } + + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.RUNTIME) + @interface AnnotationFieldOnly { + + String value(); + } + + @Target(ElementType.RECORD_COMPONENT) + @Retention(RetentionPolicy.RUNTIME) + @interface AnnotationRecordComponentOnly { + + String value(); + } + + record Person( + @Annotation("a") @AnnotationFieldOnly("b") @AnnotationRecordComponentOnly("c") String name, + @Annotation("x") @AnnotationFieldOnly("y") @AnnotationRecordComponentOnly("z") int age) {} + + public static class RecordWithAnnotations { + + public static void main(String[] args) { + Person janeDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.name()); + System.out.println(janeDoe.age()); + try { + Class.class.getDeclaredMethod("isRecord"); + } catch (NoSuchMethodException e) { + System.out.println("Class.isRecord not present"); + return; + } + System.out.println(Person.class.isRecord()); + if (Person.class.isRecord()) { + System.out.println(Person.class.getRecordComponents().length); + for (int i = 0; i < Person.class.getRecordComponents().length; i++) { + RecordComponent c = Person.class.getRecordComponents()[i]; + System.out.println(c.getName()); + System.out.println(c.getType().getName()); + System.out.println(c.getGenericSignature() == null); + System.out.println(c.getAnnotations().length); + // Collect and sort the annotations, as the order is not deterministic on Art (tested + // on Art 14 Beta 3). + List<String> annotations = new ArrayList<>(); + for (int j = 0; j < c.getAnnotations().length; j++) { + annotations.add(c.getAnnotations()[j].toString()); + } + annotations.sort(Comparator.naturalOrder()); + for (int j = 0; j < annotations.size(); j++) { + System.out.println(annotations.get(j)); + } + } + System.out.println(Person.class.getDeclaredFields().length); + List<Field> fields = new ArrayList<>(); + for (int i = 0; i < Person.class.getDeclaredFields().length; i++) { + fields.add(Person.class.getDeclaredFields()[i]); + } + fields.sort( + new Comparator<Field>() { + @Override + public int compare(Field o1, Field o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + for (int i = 0; i < fields.size(); i++) { + Field f = fields.get(i); + System.out.println(f.getDeclaredAnnotations().length); + List<String> annotations = new ArrayList<>(); + for (int j = 0; j < f.getDeclaredAnnotations().length; j++) { + annotations.add(f.getAnnotations()[j].toString()); + } + annotations.sort(Comparator.naturalOrder()); + for (int j = 0; j < annotations.size(); j++) { + System.out.println(annotations.get(j)); + } + } + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java b/src/test/examplesJava17/records/RecordComponentSignatureTest.java similarity index 74% rename from src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java rename to src/test/examplesJava17/records/RecordComponentSignatureTest.java index 27e7e0b..d2d530b 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java +++ b/src/test/examplesJava17/records/RecordComponentSignatureTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2023, 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.desugar.records; +package records; import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static org.hamcrest.MatcherAssert.assertThat; @@ -13,10 +13,12 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.TestShrinkerBuilder; +import com.android.tools.r8.ToolHelper; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.FieldSubject; +import java.lang.reflect.RecordComponent; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,15 +29,10 @@ @RunWith(Parameterized.class) public class RecordComponentSignatureTest extends TestBase { - private static final String RECORD_NAME = "RecordWithSignature"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); private static final String EXPECTED_RESULT = StringUtils.lines( "Jane Doe", "42", - "Jane Doe", - "42", "true", "2", "name", @@ -46,12 +43,11 @@ "java.lang.Object", "TA;", "0"); - private static final String EXPECTED_RESULT_R8 = - StringUtils.lines("Jane Doe", "42", "Jane Doe", "42", "true", "0"); + private static final String EXPECTED_RESULT_R8 = StringUtils.lines("Jane Doe", "42", "true", "0"); private static final String EXPECTED_RESULT_DESUGARED_NO_NATIVE_RECORDS_SUPPORT = - StringUtils.lines("Jane Doe", "42", "Jane Doe", "42", "Class.isRecord not present"); + StringUtils.lines("Jane Doe", "42", "Class.isRecord not present"); private static final String EXPECTED_RESULT_DESUGARED_NATIVE_RECORD_SUPPORT = - StringUtils.lines("Jane Doe", "42", "Jane Doe", "42", "false"); + StringUtils.lines("Jane Doe", "42", "false"); @Parameter(0) public TestParameters parameters; @@ -75,8 +71,8 @@ parameters.assumeJvmTestParameters(); assumeTrue(keepSignatures); testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), RecordWithSignature.class) .assertSuccessWithOutput(EXPECTED_RESULT); } @@ -85,8 +81,8 @@ parameters.assumeDexRuntime(); assumeTrue(keepSignatures); testForDesugaring(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), RecordWithSignature.class) .applyIf( parameters.isCfRuntime(), r -> r.assertSuccessWithOutput(EXPECTED_RESULT), @@ -98,7 +94,7 @@ .inspect( inspector -> { ClassSubject person = - inspector.clazz("records.RecordWithSignature$Person"); + inspector.clazz("records.RecordComponentSignatureTest$Person"); if (parameters.isCfRuntime()) { assertEquals(2, person.getFinalRecordComponents().size()); @@ -138,22 +134,20 @@ public void testR8() throws Exception { parameters.assumeR8TestParameters(); testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - // TODO(b/231930852): Change to android.jar for Android U when that contains - // java.lang.Record. - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .addKeepMainRule(MAIN_TYPE) + .addInnerClassesAndStrippedOuter(getClass()) + .addLibraryFiles(ToolHelper.getAndroidJar(35)) + .addKeepMainRule(RecordWithSignature.class) .applyIf(keepSignatures, TestShrinkerBuilder::addKeepAttributeSignature) .setMinApi(parameters) .compile() .inspect( inspector -> { - ClassSubject person = inspector.clazz("records.RecordWithSignature$Person"); + ClassSubject person = inspector.clazz("records.RecordComponentSignatureTest$Person"); FieldSubject age = person.uniqueFieldWithOriginalName("age"); assertThat(age, isAbsent()); assertEquals(0, person.getFinalRecordComponents().size()); }) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordWithSignature.class) .applyIf( runtimeWithRecordsSupport(parameters.getRuntime()), r -> @@ -163,4 +157,32 @@ : EXPECTED_RESULT_R8), r -> r.assertSuccessWithOutput(EXPECTED_RESULT_DESUGARED_NO_NATIVE_RECORDS_SUPPORT)); } + + record Person<N extends CharSequence, A>(N name, A age) {} + + public class RecordWithSignature { + + public static void main(String[] args) { + Person<String, Integer> janeDoe = new Person<>("Jane Doe", 42); + System.out.println(janeDoe.name()); + System.out.println(janeDoe.age()); + try { + Class.class.getDeclaredMethod("isRecord"); + } catch (NoSuchMethodException e) { + System.out.println("Class.isRecord not present"); + return; + } + System.out.println(Person.class.isRecord()); + if (Person.class.isRecord()) { + System.out.println(Person.class.getRecordComponents().length); + for (int i = 0; i < Person.class.getRecordComponents().length; i++) { + RecordComponent c = Person.class.getRecordComponents()[i]; + System.out.println(c.getName()); + System.out.println(c.getType().getName()); + System.out.println(c.getGenericSignature()); + System.out.println(c.getAnnotations().length); + } + } + } + } }
diff --git a/src/test/examplesJava17/records/RecordInterface.java b/src/test/examplesJava17/records/RecordInterface.java deleted file mode 100644 index 8fc1e56..0000000 --- a/src/test/examplesJava17/records/RecordInterface.java +++ /dev/null
@@ -1,21 +0,0 @@ -// Copyright (c) 2023, 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 records; - -public class RecordInterface { - - interface Human { - default void printHuman() { - System.out.println("Human"); - } - } - - record Person(String name, int age) implements Human {} - - public static void main(String[] args) { - Person janeDoe = new Person("Jane Doe", 42); - janeDoe.printHuman(); - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java b/src/test/examplesJava17/records/RecordInterfaceTest.java similarity index 74% rename from src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java rename to src/test/examplesJava17/records/RecordInterfaceTest.java index 169ef16..d5144b0 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java +++ b/src/test/examplesJava17/records/RecordInterfaceTest.java
@@ -2,26 +2,31 @@ // 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.desugar.records; +package records; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.DesugarGraphTestConsumer; import com.android.tools.r8.GlobalSyntheticsConsumer; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.R8FullTestBuilder; 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.desugar.graph.DesugarGraphTestConsumer; +import com.android.tools.r8.ToolHelper; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.origin.PathOrigin; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,9 +36,6 @@ @RunWith(Parameterized.class) public class RecordInterfaceTest extends TestBase { - private static final String RECORD_NAME = "RecordInterface"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); private static final String EXPECTED_RESULT = StringUtils.lines("Human"); @Parameter(0) @@ -54,17 +56,17 @@ public void testReference() throws Exception { assumeTrue(isCfRuntimeWithNativeRecordSupport()); testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), RecordInterface.class) .assertSuccessWithOutput(EXPECTED_RESULT); } @Test public void testD8() throws Exception { testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordInterface.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), @@ -91,7 +93,7 @@ .setIncludeClassesChecksum(true) .compile() .assertNoMessages() - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordInterface.class) .assertSuccessWithOutput(EXPECTED_RESULT); assertNoEdgeToRecord(consumer); } @@ -117,7 +119,7 @@ .disableDesugaring() .compile() .assertNoMessages() - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordInterface.class) .assertSuccessWithOutput(EXPECTED_RESULT); assertNoEdgeToRecord(consumer); } @@ -128,12 +130,16 @@ DesugarGraphTestConsumer consumer = new DesugarGraphTestConsumer(); Path intermediate = testForD8(Backend.DEX) + .addStrippedOuter(getClass(), fake) .apply( b -> { - // We avoid unknown origin here since they are not allowed when using a Graph - // consumer. - for (byte[] programDatum : PROGRAM_DATA) { - b.getBuilder().addClassProgramData(programDatum, fake); + try { + for (Path file : + ToolHelper.getClassFilesForInnerClasses(ImmutableList.of(getClass()))) { + b.getBuilder().addClassProgramData(Files.readAllBytes(file), fake); + } + } catch (IOException e) { + throw new RuntimeException(e); } }) .setMinApi(parameters) @@ -149,7 +155,7 @@ } private void assertNoEdgeToRecord(DesugarGraphTestConsumer consumer) { - assertEquals(0, consumer.totalEdgeCount()); + Assert.assertEquals(0, consumer.totalEdgeCount()); } @Test @@ -158,18 +164,37 @@ assumeTrue(parameters.isDexRuntime() || isCfRuntimeWithNativeRecordSupport()); R8FullTestBuilder builder = testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE); + .addKeepMainRule(RecordInterface.class); if (parameters.isCfRuntime()) { builder - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .compile() .inspect(RecordTestUtils::assertRecordsAreRecords) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordInterface.class) .assertSuccessWithOutput(EXPECTED_RESULT); return; } - builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT); + builder + .run(parameters.getRuntime(), RecordInterface.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + interface Human { + + default void printHuman() { + System.out.println("Human"); + } + } + + record Person(String name, int age) implements Human {} + + public class RecordInterface { + + public static void main(String[] args) { + Person janeDoe = new Person("Jane Doe", 42); + janeDoe.printHuman(); + } } }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java b/src/test/examplesJava17/records/RecordInvokeCustomSplitDesugaringTest.java similarity index 67% rename from src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java rename to src/test/examplesJava17/records/RecordInvokeCustomSplitDesugaringTest.java index b782568..d797244 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java +++ b/src/test/examplesJava17/records/RecordInvokeCustomSplitDesugaringTest.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 com.android.tools.r8.desugar.records; +package records; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType; @@ -25,13 +25,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import records.RecordInvokeCustom.Empty; +import records.RecordInvokeCustom.Person; @RunWith(Parameterized.class) public class RecordInvokeCustomSplitDesugaringTest extends TestBase { - private static final String RECORD_NAME = "RecordInvokeCustom"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); private static final String EXPECTED_RESULT = StringUtils.lines( "%s[]", @@ -64,7 +63,7 @@ public void testD8() throws Exception { Path desugared = testForD8(Backend.CF) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) .compile() .writeToZip(); @@ -72,7 +71,7 @@ .addProgramFiles(desugared) .setMinApi(parameters) .compile() - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordInvokeCustom.class) .assertSuccessWithOutput(EXPECTED_RESULT_D8); } @@ -81,7 +80,7 @@ parameters.assumeR8TestParameters(); Path desugared = testForD8(Backend.CF) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) .compile() .writeToZip(); @@ -94,7 +93,7 @@ testForR8(parameters.getBackend()) .addProgramFiles(desugared) .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE) + .addKeepMainRule(RecordInvokeCustom.class) .allowDiagnosticMessages() .compileWithExpectedDiagnostics( // Class com.android.tools.r8.RecordTag in desugared input is seen as java.lang.Record @@ -119,10 +118,12 @@ }) .inspect( i -> { - minifiedNames[0] = extractSimpleFinalName(i, "records.RecordInvokeCustom$Empty"); - minifiedNames[1] = extractSimpleFinalName(i, "records.RecordInvokeCustom$Person"); + minifiedNames[0] = + extractSimpleFinalName(i, "records.RecordInvokeCustomSplitDesugaringTest$Empty"); + minifiedNames[1] = + extractSimpleFinalName(i, "records.RecordInvokeCustomSplitDesugaringTest$Person"); }) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordInvokeCustom.class) .assertSuccessWithOutput( String.format(EXPECTED_RESULT, minifiedNames[0], minifiedNames[1])); } @@ -131,4 +132,47 @@ String finalName = i.clazz(name).getFinalName(); return finalName.split("\\.")[1]; } + + record Empty() {} + + record Person(String name, int age) {} + + public class RecordInvokeCustom { + + public static void main(String[] args) { + emptyTest(); + equalityTest(); + toStringTest(); + } + + private static void emptyTest() { + Empty empty1 = new Empty(); + Empty empty2 = new Empty(); + System.out.println(empty1.toString()); + System.out.println(empty1.equals(empty2)); + System.out.println(empty1.hashCode() == empty2.hashCode()); + System.out.println(empty1.toString().equals(empty2.toString())); + } + + private static void toStringTest() { + Person janeDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.toString()); + } + + private static void equalityTest() { + Person jane1 = new Person("Jane Doe", 42); + Person jane2 = new Person("Jane Doe", 42); + String nonIdenticalString = "Jane " + (System.currentTimeMillis() > 0 ? "Doe" : "Zan"); + Person jane3 = new Person(nonIdenticalString, 42); + Person bob = new Person("Bob", 42); + Person youngJane = new Person("Jane Doe", 22); + System.out.println(jane1.equals(jane2)); + System.out.println(jane1.toString().equals(jane2.toString())); + System.out.println(nonIdenticalString == "Jane Doe"); // false. + System.out.println(nonIdenticalString.equals("Jane Doe")); // true. + System.out.println(jane1.equals(jane3)); + System.out.println(jane1.equals(bob)); + System.out.println(jane1.equals(youngJane)); + } + } }
diff --git a/src/test/examplesJava17/records/RecordInvokeCustomTest.java b/src/test/examplesJava17/records/RecordInvokeCustomTest.java new file mode 100644 index 0000000..ec3956a --- /dev/null +++ b/src/test/examplesJava17/records/RecordInvokeCustomTest.java
@@ -0,0 +1,140 @@ +// Copyright (c) 2021, 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 records; + +import com.android.tools.r8.R8FullTestBuilder; +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.desugar.LibraryFilesHelper; +import com.android.tools.r8.utils.StringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import records.RecordInvokeCustom.Empty; +import records.RecordInvokeCustom.Person; + +@RunWith(Parameterized.class) +public class RecordInvokeCustomTest extends TestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines( + "%s[]", + "true", + "true", + "true", + "true", + "true", + "false", + "true", + "true", + "false", + "false", + "%s[%s=Jane Doe, %s=42]"); + private static final String EXPECTED_RESULT_D8 = + String.format(EXPECTED_RESULT, "Empty", "Person", "name", "age"); + private static final String EXPECTED_RESULT_R8 = + String.format(EXPECTED_RESULT, "a", "b", "a", "b"); + + private final TestParameters parameters; + + public RecordInvokeCustomTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters() + .withCfRuntimesStartingFromIncluding(CfVm.JDK17) + .withDexRuntimes() + .withAllApiLevelsAlsoForCf() + .build(); + } + + @Test + public void testJvm() throws Exception { + parameters.assumeJvmTestParameters(); + testForJvm(parameters) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), RecordInvokeCustom.class) + .assertSuccessWithOutput(EXPECTED_RESULT_D8); + } + + @Test + public void testD8() throws Exception { + testForD8(parameters.getBackend()) + .addInnerClassesAndStrippedOuter(getClass()) + .setMinApi(parameters) + .compile() + .run(parameters.getRuntime(), RecordInvokeCustom.class) + .assertSuccessWithOutput(EXPECTED_RESULT_D8); + } + + @Test + public void testR8() throws Exception { + parameters.assumeR8TestParameters(); + R8FullTestBuilder builder = + testForR8(parameters.getBackend()) + .addInnerClassesAndStrippedOuter(getClass()) + .setMinApi(parameters) + .addKeepMainRule(RecordInvokeCustom.class); + if (parameters.isCfRuntime()) { + builder + .addLibraryFiles(LibraryFilesHelper.getJdk15LibraryFiles(temp)) + .compile() + .inspect(RecordTestUtils::assertRecordsAreRecords) + .run(parameters.getRuntime(), RecordInvokeCustom.class) + .assertSuccessWithOutput(EXPECTED_RESULT_R8); + return; + } + builder + .run(parameters.getRuntime(), RecordInvokeCustom.class) + .assertSuccessWithOutput(EXPECTED_RESULT_R8); + } + + record Empty() {} + + record Person(String name, int age) {} + + public class RecordInvokeCustom { + + public static void main(String[] args) { + emptyTest(); + equalityTest(); + toStringTest(); + } + + private static void emptyTest() { + Empty empty1 = new Empty(); + Empty empty2 = new Empty(); + System.out.println(empty1.toString()); + System.out.println(empty1.equals(empty2)); + System.out.println(empty1.hashCode() == empty2.hashCode()); + System.out.println(empty1.toString().equals(empty2.toString())); + } + + private static void toStringTest() { + Person janeDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.toString()); + } + + private static void equalityTest() { + Person jane1 = new Person("Jane Doe", 42); + Person jane2 = new Person("Jane Doe", 42); + String nonIdenticalString = "Jane " + (System.currentTimeMillis() > 0 ? "Doe" : "Zan"); + Person jane3 = new Person(nonIdenticalString, 42); + Person bob = new Person("Bob", 42); + Person youngJane = new Person("Jane Doe", 22); + System.out.println(jane1.equals(jane2)); + System.out.println(jane1.toString().equals(jane2.toString())); + System.out.println(nonIdenticalString == "Jane Doe"); // false. + System.out.println(nonIdenticalString.equals("Jane Doe")); // true. + System.out.println(jane1.equals(jane3)); + System.out.println(jane1.equals(bob)); + System.out.println(jane1.equals(youngJane)); + } + } +}
diff --git a/src/test/examplesJava17/records/RecordShrinkField.java b/src/test/examplesJava17/records/RecordKeepRules.java similarity index 73% rename from src/test/examplesJava17/records/RecordShrinkField.java rename to src/test/examplesJava17/records/RecordKeepRules.java index 2a21904..a62132f 100644 --- a/src/test/examplesJava17/records/RecordShrinkField.java +++ b/src/test/examplesJava17/records/RecordKeepRules.java
@@ -4,7 +4,9 @@ package records; -public class RecordShrinkField { +// The code needs to be completely outside the test for the test to work. +// If these classes are inner classes, age is not correctly simplified to a 42 constant field. +public class RecordKeepRules { record Person(int unused, String name, int age) { Person(String name, int age) {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordKeepRulesTest.java b/src/test/examplesJava17/records/RecordKeepRulesTest.java similarity index 76% rename from src/test/java/com/android/tools/r8/desugar/records/RecordKeepRulesTest.java rename to src/test/examplesJava17/records/RecordKeepRulesTest.java index 68c85df..cf864fe 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordKeepRulesTest.java +++ b/src/test/examplesJava17/records/RecordKeepRulesTest.java
@@ -2,8 +2,9 @@ // 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.desugar.records; +package records; +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.utils.BooleanUtils; @@ -17,30 +18,26 @@ @RunWith(Parameterized.class) public class RecordKeepRulesTest extends TestBase { - private static final String RECORD_NAME = "RecordShrinkField"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); - private static final String KEEP_RULE_CLASS_NAME = - "-keep,allowshrinking,allowoptimization class records.RecordShrinkField$Person"; + "-keep,allowshrinking,allowoptimization class records.RecordKeepRules$Person"; private static final String KEEP_RULE_FIELD_NAMES = - "-keepclassmembers,allowshrinking,allowoptimization class records.RecordShrinkField$Person {" + "-keepclassmembers,allowshrinking,allowoptimization class records.RecordKeepRules$Person {" + " <fields>; }"; private static final String KEEP_RULE_FIELDS_NO_NAMES = - "-keepclassmembers,allowobfuscation class records.RecordShrinkField$Person { <fields>; }"; + "-keepclassmembers,allowobfuscation class records.RecordKeepRules$Person { <fields>; }"; private static final String KEEP_RULE_ALL = - "-keep class records.RecordShrinkField$Person { <fields>; }"; + "-keep class records.RecordKeepRules$Person { <fields>; }"; private static final String EXPECTED_RESULT_R8_WITH_CLASS_NAME = - StringUtils.lines("RecordShrinkField$Person[a=Jane Doe]", "RecordShrinkField$Person[a=Bob]"); + StringUtils.lines("RecordKeepRules$Person[a=Jane Doe]", "RecordKeepRules$Person[a=Bob]"); private static final String EXPECTED_RESULT_R8_WITH_FIELD_NAMES = StringUtils.lines("a[name=Jane Doe]", "a[name=Bob]"); private static final String EXPECTED_RESULT_R8_WITH_FIELD_NO_NAMES = StringUtils.lines("a[a=-1, b=Jane Doe, c=42]", "a[a=-1, b=Bob, c=42]"); private static final String EXPECTED_RESULT_R8_WITH_ALL = StringUtils.lines( - "RecordShrinkField$Person[unused=-1, name=Jane Doe, age=42]", - "RecordShrinkField$Person[unused=-1, name=Bob, age=42]"); + "RecordKeepRules$Person[unused=-1, name=Jane Doe, age=42]", + "RecordKeepRules$Person[unused=-1, name=Bob, age=42]"); private final TestParameters parameters; private final boolean proguardCompatibility; @@ -82,27 +79,27 @@ private void testR8FieldNames(String keepRules, String expectedOutput) throws Exception { testForR8Compat(parameters.getBackend(), proguardCompatibility) - .addProgramClassFileData(PROGRAM_DATA) + .addProgramClassesAndInnerClasses(RecordKeepRules.class) .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE) + .addKeepMainRule(RecordKeepRules.class) .addKeepRules(keepRules) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordKeepRules.class) .assertSuccessWithOutput(expectedOutput); } private void testR8CfThenDexFieldNames(String keepRules, String expectedOutput) throws Exception { Path desugared = testForR8Compat(Backend.CF, proguardCompatibility) - .addProgramClassFileData(PROGRAM_DATA) - .addKeepMainRule(MAIN_TYPE) + .addProgramClassesAndInnerClasses(RecordKeepRules.class) + .addKeepMainRule(RecordKeepRules.class) .addKeepRules(keepRules) - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .compile() .writeToZip(); testForD8(parameters.getBackend()) .addProgramFiles(desugared) .setMinApi(parameters) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordKeepRules.class) .assertSuccessWithOutput(expectedOutput); } }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java b/src/test/examplesJava17/records/RecordLibMergeTest.java similarity index 70% rename from src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java rename to src/test/examplesJava17/records/RecordLibMergeTest.java index ca7a7b5..fd0e655 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java +++ b/src/test/examplesJava17/records/RecordLibMergeTest.java
@@ -2,8 +2,9 @@ // 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.desugar.records; +package records; +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -18,11 +19,6 @@ @RunWith(Parameterized.class) public class RecordLibMergeTest extends TestBase { - private static final String RECORD_LIB = "RecordLib"; - private static final String RECORD_MAIN = "RecordMain"; - private static final byte[][] PROGRAM_DATA_LIB = RecordTestUtils.getProgramData(RECORD_LIB); - private static final byte[][] PROGRAM_DATA_MAIN = RecordTestUtils.getProgramData(RECORD_MAIN); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_MAIN); private static final String EXPECTED_RESULT = StringUtils.lines("true", "true"); private final TestParameters parameters; @@ -45,30 +41,30 @@ parameters.assumeR8TestParameters(); Path lib = testForR8(Backend.CF) - .addProgramClassFileData(PROGRAM_DATA_LIB) + .addProgramClassesAndInnerClasses(RecordLib.class) .addKeepRules( "-keep class records.RecordLib { public static java.lang.Object getRecord(); }") .addKeepRules("-keep class records.RecordLib$LibRecord") - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .compile() .writeToZip(); R8FullTestBuilder builder = testForR8(parameters.getBackend()) .addProgramFiles(lib) - .addProgramClassFileData(PROGRAM_DATA_MAIN) + .addProgramClassesAndInnerClasses(RecordMain.class) .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE) + .addKeepMainRule(RecordMain.class) .addKeepRules("-keep class records.RecordLib$LibRecord") .addKeepRules("-keep class records.RecordMain$MainRecord"); if (parameters.isCfRuntime()) { builder - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .compile() .inspect(RecordTestUtils::assertRecordsAreRecords) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordMain.class) .assertSuccessWithOutput(EXPECTED_RESULT); return; } - builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT); + builder.run(parameters.getRuntime(), RecordMain.class).assertSuccessWithOutput(EXPECTED_RESULT); } }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java b/src/test/examplesJava17/records/RecordMergeTest.java similarity index 67% rename from src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java rename to src/test/examplesJava17/records/RecordMergeTest.java index d05a57a..4153e38 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java +++ b/src/test/examplesJava17/records/RecordMergeTest.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 com.android.tools.r8.desugar.records; +package records; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType; import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; @@ -14,13 +14,13 @@ import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.D8TestBuilder; import com.android.tools.r8.D8TestCompileResult; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.OutputMode; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.errors.DuplicateTypesDiagnostic; import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.nio.file.Path; @@ -32,29 +32,12 @@ @RunWith(Parameterized.class) public class RecordMergeTest extends TestBase { - private static final String RECORD_NAME_1 = "RecordWithMembers"; - private static final byte[][] PROGRAM_DATA_1 = RecordTestUtils.getProgramData(RECORD_NAME_1); - private static final String MAIN_TYPE_1 = RecordTestUtils.getMainType(RECORD_NAME_1); private static final String EXPECTED_RESULT_1 = - StringUtils.lines( - "BobX", "43", "BobX", "43", "FelixX", "-1", "FelixX", "-1", "print", "Bob43", "extra"); + StringUtils.lines("BobX", "43", "FelixX", "-1", "print", "Bob43", "extra"); - private static final String RECORD_NAME_2 = "SimpleRecord"; - private static final byte[][] PROGRAM_DATA_2 = RecordTestUtils.getProgramData(RECORD_NAME_2); - private static final String MAIN_TYPE_2 = RecordTestUtils.getMainType(RECORD_NAME_2); private static final String EXPECTED_RESULT_2 = StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "true", - "true", - "false", - "false", - "false", - "false"); + "Jane Doe", "42", "true", "true", "true", "false", "false", "false", "false"); private final TestParameters parameters; @@ -71,7 +54,9 @@ public void testNoGlobalSyntheticsConsumer() throws Exception { D8TestBuilder builder = testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA_1) + .addStrippedOuter(getClass()) + .addProgramClassesAndInnerClasses(RecordWithMembers.class) + .addClasspathClassesAndInnerClasses(SimpleRecord.class) .setMinApi(parameters) .setIntermediate(true); if (isRecordsFullyDesugaredForD8(parameters)) { @@ -104,7 +89,9 @@ GlobalSyntheticsTestingConsumer globals1 = new GlobalSyntheticsTestingConsumer(); Path output1 = testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA_1) + .addStrippedOuter(getClass()) + .addProgramClassesAndInnerClasses(RecordWithMembers.class) + .addClasspathClassesAndInnerClasses(SimpleRecord.class) .setMinApi(parameters) .setIntermediate(true) .applyIf( @@ -118,7 +105,8 @@ GlobalSyntheticsTestingConsumer globals2 = new GlobalSyntheticsTestingConsumer(); Path output2 = testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA_2) + .addProgramClassesAndInnerClasses(SimpleRecord.class) + .addClasspathClassesAndInnerClasses(getClass()) .setMinApi(parameters) .setIntermediate(true) .applyIf( @@ -145,14 +133,14 @@ .inspect(this::assertHasRecordTag); result - .run(parameters.getRuntime(), MAIN_TYPE_1) + .run(parameters.getRuntime(), RecordWithMembers.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1), r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class)); result - .run(parameters.getRuntime(), MAIN_TYPE_2) + .run(parameters.getRuntime(), SimpleRecord.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), @@ -165,7 +153,9 @@ GlobalSyntheticsTestingConsumer globals1 = new GlobalSyntheticsTestingConsumer(); Path output1 = testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA_1) + .addProgramClassesAndInnerClasses(RecordWithMembers.class) + .addClasspathClasses(getClass()) + .addClasspathClassesAndInnerClasses(SimpleRecord.class) .setMinApi(parameters) .setIntermediate(true) .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals1)) @@ -174,21 +164,23 @@ D8TestCompileResult result = testForD8(parameters.getBackend()) + .addStrippedOuter(getClass()) .addProgramFiles(output1) .apply( b -> b.getBuilder().addGlobalSyntheticsResourceProviders(globals1.getProviders())) - .addProgramClassFileData(PROGRAM_DATA_2) + .addProgramClassesAndInnerClasses(SimpleRecord.class) + .addClasspathClassesAndInnerClasses(getClass()) .setMinApi(parameters) .compile(); result - .run(parameters.getRuntime(), MAIN_TYPE_1) + .run(parameters.getRuntime(), RecordWithMembers.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1), r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class)); result - .run(parameters.getRuntime(), MAIN_TYPE_2) + .run(parameters.getRuntime(), SimpleRecord.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), @@ -200,7 +192,9 @@ public void testMergeNonIntermediates() throws Exception { Path output1 = testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA_1) + .addStrippedOuter(getClass()) + .addProgramClassesAndInnerClasses(RecordWithMembers.class) + .addClasspathClassesAndInnerClasses(SimpleRecord.class) .setMinApi(parameters) .compile() .inspect(this::assertHasRecordTag) @@ -208,7 +202,8 @@ Path output2 = testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA_2) + .addProgramClassesAndInnerClasses(SimpleRecord.class) + .addClasspathClassesAndInnerClasses(getClass()) .setMinApi(parameters) .compile() .inspect(this::assertHasRecordTag) @@ -221,14 +216,14 @@ .setMinApi(parameters) .compile(); result - .run(parameters.getRuntime(), MAIN_TYPE_1) + .run(parameters.getRuntime(), RecordWithMembers.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1), r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class)); result - .run(parameters.getRuntime(), MAIN_TYPE_2) + .run(parameters.getRuntime(), SimpleRecord.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), @@ -259,4 +254,89 @@ // Note: this should be asserting on record tag. assertThat(inspector.clazz("java.lang.Record"), isAbsent()); } + + public class RecordWithMembers { + record PersonWithConstructors(String name, int age) { + + public PersonWithConstructors(String name, int age) { + this.name = name + "X"; + this.age = age; + } + + public PersonWithConstructors(String name) { + this(name, -1); + } + } + + record PersonWithMethods(String name, int age) { + public static void staticPrint() { + System.out.println("print"); + } + + @Override + public String toString() { + return name + age; + } + } + + record PersonWithFields(String name, int age) { + + // Extra instance fields are not allowed on records. + public static String globalName; + } + + public static void main(String[] args) { + personWithConstructorTest(); + personWithMethodsTest(); + personWithFieldsTest(); + } + + private static void personWithConstructorTest() { + PersonWithConstructors bob = new PersonWithConstructors("Bob", 43); + System.out.println(bob.name()); + System.out.println(bob.age()); + PersonWithConstructors felix = new PersonWithConstructors("Felix"); + System.out.println(felix.name()); + System.out.println(felix.age()); + } + + private static void personWithMethodsTest() { + PersonWithMethods.staticPrint(); + PersonWithMethods bob = new PersonWithMethods("Bob", 43); + System.out.println(bob.toString()); + } + + private static void personWithFieldsTest() { + PersonWithFields.globalName = "extra"; + System.out.println(PersonWithFields.globalName); + } + } + + public class SimpleRecord { + + record Person(String name, int age) {} + + public static void main(String[] args) { + Person janeDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.name()); + System.out.println(janeDoe.age()); + + // Test equals with self. + System.out.println(janeDoe.equals(janeDoe)); + + // Test equals with structurally equals Person. + Person otherJaneDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.equals(otherJaneDoe)); + System.out.println(otherJaneDoe.equals(janeDoe)); + + // Test equals with not-structually equals Person. + Person johnDoe = new Person("John Doe", 42); + System.out.println(janeDoe.equals(johnDoe)); + System.out.println(johnDoe.equals(janeDoe)); + + // Test equals with Object and null. + System.out.println(janeDoe.equals(new Object())); + System.out.println(janeDoe.equals(null)); + } + } }
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/examplesJava17/records/RecordProfileRewritingTest.java similarity index 86% rename from src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java rename to src/test/examplesJava17/records/RecordProfileRewritingTest.java index 602cdfc..13c5b5a 100644 --- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java +++ b/src/test/examplesJava17/records/RecordProfileRewritingTest.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 com.android.tools.r8.profile.art.completeness; +package records; import static com.android.tools.r8.ir.desugar.records.RecordFullInstructionDesugaring.EQUALS_RECORD_METHOD_NAME; import static com.android.tools.r8.ir.desugar.records.RecordFullInstructionDesugaring.GET_FIELDS_AS_OBJECTS_METHOD_NAME; @@ -16,11 +16,11 @@ import static org.junit.Assume.assumeTrue; import com.android.tools.r8.D8TestCompileResult; +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.desugar.records.RecordTestUtils; import com.android.tools.r8.profile.art.model.ExternalArtProfile; import com.android.tools.r8.profile.art.utils.ArtProfileInspector; import com.android.tools.r8.references.ClassReference; @@ -43,26 +43,12 @@ @RunWith(Parameterized.class) public class RecordProfileRewritingTest extends TestBase { - private static final String RECORD_NAME = "SimpleRecord"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); private static final String EXPECTED_RESULT = StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "true", - "true", - "false", - "false", - "false", - "false"); + "Jane Doe", "42", "true", "true", "true", "false", "false", "false", "false"); - private static final ClassReference MAIN_REFERENCE = - Reference.classFromTypeName(RecordTestUtils.getMainType(RECORD_NAME)); - private static final ClassReference PERSON_REFERENCE = - Reference.classFromTypeName(MAIN_REFERENCE.getTypeName() + "$Person"); + private static final ClassReference MAIN_REFERENCE = Reference.classFromClass(SimpleRecord.class); + private static final ClassReference PERSON_REFERENCE = Reference.classFromClass(Person.class); private static final ClassReference RECORD_REFERENCE = Reference.classFromTypeName("java.lang.Record"); private static final ClassReference OBJECT_REFERENCE = Reference.classFromClass(Object.class); @@ -81,7 +67,7 @@ parameters.assumeJvmTestParameters(); assumeTrue(runtimeWithRecordsSupport(parameters.getRuntime())); testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .run(parameters.getRuntime(), MAIN_REFERENCE.getTypeName()) .assertSuccessWithOutput(EXPECTED_RESULT); } @@ -90,7 +76,7 @@ public void testD8() throws Exception { D8TestCompileResult compileResult = testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .addArtProfileForRewriting(getArtProfile()) .setMinApi(parameters) .compile(); @@ -114,7 +100,7 @@ assumeTrue(runtimeWithRecordsSupport(parameters.getRuntime()) || parameters.isDexRuntime()); R8TestCompileResult compileResult = testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .addKeepMainRule(MAIN_REFERENCE.getTypeName()) .addKeepRules( "-neverpropagatevalue class " + PERSON_REFERENCE.getTypeName() + " { <fields>; }") @@ -122,8 +108,7 @@ .addOptionsModification(InlinerOptions::disableInlining) .applyIf( parameters.isCfRuntime(), - testBuilder -> - testBuilder.addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))) + testBuilder -> testBuilder.addLibraryProvider(JdkClassFileProvider.fromSystemJdk())) .enableProguardTestOptions() .noHorizontalClassMergingOfSynthetics() .setMinApi(parameters) @@ -218,7 +203,7 @@ : recordTagClassSubject.asTypeSubject(), personRecordClassSubject.getSuperType()); assertEquals( - recordDesugaringIsOff ? 6 : (canMergeRecordTag && !partialDesugaring) ? 11 : 10, + recordDesugaringIsOff ? 6 : (canMergeRecordTag && !partialDesugaring) ? 9 : 8, personRecordClassSubject.allMethods().size()); MethodSubject personDefaultInstanceInitializerSubject = personRecordClassSubject.init(); @@ -236,24 +221,10 @@ MethodSubject nameMethodSubject = personRecordClassSubject.uniqueMethodWithOriginalName("name"); assertThat(nameMethodSubject, isPresent()); - MethodSubject nameNestAccessorMethodSubject = - personRecordClassSubject.uniqueMethodWithOriginalName( - SyntheticItemsTestUtils.syntheticNestInstanceFieldGetter( - Reference.field(PERSON_REFERENCE, "name", STRING_REFERENCE)) - .getMethodName()); - assertThat(nameNestAccessorMethodSubject, isAbsentIf(canUseNestBasedAccesses)); - // Age getters. MethodSubject ageMethodSubject = personRecordClassSubject.uniqueMethodWithOriginalName("age"); assertThat(ageMethodSubject, isPresent()); - MethodSubject ageNestAccessorMethodSubject = - personRecordClassSubject.uniqueMethodWithOriginalName( - SyntheticItemsTestUtils.syntheticNestInstanceFieldGetter( - Reference.field(PERSON_REFERENCE, "age", Reference.INT)) - .getMethodName()); - assertThat(ageNestAccessorMethodSubject, isAbsentIf(canUseNestBasedAccesses)); - // boolean equals(Object) MethodSubject getFieldsAsObjectsMethodSubject = personRecordClassSubject.uniqueMethodWithOriginalName(GET_FIELDS_AS_OBJECTS_METHOD_NAME); @@ -272,7 +243,7 @@ // int hashCode() ClassSubject hashCodeHelperClassSubject = - inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 1)); + inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 0)); assertThat(hashCodeHelperClassSubject, isAbsentIf(recordDesugaringIsOff)); MethodSubject hashCodeHelperMethodSubject = hashCodeHelperClassSubject.uniqueMethod(); @@ -290,7 +261,7 @@ // String toString() ClassSubject toStringHelperClassSubject = - inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 0)); + inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 1)); assertThat(toStringHelperClassSubject, isAbsentIf(recordDesugaringIsOff)); MethodSubject toStringHelperMethodSubject = toStringHelperClassSubject.uniqueMethod(); @@ -317,11 +288,6 @@ hashCodeMethodSubject, toStringMethodSubject) .applyIf( - !canUseNestBasedAccesses, - i -> - i.assertContainsMethodRules( - nameNestAccessorMethodSubject, ageNestAccessorMethodSubject)) - .applyIf( canMergeRecordTag && !partialDesugaring, i -> i.assertContainsMethodRule(personDefaultInstanceInitializerSubject)) .applyIf( @@ -340,4 +306,32 @@ toStringHelperMethodSubject)) .assertContainsNoOtherRules(); } + + record Person(String name, int age) {} + + public class SimpleRecord { + + public static void main(String[] args) { + Person janeDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.name()); + System.out.println(janeDoe.age()); + + // Test equals with self. + System.out.println(janeDoe.equals(janeDoe)); + + // Test equals with structurally equals Person. + Person otherJaneDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.equals(otherJaneDoe)); + System.out.println(otherJaneDoe.equals(janeDoe)); + + // Test equals with not-structually equals Person. + Person johnDoe = new Person("John Doe", 42); + System.out.println(janeDoe.equals(johnDoe)); + System.out.println(johnDoe.equals(janeDoe)); + + // Test equals with Object and null. + System.out.println(janeDoe.equals(new Object())); + System.out.println(janeDoe.equals(null)); + } + } }
diff --git a/src/test/examplesJava17/records/RecordReflection.java b/src/test/examplesJava17/records/RecordReflection.java deleted file mode 100644 index 1b94a12..0000000 --- a/src/test/examplesJava17/records/RecordReflection.java +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright (c) 2020, 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 records; - -import java.util.Arrays; - -public class RecordReflection { - - record Empty(){} - - record Person(String name, int age) {} - - record PersonGeneric <S extends CharSequence>(S name, int age) {} - - public static void main(String[] args) { - System.out.println(Empty.class.isRecord()); - System.out.println(Arrays.toString(Empty.class.getRecordComponents())); - System.out.println(Person.class.isRecord()); - System.out.println(Arrays.toString(Person.class.getRecordComponents())); - System.out.println(PersonGeneric.class.isRecord()); - System.out.println(Arrays.toString(PersonGeneric.class.getRecordComponents())); - System.out.println(Arrays.toString(PersonGeneric.class.getTypeParameters())); - System.out.println(Object.class.isRecord()); - System.out.println(Arrays.toString(Object.class.getRecordComponents())); - } - -}
diff --git a/src/test/examplesJava17/records/RecordReflectionTest.java b/src/test/examplesJava17/records/RecordReflectionTest.java new file mode 100644 index 0000000..bab9fb7 --- /dev/null +++ b/src/test/examplesJava17/records/RecordReflectionTest.java
@@ -0,0 +1,89 @@ +// Copyright (c) 2021, 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 records; + +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.utils.StringUtils; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class RecordReflectionTest extends TestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines( + "true", + "[]", + "true", + "[java.lang.String name, int age]", + "true", + "[java.lang.CharSequence name, int age]", + "[S]", + "false", + "null"); + + private final TestParameters parameters; + + public RecordReflectionTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK17).build(); + } + + @Test + public void testJvm() throws Exception { + parameters.assumeJvmTestParameters(); + testForJvm(parameters) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), RecordReflection.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testR8Cf() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClassesAndStrippedOuter(getClass()) + .setMinApi(parameters) + .addKeepMainRule(RecordReflection.class) + .addKeepAllAttributes() + .addKeepRules("-keep class * extends java.lang.Record { private final <fields>; }") + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) + .compile() + .inspect(RecordTestUtils::assertRecordsAreRecords) + .enableJVMPreview() + .run(parameters.getRuntime(), RecordReflection.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + record Empty() {} + + record Person(String name, int age) {} + + record PersonGeneric<S extends CharSequence>(S name, int age) {} + + public class RecordReflection { + + public static void main(String[] args) { + System.out.println(Empty.class.isRecord()); + System.out.println(Arrays.toString(Empty.class.getRecordComponents())); + System.out.println(Person.class.isRecord()); + System.out.println(Arrays.toString(Person.class.getRecordComponents())); + System.out.println(PersonGeneric.class.isRecord()); + System.out.println(Arrays.toString(PersonGeneric.class.getRecordComponents())); + System.out.println(Arrays.toString(PersonGeneric.class.getTypeParameters())); + System.out.println(Object.class.isRecord()); + System.out.println(Arrays.toString(Object.class.getRecordComponents())); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java b/src/test/examplesJava17/records/RecordShrinkFieldTest.java similarity index 72% rename from src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java rename to src/test/examplesJava17/records/RecordShrinkFieldTest.java index e8587ca..232f02f 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java +++ b/src/test/examplesJava17/records/RecordShrinkFieldTest.java
@@ -2,10 +2,11 @@ // 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.desugar.records; +package records; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.utils.BooleanUtils; @@ -22,17 +23,13 @@ @RunWith(Parameterized.class) public class RecordShrinkFieldTest extends TestBase { - private static final String RECORD_NAME = "RecordShrinkField"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); - private static final String EXPECTED_RESULT_D8 = StringUtils.lines( "Person[unused=-1, name=Jane Doe, age=42]", "Person[unused=-1, name=Bob, age=42]"); private static final String EXPECTED_RESULT_R8 = StringUtils.lines("a[a=Jane Doe]", "a[a=Bob]"); private static final String EXPECTED_RESULT_R8_NO_MINIFICATION = StringUtils.lines( - "RecordShrinkField$Person[name=Jane Doe]", "RecordShrinkField$Person[name=Bob]"); + "RecordShrinkFieldTest$Person[name=Jane Doe]", "RecordShrinkFieldTest$Person[name=Bob]"); private final TestParameters parameters; private final boolean minifying; @@ -53,10 +50,10 @@ public void testD8() throws Exception { Assume.assumeTrue("Only valid in R8", minifying); testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) .compile() - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordShrinkField.class) .assertSuccessWithOutput(EXPECTED_RESULT_D8); } @@ -64,13 +61,13 @@ public void testR8() throws Exception { parameters.assumeR8TestParameters(); testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE) + .addKeepMainRule(RecordShrinkField.class) .addDontObfuscateUnless(minifying) .compile() .inspect(inspector -> inspect(inspector, false)) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordShrinkField.class) .assertSuccessWithOutput( minifying ? EXPECTED_RESULT_R8 : EXPECTED_RESULT_R8_NO_MINIFICATION); } @@ -80,28 +77,27 @@ parameters.assumeR8TestParameters(); Path desugared = testForR8(Backend.CF) - .addProgramClassFileData(PROGRAM_DATA) - .addKeepMainRule(MAIN_TYPE) + .addInnerClassesAndStrippedOuter(getClass()) + .addKeepMainRule(RecordShrinkField.class) .addDontObfuscateUnless(minifying) - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .compile() .writeToZip(); testForR8(parameters.getBackend()) .addProgramFiles(desugared) .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE) + .addKeepMainRule(RecordShrinkField.class) .addDontObfuscateUnless(minifying) .compile() .inspect(inspector -> inspect(inspector, true)) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordShrinkField.class) .assertSuccessWithOutput( minifying ? EXPECTED_RESULT_R8 : EXPECTED_RESULT_R8_NO_MINIFICATION); } private void inspect(CodeInspector inspector, boolean isCfThenDex) { - ClassSubject recordClass = - inspector.clazz(minifying ? "records.a" : "records.RecordShrinkField$Person"); - if (isCfThenDex || !minifying) { + ClassSubject recordClass = inspector.clazz(Person.class); + if (!isCfThenDex || !minifying) { assertEquals(1, recordClass.allInstanceFields().size()); assertEquals( "java.lang.String", recordClass.allInstanceFields().get(0).getField().type().toString()); @@ -109,4 +105,20 @@ assertEquals(0, recordClass.allInstanceFields().size()); } } + + record Person(int unused, String name, int age) { + Person(String name, int age) { + this(-1, name, age); + } + } + + public class RecordShrinkField { + + public static void main(String[] args) { + Person jane = new Person("Jane Doe", 42); + Person bob = new Person("Bob", 42); + System.out.println(jane); + System.out.println(bob); + } + } }
diff --git a/src/test/examplesJava17/records/RecordWithAnnotations.java b/src/test/examplesJava17/records/RecordWithAnnotations.java deleted file mode 100644 index fe9fd95..0000000 --- a/src/test/examplesJava17/records/RecordWithAnnotations.java +++ /dev/null
@@ -1,99 +0,0 @@ -// Copyright (c) 2023, 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 records; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.Field; -import java.lang.reflect.RecordComponent; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -public class RecordWithAnnotations { - - @Target({ElementType.FIELD, ElementType.RECORD_COMPONENT}) - @Retention(RetentionPolicy.RUNTIME) - @interface Annotation { - String value(); - } - - @Target(ElementType.FIELD) - @Retention(RetentionPolicy.RUNTIME) - @interface AnnotationFieldOnly { - String value(); - } - - @Target(ElementType.RECORD_COMPONENT) - @Retention(RetentionPolicy.RUNTIME) - @interface AnnotationRecordComponentOnly { - String value(); - } - - record Person( - @Annotation("a") @AnnotationFieldOnly("b") @AnnotationRecordComponentOnly("c") String name, - @Annotation("x") @AnnotationFieldOnly("y") @AnnotationRecordComponentOnly("z") int age) {} - - public static void main(String[] args) { - Person janeDoe = new Person("Jane Doe", 42); - System.out.println(janeDoe.name); - System.out.println(janeDoe.age); - System.out.println(janeDoe.name()); - System.out.println(janeDoe.age()); - try { - Class.class.getDeclaredMethod("isRecord"); - } catch (NoSuchMethodException e) { - System.out.println("Class.isRecord not present"); - return; - } - System.out.println(Person.class.isRecord()); - if (Person.class.isRecord()) { - System.out.println(Person.class.getRecordComponents().length); - for (int i = 0; i < Person.class.getRecordComponents().length; i++) { - RecordComponent c = Person.class.getRecordComponents()[i]; - System.out.println(c.getName()); - System.out.println(c.getType().getName()); - System.out.println(c.getGenericSignature() == null); - System.out.println(c.getAnnotations().length); - // Collect and sort the annotations, as the order is not deterministic on Art (tested - // on Art 14 Beta 3). - List<String> annotations = new ArrayList<>(); - for (int j = 0; j < c.getAnnotations().length; j++) { - annotations.add(c.getAnnotations()[j].toString()); - } - annotations.sort(Comparator.naturalOrder()); - for (int j = 0; j < annotations.size(); j++) { - System.out.println(annotations.get(j)); - } - } - System.out.println(Person.class.getDeclaredFields().length); - List<Field> fields = new ArrayList<>(); - for (int i = 0; i < Person.class.getDeclaredFields().length; i++) { - fields.add(Person.class.getDeclaredFields()[i]); - } - fields.sort( - new Comparator<Field>() { - @Override - public int compare(Field o1, Field o2) { - return o1.getName().compareTo(o2.getName()); - } - }); - for (int i = 0; i < fields.size(); i++) { - Field f = fields.get(i); - System.out.println(f.getDeclaredAnnotations().length); - List<String> annotations = new ArrayList<>(); - for (int j = 0; j < f.getDeclaredAnnotations().length; j++) { - annotations.add(f.getAnnotations()[j].toString()); - } - annotations.sort(Comparator.naturalOrder()); - for (int j = 0; j < annotations.size(); j++) { - System.out.println(annotations.get(j)); - } - } - } - } -}
diff --git a/src/test/examplesJava17/records/RecordWithConstClass.java b/src/test/examplesJava17/records/RecordWithConstClass.java deleted file mode 100644 index db06f51..0000000 --- a/src/test/examplesJava17/records/RecordWithConstClass.java +++ /dev/null
@@ -1,18 +0,0 @@ -// Copyright (c) 2021, 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 records; - -import records.differentpackage.PrivateConstClass; - -public class RecordWithConstClass { - - record MyRecordWithConstClass(Class<?> theClass) { } - - public static void main(String[] args) { - MyRecordWithConstClass inst = - new MyRecordWithConstClass(PrivateConstClass.getPrivateConstClass()); - System.out.println(inst); - } -}
diff --git a/src/test/examplesJava17/records/RecordWithMembers.java b/src/test/examplesJava17/records/RecordWithMembers.java deleted file mode 100644 index 6de2b99..0000000 --- a/src/test/examplesJava17/records/RecordWithMembers.java +++ /dev/null
@@ -1,69 +0,0 @@ -// Copyright (c) 2020, 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 records; - -public class RecordWithMembers { - - - record PersonWithConstructors(String name, int age) { - - public PersonWithConstructors(String name, int age) { - this.name = name + "X"; - this.age = age; - } - - public PersonWithConstructors(String name) { - this(name, -1); - } - } - - record PersonWithMethods(String name, int age) { - public static void staticPrint() { - System.out.println("print"); - } - - @Override - public String toString() { - return name + age; - } - } - - record PersonWithFields(String name, int age) { - - // Extra instance fields are not allowed on records. - public static String globalName; - - } - - public static void main(String[] args) { - personWithConstructorTest(); - personWithMethodsTest(); - personWithFieldsTest(); - } - - private static void personWithConstructorTest() { - PersonWithConstructors bob = new PersonWithConstructors("Bob", 43); - System.out.println(bob.name); - System.out.println(bob.age); - System.out.println(bob.name()); - System.out.println(bob.age()); - PersonWithConstructors felix = new PersonWithConstructors("Felix"); - System.out.println(felix.name); - System.out.println(felix.age); - System.out.println(felix.name()); - System.out.println(felix.age()); - } - - private static void personWithMethodsTest() { - PersonWithMethods.staticPrint(); - PersonWithMethods bob = new PersonWithMethods("Bob", 43); - System.out.println(bob.toString()); - } - - private static void personWithFieldsTest() { - PersonWithFields.globalName = "extra"; - System.out.println(PersonWithFields.globalName); - } -}
diff --git a/src/test/examplesJava17/records/RecordWithMembersTest.java b/src/test/examplesJava17/records/RecordWithMembersTest.java new file mode 100644 index 0000000..41d36ea --- /dev/null +++ b/src/test/examplesJava17/records/RecordWithMembersTest.java
@@ -0,0 +1,137 @@ +// Copyright (c) 2021, 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 records; + +import com.android.tools.r8.JdkClassFileProvider; +import com.android.tools.r8.R8FullTestBuilder; +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.utils.StringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class RecordWithMembersTest extends TestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines("BobX", "43", "FelixX", "-1", "print", "Bob43", "extra"); + + private final TestParameters parameters; + + public RecordWithMembersTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters() + .withCfRuntimesStartingFromIncluding(CfVm.JDK17) + .withDexRuntimes() + .withAllApiLevelsAlsoForCf() + .build(); + } + + @Test + public void testJvm() throws Exception { + parameters.assumeJvmTestParameters(); + testForJvm(parameters) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), RecordWithMembers.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testD8() throws Exception { + testForD8(parameters.getBackend()) + .addInnerClassesAndStrippedOuter(getClass()) + .setMinApi(parameters) + .compile() + .run(parameters.getRuntime(), RecordWithMembers.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testR8() throws Exception { + parameters.assumeR8TestParameters(); + R8FullTestBuilder builder = + testForR8(parameters.getBackend()) + .addInnerClassesAndStrippedOuter(getClass()) + .setMinApi(parameters) + .addKeepMainRule(RecordWithMembers.class); + if (parameters.isCfRuntime()) { + builder + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) + .compile() + .inspect(RecordTestUtils::assertRecordsAreRecords) + .run(parameters.getRuntime(), RecordWithMembers.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + return; + } + builder + .run(parameters.getRuntime(), RecordWithMembers.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + record PersonWithConstructors(String name, int age) { + + public PersonWithConstructors(String name, int age) { + this.name = name + "X"; + this.age = age; + } + + public PersonWithConstructors(String name) { + this(name, -1); + } + } + + record PersonWithMethods(String name, int age) { + public static void staticPrint() { + System.out.println("print"); + } + + @Override + public String toString() { + return name + age; + } + } + + record PersonWithFields(String name, int age) { + + // Extra instance fields are not allowed on records. + public static String globalName; + } + + public class RecordWithMembers { + + public static void main(String[] args) { + personWithConstructorTest(); + personWithMethodsTest(); + personWithFieldsTest(); + } + + private static void personWithConstructorTest() { + PersonWithConstructors bob = new PersonWithConstructors("Bob", 43); + System.out.println(bob.name()); + System.out.println(bob.age()); + PersonWithConstructors felix = new PersonWithConstructors("Felix"); + System.out.println(felix.name()); + System.out.println(felix.age()); + } + + private static void personWithMethodsTest() { + PersonWithMethods.staticPrint(); + PersonWithMethods bob = new PersonWithMethods("Bob", 43); + System.out.println(bob.toString()); + } + + private static void personWithFieldsTest() { + PersonWithFields.globalName = "extra"; + System.out.println(PersonWithFields.globalName); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java b/src/test/examplesJava17/records/RecordWithNonMaterializableConstClassTest.java similarity index 67% rename from src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java rename to src/test/examplesJava17/records/RecordWithNonMaterializableConstClassTest.java index bcf4294..4cda982 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java +++ b/src/test/examplesJava17/records/RecordWithNonMaterializableConstClassTest.java
@@ -2,9 +2,9 @@ // 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.desugar.records; +package records; - +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -17,24 +17,19 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; +import records.differentpackage.PrivateConstClass; @RunWith(Parameterized.class) public class RecordWithNonMaterializableConstClassTest extends TestBase { - private static final String RECORD_NAME = "RecordWithConstClass"; private static final String PRIVATE_CLASS_NAME = "records.differentpackage.PrivateConstClass$PrivateClass"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final byte[][] EXTRA_DATA = - RecordTestUtils.getProgramData("differentpackage/PrivateConstClass"); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); + private static final Class<?> EXTRA_DATA = PrivateConstClass.class; private static final String EXPECTED_RESULT_FORMAT = StringUtils.lines("%s[%s=class " + PRIVATE_CLASS_NAME + "]"); private static final String EXPECTED_RESULT_D8 = String.format(EXPECTED_RESULT_FORMAT, "MyRecordWithConstClass", "theClass"); private static final String EXPECTED_RESULT_R8 = String.format(EXPECTED_RESULT_FORMAT, "a", "a"); - private static final String EXPECTED_RESULT_R8_ART14 = - String.format(EXPECTED_RESULT_FORMAT, "a", "theClass"); @Parameter(0) public TestParameters parameters; @@ -52,20 +47,22 @@ public void testJvm() throws Exception { parameters.assumeJvmTestParameters(); testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .addProgramClassFileData(EXTRA_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) + .addInnerClassesAndStrippedOuter(getClass()) + .addProgramClasses(EXTRA_DATA) + .addInnerClasses(EXTRA_DATA) + .run(parameters.getRuntime(), RecordWithConstClass.class) .assertSuccessWithOutput(EXPECTED_RESULT_D8); } @Test public void testD8() throws Exception { testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - .addProgramClassFileData(EXTRA_DATA) + .addInnerClassesAndStrippedOuter(getClass()) + .addProgramClasses(EXTRA_DATA) + .addInnerClasses(EXTRA_DATA) .setMinApi(parameters) .compile() - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordWithConstClass.class) .assertSuccessWithOutput(EXPECTED_RESULT_D8); } @@ -73,12 +70,13 @@ public void testR8() throws Exception { parameters.assumeR8TestParameters(); testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - .addProgramClassFileData(EXTRA_DATA) + .addInnerClassesAndStrippedOuter(getClass()) + .addProgramClasses(EXTRA_DATA) + .addInnerClasses(EXTRA_DATA) .apply(this::configureR8) .setMinApi(parameters) .compile() - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordWithConstClass.class) .assertSuccessWithOutput(EXPECTED_RESULT_R8); } @@ -87,9 +85,10 @@ parameters.assumeR8TestParameters(); Path desugared = testForR8(Backend.CF) - .addProgramClassFileData(PROGRAM_DATA) - .addProgramClassFileData(EXTRA_DATA) - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addInnerClassesAndStrippedOuter(getClass()) + .addProgramClasses(EXTRA_DATA) + .addInnerClasses(EXTRA_DATA) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .apply(this::configureR8) .compile() .writeToZip(); @@ -100,16 +99,27 @@ .apply(this::configureR8) .setMinApi(parameters) .compile() - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), RecordWithConstClass.class) .assertSuccessWithOutput(EXPECTED_RESULT_R8); } private void configureR8(R8FullTestBuilder testBuilder) { testBuilder - .addKeepMainRule(MAIN_TYPE) + .addKeepMainRule(RecordWithConstClass.class) .addKeepRules("-keep class " + PRIVATE_CLASS_NAME) .applyIf( parameters.isCfRuntime(), - b -> b.addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))); + b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk())); + } + + record MyRecordWithConstClass(Class<?> theClass) {} + + public static class RecordWithConstClass { + + public static void main(String[] args) { + MyRecordWithConstClass inst = + new MyRecordWithConstClass(PrivateConstClass.getPrivateConstClass()); + System.out.println(inst); + } } }
diff --git a/src/test/examplesJava17/records/RecordWithSignature.java b/src/test/examplesJava17/records/RecordWithSignature.java deleted file mode 100644 index be31024..0000000 --- a/src/test/examplesJava17/records/RecordWithSignature.java +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright (c) 2023, 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 records; - -import java.lang.reflect.RecordComponent; - -public class RecordWithSignature { - - record Person<N extends CharSequence, A>(N name, A age) {} - - public static void main(String[] args) { - Person<String, Integer> janeDoe = new Person<>("Jane Doe", 42); - System.out.println(janeDoe.name); - System.out.println(janeDoe.age); - System.out.println(janeDoe.name()); - System.out.println(janeDoe.age()); - try { - Class.class.getDeclaredMethod("isRecord"); - } catch (NoSuchMethodException e) { - System.out.println("Class.isRecord not present"); - return; - } - System.out.println(Person.class.isRecord()); - if (Person.class.isRecord()) { - System.out.println(Person.class.getRecordComponents().length); - for (int i = 0; i < Person.class.getRecordComponents().length; i++) { - RecordComponent c = Person.class.getRecordComponents()[i]; - System.out.println(c.getName()); - System.out.println(c.getType().getName()); - System.out.println(c.getGenericSignature()); - System.out.println(c.getAnnotations().length); - } - } - } -}
diff --git a/src/test/examplesJava17/records/SimpleRecord.java b/src/test/examplesJava17/records/SimpleRecord.java deleted file mode 100644 index 3f908dc..0000000 --- a/src/test/examplesJava17/records/SimpleRecord.java +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright (c) 2020, 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 records; - -public class SimpleRecord { - - record Person(String name, int age) {} - - public static void main(String[] args) { - Person janeDoe = new Person("Jane Doe", 42); - System.out.println(janeDoe.name); - System.out.println(janeDoe.age); - System.out.println(janeDoe.name()); - System.out.println(janeDoe.age()); - - // Test equals with self. - System.out.println(janeDoe.equals(janeDoe)); - - // Test equals with structurally equals Person. - Person otherJaneDoe = new Person("Jane Doe", 42); - System.out.println(janeDoe.equals(otherJaneDoe)); - System.out.println(otherJaneDoe.equals(janeDoe)); - - // Test equals with not-structually equals Person. - Person johnDoe = new Person("John Doe", 42); - System.out.println(janeDoe.equals(johnDoe)); - System.out.println(johnDoe.equals(janeDoe)); - - // Test equals with Object and null. - System.out.println(janeDoe.equals(new Object())); - System.out.println(janeDoe.equals(null)); - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/examplesJava17/records/SimpleRecordTest.java similarity index 71% rename from src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java rename to src/test/examplesJava17/records/SimpleRecordTest.java index 9dbd8d2..d7c8131 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java +++ b/src/test/examplesJava17/records/SimpleRecordTest.java
@@ -2,18 +2,18 @@ // 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.desugar.records; +package records; -import static com.android.tools.r8.desugar.records.RecordTestUtils.assertNoJavaLangRecord; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.GlobalSyntheticsConsumer; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; +import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.StringUtils; @@ -28,22 +28,9 @@ @RunWith(Parameterized.class) public class SimpleRecordTest extends TestBase { - private static final String RECORD_NAME = "SimpleRecord"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); private static final String EXPECTED_RESULT = StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "true", - "true", - "false", - "false", - "false", - "false"); + "Jane Doe", "42", "true", "true", "true", "false", "false", "false", "false"); @Parameter(0) public TestParameters parameters; @@ -69,8 +56,8 @@ assumeTrue(isCfRuntimeWithNativeRecordSupport()); assumeFalse(forceInvokeRangeForInvokeCustom); testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) + .addInnerClassesAndStrippedOuter(getClass()) + .run(parameters.getRuntime(), SimpleRecord.class) .assertSuccessWithOutput(EXPECTED_RESULT); } @@ -78,13 +65,13 @@ public void testD8() throws Exception { assumeFalse(forceInvokeRangeForInvokeCustom); testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) .compile() .inspectWithOptions( - i -> assertNoJavaLangRecord(i, parameters), + i -> RecordTestUtils.assertNoJavaLangRecord(i, parameters), options -> options.testing.disableRecordApplicationReaderMap = true) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), SimpleRecord.class) .applyIf( isRecordsFullyDesugaredForD8(parameters) || runtimeWithRecordsSupport(parameters.getRuntime()), @@ -108,7 +95,7 @@ .addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider())) .setMinApi(parameters) .setIncludeClassesChecksum(true) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), SimpleRecord.class) .assertSuccessWithOutput(EXPECTED_RESULT); } @@ -129,14 +116,14 @@ .setMinApi(parameters) .setIncludeClassesChecksum(true) .disableDesugaring() - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), SimpleRecord.class) .assertSuccessWithOutput(EXPECTED_RESULT); } private Path compileIntermediate(GlobalSyntheticsConsumer globalSyntheticsConsumer) throws Exception { return testForD8(Backend.DEX) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) .setIntermediate(true) .setIncludeClassesChecksum(true) @@ -156,25 +143,25 @@ opptions -> opptions.testing.forceInvokeRangeForInvokeCustom = forceInvokeRangeForInvokeCustom) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE); + .addKeepMainRule(SimpleRecord.class); if (parameters.isCfRuntime()) { builder - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .compile() .inspect(RecordTestUtils::assertRecordsAreRecords) - .inspect(inspector -> inspector.clazz("records.SimpleRecord$Person").isRenamed()) - .run(parameters.getRuntime(), MAIN_TYPE) + .inspect(inspector -> inspector.clazz(Person.class).isRenamed()) + .run(parameters.getRuntime(), SimpleRecord.class) .assertSuccessWithOutput(EXPECTED_RESULT); return; } builder .compile() .inspectWithOptions( - i -> assertNoJavaLangRecord(i, parameters), + i -> RecordTestUtils.assertNoJavaLangRecord(i, parameters), options -> options.testing.disableRecordApplicationReaderMap = true) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), SimpleRecord.class) .assertSuccessWithOutput(EXPECTED_RESULT); } @@ -185,25 +172,53 @@ assumeTrue(forceInvokeRangeForInvokeCustom || !parameters.isDexRuntime()); R8FullTestBuilder builder = testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) + .addInnerClassesAndStrippedOuter(getClass()) .addDontObfuscate() .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE); + .addKeepMainRule(SimpleRecord.class); if (parameters.isCfRuntime()) { builder - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) + .addLibraryProvider(JdkClassFileProvider.fromSystemJdk()) .compile() .inspect(RecordTestUtils::assertRecordsAreRecords) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), SimpleRecord.class) .assertSuccessWithOutput(EXPECTED_RESULT); return; } builder .compile() .inspectWithOptions( - i -> assertNoJavaLangRecord(i, parameters), + i -> RecordTestUtils.assertNoJavaLangRecord(i, parameters), options -> options.testing.disableRecordApplicationReaderMap = true) - .run(parameters.getRuntime(), MAIN_TYPE) + .run(parameters.getRuntime(), SimpleRecord.class) .assertSuccessWithOutput(EXPECTED_RESULT); } + + record Person(String name, int age) {} + + public class SimpleRecord { + + public static void main(String[] args) { + Person janeDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.name()); + System.out.println(janeDoe.age()); + + // Test equals with self. + System.out.println(janeDoe.equals(janeDoe)); + + // Test equals with structurally equals Person. + Person otherJaneDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.equals(otherJaneDoe)); + System.out.println(otherJaneDoe.equals(janeDoe)); + + // Test equals with not-structually equals Person. + Person johnDoe = new Person("John Doe", 42); + System.out.println(janeDoe.equals(johnDoe)); + System.out.println(johnDoe.equals(janeDoe)); + + // Test equals with Object and null. + System.out.println(janeDoe.equals(new Object())); + System.out.println(janeDoe.equals(null)); + } + } }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java index d3514ae..3ead5bf 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
@@ -16,13 +16,13 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.D8TestCompileResult; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ThrowableConsumer; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.BooleanUtils;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelHorizontalMergeAndD8MergeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelHorizontalMergeAndD8MergeTest.java index 4c99837..af120aa 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelHorizontalMergeAndD8MergeTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelHorizontalMergeAndD8MergeTest.java
@@ -12,6 +12,7 @@ import static org.junit.Assert.assertFalse; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.OutputMode; import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestBase; @@ -19,7 +20,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java index a4aa543..e321b56 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertFalse; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.NeverInline; import com.android.tools.r8.OutputMode; import com.android.tools.r8.SingleTestRunResult; @@ -18,7 +19,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionAndroidApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionAndroidApiTest.java index 2a7ebee..288a050 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionAndroidApiTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionAndroidApiTest.java
@@ -9,6 +9,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.NeverInline; import com.android.tools.r8.OutputMode; import com.android.tools.r8.R8TestCompileResult; @@ -19,7 +20,6 @@ import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.references.Reference; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.DescriptorUtils;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionInstantiateAndCatchTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionInstantiateAndCatchTest.java index e212a7a..e7508c5 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionInstantiateAndCatchTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionInstantiateAndCatchTest.java
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.OutputMode; import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestBase; @@ -18,7 +19,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.references.Reference; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java index 7b98e5c..898bd3e 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.OutputMode; import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestBase; @@ -18,7 +19,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.references.Reference; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java index bb28672..eaa397f 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
@@ -12,13 +12,13 @@ import static org.junit.Assert.assertFalse; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.OutputMode; import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java index 15ffd25..a67674e 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java
@@ -12,13 +12,13 @@ import static org.junit.Assert.assertFalse; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.OutputMode; import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoHorizontalMergeAndD8MergeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoHorizontalMergeAndD8MergeTest.java index 880360d..a02fe01 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoHorizontalMergeAndD8MergeTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoHorizontalMergeAndD8MergeTest.java
@@ -12,13 +12,13 @@ import static org.junit.Assert.assertFalse; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.OutputMode; import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/CompilationDependentSetTest.java b/src/test/java/com/android/tools/r8/desugar/graph/CompilationDependentSetTest.java index 258805a..c890f40 100644 --- a/src/test/java/com/android/tools/r8/desugar/graph/CompilationDependentSetTest.java +++ b/src/test/java/com/android/tools/r8/desugar/graph/CompilationDependentSetTest.java
@@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.D8TestBuilder; +import com.android.tools.r8.DesugarGraphTestConsumer; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection;
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/InterfaceBridgeDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/InterfaceBridgeDependencyTest.java index 6c11011..bfd0048 100644 --- a/src/test/java/com/android/tools/r8/desugar/graph/InterfaceBridgeDependencyTest.java +++ b/src/test/java/com/android/tools/r8/desugar/graph/InterfaceBridgeDependencyTest.java
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.D8TestBuilder; +import com.android.tools.r8.DesugarGraphTestConsumer; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection;
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java index aab7123..42f49fb 100644 --- a/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java +++ b/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java
@@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.D8TestBuilder; +import com.android.tools.r8.DesugarGraphTestConsumer; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection;
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java index cdb01f6..37674a7 100644 --- a/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java +++ b/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java
@@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.D8TestBuilder; +import com.android.tools.r8.DesugarGraphTestConsumer; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection;
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/NestDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/NestDependencyTest.java index df53dc1..4f97f34 100644 --- a/src/test/java/com/android/tools/r8/desugar/graph/NestDependencyTest.java +++ b/src/test/java/com/android/tools/r8/desugar/graph/NestDependencyTest.java
@@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.D8TestBuilder; +import com.android.tools.r8.DesugarGraphTestConsumer; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection;
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/Regress167562221Test.java b/src/test/java/com/android/tools/r8/desugar/graph/Regress167562221Test.java index 650338c..629de48 100644 --- a/src/test/java/com/android/tools/r8/desugar/graph/Regress167562221Test.java +++ b/src/test/java/com/android/tools/r8/desugar/graph/Regress167562221Test.java
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertEquals; +import com.android.tools.r8.DesugarGraphTestConsumer; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection;
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java deleted file mode 100644 index 41f97f8..0000000 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java +++ /dev/null
@@ -1,394 +0,0 @@ -// Copyright (c) 2023, 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.desugar.records; - -import static com.android.tools.r8.utils.codeinspector.AnnotationMatchers.hasAnnotationTypes; -import static com.android.tools.r8.utils.codeinspector.AnnotationMatchers.hasElements; -import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; - -import com.android.tools.r8.TestBase; -import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.TestShrinkerBuilder; -import com.android.tools.r8.utils.BooleanUtils; -import com.android.tools.r8.utils.Pair; -import com.android.tools.r8.utils.StringUtils; -import com.android.tools.r8.utils.codeinspector.ClassSubject; -import com.android.tools.r8.utils.codeinspector.FieldSubject; -import java.util.List; -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 RecordComponentAnnotationsTest extends TestBase { - - private static final String RECORD_NAME = "RecordWithAnnotations"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); - private static final String JVM_UNTIL_20_EXPECTED_RESULT = - StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "2", - "name", - "java.lang.String", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(\"a\")", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(\"c\")", - "age", - "int", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(\"x\")", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(\"z\")", - "2", - "2", - "@records.RecordWithAnnotations$Annotation(\"x\")", - "@records.RecordWithAnnotations$AnnotationFieldOnly(\"y\")", - "2", - "@records.RecordWithAnnotations$Annotation(\"a\")", - "@records.RecordWithAnnotations$AnnotationFieldOnly(\"b\")"); - private static final String JVM_FROM_21_EXPECTED_RESULT = - JVM_UNTIL_20_EXPECTED_RESULT.replaceAll( - "RecordWithAnnotations\\$Annotation", "RecordWithAnnotations.Annotation"); - private static final String ART_EXPECTED_RESULT = - StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "2", - "name", - "java.lang.String", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(value=a)", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(value=c)", - "age", - "int", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(value=x)", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(value=z)", - "2", - "2", - "@records.RecordWithAnnotations$Annotation(value=x)", - "@records.RecordWithAnnotations$AnnotationFieldOnly(value=y)", - "2", - "@records.RecordWithAnnotations$Annotation(value=a)", - "@records.RecordWithAnnotations$AnnotationFieldOnly(value=b)"); - private static final String JVM_EXPECTED_RESULT_R8 = - StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "2", - "a", - "java.lang.String", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(\"a\")", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(\"c\")", - "b", - "int", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(\"x\")", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(\"z\")", - "2", - "2", - "@records.RecordWithAnnotations$Annotation(\"a\")", - "@records.RecordWithAnnotations$AnnotationFieldOnly(\"b\")", - "2", - "@records.RecordWithAnnotations$Annotation(\"x\")", - "@records.RecordWithAnnotations$AnnotationFieldOnly(\"y\")"); - private static final String ART_EXPECTED_RESULT_R8 = - StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "2", - "a", - "java.lang.String", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(value=a)", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(value=c)", - "b", - "int", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(value=x)", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(value=z)", - "2", - "2", - "@records.RecordWithAnnotations$Annotation(value=a)", - "@records.RecordWithAnnotations$AnnotationFieldOnly(value=b)", - "2", - "@records.RecordWithAnnotations$Annotation(value=x)", - "@records.RecordWithAnnotations$AnnotationFieldOnly(value=y)"); - private static final String JVM_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS = - StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "2", - "a", - "java.lang.String", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(\"a\")", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(\"c\")", - "b", - "int", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(\"x\")", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(\"z\")", - "2", - "0", - "0"); - private static final String ART_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS = - StringUtils.lines( - "Jane Doe", - "42", - "Jane Doe", - "42", - "true", - "2", - "a", - "java.lang.String", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(value=a)", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(value=c)", - "b", - "int", - "true", - "2", - "@records.RecordWithAnnotations$Annotation(value=x)", - "@records.RecordWithAnnotations$AnnotationRecordComponentOnly(value=z)", - "2", - "0", - "0"); - private static final String EXPECTED_RESULT_DESUGARED_RECORD_SUPPORT = - StringUtils.lines("Jane Doe", "42", "Jane Doe", "42", "false"); - private static final String EXPECTED_RESULT_DESUGARED_NO_RECORD_SUPPORT = - StringUtils.lines("Jane Doe", "42", "Jane Doe", "42", "Class.isRecord not present"); - - @Parameter(0) - public TestParameters parameters; - - @Parameter(1) - public Boolean keepAnnotations; - - // Enable once records are no longer partially desugared on platform. - public boolean recordDesugaringIsOffOnDex = false; - - @Parameters(name = "{0}, keepAnnotations: {1}") - public static List<Object[]> data() { - return buildParameters( - getTestParameters() - .withDexRuntimesAndAllApiLevels() - .withCfRuntimesStartingFromIncluding(CfVm.JDK17) - .withAllApiLevelsAlsoForCf() - .build(), - BooleanUtils.values()); - } - - @Test - public void testJvm() throws Exception { - parameters.assumeJvmTestParameters(); - assumeTrue(keepAnnotations); - testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput( - parameters.getRuntime().asCf().getVm().isLessThanOrEqualTo(CfVm.JDK20) - ? JVM_UNTIL_20_EXPECTED_RESULT - : JVM_FROM_21_EXPECTED_RESULT); - } - - @Test - public void testDesugaring() throws Exception { - parameters.assumeDexRuntime(); - assumeTrue(keepAnnotations); - testForDesugaring(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) - .applyIf( - parameters.isDexRuntime(), - r -> - r.assertSuccessWithOutput( - runtimeWithRecordsSupport(parameters.getRuntime()) - ? EXPECTED_RESULT_DESUGARED_RECORD_SUPPORT - : EXPECTED_RESULT_DESUGARED_NO_RECORD_SUPPORT), - r -> - r.assertSuccessWithOutput(ART_EXPECTED_RESULT) - .inspect( - inspector -> { - ClassSubject person = - inspector.clazz("records.RecordWithAnnotations$Person"); - FieldSubject name = person.uniqueFieldWithOriginalName("name"); - assertThat(name, isPresentAndNotRenamed()); - FieldSubject age = person.uniqueFieldWithOriginalName("age"); - assertThat(age, isPresentAndNotRenamed()); - assertEquals(2, person.getFinalRecordComponents().size()); - - assertEquals( - name.getFinalName(), - person.getFinalRecordComponents().get(0).getName()); - assertTrue( - person - .getFinalRecordComponents() - .get(0) - .getType() - .is("java.lang.String")); - assertNull(person.getFinalRecordComponents().get(0).getSignature()); - assertEquals( - 2, person.getFinalRecordComponents().get(0).getAnnotations().size()); - assertThat( - person.getFinalRecordComponents().get(0).getAnnotations(), - hasAnnotationTypes( - inspector.getTypeSubject( - "records.RecordWithAnnotations$Annotation"), - inspector.getTypeSubject( - "records.RecordWithAnnotations$AnnotationRecordComponentOnly"))); - assertThat( - person.getFinalRecordComponents().get(0).getAnnotations().get(0), - hasElements(new Pair<>("value", "a"))); - assertThat( - person.getFinalRecordComponents().get(0).getAnnotations().get(1), - hasElements(new Pair<>("value", "c"))); - - assertEquals( - age.getFinalName(), - person.getFinalRecordComponents().get(1).getName()); - assertTrue(person.getFinalRecordComponents().get(1).getType().is("int")); - assertNull(person.getFinalRecordComponents().get(1).getSignature()); - assertEquals( - 2, person.getFinalRecordComponents().get(1).getAnnotations().size()); - assertThat( - person.getFinalRecordComponents().get(1).getAnnotations(), - hasAnnotationTypes( - inspector.getTypeSubject( - "records.RecordWithAnnotations$Annotation"), - inspector.getTypeSubject( - "records.RecordWithAnnotations$AnnotationRecordComponentOnly"))); - assertThat( - person.getFinalRecordComponents().get(1).getAnnotations().get(0), - hasElements(new Pair<>("value", "x"))); - assertThat( - person.getFinalRecordComponents().get(1).getAnnotations().get(1), - hasElements(new Pair<>("value", "z"))); - })); - } - - @Test - public void testR8() throws Exception { - parameters.assumeR8TestParameters(); - testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - // TODO(b/231930852): Change to android.jar for Android U when that contains - // java.lang.Record. - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .addKeepMainRule(MAIN_TYPE) - .addKeepClassAndMembersRulesWithAllowObfuscation("records.RecordWithAnnotations$Person") - .addKeepClassAndMembersRules( - "records.RecordWithAnnotations$Annotation", - "records.RecordWithAnnotations$AnnotationFieldOnly", - "records.RecordWithAnnotations$AnnotationRecordComponentOnly") - .applyIf(keepAnnotations, TestShrinkerBuilder::addKeepRuntimeVisibleAnnotations) - .setMinApi(parameters) - .compile() - .inspect( - inspector -> { - ClassSubject person = inspector.clazz("records.RecordWithAnnotations$Person"); - FieldSubject name = person.uniqueFieldWithOriginalName("name"); - FieldSubject age = person.uniqueFieldWithOriginalName("age"); - if (parameters.isCfRuntime()) { - assertEquals(2, person.getFinalRecordComponents().size()); - - assertEquals( - name.getFinalName(), person.getFinalRecordComponents().get(0).getName()); - assertTrue( - person.getFinalRecordComponents().get(0).getType().is("java.lang.String")); - assertNull(person.getFinalRecordComponents().get(0).getSignature()); - assertEquals(2, person.getFinalRecordComponents().get(0).getAnnotations().size()); - assertThat( - person.getFinalRecordComponents().get(0).getAnnotations(), - hasAnnotationTypes( - inspector.getTypeSubject("records.RecordWithAnnotations$Annotation"), - inspector.getTypeSubject( - "records.RecordWithAnnotations$AnnotationRecordComponentOnly"))); - assertThat( - person.getFinalRecordComponents().get(0).getAnnotations().get(0), - hasElements(new Pair<>("value", "a"))); - assertThat( - person.getFinalRecordComponents().get(0).getAnnotations().get(1), - hasElements(new Pair<>("value", "c"))); - - assertEquals( - age.getFinalName(), person.getFinalRecordComponents().get(1).getName()); - assertTrue(person.getFinalRecordComponents().get(1).getType().is("int")); - assertNull(person.getFinalRecordComponents().get(1).getSignature()); - assertEquals(2, person.getFinalRecordComponents().get(1).getAnnotations().size()); - assertThat( - person.getFinalRecordComponents().get(1).getAnnotations(), - hasAnnotationTypes( - inspector.getTypeSubject("records.RecordWithAnnotations$Annotation"), - inspector.getTypeSubject( - "records.RecordWithAnnotations$AnnotationRecordComponentOnly"))); - assertThat( - person.getFinalRecordComponents().get(1).getAnnotations().get(0), - hasElements(new Pair<>("value", "x"))); - assertThat( - person.getFinalRecordComponents().get(1).getAnnotations().get(1), - hasElements(new Pair<>("value", "z"))); - } else { - assertEquals(0, person.getFinalRecordComponents().size()); - } - }) - .run(parameters.getRuntime(), MAIN_TYPE) - .applyIf( - // TODO(b/274888318): EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS still has component - // annotations. - parameters.isCfRuntime(), - r -> - r.assertSuccessWithOutput( - keepAnnotations - ? JVM_EXPECTED_RESULT_R8 - : JVM_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS), - recordDesugaringIsOffOnDex, - r -> - r.assertSuccessWithOutput( - keepAnnotations - ? ART_EXPECTED_RESULT_R8 - : ART_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS), - r -> - r.assertSuccessWithOutput( - runtimeWithRecordsSupport(parameters.getRuntime()) - ? EXPECTED_RESULT_DESUGARED_RECORD_SUPPORT - : EXPECTED_RESULT_DESUGARED_NO_RECORD_SUPPORT)); - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java deleted file mode 100644 index 8be5a1d..0000000 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java +++ /dev/null
@@ -1,96 +0,0 @@ -// Copyright (c) 2021, 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.desugar.records; - -import com.android.tools.r8.R8FullTestBuilder; -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.desugar.LibraryFilesHelper; -import com.android.tools.r8.utils.StringUtils; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -public class RecordInvokeCustomTest extends TestBase { - - private static final String RECORD_NAME = "RecordInvokeCustom"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); - private static final String EXPECTED_RESULT = - StringUtils.lines( - "%s[]", - "true", - "true", - "true", - "true", - "true", - "false", - "true", - "true", - "false", - "false", - "%s[%s=Jane Doe, %s=42]"); - private static final String EXPECTED_RESULT_D8 = - String.format(EXPECTED_RESULT, "Empty", "Person", "name", "age"); - private static final String EXPECTED_RESULT_R8 = - String.format(EXPECTED_RESULT, "a", "b", "a", "b"); - - private final TestParameters parameters; - - public RecordInvokeCustomTest(TestParameters parameters) { - this.parameters = parameters; - } - - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK17) - .withDexRuntimes() - .withAllApiLevelsAlsoForCf() - .build(); - } - - @Test - public void testJvm() throws Exception { - parameters.assumeJvmTestParameters(); - testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput(EXPECTED_RESULT_D8); - } - - @Test - public void testD8() throws Exception { - testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - .setMinApi(parameters) - .compile() - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput(EXPECTED_RESULT_D8); - } - - @Test - public void testR8() throws Exception { - parameters.assumeR8TestParameters(); - R8FullTestBuilder builder = - testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE); - if (parameters.isCfRuntime()) { - builder - .addLibraryFiles(LibraryFilesHelper.getJdk15LibraryFiles(temp)) - .compile() - .inspect(RecordTestUtils::assertRecordsAreRecords) - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput(EXPECTED_RESULT_R8); - return; - } - builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT_R8); - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java deleted file mode 100644 index 6a63449..0000000 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright (c) 2021, 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.desugar.records; - -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.desugar.LibraryFilesHelper; -import com.android.tools.r8.utils.StringUtils; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -public class RecordReflectionTest extends TestBase { - - private static final String RECORD_NAME = "RecordReflection"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); - private static final String EXPECTED_RESULT = - StringUtils.lines( - "true", - "[]", - "true", - "[java.lang.String name, int age]", - "true", - "[java.lang.CharSequence name, int age]", - "[S]", - "false", - "null"); - - private final TestParameters parameters; - - public RecordReflectionTest(TestParameters parameters) { - this.parameters = parameters; - } - - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK17).build(); - } - - @Test - public void testJvm() throws Exception { - parameters.assumeJvmTestParameters(); - testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput(EXPECTED_RESULT); - } - - @Test - public void testR8Cf() throws Exception { - testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE) - .addKeepAllAttributes() - .addKeepRules("-keep class * extends java.lang.Record { private final <fields>; }") - .addLibraryFiles(LibraryFilesHelper.getJdk15LibraryFiles(temp)) - .compile() - .inspect(RecordTestUtils::assertRecordsAreRecords) - .enableJVMPreview() - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput(EXPECTED_RESULT); - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java deleted file mode 100644 index 1194f76..0000000 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java +++ /dev/null
@@ -1,100 +0,0 @@ -// Copyright (c) 2021, 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.desugar.records; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.android.tools.r8.TestParameters; -import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.desugar.LibraryFilesHelper; -import com.android.tools.r8.utils.AndroidApiLevel; -import com.android.tools.r8.utils.StringUtils; -import com.android.tools.r8.utils.codeinspector.CodeInspector; -import com.android.tools.r8.utils.codeinspector.FoundClassSubject; -import com.google.common.io.ByteStreams; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import org.junit.rules.TemporaryFolder; - -/** - * Records are compiled using: third_party/openjdk/jdk-15/linux/bin/javac --release 15 - * --enable-preview path/to/file.java - */ -public class RecordTestUtils { - - private static final String EXAMPLE_FOLDER = "examplesJava17"; - private static final String RECORD_FOLDER = "records"; - - public static Path jar() { - return Paths.get(ToolHelper.TESTS_BUILD_DIR, EXAMPLE_FOLDER, RECORD_FOLDER + ".jar"); - } - - public static Path[] getJdk15LibraryFiles(TemporaryFolder temp) throws Exception { - return LibraryFilesHelper.getJdk15LibraryFiles(temp); - } - - public static byte[][] getProgramData(String mainClassSimpleName) { - byte[][] bytes = classDataFromPrefix(RECORD_FOLDER + "/" + mainClassSimpleName); - assert bytes.length > 0 : "Did not find any program data for " + mainClassSimpleName; - return bytes; - } - - public static String getMainType(String mainClassSimpleName) { - return RECORD_FOLDER + "." + mainClassSimpleName; - } - - private static byte[][] classDataFromPrefix(String prefix) { - Path examplePath = jar(); - if (!Files.exists(examplePath)) { - throw new RuntimeException( - "Could not find path " - + examplePath - + ". Build " - + EXAMPLE_FOLDER - + " by running tools/gradle.py build" - + StringUtils.capitalize(EXAMPLE_FOLDER)); - } - List<byte[]> result = new ArrayList<>(); - try (ZipFile zipFile = new ZipFile(examplePath.toFile())) { - Enumeration<? extends ZipEntry> entries = zipFile.entries(); - while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); - if (zipEntry.getName().startsWith(prefix)) { - result.add(ByteStreams.toByteArray(zipFile.getInputStream(zipEntry))); - } - } - } catch (IOException e) { - throw new RuntimeException("Could not read zip-entry from " + examplePath.toString(), e); - } - if (result.isEmpty()) { - throw new RuntimeException("Did not find any class with prefix " + prefix); - } - return result.toArray(new byte[0][0]); - } - - public static void assertRecordsAreRecords(CodeInspector inspector) { - for (FoundClassSubject clazz : inspector.allClasses()) { - if (clazz.getDexProgramClass().superType.toString().equals("java.lang.Record")) { - assertTrue(clazz.getDexProgramClass().isRecord()); - } - } - } - - public static void assertNoJavaLangRecord(CodeInspector inspector, TestParameters parameters) { - if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.V)) { - assertFalse(inspector.clazz("java.lang.RecordTag").isPresent()); - } else { - assertFalse(inspector.clazz("java.lang.Record").isPresent()); - } - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java deleted file mode 100644 index e4c6be4..0000000 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java +++ /dev/null
@@ -1,80 +0,0 @@ -// Copyright (c) 2021, 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.desugar.records; - -import com.android.tools.r8.R8FullTestBuilder; -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.utils.StringUtils; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -public class RecordWithMembersTest extends TestBase { - - private static final String RECORD_NAME = "RecordWithMembers"; - private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); - private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); - private static final String EXPECTED_RESULT = - StringUtils.lines( - "BobX", "43", "BobX", "43", "FelixX", "-1", "FelixX", "-1", "print", "Bob43", "extra"); - - private final TestParameters parameters; - - public RecordWithMembersTest(TestParameters parameters) { - this.parameters = parameters; - } - - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK17) - .withDexRuntimes() - .withAllApiLevelsAlsoForCf() - .build(); - } - - @Test - public void testJvm() throws Exception { - parameters.assumeJvmTestParameters(); - testForJvm(parameters) - .addProgramClassFileData(PROGRAM_DATA) - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput(EXPECTED_RESULT); - } - - @Test - public void testD8() throws Exception { - testForD8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - .setMinApi(parameters) - .compile() - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput(EXPECTED_RESULT); - } - - @Test - public void testR8() throws Exception { - parameters.assumeR8TestParameters(); - R8FullTestBuilder builder = - testForR8(parameters.getBackend()) - .addProgramClassFileData(PROGRAM_DATA) - .setMinApi(parameters) - .addKeepMainRule(MAIN_TYPE); - if (parameters.isCfRuntime()) { - builder - .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .compile() - .inspect(RecordTestUtils::assertRecordsAreRecords) - .run(parameters.getRuntime(), MAIN_TYPE) - .assertSuccessWithOutput(EXPECTED_RESULT); - return; - } - builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT); - } -}
diff --git a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java index d988c22..fcd2106 100644 --- a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java +++ b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
@@ -9,10 +9,10 @@ import com.android.tools.r8.GlobalSyntheticsGenerator; import com.android.tools.r8.GlobalSyntheticsGeneratorCommand; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java index d37c3f8..7853976 100644 --- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java +++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
@@ -4,21 +4,14 @@ package com.android.tools.r8.ir.desugar.annotations; -import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; -import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType; -import static com.android.tools.r8.utils.codeinspector.AssertUtils.assertFailsCompilation; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; -import static junit.framework.TestCase.assertTrue; -import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.AllOf.allOf; import static org.junit.Assert.assertEquals; import com.android.tools.r8.AsmTestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.MethodSubject; @@ -26,6 +19,7 @@ import java.nio.file.Path; import java.util.Collections; import java.util.List; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -40,6 +34,7 @@ public static final String CRTS_INNER_NAME = "CovariantReturnTypes"; public static final String CRTS_BINARY_NAME = CRT_BINARY_NAME + "$" + CRTS_INNER_NAME; + public static final String CRT_TYPE_NAME = CRT_BINARY_NAME.replace('/', '.'); public static final String CRTS_TYPE_NAME = CRT_BINARY_NAME.replace('/', '.'); @Parameter(0) @@ -105,8 +100,7 @@ checkPresenceOfCovariantAnnotations(input, true); // Version 2 of the library should always work. - succeedsWithOption(input, true, true); - failsCompilationByDefault(input); + succeedsIndependentOfFlag(input, true); } @Test @@ -130,7 +124,7 @@ // If CovariantReturnType annotations are ignored, then there will be no methods with the // signatures "L.../B;->method()L.../B;" and "L.../C;->method()L.../C;". - failsCompilationByDefault(input); + failsWithOption(input, false); } @Test @@ -224,24 +218,7 @@ .assertSuccessWithOutput(getExpectedOutput()); } - private void failsCompilationByDefault(List<byte[]> input) throws Exception { - assertFailsCompilation( - () -> - testForD8(parameters.getBackend()) - .addProgramClassFileData(input) - .setMinApi(parameters) - .compileWithExpectedDiagnostics( - diagnostics -> - diagnostics.assertErrorThatMatches( - allOf( - diagnosticType(StringDiagnostic.class), - diagnosticMessage( - equalTo( - "Unexpected @CovariantReturnType annotation in non-platform" - + " build")))))); - } - - private void failsRuntimeWithOption(List<byte[]> input, boolean option) throws Exception { + private void failsWithOption(List<byte[]> input, boolean option) throws Exception { testForD8(parameters.getBackend()) .addProgramClassFileData(input) .addOptionsModification(options -> options.processCovariantReturnTypeAnnotations = option) @@ -259,8 +236,8 @@ } private void failsIndependentOfFlag(List<byte[]> input) throws Exception { - failsRuntimeWithOption(input, true); - failsRuntimeWithOption(input, false); + failsWithOption(input, true); + failsWithOption(input, false); } private void checkPresenceOfCovariantAnnotations(List<byte[]> input, boolean expected) @@ -294,34 +271,34 @@ MethodSubject methodA = clazzA.method(A.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodA, isPresent()); - assertTrue(!methodA.getMethod().isSyntheticMethod()); + Assert.assertTrue(!methodA.getMethod().isSyntheticMethod()); MethodSubject methodB = clazzB.method(A.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodB, isPresent()); - assertTrue(!methodB.getMethod().isSyntheticMethod()); + Assert.assertTrue(!methodB.getMethod().isSyntheticMethod()); MethodSubject methodC = clazzC.method(A.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodC, isPresent()); - assertTrue(!methodC.getMethod().isSyntheticMethod()); + Assert.assertTrue(!methodC.getMethod().isSyntheticMethod()); // Check that a synthetic method has been added to class B. MethodSubject methodB2 = clazzB.method(B.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodB2, isPresent()); - assertTrue(methodB2.getMethod().isSyntheticMethod()); + Assert.assertTrue(methodB2.getMethod().isSyntheticMethod()); // Check that two synthetic methods have been added to class C. MethodSubject methodC2 = clazzC.method(B.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodC2, isPresent()); - assertTrue(methodC2.getMethod().isSyntheticMethod()); + Assert.assertTrue(methodC2.getMethod().isSyntheticMethod()); MethodSubject methodC3 = clazzC.method(C.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodC3, isPresent()); - assertTrue(methodC3.getMethod().isSyntheticMethod()); + Assert.assertTrue(methodC3.getMethod().isSyntheticMethod()); // Get classes D, E, and F. ClassSubject clazzD = inspector.clazz(D.class.getCanonicalName()); @@ -337,34 +314,34 @@ MethodSubject methodD = clazzD.method(D.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodD, isPresent()); - assertTrue(!methodD.getMethod().isSyntheticMethod()); + Assert.assertTrue(!methodD.getMethod().isSyntheticMethod()); MethodSubject methodE = clazzE.method(D.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodE, isPresent()); - assertTrue(!methodE.getMethod().isSyntheticMethod()); + Assert.assertTrue(!methodE.getMethod().isSyntheticMethod()); MethodSubject methodF = clazzF.method(D.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodF, isPresent()); - assertTrue(!methodF.getMethod().isSyntheticMethod()); + Assert.assertTrue(!methodF.getMethod().isSyntheticMethod()); // Check that a synthetic method has been added to class E. MethodSubject methodE2 = clazzE.method(E.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodE2, isPresent()); - assertTrue(methodE2.getMethod().isSyntheticMethod()); + Assert.assertTrue(methodE2.getMethod().isSyntheticMethod()); // Check that two synthetic methods have been added to class F. MethodSubject methodF2 = clazzF.method(E.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodF2, isPresent()); - assertTrue(methodF2.getMethod().isSyntheticMethod()); + Assert.assertTrue(methodF2.getMethod().isSyntheticMethod()); MethodSubject methodF3 = clazzF.method(F.class.getCanonicalName(), "method", Collections.emptyList()); assertThat(methodF3, isPresent()); - assertTrue(methodF3.getMethod().isSyntheticMethod()); + Assert.assertTrue(methodF3.getMethod().isSyntheticMethod()); } private String getExpectedOutput() {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java index f921cd6..8d9cc75 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -3,13 +3,17 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72; +import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20; import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_7_0; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.KotlinTestParameters; +import com.android.tools.r8.R8TestBuilder; import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.utils.codeinspector.AnnotationSubject; @@ -60,7 +64,16 @@ .allowUnusedDontWarnJavaLangClassValue( kotlinc.getCompilerVersion().isGreaterThan(KOTLINC_1_7_0)) .apply(configureForLibraryWithEmbeddedProguardRules()) - .compile() + .applyIf(kotlinc.is(KOTLINC_1_4_20), R8TestBuilder::allowDiagnosticWarningMessages) + .compileWithExpectedDiagnostics( + diagnostics -> { + if (kotlinc.is(KOTLINC_1_4_20)) { + diagnostics.assertWarningsMatch( + diagnosticMessage( + containsString( + "'META-INF/versions/9/module-info.class' already exists."))); + } + }) .assertNoErrorMessages() .run(parameters.getRuntime(), mainClassName); CodeInspector inspector = result.inspector();
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java index 7a54089..2149835 100644 --- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -115,7 +115,9 @@ && kotlinParameters.getCompiler().isNot(KOTLINC_1_9_21) && !kotlinParameters.isKotlinDev(), TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) - .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib) + .applyIf( + kotlinParameters.getCompiler().isNot(KOTLINC_1_3_72), + KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib) .writeToZip(foo.toPath()) .run(parameters.getRuntime(), PKG + ".SimpleReflectKt") .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/synthesis/globals/GlobalSyntheticStubContextRegressionTest.java b/src/test/java/com/android/tools/r8/synthesis/globals/GlobalSyntheticStubContextRegressionTest.java index 54f5f0c..9a56493 100644 --- a/src/test/java/com/android/tools/r8/synthesis/globals/GlobalSyntheticStubContextRegressionTest.java +++ b/src/test/java/com/android/tools/r8/synthesis/globals/GlobalSyntheticStubContextRegressionTest.java
@@ -7,6 +7,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.OutputMode; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java b/src/test/testbase/java/com/android/tools/r8/DesugarGraphTestConsumer.java similarity index 97% rename from src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java rename to src/test/testbase/java/com/android/tools/r8/DesugarGraphTestConsumer.java index 541bfea..29efc40 100644 --- a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java +++ b/src/test/testbase/java/com/android/tools/r8/DesugarGraphTestConsumer.java
@@ -1,13 +1,12 @@ // Copyright (c) 2019, 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.desugar.graph; +package com.android.tools.r8; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; -import com.android.tools.r8.DesugarGraphConsumer; import com.android.tools.r8.origin.GlobalSyntheticOrigin; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.StringUtils;
diff --git a/src/test/java/com/android/tools/r8/synthesis/globals/GlobalSyntheticsTestingConsumer.java b/src/test/testbase/java/com/android/tools/r8/GlobalSyntheticsTestingConsumer.java similarity index 90% rename from src/test/java/com/android/tools/r8/synthesis/globals/GlobalSyntheticsTestingConsumer.java rename to src/test/testbase/java/com/android/tools/r8/GlobalSyntheticsTestingConsumer.java index 9f1fdaf..da3905b 100644 --- a/src/test/java/com/android/tools/r8/synthesis/globals/GlobalSyntheticsTestingConsumer.java +++ b/src/test/testbase/java/com/android/tools/r8/GlobalSyntheticsTestingConsumer.java
@@ -1,17 +1,13 @@ // Copyright (c) 2022, 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.synthesis.globals; +package com.android.tools.r8; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import com.android.tools.r8.ByteDataView; -import com.android.tools.r8.DiagnosticsHandler; -import com.android.tools.r8.GlobalSyntheticsConsumer; -import com.android.tools.r8.GlobalSyntheticsResourceProvider; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.references.ClassReference; import java.io.ByteArrayInputStream;
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestBaseBuilder.java index 6746b89..98cb209 100644 --- a/src/test/testbase/java/com/android/tools/r8/TestBaseBuilder.java +++ b/src/test/testbase/java/com/android/tools/r8/TestBaseBuilder.java
@@ -4,12 +4,16 @@ package com.android.tools.r8; +import static com.android.tools.r8.TestBase.descriptor; + import com.android.tools.r8.ProgramResource.Kind; import com.android.tools.r8.dump.CompilerDump; import com.android.tools.r8.dump.DumpOptions; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.references.ClassReference; import com.android.tools.r8.references.Reference; +import com.android.tools.r8.transformers.ClassFileTransformer.FieldPredicate; +import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.ListUtils; import com.google.common.collect.ImmutableMap; @@ -35,6 +39,19 @@ this.builder = builder; } + public T addStrippedOuter(Class<?> clazz, Origin origin) throws IOException { + builder.addClassProgramData( + TestBase.transformer(clazz) + .removeFields(FieldPredicate.all()) + .removeMethods(MethodPredicate.all()) + .removeAllAnnotations() + .setSuper(descriptor(Object.class)) + .setImplements() + .transform(), + origin); + return self(); + } + @Override public T addProgramClassFileData(Collection<byte[]> classes) { for (byte[] clazz : classes) {
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestBuilder.java index 5f9d323..4b18fcb 100644 --- a/src/test/testbase/java/com/android/tools/r8/TestBuilder.java +++ b/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
@@ -17,6 +17,8 @@ import com.android.tools.r8.utils.structural.Ordered; import com.google.common.collect.ImmutableList; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; @@ -212,6 +214,21 @@ return self(); } + public T addClasspathClassesAndInnerClasses(Class<?> clazz) + throws InvocationTargetException, IllegalAccessException { + Method getNestMembers; + try { + getNestMembers = Class.class.getDeclaredMethod("getNestMembers"); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Can only use this method from Java 11 and above."); + } + Class<?>[] nestMembers = (Class<?>[]) getNestMembers.invoke(clazz); + Arrays.stream(nestMembers) + .filter(c -> c.toString().startsWith(clazz.toString())) + .forEach(this::addClasspathClasses); + return self(); + } + public T addClasspathClasses(Class<?>... classes) { return addClasspathClasses(Arrays.asList(classes)); }
diff --git a/tools/perf/chart.js b/tools/perf/chart.js index 96c7f07..baaa33b 100644 --- a/tools/perf/chart.js +++ b/tools/perf/chart.js
@@ -69,6 +69,9 @@ } else { update(false, false); } + document.addEventListener('keydown', e => { + state.handleKeyDownEvent(e, () => update(true, false)); + }); } function setDataProvider(theDataProvider) {
diff --git a/tools/perf/state.js b/tools/perf/state.js index 4d0128f..ed07b39 100644 --- a/tools/perf/state.js +++ b/tools/perf/state.js
@@ -99,6 +99,34 @@ } } +function handleKeyDownEvent(e, callback) { + if (selectedBenchmarks.size != 1) { + return; + } + const [selectedBenchmark] = selectedBenchmarks; + var benchmarkToSelect = null; + var previousBenchmark = null; + for (const benchmark of benchmarks.values()) { + if (previousBenchmark != null) { + if (e.key == 'ArrowLeft' && benchmark == selectedBenchmark) { + benchmarkToSelect = previousBenchmark; + break; + } else if (e.key === 'ArrowRight' && previousBenchmark == selectedBenchmark) { + benchmarkToSelect = benchmark; + break; + } + } + previousBenchmark = benchmark; + } + if (benchmarkToSelect != null) { + selectedBenchmarks.clear(); + selectedBenchmarks.add(benchmarkToSelect); + document.getElementById(selectedBenchmark).checked = false; + document.getElementById(benchmarkToSelect).checked = true; + callback(); + } +} + function isLegendSelected(legend) { return selectedLegends.has(legend); } @@ -111,6 +139,7 @@ selectedLegends: selectedLegends, forEachBenchmark: forEachBenchmark, forEachSelectedBenchmark: forEachSelectedBenchmark, + handleKeyDownEvent: handleKeyDownEvent, hasLegend: hasLegend, initializeBenchmarks: initializeBenchmarks, initializeLegends: initializeLegends,