Version 1.6.50

Cherry pick: Always initialize and write checksum encodings when requested.
CL: https://r8-review.googlesource.com/c/r8/+/45195
Bug: 143949125

Cherry pick: Add information on checksums present in marker
CL: https://r8-review.googlesource.com/c/r8/+/45114

Change-Id: I0704fa0f2dc6f789242cb1f69d558a28412c23db
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index c9f2032..bda3597 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -101,7 +101,7 @@
     options.minApiLevel = AndroidApiLevel.P.getLevel();
     DexApplication dexApp =
         new ApplicationReader(app, options, new Timing("ExtractMarker")).read();
-    return dexApp.dexItemFactory.extractMarker();
+    return dexApp.dexItemFactory.extractMarkers();
   }
 
   public static void main(String[] args)
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index db6daa1..ad2811c 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.6.49";
+  public static final String LABEL = "1.6.50";
 
   private Version() {
   }
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 6cdea02..c62df61 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -244,7 +244,7 @@
       application.dexItemFactory.sort(namingLens);
       assert markers == null
           || markers.isEmpty()
-          || application.dexItemFactory.extractMarker() != null;
+          || application.dexItemFactory.extractMarkers() != null;
 
       SortAnnotations sortAnnotations = new SortAnnotations();
       application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
diff --git a/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java b/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java
index eaff74e..ffdb9a4 100644
--- a/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java
+++ b/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java
@@ -18,7 +18,7 @@
   private static final char PREFIX_CHAR1 = '~';
   private static final char PREFIX_CHAR2 = '~';
 
-  private Object2LongMap<String> dictionary = null;
+  private final Object2LongMap<String> dictionary = new Object2LongOpenHashMap<>();
 
   public ClassesChecksum() {
     assert PREFIX.length() == 3;
@@ -27,14 +27,7 @@
     assert PREFIX.charAt(2) == PREFIX_CHAR2;
   }
 
-  private void ensureMap() {
-    if (dictionary == null) {
-      dictionary = new Object2LongOpenHashMap<>();
-    }
-  }
-
   private void append(JsonObject json) {
-    ensureMap();
     json.entrySet()
         .forEach(
             entry ->
@@ -42,7 +35,6 @@
   }
 
   public void addChecksum(String classDescriptor, long crc) {
-    ensureMap();
     dictionary.put(classDescriptor, crc);
   }
 
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 85d3568..7ab2a56 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -21,6 +21,7 @@
   public static final String MIN_API = "min-api";
   public static final String SHA1 = "sha-1";
   public static final String COMPILATION_MODE = "compilation-mode";
+  public static final String HAS_CHECKSUMS = "has-checksums";
   public static final String PG_MAP_ID = "pg-map-id";
 
   public enum Tool {
@@ -107,6 +108,16 @@
     return this;
   }
 
+  public boolean getHasChecksums() {
+    return jsonObject.get(HAS_CHECKSUMS).getAsBoolean();
+  }
+
+  public Marker setHasChecksums(boolean hasChecksums) {
+    assert !jsonObject.has(HAS_CHECKSUMS);
+    jsonObject.addProperty(HAS_CHECKSUMS, hasChecksums);
+    return this;
+  }
+
   public String getPgMapId() {
     return jsonObject.get(PG_MAP_ID).getAsString();
   }
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 b9df2a1..c99cf51 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1155,19 +1155,6 @@
   }
 
   // Debugging support to extract marking string.
-  public synchronized Collection<Marker> extractMarker() {
-    // This is slow but it is not needed for any production code yet.
-    List<Marker> markers = new ArrayList<>();
-    for (DexString dexString : strings.keySet()) {
-      Marker result = Marker.parse(dexString);
-      if (result != null) {
-        markers.add(result);
-      }
-    }
-    return markers;
-  }
-
-  // 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.
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 218baef..0098813 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -282,7 +282,8 @@
     Marker marker =
         new Marker(tool)
             .setVersion(Version.LABEL)
-            .setCompilationMode(debug ? CompilationMode.DEBUG : CompilationMode.RELEASE);
+            .setCompilationMode(debug ? CompilationMode.DEBUG : CompilationMode.RELEASE)
+            .setHasChecksums(encodeChecksums);
     if (!isGeneratingClassFiles()) {
       marker.setMinApi(minApiLevel);
     }
diff --git a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
index 948ab96..008ff1f 100644
--- a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
@@ -4,32 +4,57 @@
 package com.android.tools.r8;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.utils.BooleanUtils;
 import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.Set;
+import org.junit.Assume;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
-public class ExtractMarkerTest {
+@RunWith(Parameterized.class)
+public class ExtractMarkerTest extends TestBase {
   private static final String CLASS_FILE =
       ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
 
-  private static void verifyMarker(Marker marker, Tool tool) {
+  private final TestParameters parameters;
+  private boolean includeClassesChecksum;
+
+  @Parameterized.Parameters(name = "{0}, includeClassesChecksum: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevels().build(), BooleanUtils.values());
+  }
+
+  public ExtractMarkerTest(TestParameters parameters, boolean includeClassesChecksum) {
+    this.parameters = parameters;
+    this.includeClassesChecksum = includeClassesChecksum;
+  }
+
+  private void verifyMarkerDex(Marker marker, Tool tool) {
     assertEquals(tool, marker.getTool());
     assertEquals(Version.LABEL, marker.getVersion());
     assertEquals(CompilationMode.DEBUG.toString().toLowerCase(), marker.getCompilationMode());
+    assertEquals(parameters.getApiLevel().getLevel(), marker.getMinApi().intValue());
+    assertEquals(includeClassesChecksum, marker.getHasChecksums());
   }
 
   @Test
   public void extractMarkerTestDex() throws CompilationFailedException {
-    boolean[] testExecuted = {false};
+    Assume.assumeTrue(parameters.getRuntime().isDex());
 
+    boolean[] testExecuted = {false};
     D8.run(
         D8Command.builder()
             .addProgramFiles(Paths.get(CLASS_FILE))
+            .setMinApiLevel(parameters.getApiLevel().getLevel())
+            .setIncludeClassesChecksum(includeClassesChecksum)
             .setProgramConsumer(
                 new DexIndexedConsumer.ForwardingConsumer(null) {
                   @Override
@@ -47,7 +72,7 @@
                     } catch (Exception e) {
                       throw new RuntimeException(e);
                     }
-                    verifyMarker(marker, Tool.D8);
+                    verifyMarkerDex(marker, Tool.D8);
                     testExecuted[0] = true;
                   }
                 })
@@ -55,8 +80,18 @@
     assertTrue(testExecuted[0]);
   }
 
+  private static void verifyMarkerCf(Marker marker, Tool tool) {
+    assertEquals(tool, marker.getTool());
+    assertEquals(Version.LABEL, marker.getVersion());
+    assertEquals(CompilationMode.DEBUG.toString().toLowerCase(), marker.getCompilationMode());
+    assertFalse(marker.getHasChecksums());
+  }
+
   @Test
   public void extractMarkerTestCf() throws CompilationFailedException {
+    Assume.assumeTrue(parameters.getRuntime().isCf());
+    Assume.assumeFalse(includeClassesChecksum);
+
     boolean[] testExecuted = {false};
     R8.run(
         R8Command.builder()
@@ -78,7 +113,7 @@
                     } catch (Exception e) {
                       throw new RuntimeException(e);
                     }
-                    verifyMarker(marker, Tool.R8);
+                    verifyMarkerCf(marker, Tool.R8);
                     testExecuted[0] = true;
                   }
                 })
diff --git a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
index 93c0323..866dcf5 100644
--- a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
@@ -71,7 +71,7 @@
         new ApplicationReader(
                 app, options, new Timing("D8FrameworkDexPassthroughMarkerTest"))
             .read();
-    Collection<Marker> markers = dexApp.dexItemFactory.extractMarker();
+    Collection<Marker> markers = dexApp.dexItemFactory.extractMarkers();
     assertEquals(1, markers.size());
     Marker readMarker = markers.iterator().next();
     assertEquals(marker, readMarker);
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeChecksumsFileWithNoClassesTest.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeChecksumsFileWithNoClassesTest.java
new file mode 100644
index 0000000..57fe4b3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeChecksumsFileWithNoClassesTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2019, 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.dexfilemerger;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DexMergeChecksumsFileWithNoClassesTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public DexMergeChecksumsFileWithNoClassesTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testChecksumWithNoClasses() throws Exception {
+    Path out1 =
+        testForD8()
+            .setMinApi(parameters.getApiLevel())
+            .setMode(CompilationMode.DEBUG)
+            .setIncludeClassesChecksum(true)
+            .setIntermediate(true)
+            .compile()
+            .inspect(this::checkContainsChecksums)
+            .writeToZip();
+
+    Path out2 =
+        testForD8()
+            .setMinApi(parameters.getApiLevel())
+            .setMode(CompilationMode.DEBUG)
+            .setIncludeClassesChecksum(true)
+            .addProgramClasses(TestClass.class)
+            .setIntermediate(true)
+            .compile()
+            .inspect(this::checkContainsChecksums)
+            .writeToZip();
+
+    testForD8()
+        .addProgramFiles(out1, out2)
+        .setIncludeClassesChecksum(true)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::checkContainsChecksums);
+  }
+
+  private void checkContainsChecksums(CodeInspector inspector) {
+    inspector.getMarkers().forEach(m -> assertTrue(m.getHasChecksums()));
+    // It may be prudent to check that the dex file also has the encoding string, but that is
+    // not easily accessed.
+    inspector.allClasses().forEach(c -> c.getDexClass().asProgramClass().getChecksum());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println("Hello world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 2a91e4b..2bc1dd4 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.cf.code.CfTryCatch;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.Code;
@@ -48,6 +49,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -391,6 +393,10 @@
     }
   }
 
+  public Collection<Marker> getMarkers() {
+    return dexItemFactory.extractMarkers();
+  }
+
   // Build the generic signature using the current mapping if any.
   class GenericSignatureGenerator implements GenericSignatureAction<String> {