Add a test for disallowing inlining into main dex list classes
Bug: 178353726
Change-Id: I882d7dba97bdd1f73e0e90a639e6d6584323bc31
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
new file mode 100644
index 0000000..94392e6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
@@ -0,0 +1,151 @@
+// 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.maindexlist;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoStaticClassMerging;
+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.ToolHelper;
+import com.android.tools.r8.references.ClassReference;
+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.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MainDexListFromGenerateMainDexInliningTest extends TestBase {
+
+ private static List<ClassReference> mainDexList;
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ mainDexList =
+ testForMainDexListGenerator(getStaticTemp())
+ .addInnerClasses(MainDexListFromGenerateMainDexInliningTest.class)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}")
+ .run()
+ .getMainDexList();
+ }
+
+ public MainDexListFromGenerateMainDexInliningTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ // The generated main dex list should contain Main (which is a root) and A (which is a direct
+ // dependency of Main).
+ assertEquals(2, mainDexList.size());
+ assertEquals(A.class.getTypeName(), mainDexList.get(0).getTypeName());
+ assertEquals(Main.class.getTypeName(), mainDexList.get(1).getTypeName());
+
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInliningAnnotations()
+ .addKeepClassAndMembersRules(Main.class)
+ .addMainDexListClassReferences(mainDexList)
+ .collectMainDexClasses()
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoStaticClassMergingAnnotations()
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject fooMethodSubject = mainClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooMethodSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ // TODO(b/178353726): Should be present, but was inlined.
+ assertThat(aClassSubject, isAbsent());
+
+ MethodSubject barMethodSubject = aClassSubject.uniqueMethodWithName("bar");
+ // TODO(b/178353726): Should be present, but was inlined.
+ assertThat(barMethodSubject, isAbsent());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ MethodSubject bazMethodSubject = bClassSubject.uniqueMethodWithName("baz");
+ assertThat(bazMethodSubject, isPresent());
+
+ // TODO(b/178353726): foo() should invoke bar() and bar() should invoke baz().
+ assertThat(fooMethodSubject, invokesMethod(bazMethodSubject));
+
+ // TODO(b/178353726): Main is the only class guaranteed to be in the main dex, but it has a
+ // direct reference to B.
+ compileResult.inspectMainDexClasses(
+ mainDexClasses -> {
+ assertEquals(1, mainDexClasses.size());
+ assertEquals(mainClassSubject.getFinalName(), mainDexClasses.iterator().next());
+ });
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Main.main()");
+ }
+
+ static void foo() {
+ // TODO(b/178353726): Should not allow inlining bar into foo(), since that adds B as a direct
+ // dependence, and we don't include the direct dependencies of main dex list classes.
+ A.bar();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ @NoStaticClassMerging
+ static class A {
+
+ static void bar() {
+ B.baz();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ @NoStaticClassMerging
+ static class B {
+
+ @NeverInline
+ static void baz() {
+ System.out.println("B.baz");
+ }
+ }
+}