Deprecated support for deriving main-dex list synthetics.

Bug: 181858113
Change-Id: Ibeba3c2b1f8fa26e909d1143de326fea3ccf8009
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java b/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
index 5d3309a..96e263c 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
@@ -76,6 +76,11 @@
     assert tracedDependencies.stream().noneMatch(tracedRoots::contains);
   }
 
+  // TODO(b/181858113): Remove once deprecated main-dex-list is removed.
+  public boolean isSyntheticContextOnMainDexList(DexType syntheticContextType) {
+    return classList.contains(syntheticContextType);
+  }
+
   public boolean isNone() {
     assert none() == NONE;
     return this == NONE;
@@ -389,5 +394,18 @@
     public MainDexInfo build(MainDexInfo previous) {
       return build(previous.classList);
     }
+
+    public MainDexInfo build() {
+      return new MainDexInfo(list, roots, methodRoots, dependencies, tracedMethodRootsCleared);
+    }
+  }
+
+  public Builder builderFromCopy() {
+    Builder builder = new Builder(tracedMethodRootsCleared);
+    builder.list.addAll(classList);
+    builder.roots.addAll(tracedRoots);
+    builder.methodRoots.addAll(tracedMethodRoots);
+    builder.dependencies.addAll(tracedDependencies);
+    return builder;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 3e8f334..04ec40a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.MainDexInfo;
 import java.util.Comparator;
 
 /**
@@ -129,4 +130,9 @@
   public String toString() {
     return "SynthesizingContext{" + getSynthesizingContextType() + "}";
   }
+
+  // TODO(b/181858113): Remove once deprecated main-dex-list is removed.
+  boolean isDerivedFromMainDexList(MainDexInfo mainDexInfo) {
+    return mainDexInfo.isSyntheticContextOnMainDexList(inputContextType);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 916daac..0204788 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.TreeFixerBase;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.SetUtils;
@@ -52,12 +53,17 @@
     public final CommittedItems commit;
     public final NonIdentityGraphLens lens;
     public final PrunedItems prunedItems;
+    public final MainDexInfo mainDexInfo;
 
     public Result(
-        CommittedItems commit, SyntheticFinalizationGraphLens lens, PrunedItems prunedItems) {
+        CommittedItems commit,
+        SyntheticFinalizationGraphLens lens,
+        PrunedItems prunedItems,
+        MainDexInfo mainDexInfo) {
       this.commit = commit;
       this.lens = lens;
       this.prunedItems = prunedItems;
+      this.mainDexInfo = mainDexInfo;
     }
   }
 
@@ -229,7 +235,7 @@
     assert !appView.appInfo().hasClassHierarchy();
     assert !appView.appInfo().hasLiveness();
     Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
-    appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexInfo()));
+    appView.setAppInfo(new AppInfo(result.commit, result.mainDexInfo));
     if (result.lens != null) {
       appView.setAppInfo(
           appView
@@ -245,6 +251,7 @@
     assert !appView.appInfo().hasLiveness();
     Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
     appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(result.commit));
+    appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(result.mainDexInfo));
     if (result.lens != null) {
       appView.setGraphLens(result.lens);
       appView.setAppInfo(
@@ -259,6 +266,7 @@
   public static void finalizeWithLiveness(AppView<AppInfoWithLiveness> appView) {
     Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
     appView.setAppInfo(appView.appInfo().rebuildWithLiveness(result.commit));
+    appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(result.mainDexInfo));
     appView.rewriteWithLens(result.lens);
     appView.pruneItems(result.prunedItems);
   }
@@ -272,6 +280,7 @@
     ImmutableMap.Builder<DexType, SyntheticProgramClassReference> finalClassesBuilder =
         ImmutableMap.builder();
     List<DexProgramClass> finalSyntheticProgramDefinitions = new ArrayList<>();
+    Set<DexType> derivedMainDexTypes = Sets.newIdentityHashSet();
     {
       Map<String, NumberGenerator> generators = new HashMap<>();
       application =
@@ -287,7 +296,8 @@
               (clazz, reference) -> {
                 finalSyntheticProgramDefinitions.add(clazz);
                 finalMethodsBuilder.put(clazz.getType(), reference);
-              });
+              },
+              derivedMainDexTypes);
     }
     ImmutableMap<DexType, SyntheticMethodReference> finalMethods = finalMethodsBuilder.build();
     ImmutableMap<DexType, SyntheticProgramClassReference> finalClasses =
@@ -302,6 +312,10 @@
           }
         });
 
+    // TODO(b/181858113): Remove once deprecated main-dex-list is removed.
+    MainDexInfo.Builder mainDexInfoBuilder = appView.appInfo().getMainDexInfo().builderFromCopy();
+    derivedMainDexTypes.forEach(mainDexInfoBuilder::addList);
+
     return new Result(
         new CommittedItems(
             SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION,
@@ -310,10 +324,8 @@
                 committed.getLegacyTypes(), finalMethods, finalClasses),
             ImmutableList.of()),
         lensBuilder.build(appView.graphLens(), appView.dexItemFactory()),
-        PrunedItems.builder()
-            .setPrunedApp(application)
-            .addRemovedClasses(prunedSynthetics)
-            .build());
+        PrunedItems.builder().setPrunedApp(application).addRemovedClasses(prunedSynthetics).build(),
+        mainDexInfoBuilder.build());
   }
 
   private <R extends SyntheticReference<R, D, ?>, D extends SyntheticDefinition<R, D, ?>>
@@ -359,9 +371,11 @@
       Map<DexType, EquivalenceGroup<SyntheticProgramClassDefinition>> syntheticClassGroups,
       Builder lensBuilder,
       BiConsumer<DexProgramClass, SyntheticProgramClassReference> addFinalSyntheticClass,
-      BiConsumer<DexProgramClass, SyntheticMethodReference> addFinalSyntheticMethod) {
+      BiConsumer<DexProgramClass, SyntheticMethodReference> addFinalSyntheticMethod,
+      Set<DexType> derivedMainDexSynthetics) {
     DexApplication application = appView.appInfo().app();
     DexItemFactory factory = appView.dexItemFactory();
+    MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
     List<DexProgramClass> newProgramClasses = new ArrayList<>();
     Set<DexType> pruned = Sets.newIdentityHashSet();
 
@@ -383,6 +397,9 @@
             if (memberReference != externalSyntheticMethod.method) {
               lensBuilder.moveSyntheticMethod(memberReference, externalSyntheticMethod.method);
             }
+            if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
+              derivedMainDexSynthetics.add(syntheticType);
+            }
           }
         });
 
@@ -406,6 +423,9 @@
             if (member != representative) {
               deduplicatedClasses.add(memberClass);
             }
+            if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
+              derivedMainDexSynthetics.add(syntheticType);
+            }
           }
         });
 
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 26477d3..e9cd5ff 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -285,13 +285,6 @@
     return setMinApi(minApi);
   }
 
-  public T setMinApiThreshold(TestRuntime runtime) {
-    if (runtime.isDex()) {
-      setMinApiThreshold(runtime.asDex().getMinApiLevel());
-    }
-    return self();
-  }
-
   public T setMinApi(AndroidApiLevel minApiLevel) {
     return setMinApi(minApiLevel.getLevel());
   }
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
index 6c2ad2e..8252f4d 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
@@ -8,11 +8,14 @@
 
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.nio.file.Files;
@@ -54,7 +57,7 @@
           testForD8()
               .addInnerClasses(MainDexWithSynthesizedClassesTest.class)
               .addMainDexKeepClassAndMemberRules(TestClass.class)
-              .setMinApiThreshold(parameters.getApiLevel())
+              .setMinApi(parameters.getApiLevel())
               .compile();
       checkCompilationResult(compileResult);
     }
@@ -66,19 +69,67 @@
     D8TestCompileResult intermediateResult =
         testForD8()
             .addInnerClasses(MainDexWithSynthesizedClassesTest.class)
-            .setMinApiThreshold(parameters.getApiLevel())
+            .setMinApi(parameters.getApiLevel())
             .setIntermediate(true)
             .compile();
     D8TestCompileResult compileResult =
         testForD8()
             .addProgramFiles(intermediateResult.writeToZip())
-            .addMainDexKeepClassRules(TestClass.class, A.class)
-            .setMinApiThreshold(parameters.getApiLevel())
+            .addMainDexKeepClassAndMemberRules(TestClass.class)
+            .setMinApi(parameters.getApiLevel())
             .compile();
     checkCompilationResult(compileResult);
   }
 
+  /**
+   * This test checks for maintained support of including synthetics from main-dex-list entries in
+   * the main-dex file. This test simulates that the tracing done at the class-file level has
+   * determined that TestClass and A are both traced. Thus the synthetic lambda from A will be
+   * included in the main-dex file.
+   *
+   * <p>TODO(b/181858113): Remove once deprecated main-dex-list is removed.
+   */
+  @Test
+  public void testDeprecatedSyntheticsFromMainDexListD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    D8TestCompileResult compileResult =
+        testForD8()
+            .addInnerClasses(MainDexWithSynthesizedClassesTest.class)
+            .addMainDexListClasses(TestClass.class, A.class)
+            .setMinApi(parameters.getApiLevel())
+            .compile();
+    checkCompilationResult(compileResult);
+  }
+
+  /**
+   * This test checks for maintained support of including synthetics from main-dex-list entries in
+   * the main-dex file. This test simulates that the tracing done at the class-file level has
+   * determined that TestClass and A are both traced. Thus the synthetic lambda from A will be
+   * included in the main-dex file.
+   *
+   * <p>TODO(b/181858113): Remove once deprecated main-dex-list is removed.
+   */
+  @Test
+  public void testDeprecatedSyntheticsFromMainDexListR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(MainDexWithSynthesizedClassesTest.class)
+            .setMinApi(parameters.getApiLevel())
+            .addOptionsModification(o -> o.minimalMainDex = true)
+            .addMainDexListClasses(TestClass.class, A.class)
+            .noMinification()
+            .noTreeShaking()
+            .compile();
+    checkCompilationResult(compileResult, compileResult.app);
+  }
+
   private void checkCompilationResult(D8TestCompileResult compileResult) throws Exception {
+    checkCompilationResult(compileResult, compileResult.app);
+  }
+
+  private void checkCompilationResult(TestCompileResult compileResult, AndroidApp app)
+      throws Exception {
     if (parameters.getRuntime().asDex().getMinApiLevel().getLevel()
         < nativeMultiDexLevel.getLevel()) {
       compileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
@@ -86,7 +137,7 @@
       compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput(EXPECTED);
     }
     Path out = temp.newFolder().toPath();
-    compileResult.apply(b -> b.app.writeToDirectory(out, OutputMode.DexIndexed));
+    app.writeToDirectory(out, OutputMode.DexIndexed);
     Path classes = out.resolve("classes.dex");
     Path classes2 = out.resolve("classes2.dex");
     assertTrue(Files.exists(classes));