Add a test for adding lambdas to baseline profile
Bug: b/265729283
Change-Id: I375e37883e20078b03cdfd398448500ab5d470d4
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
new file mode 100644
index 0000000..3e9ef1c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
@@ -0,0 +1,97 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.model.ExternalArtProfileMethodRule;
+import com.android.tools.r8.profile.art.utils.ArtProfileTestingUtils;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+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 SyntheticLambdaClassProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .apply(
+ testBuilder ->
+ ArtProfileTestingUtils.addArtProfileForRewriting(
+ getArtProfile(), this::inspectResidualArtProfile, testBuilder))
+ .noHorizontalClassMergingOfSynthetics()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private ExternalArtProfile getArtProfile() {
+ return ExternalArtProfile.builder()
+ .addRule(
+ ExternalArtProfileMethodRule.builder()
+ .setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
+ .build())
+ .build();
+ }
+
+ private void inspect(CodeInspector inspector) {
+ // Verify that two lambdas were synthesized when compiling to dex.
+ assertThat(
+ inspector.clazz(SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 0)),
+ onlyIf(parameters.isDexRuntime(), isPresent()));
+ assertThat(
+ inspector.clazz(SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 1)),
+ onlyIf(parameters.isDexRuntime(), isPresent()));
+ }
+
+ private void inspectResidualArtProfile(ExternalArtProfile residualArtProfile) {
+ if (parameters.isCfRuntime()) {
+ assertEquals(getArtProfile(), residualArtProfile);
+ } else {
+ assert parameters.isDexRuntime();
+ // TODO(b/265729283): Since Main.main() is in the art profile, so should the two synthetic
+ // lambdas be.
+ assertEquals(getArtProfile(), residualArtProfile);
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Runnable lambda =
+ System.currentTimeMillis() > 0
+ ? () -> System.out.println("Hello, world!")
+ : () -> {
+ throw new RuntimeException();
+ };
+ lambda.run();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfile.java b/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfile.java
new file mode 100644
index 0000000..1c82f55
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfile.java
@@ -0,0 +1,69 @@
+// 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.profile.art.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Represents an ART baseline profile (for testing).
+ *
+ * <p>This is similar to {@link com.android.tools.r8.profile.art.ArtProfile}, but unlike ArtProfile,
+ * this ExternalArtProfile is backed by {@link com.android.tools.r8.references.Reference}, and not
+ * {@link com.android.tools.r8.graph.DexItem}, so that no {@link
+ * com.android.tools.r8.graph.DexItemFactory} is needed to create an ExternalArtProfile.
+ */
+public class ExternalArtProfile {
+
+ private final List<ExternalArtProfileRule> rules;
+
+ ExternalArtProfile(List<ExternalArtProfileRule> rules) {
+ this.rules = rules;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public void forEach(
+ Consumer<ExternalArtProfileClassRule> classRuleConsumer,
+ Consumer<ExternalArtProfileMethodRule> methodRuleConsumer) {
+ for (ExternalArtProfileRule rule : rules) {
+ rule.accept(classRuleConsumer, methodRuleConsumer);
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ ExternalArtProfile profile = (ExternalArtProfile) obj;
+ return rules.equals(profile.rules);
+ }
+
+ @Override
+ public int hashCode() {
+ return rules.hashCode();
+ }
+
+ public static class Builder {
+
+ private final List<ExternalArtProfileRule> rules = new ArrayList<>();
+
+ public Builder addRule(ExternalArtProfileRule rule) {
+ rules.add(rule);
+ return this;
+ }
+
+ public ExternalArtProfile build() {
+ return new ExternalArtProfile(rules);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileClassRule.java b/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileClassRule.java
new file mode 100644
index 0000000..84cc34b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileClassRule.java
@@ -0,0 +1,68 @@
+// 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.profile.art.model;
+
+import com.android.tools.r8.references.ClassReference;
+import java.util.function.Consumer;
+
+/**
+ * Represents a class rule from an ART baseline profile, backed by {@link ClassReference}. Class
+ * rules currently contain no other information than the class reference.
+ */
+public class ExternalArtProfileClassRule extends ExternalArtProfileRule {
+
+ private final ClassReference classReference;
+
+ ExternalArtProfileClassRule(ClassReference classReference) {
+ assert classReference != null;
+ this.classReference = classReference;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void accept(
+ Consumer<ExternalArtProfileClassRule> classRuleConsumer,
+ Consumer<ExternalArtProfileMethodRule> methodRuleConsumer) {
+ classRuleConsumer.accept(this);
+ }
+
+ public ClassReference getClassReference() {
+ return classReference;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ ExternalArtProfileClassRule methodRule = (ExternalArtProfileClassRule) obj;
+ return classReference.equals(methodRule.classReference);
+ }
+
+ @Override
+ public int hashCode() {
+ return classReference.hashCode();
+ }
+
+ public static class Builder {
+
+ private ClassReference classReference;
+
+ public Builder setClassReference(ClassReference classReference) {
+ this.classReference = classReference;
+ return this;
+ }
+
+ public ExternalArtProfileClassRule build() {
+ return new ExternalArtProfileClassRule(classReference);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileMethodRule.java b/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileMethodRule.java
new file mode 100644
index 0000000..fae6dbe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileMethodRule.java
@@ -0,0 +1,77 @@
+// 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.profile.art.model;
+
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl;
+import com.android.tools.r8.references.MethodReference;
+import java.util.function.Consumer;
+
+/** Represents a method rule from an ART baseline profile, backed by {@link MethodReference}. */
+public class ExternalArtProfileMethodRule extends ExternalArtProfileRule {
+
+ private final MethodReference methodReference;
+ private final ArtProfileMethodRuleInfo methodRuleInfo;
+
+ ExternalArtProfileMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ assert methodReference != null;
+ assert methodRuleInfo != null;
+ this.methodReference = methodReference;
+ this.methodRuleInfo = methodRuleInfo;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void accept(
+ Consumer<ExternalArtProfileClassRule> classRuleConsumer,
+ Consumer<ExternalArtProfileMethodRule> methodRuleConsumer) {
+ methodRuleConsumer.accept(this);
+ }
+
+ public MethodReference getMethodReference() {
+ return methodReference;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ ExternalArtProfileMethodRule methodRule = (ExternalArtProfileMethodRule) obj;
+ return methodReference.equals(methodRule.methodReference);
+ }
+
+ @Override
+ public int hashCode() {
+ return methodReference.hashCode();
+ }
+
+ public static class Builder {
+
+ private MethodReference methodReference;
+ private ArtProfileMethodRuleInfo methodRuleInfo = ArtProfileMethodRuleInfoImpl.empty();
+
+ public Builder setMethodReference(MethodReference methodReference) {
+ this.methodReference = methodReference;
+ return this;
+ }
+
+ public Builder setMethodRuleInfo(ArtProfileMethodRuleInfo methodRuleInfo) {
+ this.methodRuleInfo = methodRuleInfo;
+ return this;
+ }
+
+ public ExternalArtProfileMethodRule build() {
+ return new ExternalArtProfileMethodRule(methodReference, methodRuleInfo);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileRule.java b/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileRule.java
new file mode 100644
index 0000000..bd3c99e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/model/ExternalArtProfileRule.java
@@ -0,0 +1,14 @@
+// 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.profile.art.model;
+
+import java.util.function.Consumer;
+
+public abstract class ExternalArtProfileRule {
+
+ public abstract void accept(
+ Consumer<ExternalArtProfileClassRule> classRuleConsumer,
+ Consumer<ExternalArtProfileMethodRule> methodRuleConsumer);
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileTestingUtils.java b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileTestingUtils.java
new file mode 100644
index 0000000..0fa755b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileTestingUtils.java
@@ -0,0 +1,95 @@
+// 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.profile.art.utils;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.ArtProfileBuilder;
+import com.android.tools.r8.profile.art.ArtProfileClassRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileConsumer;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
+import com.android.tools.r8.profile.art.ArtProfileRuleConsumer;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.model.ExternalArtProfileClassRule;
+import com.android.tools.r8.profile.art.model.ExternalArtProfileMethodRule;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import java.util.function.Consumer;
+
+public class ArtProfileTestingUtils {
+
+ /**
+ * Adds the given {@param artProfile} as an ART profile for rewriting. The residual ART profile
+ * will be forwarded to the given test inspector, {@param residualArtProfileInspector}.
+ */
+ public static void addArtProfileForRewriting(
+ ExternalArtProfile artProfile,
+ Consumer<ExternalArtProfile> residualArtProfileInspector,
+ R8TestBuilder<?> testBuilder) {
+ // Provider for passing the original ART profile to the compilation.
+ ArtProfileProvider artProfileProvider =
+ new ArtProfileProvider() {
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder profileBuilder) {
+ artProfile.forEach(
+ classRule ->
+ profileBuilder.addClassRule(
+ classRuleBuilder ->
+ classRuleBuilder.setClassReference(classRule.getClassReference())),
+ methodRule ->
+ profileBuilder.addMethodRule(
+ methodRuleBuilder ->
+ methodRuleBuilder.setMethodReference(methodRule.getMethodReference())));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+
+ // Consumer for accepting the residual ART profile from the compilation.
+ ArtProfileConsumer residualArtProfileConsumer =
+ new ArtProfileConsumer() {
+
+ final ExternalArtProfile.Builder residualArtProfileBuilder = ExternalArtProfile.builder();
+
+ @Override
+ public ArtProfileRuleConsumer getRuleConsumer() {
+ return new ArtProfileRuleConsumer() {
+
+ @Override
+ public void acceptClassRule(
+ ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+ residualArtProfileBuilder.addRule(
+ ExternalArtProfileClassRule.builder()
+ .setClassReference(classReference)
+ .build());
+ }
+
+ @Override
+ public void acceptMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ residualArtProfileBuilder.addRule(
+ ExternalArtProfileMethodRule.builder()
+ .setMethodReference(methodReference)
+ .setMethodRuleInfo(methodRuleInfo)
+ .build());
+ }
+ };
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ residualArtProfileInspector.accept(residualArtProfileBuilder.build());
+ }
+ };
+
+ testBuilder.addArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer);
+ }
+}