Tests for adding synthetics to baseline profile
Bug: b/265729283
Change-Id: I736069f4ceeecf5783f75cd6b608cf89dce5e670
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
index 428bdd0..310856f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
@@ -28,7 +28,7 @@
/** This class defines the desugaring of a single invoke-special instruction. */
public class InvokeSpecialToSelfDesugaring implements CfInstructionDesugaring {
- private static final String INVOKE_SPECIAL_BRIDGE_PREFIX = "$invoke$special$";
+ public static final String INVOKE_SPECIAL_BRIDGE_PREFIX = "$invoke$special$";
private final DexItemFactory dexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index 1b3e48e..ad59051 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -56,14 +56,13 @@
// Short names to avoid creating long strings
public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
- private static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
- private static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX =
- NEST_ACCESS_NAME_PREFIX + "sm";
- private static final String NEST_ACCESS_FIELD_GET_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fget";
- private static final String NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX =
+ public static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
+ public static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "sm";
+ public static final String NEST_ACCESS_FIELD_GET_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fget";
+ public static final String NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX =
NEST_ACCESS_NAME_PREFIX + "sfget";
- private static final String NEST_ACCESS_FIELD_PUT_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fput";
- private static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX =
+ public static final String NEST_ACCESS_FIELD_PUT_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fput";
+ public static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX =
NEST_ACCESS_NAME_PREFIX + "sfput";
protected final AppView<?> appView;
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index b95ac53..335e328 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -68,6 +68,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.nest.Nest;
import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MapVersion;
@@ -819,7 +820,7 @@
new CallSiteOptimizationOptions();
private final CfCodeAnalysisOptions cfCodeAnalysisOptions = new CfCodeAnalysisOptions();
private final ClassInlinerOptions classInlinerOptions = new ClassInlinerOptions();
- private final InlinerOptions inlinerOptions = new InlinerOptions();
+ private final InlinerOptions inlinerOptions = new InlinerOptions(this);
private final HorizontalClassMergerOptions horizontalClassMergerOptions =
new HorizontalClassMergerOptions();
private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
@@ -1528,7 +1529,7 @@
boolean test(AppView<?> appView, ProgramMethod method, int inliningDepth);
}
- public class InlinerOptions {
+ public static class InlinerOptions {
public boolean enableInlining =
!parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.disableinlining", false);
@@ -1559,17 +1560,31 @@
public ApplyInliningToInlineePredicate applyInliningToInlineePredicateForTesting = null;
+ private final InternalOptions options;
+
+ public InlinerOptions(InternalOptions options) {
+ this.options = options;
+ }
+
+ public static void disableInlining(InternalOptions options) {
+ options.inlinerOptions().enableInlining = false;
+ }
+
+ public static void setOnlyForceInlining(InternalOptions options) {
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ }
+
public int getSimpleInliningInstructionLimit() {
// If a custom simple inlining instruction limit is set, then use that.
if (simpleInliningInstructionLimit >= 0) {
return simpleInliningInstructionLimit;
}
// Allow 3 instructions when generating to class files.
- if (isGeneratingClassFiles()) {
+ if (options.isGeneratingClassFiles()) {
return 3;
}
// Allow the size of the dex code to be up to 5 bytes.
- assert isGeneratingDex();
+ assert options.isGeneratingDex();
return 5;
}
@@ -1578,7 +1593,7 @@
if (applyInliningToInlineePredicateForTesting != null) {
return applyInliningToInlineePredicateForTesting.test(appView, inlinee, inliningDepth);
}
- if (protoShrinking.shouldApplyInliningToInlinee(appView, inlinee, inliningDepth)) {
+ if (options.protoShrinking().shouldApplyInliningToInlinee(appView, inlinee, inliningDepth)) {
return true;
}
return false;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index b487caf..8b2877f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -205,7 +205,7 @@
};
}
- static ApiModelingClassVerificationHelper verifyThat(
+ public static ApiModelingClassVerificationHelper verifyThat(
CodeInspector inspector, TestParameters parameters, Class<?> clazz) {
return new ApiModelingClassVerificationHelper(inspector, parameters, clazz);
}
@@ -295,7 +295,7 @@
hasNotOulinedInstructionWithClassReference(method, CodeMatchers::containsInstanceOf);
}
- void hasConstClassOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
+ public void hasConstClassOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
hasConstClassOutlinedFrom(method);
} else {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java
new file mode 100644
index 0000000..02a4fe6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java
@@ -0,0 +1,138 @@
+// 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.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 ApiOutlineProfileRewritingTest extends TestBase {
+
+ private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public AndroidApiLevel getApiLevelForRuntime() {
+ return parameters.isCfRuntime()
+ ? AndroidApiLevel.B
+ : parameters.getRuntime().maxSupportedApiLevel();
+ }
+
+ public boolean isLibraryClassAlwaysPresent() {
+ return parameters.isCfRuntime()
+ || parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+ }
+
+ public boolean isLibraryClassPresentInCurrentRuntime() {
+ return getApiLevelForRuntime().isGreaterThanOrEqualTo(classApiLevel);
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(parameters.getApiLevel() == AndroidApiLevel.B);
+ assertFalse(isLibraryClassPresentInCurrentRuntime());
+ testForJvm()
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::inspectRunResult);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .applyIf(
+ isLibraryClassPresentInCurrentRuntime(),
+ testBuilder -> testBuilder.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::inspectRunResult);
+ }
+
+ private ExternalArtProfile getArtProfile() {
+ return ExternalArtProfile.builder()
+ .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .build();
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector)
+ throws Exception {
+ // Verify that outlining happened.
+ verifyThat(inspector, parameters, LibraryClass.class)
+ .hasConstClassOutlinedFromUntil(
+ Main.class.getMethod("main", String[].class), classApiLevel);
+
+ // Check outline was added to ART profile.
+ ClassSubject apiOutlineClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticApiOutlineClass(Main.class, 0));
+ assertThat(apiOutlineClassSubject, notIf(isPresent(), isLibraryClassAlwaysPresent()));
+
+ MethodSubject apiOutlineMethodSubject = apiOutlineClassSubject.uniqueMethod();
+ assertThat(apiOutlineMethodSubject, notIf(isPresent(), isLibraryClassAlwaysPresent()));
+
+ // TODO(b/265729283): When outlining the residual profile should include the outline method and
+ // its holder.
+ profileInspector
+ .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .assertContainsNoOtherRules();
+ }
+
+ private void inspectRunResult(SingleTestRunResult<?> runResult) {
+ runResult.applyIf(
+ isLibraryClassPresentInCurrentRuntime(),
+ ignore -> runResult.assertSuccessWithOutputLines("class " + typeName(LibraryClass.class)),
+ ignore -> runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(LibraryClass.class);
+ }
+ }
+
+ // Only present from api 23.
+ public static class LibraryClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/BackportProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/BackportProfileRewritingTest.java
new file mode 100644
index 0000000..ba5013c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/BackportProfileRewritingTest.java
@@ -0,0 +1,87 @@
+// 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 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.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Objects;
+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 BackportProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .addOptionsModification(InlinerOptions::disableInlining)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("true");
+ }
+
+ private ExternalArtProfile getArtProfile() {
+ return ExternalArtProfile.builder()
+ .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .build();
+ }
+
+ private boolean isBackportingObjectsNonNull() {
+ return parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N);
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ ClassSubject backportClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticBackportClass(Main.class, 0));
+ assertThat(backportClassSubject, onlyIf(isBackportingObjectsNonNull(), isPresent()));
+
+ profileInspector.assertContainsMethodRules(MethodReferenceUtils.mainMethod(Main.class));
+
+ // TODO(b/265729283): Should include the backported method and its holder.
+ if (isBackportingObjectsNonNull()) {
+ MethodSubject backportMethodSubject = backportClassSubject.uniqueMethod();
+ assertThat(backportMethodSubject, isPresent());
+ }
+
+ profileInspector.assertContainsNoOtherRules();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(Objects.nonNull(args));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java
new file mode 100644
index 0000000..4e59b75
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java
@@ -0,0 +1,117 @@
+// 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 org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+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.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 DefaultInterfaceMethodProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .addOptionsModification(InlinerOptions::disableInlining)
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private ExternalArtProfile getArtProfile() throws Exception {
+ return ExternalArtProfile.builder()
+ .addMethodRule(Reference.methodFromMethod(I.class.getDeclaredMethod("m")))
+ .build();
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ ClassSubject iClassSubject = inspector.clazz(I.class);
+ assertThat(iClassSubject, isPresent());
+
+ MethodSubject interfaceMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
+ assertThat(interfaceMethodSubject, isPresent());
+
+ profileInspector.assertContainsMethodRule(interfaceMethodSubject);
+ } else {
+ ClassSubject iClassSubject = inspector.clazz(I.class);
+ assertThat(iClassSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject companionClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
+ assertThat(companionClassSubject, isPresent());
+
+ MethodSubject interfaceMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
+ assertThat(interfaceMethodSubject, isPresent());
+
+ MethodSubject implementationMethodSubject =
+ aClassSubject.method(interfaceMethodSubject.getFinalReference());
+ assertThat(implementationMethodSubject, isPresent());
+
+ MethodSubject movedMethodSubject = companionClassSubject.uniqueMethod();
+ assertThat(movedMethodSubject, isPresent());
+
+ // TODO(b/265729283): Should also include the two methods from desugaring.
+ profileInspector.assertContainsMethodRule(interfaceMethodSubject);
+ }
+
+ profileInspector.assertContainsNoOtherRules();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ I i = System.currentTimeMillis() > 0 ? new A() : new B();
+ i.m();
+ }
+ }
+
+ interface I {
+
+ default void m() {
+ System.out.println("Hello, world!");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A implements I {}
+
+ @NoHorizontalClassMerging
+ static class B implements I {}
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java
new file mode 100644
index 0000000..13a8c9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java
@@ -0,0 +1,135 @@
+// 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 org.hamcrest.MatcherAssert.assertThat;
+
+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.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 EnumUnboxingUtilityMethodProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .addOptionsModification(InlinerOptions::disableInlining)
+ .noHorizontalClassMergingOfSynthetics()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private ExternalArtProfile getArtProfile() throws Exception {
+ return ExternalArtProfile.builder()
+ .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .addMethodRule(Reference.methodFromMethod(MyEnum.class.getDeclaredMethod("greet")))
+ .addMethodRule(Reference.methodFromMethod(MyEnum.class.getDeclaredMethod("other")))
+ .addMethodRule(Reference.methodFromMethod(MyEnum.class.getDeclaredMethod("values")))
+ .build();
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+ assertThat(mainClassSubject.mainMethod(), isPresent());
+
+ ClassSubject enumUnboxingLocalUtilityClassSubject =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticEnumUnboxingLocalUtilityClass(MyEnum.class));
+ assertThat(enumUnboxingLocalUtilityClassSubject, isPresent());
+
+ MethodSubject localGreetMethodSubject =
+ enumUnboxingLocalUtilityClassSubject.uniqueMethodWithOriginalName("greet");
+ assertThat(localGreetMethodSubject, isPresent());
+
+ MethodSubject localOtherMethodSubject =
+ enumUnboxingLocalUtilityClassSubject.uniqueMethodWithOriginalName("other");
+ assertThat(localOtherMethodSubject, isPresent());
+
+ MethodSubject localValuesMethodSubject =
+ enumUnboxingLocalUtilityClassSubject.uniqueMethodWithOriginalName("values");
+ assertThat(localValuesMethodSubject, isPresent());
+
+ ClassSubject enumUnboxingSharedUtilityClassSubject =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticEnumUnboxingSharedUtilityClass(MyEnum.class));
+ assertThat(enumUnboxingSharedUtilityClassSubject, isPresent());
+ assertThat(enumUnboxingSharedUtilityClassSubject.clinit(), isPresent());
+ assertThat(
+ enumUnboxingSharedUtilityClassSubject.uniqueMethodWithOriginalName("ordinal"), isPresent());
+ assertThat(
+ enumUnboxingSharedUtilityClassSubject.uniqueMethodWithOriginalName("values"), isPresent());
+
+ // TODO(b/265729283): Should also include the above methods from enum unboxing.
+ profileInspector
+ .assertContainsMethodRules(
+ mainClassSubject.mainMethod(),
+ localGreetMethodSubject,
+ localOtherMethodSubject,
+ localValuesMethodSubject)
+ .assertContainsNoOtherRules();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ MyEnum a = System.currentTimeMillis() > 0 ? MyEnum.A : MyEnum.B;
+ MyEnum b = a.other();
+ a.greet();
+ b.greet();
+ }
+ }
+
+ enum MyEnum {
+ A,
+ B;
+
+ void greet() {
+ switch (this) {
+ case A:
+ System.out.print("Hello");
+ break;
+ case B:
+ System.out.println(", world!");
+ break;
+ }
+ }
+
+ MyEnum other() {
+ return MyEnum.values()[1 - ordinal()];
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedConstructorMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedConstructorMethodProfileRewritingTest.java
new file mode 100644
index 0000000..985b723
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedConstructorMethodProfileRewritingTest.java
@@ -0,0 +1,113 @@
+// 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 org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 HorizontallyMergedConstructorMethodProfileRewritingTest extends TestBase {
+
+ private enum ArtProfileInputOutput {
+ A_CONSTRUCTOR,
+ B_CONSTRUCTOR;
+
+ public ExternalArtProfile getArtProfile() throws Exception {
+ switch (this) {
+ case A_CONSTRUCTOR:
+ return ExternalArtProfile.builder()
+ .addMethodRule(Reference.methodFromMethod(A.class.getDeclaredConstructor()))
+ .build();
+ case B_CONSTRUCTOR:
+ return ExternalArtProfile.builder()
+ .addMethodRule(Reference.methodFromMethod(B.class.getDeclaredConstructor()))
+ .build();
+ default:
+ throw new RuntimeException();
+ }
+ }
+
+ public void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject syntheticConstructorSubject = aClassSubject.uniqueMethod();
+ assertThat(syntheticConstructorSubject, isPresent());
+
+ // TODO(b/265729283): Should contain the constructor.
+ profileInspector.assertEmpty();
+ }
+ }
+
+ @Parameter(0)
+ public ArtProfileInputOutput artProfileInputOutput;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ArtProfileInputOutput.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(artProfileInputOutput.getArtProfile())
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertMergedInto(B.class, A.class).assertNoOtherClassesMerged())
+ .addOptionsModification(InlinerOptions::setOnlyForceInlining)
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(artProfileInputOutput::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A();
+ new B();
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ public A() {
+ System.out.print("Hello");
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ public B() {
+ System.out.println(", world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java
new file mode 100644
index 0000000..29e39f9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java
@@ -0,0 +1,124 @@
+// 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 org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 HorizontallyMergedVirtualMethodProfileRewritingTest extends TestBase {
+
+ private enum ArtProfileInputOutput {
+ A_METHOD,
+ B_METHOD;
+
+ public ExternalArtProfile getArtProfile() throws Exception {
+ switch (this) {
+ case A_METHOD:
+ return ExternalArtProfile.builder()
+ .addMethodRule(Reference.methodFromMethod(A.class.getDeclaredMethod("m")))
+ .build();
+ case B_METHOD:
+ return ExternalArtProfile.builder()
+ .addMethodRule(Reference.methodFromMethod(B.class.getDeclaredMethod("m")))
+ .build();
+ default:
+ throw new RuntimeException();
+ }
+ }
+
+ public void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ String stringConstInMovedMethod = this == A_METHOD ? "Hello" : ", world!";
+ MethodSubject movedMethodSubject =
+ aClassSubject.uniqueMethodThatMatches(
+ method ->
+ method.isPrivate()
+ && method
+ .streamInstructions()
+ .anyMatch(
+ instruction -> instruction.isConstString(stringConstInMovedMethod)));
+ assertThat(movedMethodSubject, isPresent());
+
+ MethodSubject syntheticBridgeMethodSubject =
+ aClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
+ assertThat(syntheticBridgeMethodSubject, isPresent());
+
+ // TODO(b/265729283): Should contain the synthetic bridge method from above.
+ profileInspector.assertContainsMethodRule(movedMethodSubject).assertContainsNoOtherRules();
+ }
+ }
+
+ @Parameter(0)
+ public ArtProfileInputOutput artProfileInputOutput;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ArtProfileInputOutput.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(artProfileInputOutput.getArtProfile())
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertMergedInto(B.class, A.class).assertNoOtherClassesMerged())
+ .addOptionsModification(InlinerOptions::setOnlyForceInlining)
+ .addOptionsModification(
+ options -> options.callSiteOptimizationOptions().setEnableMethodStaticizing(false))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(artProfileInputOutput::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().m();
+ new B().m();
+ }
+ }
+
+ static class A {
+
+ public void m() {
+ System.out.print("Hello");
+ }
+ }
+
+ static class B {
+
+ public void m() {
+ System.out.println(", world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/InvokeSpecialToVirtualMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/InvokeSpecialToVirtualMethodProfileRewritingTest.java
new file mode 100644
index 0000000..75b9758
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/InvokeSpecialToVirtualMethodProfileRewritingTest.java
@@ -0,0 +1,128 @@
+// 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 org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeTrue;
+
+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.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+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;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class InvokeSpecialToVirtualMethodProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClassFileData(getTransformedMain())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!", "Hello, world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getTransformedMain())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .addOptionsModification(InlinerOptions::disableInlining)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!", "Hello, world!");
+ }
+
+ private byte[] getTransformedMain() throws IOException {
+ BooleanBox anyRewritten = new BooleanBox();
+ return transformer(Main.class)
+ .transformMethodInsnInMethod(
+ "main",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (opcode == Opcodes.INVOKEVIRTUAL) {
+ assertEquals("m", name);
+ if (anyRewritten.isFalse()) {
+ visitor.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, owner, name, descriptor, isInterface);
+ anyRewritten.set();
+ return;
+ }
+ }
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ })
+ .transform();
+ }
+
+ private ExternalArtProfile getArtProfile() throws Exception {
+ return ExternalArtProfile.builder()
+ .addMethodRule(Reference.methodFromMethod(Main.class.getDeclaredMethod("m")))
+ .build();
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector)
+ throws Exception {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject mMethodSubject = mainClassSubject.uniqueMethodWithOriginalName("m");
+ assertThat(mMethodSubject, isPresent());
+
+ if (parameters.isDexRuntime()) {
+ MethodSubject mMovedMethodSubject =
+ mainClassSubject.method(
+ SyntheticItemsTestUtils.syntheticInvokeSpecialMethod(
+ Main.class.getDeclaredMethod("m")));
+ assertThat(mMovedMethodSubject, isPresent());
+ assertNotEquals(
+ mMethodSubject.getProgramMethod().getName(),
+ mMovedMethodSubject.getProgramMethod().getName());
+ }
+
+ // TODO(b/265729283): Should also contain the synthetic method above when compiling to dex.
+ profileInspector.assertContainsMethodRule(mMethodSubject).assertContainsNoOtherRules();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Main main = new Main();
+ main.m(); // transformed to invoke-special
+ main.m(); // remains an invoke-virtual (so that Main.m() survives shaking)
+ }
+
+ public void m() {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java
new file mode 100644
index 0000000..e2f0c2d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java
@@ -0,0 +1,94 @@
+// 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 org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+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.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 MovedStaticInterfaceMethodProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private ExternalArtProfile getArtProfile() throws Exception {
+ return ExternalArtProfile.builder()
+ .addMethodRule(Reference.methodFromMethod(I.class.getDeclaredMethod("m")))
+ .build();
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ ClassSubject iClassSubject = inspector.clazz(I.class);
+ assertThat(iClassSubject, isPresent());
+
+ MethodSubject mMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
+ assertThat(mMethodSubject, isPresent());
+
+ profileInspector.assertContainsMethodRule(mMethodSubject).assertContainsNoOtherRules();
+ } else {
+ ClassSubject companionClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
+ assertThat(companionClassSubject, isPresent());
+
+ MethodSubject mMethodSubject = companionClassSubject.uniqueMethodWithOriginalName("m");
+ assertThat(mMethodSubject, isPresent());
+
+ // TODO(b/265729283): Should contain the companion method.
+ profileInspector.assertEmpty();
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ I.m();
+ }
+ }
+
+ interface I {
+
+ @NeverInline
+ static void m() {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
new file mode 100644
index 0000000..81a74af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -0,0 +1,181 @@
+// 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.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+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.profile.art.completeness.NestBasedAccessBridgesProfileRewritingTest.Main.NestMember;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+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 NestBasedAccessBridgesProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK11));
+ testForJvm()
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("1", "2", "3", "4");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeFalse(parameters.isCfRuntime() && parameters.asCfRuntime().isOlderThan(CfVm.JDK11));
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("1", "2", "3", "4");
+ }
+
+ private List<byte[]> getProgramClassFileData() throws Exception {
+ return ImmutableList.of(
+ transformer(Main.class).setNest(Main.class, NestMember.class).transform(),
+ transformer(NestMember.class)
+ .setNest(Main.class, NestMember.class)
+ .setPrivate(NestMember.class.getDeclaredField("instanceField"))
+ .setPrivate(NestMember.class.getDeclaredField("staticField"))
+ .setPrivate(NestMember.class.getDeclaredMethod("instanceMethod"))
+ .setPrivate(NestMember.class.getDeclaredMethod("staticMethod"))
+ .transform());
+ }
+
+ private ExternalArtProfile getArtProfile() {
+ return ExternalArtProfile.builder()
+ .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .build();
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector)
+ throws Exception {
+ ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
+ assertThat(nestMemberClassSubject, isPresent());
+
+ MethodSubject syntheticNestInstanceFieldGetterMethodSubject =
+ nestMemberClassSubject.uniqueMethodWithOriginalName(
+ SyntheticItemsTestUtils.syntheticNestInstanceFieldGetter(
+ NestMember.class.getDeclaredField("instanceField"))
+ .getMethodName());
+ assertThat(
+ syntheticNestInstanceFieldGetterMethodSubject,
+ notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+ MethodSubject syntheticNestInstanceFieldSetterMethodSubject =
+ nestMemberClassSubject.uniqueMethodWithOriginalName(
+ SyntheticItemsTestUtils.syntheticNestInstanceFieldSetter(
+ NestMember.class.getDeclaredField("instanceField"))
+ .getMethodName());
+ assertThat(
+ syntheticNestInstanceFieldSetterMethodSubject,
+ notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+ MethodSubject syntheticNestInstanceMethodAccessorMethodSubject =
+ nestMemberClassSubject.uniqueMethodWithOriginalName(
+ SyntheticItemsTestUtils.syntheticNestInstanceMethodAccessor(
+ NestMember.class.getDeclaredMethod("instanceMethod"))
+ .getMethodName());
+ assertThat(
+ syntheticNestInstanceMethodAccessorMethodSubject,
+ notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+ MethodSubject syntheticNestStaticFieldGetterMethodSubject =
+ nestMemberClassSubject.uniqueMethodWithOriginalName(
+ SyntheticItemsTestUtils.syntheticNestStaticFieldGetter(
+ NestMember.class.getDeclaredField("staticField"))
+ .getMethodName());
+ assertThat(
+ syntheticNestStaticFieldGetterMethodSubject,
+ notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+ MethodSubject syntheticNestStaticFieldSetterMethodSubject =
+ nestMemberClassSubject.uniqueMethodWithOriginalName(
+ SyntheticItemsTestUtils.syntheticNestStaticFieldSetter(
+ NestMember.class.getDeclaredField("staticField"))
+ .getMethodName());
+ assertThat(
+ syntheticNestStaticFieldSetterMethodSubject,
+ notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+ MethodSubject syntheticNestStaticMethodAccessorMethodSubject =
+ nestMemberClassSubject.uniqueMethodWithOriginalName(
+ SyntheticItemsTestUtils.syntheticNestStaticMethodAccessor(
+ NestMember.class.getDeclaredMethod("staticMethod"))
+ .getMethodName());
+ assertThat(
+ syntheticNestStaticMethodAccessorMethodSubject,
+ notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+ // TODO(b/265729283): Should contain the nest bridge methods.
+ profileInspector
+ .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .assertContainsNoOtherRules();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ NestMember nestMember = new NestMember();
+ nestMember.instanceField = System.currentTimeMillis() > 0 ? 1 : 0;
+ System.out.println(nestMember.instanceField);
+ System.out.println(nestMember.instanceMethod());
+ NestMember.staticField = System.currentTimeMillis() > 0 ? 3 : 0;
+ System.out.println(NestMember.staticField);
+ System.out.println(NestMember.staticMethod());
+ }
+
+ static class NestMember {
+
+ /*private*/ int instanceField;
+ /*private*/ static int staticField;
+
+ @NeverInline
+ /*private*/ int instanceMethod() {
+ return System.currentTimeMillis() > 0 ? 2 : 0;
+ }
+
+ @NeverInline
+ /*private*/ static int staticMethod() {
+ return System.currentTimeMillis() > 0 ? 4 : 0;
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/OutlineOptimizationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/OutlineOptimizationProfileRewritingTest.java
new file mode 100644
index 0000000..59e0636
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/OutlineOptimizationProfileRewritingTest.java
@@ -0,0 +1,102 @@
+// 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 org.hamcrest.MatcherAssert.assertThat;
+
+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.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 OutlineOptimizationProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .addOptionsModification(InlinerOptions::disableInlining)
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!", "Hello, world!");
+ }
+
+ private ExternalArtProfile getArtProfile() {
+ return ExternalArtProfile.builder()
+ .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .build();
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ ClassSubject outlineClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(Main.class, 0));
+ assertThat(outlineClassSubject, isPresent());
+
+ MethodSubject outlineMethodSubject = outlineClassSubject.uniqueMethod();
+ assertThat(outlineMethodSubject, isPresent());
+
+ // TODO(b/265729283): Should contain the outline class and method.
+ profileInspector
+ .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .assertContainsNoOtherRules();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ greet1();
+ greet2();
+ }
+
+ static void greet1() {
+ hello();
+ world();
+ }
+
+ static void greet2() {
+ hello();
+ world();
+ }
+
+ public static void hello() {
+ System.out.print("Hello");
+ }
+
+ public static void world() {
+ System.out.println(", world!");
+ }
+ }
+}
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
index e45e4a5..704fe6a 100644
--- 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
@@ -10,13 +10,16 @@
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.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Collections;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -26,12 +29,115 @@
@RunWith(Parameterized.class)
public class SyntheticLambdaClassProfileRewritingTest extends TestBase {
+ private enum ArtProfileInputOutput {
+ MAIN_METHOD,
+ LAMBDA_BRIDGE_METHOD;
+
+ public ExternalArtProfile getArtProfile() {
+ switch (this) {
+ case MAIN_METHOD:
+ // Profile containing Main.main(). Should be rewritten to include the two lambda classes
+ // and their constructors.
+ return ExternalArtProfile.builder()
+ .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .build();
+ case LAMBDA_BRIDGE_METHOD:
+ // Profile containing the two lambda implementation methods, Main.lambda$main${0,1}.
+ // Should be rewritten to include the two lambda accessibility bridge methods.
+ return ExternalArtProfile.builder()
+ .addMethodRule(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "lambda$main$0",
+ Collections.emptyList(),
+ null))
+ .addMethodRule(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "lambda$main$1",
+ Collections.emptyList(),
+ null))
+ .build();
+ default:
+ throw new RuntimeException();
+ }
+ }
+
+ public void inspect(
+ ArtProfileInspector profileInspector, CodeInspector inspector, TestParameters parameters) {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+
+ MethodSubject lambdaImplementationMethod0 =
+ mainClassSubject.uniqueMethodWithOriginalName("lambda$main$0");
+ assertThat(lambdaImplementationMethod0, isPresent());
+
+ MethodSubject lambdaImplementationMethod1 =
+ mainClassSubject.uniqueMethodWithOriginalName("lambda$main$1");
+ assertThat(lambdaImplementationMethod1, isPresent());
+
+ // 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()));
+
+ if (parameters.isCfRuntime()) {
+ switch (this) {
+ case MAIN_METHOD:
+ profileInspector
+ .assertContainsMethodRule(mainMethodSubject)
+ .assertContainsNoOtherRules();
+ break;
+ case LAMBDA_BRIDGE_METHOD:
+ profileInspector
+ .assertContainsMethodRules(lambdaImplementationMethod0, lambdaImplementationMethod1)
+ .assertContainsNoOtherRules();
+ break;
+ default:
+ throw new RuntimeException();
+ }
+ } else {
+ assert parameters.isDexRuntime();
+ switch (this) {
+ case MAIN_METHOD:
+ // TODO(b/265729283): Since Main.main() is in the art profile, so should the two
+ // synthetic lambdas be along with their initializers. Since Main.lambda$main$*() is
+ // not in the art profile, the interface method implementation does not need to be
+ // included in the profile.
+ profileInspector
+ .assertContainsMethodRule(mainMethodSubject)
+ .assertContainsNoOtherRules();
+ break;
+ case LAMBDA_BRIDGE_METHOD:
+ // TODO(b/265729283): Since Main.lambda$main$*() is in the art profile, so should the
+ // two accessibility bridges be.
+ profileInspector
+ .assertContainsMethodRules(lambdaImplementationMethod0, lambdaImplementationMethod1)
+ .assertContainsNoOtherRules();
+ break;
+ default:
+ throw new RuntimeException();
+ }
+ }
+ }
+ }
+
@Parameter(0)
+ public ArtProfileInputOutput artProfileInputOutput;
+
+ @Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{1}, {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ArtProfileInputOutput.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
@Test
@@ -39,43 +145,20 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
- .addArtProfileForRewriting(getArtProfile())
+ .addKeepRules(
+ "-neverinline class " + Main.class.getTypeName() + " { void lambda$main$*(); }")
+ .addArtProfileForRewriting(artProfileInputOutput.getArtProfile())
+ .enableProguardTestOptions()
.noHorizontalClassMergingOfSynthetics()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(this::inspect)
- .inspectResidualArtProfile(this::inspectResidualArtProfile)
+ .inspectResidualArtProfile(
+ (profileInspector, inspector) ->
+ artProfileInputOutput.inspect(profileInspector, inspector, parameters))
.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(ArtProfileInspector profileInspector) {
- // TODO(b/265729283): Since Main.main() is in the art profile, so should the two synthetic
- // lambdas be when compiling to dex.
- profileInspector
- .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
- .assertContainsNoOtherRules();
- }
-
static class Main {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java
new file mode 100644
index 0000000..b1e46f9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java
@@ -0,0 +1,93 @@
+// 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 org.hamcrest.MatcherAssert.assertThat;
+
+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.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 VerticalClassMergingBridgeProfileRewritingTest 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)
+ .addArtProfileForRewriting(getArtProfile())
+ .addOptionsModification(InlinerOptions::disableInlining)
+ .addOptionsModification(
+ options -> options.callSiteOptimizationOptions().setEnableMethodStaticizing(false))
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(A.class))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private ExternalArtProfile getArtProfile() throws Exception {
+ return ExternalArtProfile.builder()
+ .addMethodRule(Reference.methodFromMethod(A.class.getDeclaredMethod("m")))
+ .build();
+ }
+
+ private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ MethodSubject movedMethodSubject =
+ bClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isPrivate);
+ assertThat(movedMethodSubject, isPresent());
+
+ MethodSubject syntheticBridgeMethodSubject =
+ bClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
+ assertThat(syntheticBridgeMethodSubject, isPresent());
+
+ // TODO(b/265729283): Should also contain the synthetic bridge method above.
+ profileInspector.assertContainsMethodRule(movedMethodSubject).assertContainsNoOtherRules();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new B().m();
+ }
+ }
+
+ static class A {
+
+ public void m() {
+ System.out.println("Hello, world!");
+ }
+ }
+
+ static class B extends A {}
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
index 22b5370..507dd67 100644
--- a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
+++ b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
@@ -63,13 +63,20 @@
return this;
}
+ public ArtProfileInspector assertContainsMethodRules(MethodReference... methodReferences) {
+ for (MethodReference methodReference : methodReferences) {
+ assertContainsMethodRule(methodReference);
+ }
+ return this;
+ }
+
public ArtProfileInspector assertContainsMethodRule(MethodSubject methodSubject) {
return assertContainsMethodRule(methodSubject.getFinalReference());
}
- public ArtProfileInspector assertContainsMethodRules(MethodReference... methodReferences) {
- for (MethodReference methodReference : methodReferences) {
- assertContainsMethodRule(methodReference);
+ public ArtProfileInspector assertContainsMethodRules(MethodSubject... methodSubjects) {
+ for (MethodSubject methodSubject : methodSubjects) {
+ assertContainsMethodRule(methodSubject);
}
return this;
}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 1b67cfc..19ed989 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -3,18 +3,28 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_FIELD_GET_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_FIELD_PUT_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_METHOD_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_METHOD_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX;
import static com.android.tools.r8.synthesis.SyntheticNaming.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
import static org.hamcrest.CoreMatchers.containsString;
+import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.synthesis.SyntheticNaming.Phase;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.Collections;
import org.hamcrest.Matcher;
public class SyntheticItemsTestUtils {
@@ -53,6 +63,15 @@
originalMethod.getMethodDescriptor());
}
+ public static MethodReference syntheticInvokeSpecialMethod(Method method) {
+ MethodReference originalMethod = Reference.methodFromMethod(method);
+ return Reference.method(
+ originalMethod.getHolderClass(),
+ InvokeSpecialToSelfDesugaring.INVOKE_SPECIAL_BRIDGE_PREFIX + method.getName(),
+ originalMethod.getFormalTypes(),
+ originalMethod.getReturnType());
+ }
+
public static MethodReference syntheticBackportWithForwardingMethod(
ClassReference clazz, int id, MethodReference method) {
// For backports with forwarding the backported method is not static, so the original method
@@ -127,6 +146,68 @@
originalMethod.getMethodDescriptor());
}
+ public static MethodReference syntheticNestInstanceFieldGetter(Field field) {
+ FieldReference fieldReference = Reference.fieldFromField(field);
+ return Reference.method(
+ fieldReference.getHolderClass(),
+ NEST_ACCESS_FIELD_GET_NAME_PREFIX + field.getName(),
+ Collections.emptyList(),
+ fieldReference.getFieldType());
+ }
+
+ public static MethodReference syntheticNestInstanceFieldSetter(Field field) {
+ FieldReference fieldReference = Reference.fieldFromField(field);
+ return Reference.method(
+ fieldReference.getHolderClass(),
+ NEST_ACCESS_FIELD_PUT_NAME_PREFIX + field.getName(),
+ ImmutableList.of(fieldReference.getFieldType()),
+ null);
+ }
+
+ public static MethodReference syntheticNestInstanceMethodAccessor(Method method) {
+ MethodReference originalMethod = Reference.methodFromMethod(method);
+ return Reference.methodFromDescriptor(
+ originalMethod.getHolderClass(),
+ NEST_ACCESS_METHOD_NAME_PREFIX + method.getName(),
+ originalMethod.getMethodDescriptor());
+ }
+
+ public static MethodReference syntheticNestStaticFieldGetter(Field field) {
+ FieldReference fieldReference = Reference.fieldFromField(field);
+ return Reference.method(
+ fieldReference.getHolderClass(),
+ NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX + field.getName(),
+ Collections.emptyList(),
+ fieldReference.getFieldType());
+ }
+
+ public static MethodReference syntheticNestStaticFieldSetter(Field field) {
+ FieldReference fieldReference = Reference.fieldFromField(field);
+ return Reference.method(
+ fieldReference.getHolderClass(),
+ NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX + field.getName(),
+ ImmutableList.of(fieldReference.getFieldType()),
+ null);
+ }
+
+ public static MethodReference syntheticNestStaticMethodAccessor(Method method) {
+ MethodReference originalMethod = Reference.methodFromMethod(method);
+ return Reference.methodFromDescriptor(
+ originalMethod.getHolderClass(),
+ NEST_ACCESS_STATIC_METHOD_NAME_PREFIX + method.getName(),
+ originalMethod.getMethodDescriptor());
+ }
+
+ public static ClassReference syntheticEnumUnboxingLocalUtilityClass(Class<?> clazz) {
+ return Reference.classFromTypeName(
+ clazz.getTypeName() + naming.ENUM_UNBOXING_LOCAL_UTILITY_CLASS.getDescriptor());
+ }
+
+ public static ClassReference syntheticEnumUnboxingSharedUtilityClass(Class<?> clazz) {
+ return Reference.classFromTypeName(
+ clazz.getTypeName() + naming.ENUM_UNBOXING_SHARED_UTILITY_CLASS.getDescriptor());
+ }
+
public static boolean isEnumUnboxingSharedUtilityClass(ClassReference reference) {
return SyntheticNaming.isSynthetic(reference, null, naming.ENUM_UNBOXING_SHARED_UTILITY_CLASS);
}