DexFileMerger: don't add marker and keep all markers from input files.

Bug: 77883315
Change-Id: Id12ff2c01fff201ab382d8dfdae46121245dbd7a
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 4a26889..39bdd96 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -25,6 +25,7 @@
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
@@ -135,6 +136,9 @@
     if (options.hasMarker()) {
       return options.getMarker();
     }
+    if (options.testing.dontCreateMarkerInD8) {
+      return null;
+    }
     Marker marker = new Marker(Tool.D8)
         .setVersion(Version.LABEL)
         .setMinApi(options.minApiLevel);
@@ -163,7 +167,14 @@
         options.methodsFilter.forEach((m) -> System.out.println("  - " + m));
       }
       Marker marker = getMarker(options);
-      new ApplicationWriter(app, options, marker, null, NamingLens.getIdentityLens(), null, null)
+      new ApplicationWriter(
+              app,
+              options,
+              marker == null ? null : Collections.singletonList(marker),
+              null,
+              NamingLens.getIdentityLens(),
+              null,
+              null)
           .write(executor);
       options.printWarnings();
     } catch (ExecutionException e) {
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index 2e121c3..627d5ef 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -16,6 +17,7 @@
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -86,9 +88,10 @@
         AppInfo appInfo = new AppInfo(app);
         app = D8.optimize(app, appInfo, options, timing, executor);
 
+        List<Marker> markers = app.dexItemFactory.extractMarkers();
+
         assert !options.hasMethodsFilter();
-        new ApplicationWriter(
-                app, options, D8.getMarker(options), null, NamingLens.getIdentityLens(), null, null)
+        new ApplicationWriter(app, options, markers, null, NamingLens.getIdentityLens(), null, null)
             .write(executor);
         options.printWarnings();
       } catch (ExecutionException e) {
@@ -101,4 +104,11 @@
       executor.shutdown();
     }
   }
+
+  public static void runForTesting(D8Command command, boolean dontCreateMarkerInD8)
+      throws IOException, CompilationException {
+    InternalOptions options = command.getInternalOptions();
+    options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
+    D8.runForTesting(command.getInputApp(), options);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 8573e0f..8c29ca8 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexApplication.Builder;
@@ -22,6 +23,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -74,10 +76,11 @@
           DexIndexedConsumer consumer = new DirectoryConsumer(outputDir);
 
           try {
+            Marker marker = D8.getMarker(options);
             new ApplicationWriter(
                     featureApp,
                     options,
-                    D8.getMarker(options),
+                    marker == null ? null : Collections.singletonList(marker),
                     null,
                     NamingLens.getIdentityLens(),
                     null,
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 4f12ce1..6821c4e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -70,6 +70,7 @@
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.StandardOpenOption;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -195,7 +196,7 @@
         new ApplicationWriter(
                 application,
                 options,
-                marker,
+                marker == null ? null : Collections.singletonList(marker),
                 deadCode,
                 namingLens,
                 proguardSeedsData,
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 30664a2..d5a7013 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -46,7 +46,7 @@
   public final NamingLens namingLens;
   public final String proguardSeedsData;
   public final InternalOptions options;
-  public DexString markerString;
+  public List<DexString> markerStrings;
   public DexIndexedConsumer programConsumer;
   public final ProguardMapSupplier proguardMapSupplier;
 
@@ -110,7 +110,7 @@
   public ApplicationWriter(
       DexApplication application,
       InternalOptions options,
-      Marker marker,
+      List<Marker> markers,
       String deadCode,
       NamingLens namingLens,
       String proguardSeedsData,
@@ -118,7 +118,7 @@
     this(
         application,
         options,
-        marker,
+        markers,
         deadCode,
         namingLens,
         proguardSeedsData,
@@ -129,7 +129,7 @@
   public ApplicationWriter(
       DexApplication application,
       InternalOptions options,
-      Marker marker,
+      List<Marker> markers,
       String deadCode,
       NamingLens namingLens,
       String proguardSeedsData,
@@ -139,9 +139,12 @@
     this.application = application;
     assert options != null;
     this.options = options;
-    this.markerString = (marker == null)
-        ? null
-        : application.dexItemFactory.createString(marker.toString());
+    if (markers != null && !markers.isEmpty()) {
+      this.markerStrings = new ArrayList<>();
+      for (Marker marker : markers) {
+        this.markerStrings.add(application.dexItemFactory.createString(marker.toString()));
+      }
+    }
     this.deadCode = deadCode;
     this.namingLens = namingLens;
     this.proguardSeedsData = proguardSeedsData;
@@ -174,7 +177,9 @@
       insertAttributeAnnotations();
 
       application.dexItemFactory.sort(namingLens);
-      assert this.markerString == null || application.dexItemFactory.extractMarker() != null;
+      assert this.markerStrings == null
+          || this.markerStrings.isEmpty()
+          || application.dexItemFactory.extractMarker() != null;
 
       SortAnnotations sortAnnotations = new SortAnnotations();
       application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 5369069..ae6acd9 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -275,8 +275,10 @@
       mainDexFile = new VirtualFile(0, writer.namingLens);
       assert virtualFiles.isEmpty();
       virtualFiles.add(mainDexFile);
-      if (writer.markerString != null) {
-        mainDexFile.transaction.addString(writer.markerString);
+      if (writer.markerStrings != null && !writer.markerStrings.isEmpty()) {
+        for (DexString markerString : writer.markerStrings) {
+          mainDexFile.transaction.addString(markerString);
+        }
         mainDexFile.commitTransaction();
       }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 5fe9134..364c0ee 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -483,7 +483,7 @@
   }
 
   // Debugging support to extract marking string.
-  synchronized public Marker extractMarker() {
+  public synchronized Marker extractMarker() {
     // This is slow but it is not needed for any production code yet.
     for (DexString dexString : strings.keySet()) {
       Marker result = Marker.parse(dexString.toString());
@@ -494,6 +494,20 @@
     return null;
   }
 
+  // Debugging support to extract marking string.
+  // Find all markers.
+  public synchronized List<Marker> extractMarkers() {
+    // This is slow but it is not needed for any production code yet.
+    List<Marker> markers = new ArrayList<>();
+    for (DexString dexString : strings.keySet()) {
+      Marker marker = Marker.parse(dexString.toString());
+      if (marker != null) {
+        markers.add(marker);
+      }
+    }
+    return markers;
+  }
+
   synchronized public DexType createType(DexString descriptor) {
     assert !sorted;
     assert descriptor != null;
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 04c5f72..a8ff665 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -411,6 +411,7 @@
 
     public boolean invertConditionals = false;
     public boolean placeExceptionalBlocksLast = false;
+    public boolean dontCreateMarkerInD8 = false;
   }
 
   public boolean canUseInvokePolymorphicOnVarHandle() {
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
index e99e696..60edc6d 100644
--- a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
@@ -8,12 +8,15 @@
 
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexFileMergerHelper;
+import com.android.tools.r8.ExtractMarker;
 import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.maindexlist.MainDexListTests;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -35,16 +38,57 @@
 
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
-  @Test
-  public void mergeTwoFiles() throws CompilationFailedException, IOException {
+  private Path createMergerInputWithTwoClasses(OutputMode outputMode, boolean dontCreateMarkerInD8)
+      throws CompilationFailedException, CompilationException, IOException {
     // Compile Class1 and Class2
     Path mergerInputZip = temp.newFolder().toPath().resolve("merger-input.zip");
-    D8.run(
+    D8Command command =
         D8Command.builder()
-            .setOutput(mergerInputZip, OutputMode.DexFilePerClassFile)
+            .setOutput(mergerInputZip, outputMode)
             .addProgramFiles(Paths.get(CLASS1_CLASS))
             .addProgramFiles(Paths.get(CLASS2_CLASS))
-            .build());
+            .build();
+
+    DexFileMergerHelper.runForTesting(command, dontCreateMarkerInD8);
+
+    return mergerInputZip;
+  }
+
+  private void testMarkerPreservedOrNotAdded(boolean testNotAdding)
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexIndexed, testNotAdding);
+
+    Marker inputMarker = ExtractMarker.extractMarkerFromDexFile(mergerInputZip);
+    assertEquals(inputMarker == null, testNotAdding);
+
+    Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
+    DexFileMerger.main(
+        new String[] {
+          "--input", mergerInputZip.toString(), "--output", mergerOutputZip.toString()
+        });
+
+    Marker outputMarker = ExtractMarker.extractMarkerFromDexFile(mergerOutputZip);
+    assertEquals(outputMarker == null, testNotAdding);
+  }
+
+  @Test
+  public void testMarkerPreserved()
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    testMarkerPreservedOrNotAdded(false);
+  }
+
+  @Test
+  public void testMarkerNotAdded()
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    testMarkerPreservedOrNotAdded(true);
+  }
+
+  @Test
+  public void mergeTwoFiles() throws CompilationFailedException, CompilationException, IOException {
+    Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexFilePerClassFile, false);
 
     Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
     DexFileMerger.main(