Version 1.6.43

Cherry pick: Require a checksum supplier for all program classes.

CL: https://r8-review.googlesource.com/c/r8/+/44921
Bug: 141736069
Change-Id: I7824c0148649a13bc8024c2cf93fc79e2a6a700b
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index ed74e0b..36adabe 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -217,7 +217,6 @@
               null,
               options,
               marker == null ? null : ImmutableList.copyOf(markers),
-              app.getChecksums(),
               GraphLense.getIdentityLense(),
               PrefixRewritingNamingLens.createPrefixRewritingNamingLens(options, rewritePrefix),
               null)
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index e728f6d..1ed8cac 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -100,7 +100,6 @@
                 null,
                 options,
                 markers,
-                null,
                 GraphLense.getIdentityLense(),
                 NamingLens.getIdentityLens(),
                 null);
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 595e1e3..2dee6ef 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -101,7 +101,6 @@
                   null,
                   options,
                   markers,
-                  null,
                   GraphLense.getIdentityLense(),
                   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 4cd2f7b..813bbd6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -207,7 +207,6 @@
                 appView,
                 options,
                 Collections.singletonList(marker),
-                null,
                 graphLense,
                 namingLens,
                 proguardMapSupplier)
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index e337dd1..ff5752e 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.42";
+  public static final String LABEL = "1.6.43";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 6b386c5..1d09319 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -186,8 +186,7 @@
     StringConsumer proguardMapConsumer = options.proguardMapConsumer;
     AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
     ApplicationWriter writer =
-        new ApplicationWriter(
-            app, null, options, null, null, null, NamingLens.getIdentityLens(), null);
+        new ApplicationWriter(app, null, options, null, null, NamingLens.getIdentityLens(), null);
     writer.write(executor);
     options.signalFinishedToConsumers();
     compatSink.build().writeToDirectory(output, OutputMode.DexIndexed);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index cfe4d9f..ae946fc 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -59,7 +59,6 @@
   private final DexItemFactory itemFactory;
   private final Timing timing;
   private final AndroidApp inputApp;
-  private final ClassesChecksum checksums = new ClassesChecksum();
 
   public interface ProgramClassConflictResolver {
     DexProgramClass resolveClassConflict(DexProgramClass a, DexProgramClass b);
@@ -124,10 +123,6 @@
       ClassReader classReader = new ClassReader(executorService, futures);
       JarClassFileReader jcf = classReader.readSources();
       ThreadUtils.awaitFutures(futures);
-      // Merge all the checksum gathered from the class file's CRC as well as the marker
-      // implanted into the dex file.
-      builder.mergeChecksums(jcf.getChecksums());
-      builder.mergeChecksums(classReader.application.options.itemFactory.extractChecksum());
       classReader.initializeLazyClassCollection(builder);
       for (ProgramResourceProvider provider : inputApp.getProgramResourceProviders()) {
         DataResourceProvider dataResourceProvider = provider.getDataResourceProvider();
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 431805b..6cdea02 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -46,12 +46,10 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
 import com.google.common.collect.ObjectArrays;
-import com.google.common.primitives.Longs;
+import it.unimi.dsi.fastutil.objects.Object2LongMap;
+import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
 import java.io.IOException;
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
@@ -63,7 +61,6 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.stream.Collectors;
-import java.util.zip.CRC32;
 
 public class ApplicationWriter {
 
@@ -74,7 +71,6 @@
   public final InternalOptions options;
   public List<Marker> markers;
   public List<DexString> markerStrings;
-  private final ClassesChecksum checksums;
 
   public DexIndexedConsumer programConsumer;
   public final ProguardMapSupplier proguardMapSupplier;
@@ -141,7 +137,6 @@
       AppView<?> appView,
       InternalOptions options,
       List<Marker> markers,
-      ClassesChecksum checksums,
       GraphLense graphLense,
       NamingLens namingLens,
       ProguardMapSupplier proguardMapSupplier) {
@@ -150,7 +145,6 @@
         appView,
         options,
         markers,
-        checksums,
         graphLense,
         namingLens,
         proguardMapSupplier,
@@ -162,7 +156,6 @@
       AppView<?> appView,
       InternalOptions options,
       List<Marker> markers,
-      ClassesChecksum checksums,
       GraphLense graphLense,
       NamingLens namingLens,
       ProguardMapSupplier proguardMapSupplier,
@@ -173,7 +166,6 @@
     assert options != null;
     this.options = options;
     this.markers = markers;
-    this.checksums = checksums;
     this.graphLense = graphLense;
     this.namingLens = namingLens;
     this.proguardMapSupplier = proguardMapSupplier;
@@ -207,49 +199,17 @@
    * This needs to be done after distribute but before dex string sorting.
    */
   private void encodeChecksums(Iterable<VirtualFile> files) {
-    ImmutableMap<String, Long> inputChecksums = checksums.getChecksums();
-    Map<String, Long> synthesizedChecksums = Maps.newHashMap();
-    for (DexProgramClass clazz : application.classes()) {
-      Collection<DexProgramClass> synthesizedFrom = clazz.getSynthesizedFrom();
-
-      if (synthesizedFrom.isEmpty()) {
-        if (inputChecksums.containsKey(clazz.getType().descriptor.toASCIIString())) {
-          continue;
-        } else {
-          throw new CompilationError(clazz + " from " + clazz.origin +
-              " has no checksum information while checksum encoding is requested");
-        }
-      }
-
-      // Checksum of synthesized classes are compute based off the depending input. This might
-      // create false positives (ie: unchanged lambda class detected as changed even thought only
-      // an unrelated part from a synthesizedFrom class is changed).
-
-      // Ideally, we should use some hashcode of the dex program class that is deterministic across
-      // compiles.
-      ByteBuffer buffer = ByteBuffer.allocate(synthesizedFrom.size() * Longs.BYTES);
-      for (DexProgramClass from : synthesizedFrom) {
-        buffer.putLong(inputChecksums.get(from.getType().descriptor.toASCIIString()));
-      }
-      CRC32 crc = new CRC32();
-      crc.update(buffer.array());
-      synthesizedChecksums.put(clazz.getType().descriptor.toASCIIString(), crc.getValue());
+    List<DexProgramClass> classes = application.classes();
+    Object2LongMap<String> inputChecksums = new Object2LongOpenHashMap<>(classes.size());
+    for (DexProgramClass clazz : classes) {
+      inputChecksums.put(clazz.getType().descriptor.toASCIIString(), clazz.getChecksum());
     }
-
-    for (VirtualFile f : files) {
+    for (VirtualFile file : files) {
       ClassesChecksum toWrite = new ClassesChecksum();
-      for (String desc : f.getClassDescriptors()) {
-        Long checksum = inputChecksums.get(desc);
-        if (checksum == null) {
-          checksum = synthesizedChecksums.get(desc);
-        }
-
-        // All classes should have a checksum from the inputChecksum (previous marker) or it was
-        // computed eariler in the function. Otherwise, we would have throw an compilation error.
-        assert checksum != null;
-        toWrite.addChecksum(desc, checksum);
+      for (String desc : file.getClassDescriptors()) {
+        toWrite.addChecksum(desc, inputChecksums.getLong(desc));
       }
-      f.injectString(application.dexItemFactory.createString(toWrite.toString()));
+      file.injectString(application.dexItemFactory.createString(toWrite.toJsonString()));
     }
   }
 
@@ -281,7 +241,6 @@
       if (options.encodeChecksums) {
         encodeChecksums(virtualFiles);
       }
-
       application.dexItemFactory.sort(namingLens);
       assert markers == null
           || markers.isEmpty()
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 d5c7a64..07d2176 100644
--- a/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java
+++ b/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java
@@ -1,12 +1,12 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.graph.DexString;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import com.google.gson.JsonSyntaxException;
+import it.unimi.dsi.fastutil.objects.Object2LongMap;
+import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
 import java.util.Comparator;
 import java.util.Map;
 
@@ -16,53 +16,47 @@
   private static final char PREFIX_CHAR1 = '~';
   private static final char PREFIX_CHAR2 = '~';
 
+  private Object2LongMap<String> dictionary = null;
 
-  // private final JsonObject dictionary;
-  Map<String, Long> dictionary = Maps.newHashMap();
-
-  /**
-   * Checksum to be inserted.
-   */
   public ClassesChecksum() {
   }
 
-  private ClassesChecksum(JsonObject json) {
-    json.entrySet().forEach(entry ->
-        dictionary.put(entry.getKey(), Long.parseLong(entry.getValue().getAsString(), 16)));
-  }
-
-  public synchronized ClassesChecksum addChecksum(String classDescriptor, Long crc) {
-    dictionary.put(classDescriptor, crc);
-    return this;
-  }
-
-  public synchronized ImmutableMap<String, Long> getChecksums() {
-    return ImmutableMap.copyOf(dictionary);
-  }
-
-  public synchronized ClassesChecksum merge(ClassesChecksum other) {
-    if (other != null) {
-      other.dictionary.entrySet().stream().forEach(entry -> this.dictionary.put(
-          entry.getKey(), entry.getValue()));
+  private void ensureMap() {
+    if (dictionary == null) {
+      dictionary = new Object2LongOpenHashMap<>();
     }
-    return this;
   }
 
-  @Override
-  public synchronized String toString() {
+  private void append(JsonObject json) {
+    ensureMap();
+    json.entrySet()
+        .forEach(
+            entry ->
+                dictionary.put(entry.getKey(), Long.parseLong(entry.getValue().getAsString(), 16)));
+  }
+
+  public void addChecksum(String classDescriptor, long crc) {
+    ensureMap();
+    dictionary.put(classDescriptor, crc);
+  }
+
+  public Object2LongMap<String> getChecksums() {
+    return dictionary;
+  }
+
+  public String toJsonString() {
     // In order to make printing of markers deterministic we sort the entries by key.
     final JsonObject sortedJson = new JsonObject();
-    dictionary.entrySet()
-        .stream()
+    dictionary.object2LongEntrySet().stream()
         .sorted(Comparator.comparing(Map.Entry::getKey))
         .forEach(
-            entry -> sortedJson.addProperty(entry.getKey(), Long.toHexString(entry.getValue())));
+            entry ->
+                sortedJson.addProperty(entry.getKey(), Long.toString(entry.getLongValue(), 16)));
     return "" + PREFIX_CHAR0 + PREFIX_CHAR1 + PREFIX_CHAR2 + sortedJson;
   }
 
-  // Try to parse str as a marker.
-  // Returns null if parsing fails.
-  public static ClassesChecksum parse(DexString dexString) {
+  // Try to parse the string as a marker and append its content if successful.
+  public void tryParseAndAppend(DexString dexString) {
     if (dexString.size > 2
         && dexString.content[0] == PREFIX_CHAR0
         && dexString.content[1] == PREFIX_CHAR1
@@ -71,11 +65,10 @@
       try {
         JsonElement result = new JsonParser().parse(str);
         if (result.isJsonObject()) {
-          return new ClassesChecksum(result.getAsJsonObject());
+          append(result.getAsJsonObject());
         }
       } catch (JsonSyntaxException ignored) {}
     }
-    return null;
   }
 
   public static boolean preceedChecksumMarker(DexString string) {
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 28b60a3..e722669 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -40,6 +40,8 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -60,11 +62,11 @@
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.io.ByteStreams;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2LongMap;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -87,7 +89,7 @@
   private int[] stringIDs;
   private final ClassKind classKind;
   private final InternalOptions options;
-  private ImmutableMap<String, Long> checksums;
+  private Object2LongMap<String> checksums;
 
   public static DexSection[] parseMapFrom(Path file) throws IOException {
     return parseMapFrom(Files.newInputStream(file), new PathOrigin(file));
@@ -712,9 +714,10 @@
       DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
       AnnotationsDirectory annotationsDirectory = annotationsDirectoryAt(annotationsOffsets[i]);
 
+      Long checksum = null;
       if (checksums != null && classDataOffsets[i] != 0) {
         String desc = type.descriptor.toASCIIString();
-        Long checksum = checksums.get(desc);
+        checksum = checksums.getOrDefault(desc, null);
         if (!options.dexClassChecksumFilter.test(desc, checksum)) {
           continue;
         }
@@ -752,6 +755,10 @@
       AttributesAndAnnotations attrs =
           new AttributesAndAnnotations(type, annotationsDirectory.clazz, options.itemFactory);
 
+      Long finalChecksum = checksum;
+      ChecksumSupplier checksumSupplier =
+          finalChecksum == null ? DexProgramClass::invalidChecksumRequest : c -> finalChecksum;
+
       DexClass clazz =
           classKind.create(
               type,
@@ -770,7 +777,8 @@
               instanceFields,
               directMethods,
               virtualMethods,
-              dexItemFactory.getSkipNameValidationForTesting());
+              dexItemFactory.getSkipNameValidationForTesting(),
+              checksumSupplier);
       classCollection.accept(clazz);  // Update the application object.
     }
   }
@@ -939,23 +947,15 @@
   }
 
   private void populateChecksums() {
-    ClassesChecksum checksums = null;
+    ClassesChecksum parsedChecksums = new ClassesChecksum();
     for (int i = stringIDs.length - 1; i >= 0; i--) {
       DexString value = indexedItems.getString(i);
-      ClassesChecksum checksum = ClassesChecksum.parse(value);
-      if (checksum != null) {
-        if (checksums == null) {
-          checksums = checksum;
-        } else {
-          checksums.merge(checksum);
-        }
-      } else if (ClassesChecksum.preceedChecksumMarker(value)) {
+      if (ClassesChecksum.preceedChecksumMarker(value)) {
         break;
       }
+      parsedChecksums.tryParseAndAppend(value);
     }
-    if (checksums != null) {
-      this.checksums = checksums.getChecksums();
-    }
+    this.checksums = parsedChecksums.getChecksums();
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index 6443795..14fa13e 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -4,7 +4,8 @@
 
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.origin.Origin;
 import java.util.List;
 import java.util.function.Consumer;
@@ -13,13 +14,89 @@
 /** Kind of the application class. Can be program, classpath or library. */
 public enum ClassKind {
   PROGRAM(DexProgramClass::new, DexClass::isProgramClass),
-  CLASSPATH(DexClasspathClass::new, DexClass::isClasspathClass),
-  LIBRARY(DexLibraryClass::new, DexClass::isLibraryClass);
+  CLASSPATH(
+      (type,
+          kind,
+          origin,
+          accessFlags,
+          superType,
+          interfaces,
+          sourceFile,
+          nestHost,
+          nestMembers,
+          enclosingMember,
+          innerClasses,
+          annotations,
+          staticFields,
+          instanceFields,
+          directMethods,
+          virtualMethods,
+          skipNameValidationForTesting,
+          checksumSupplier) -> {
+        return new DexClasspathClass(
+            type,
+            kind,
+            origin,
+            accessFlags,
+            superType,
+            interfaces,
+            sourceFile,
+            nestHost,
+            nestMembers,
+            enclosingMember,
+            innerClasses,
+            annotations,
+            staticFields,
+            instanceFields,
+            directMethods,
+            virtualMethods,
+            skipNameValidationForTesting);
+      },
+      DexClass::isClasspathClass),
+  LIBRARY(
+      (type,
+          kind,
+          origin,
+          accessFlags,
+          superType,
+          interfaces,
+          sourceFile,
+          nestHost,
+          nestMembers,
+          enclosingMember,
+          innerClasses,
+          annotations,
+          staticFields,
+          instanceFields,
+          directMethods,
+          virtualMethods,
+          skipNameValidationForTesting,
+          checksumSupplier) -> {
+        return new DexLibraryClass(
+            type,
+            kind,
+            origin,
+            accessFlags,
+            superType,
+            interfaces,
+            sourceFile,
+            nestHost,
+            nestMembers,
+            enclosingMember,
+            innerClasses,
+            annotations,
+            staticFields,
+            instanceFields,
+            directMethods,
+            virtualMethods,
+            skipNameValidationForTesting);
+      },
+      DexClass::isLibraryClass);
 
   private interface Factory {
     DexClass create(
         DexType type,
-        ProgramResource.Kind kind,
+        Kind kind,
         Origin origin,
         ClassAccessFlags accessFlags,
         DexType superType,
@@ -34,7 +111,8 @@
         DexEncodedField[] instanceFields,
         DexEncodedMethod[] directMethods,
         DexEncodedMethod[] virtualMethods,
-        boolean skipNameValidationForTesting);
+        boolean skipNameValidationForTesting,
+        ChecksumSupplier checksumSupplier);
   }
 
   private final Factory factory;
@@ -47,7 +125,7 @@
 
   public DexClass create(
       DexType type,
-      ProgramResource.Kind kind,
+      Kind kind,
       Origin origin,
       ClassAccessFlags accessFlags,
       DexType superType,
@@ -62,7 +140,8 @@
       DexEncodedField[] instanceFields,
       DexEncodedMethod[] directMethods,
       DexEncodedMethod[] virtualMethods,
-      boolean skipNameValidationForTesting) {
+      boolean skipNameValidationForTesting,
+      ChecksumSupplier checksumSupplier) {
     return factory.create(
         type,
         kind,
@@ -80,7 +159,8 @@
         instanceFields,
         directMethods,
         virtualMethods,
-        skipNameValidationForTesting);
+        skipNameValidationForTesting,
+        checksumSupplier);
   }
 
   public boolean isOfKind(DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index c2375c5..29af63e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -8,7 +8,6 @@
 
 import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
-import com.android.tools.r8.dex.ClassesChecksum;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.InternalOptions;
@@ -35,7 +34,6 @@
 
   public final InternalOptions options;
   public final DexItemFactory dexItemFactory;
-  public final ClassesChecksum checksums;
 
   // Information on the lexicographically largest string referenced from code.
   public final DexString highestSortingString;
@@ -45,14 +43,12 @@
       ClassNameMapper proguardMap,
       ImmutableList<DataResourceProvider> dataResourceProviders,
       ImmutableSet<DexType> mainDexList,
-      ClassesChecksum checksums,
       InternalOptions options,
       DexString highestSortingString,
       Timing timing) {
     this.proguardMap = proguardMap;
     this.dataResourceProviders = dataResourceProviders;
     this.mainDexList = mainDexList;
-    this.checksums = checksums;
     this.options = options;
     this.dexItemFactory = options.itemFactory;
     this.highestSortingString = highestSortingString;
@@ -108,10 +104,6 @@
     return classes;
   }
 
-  public ClassesChecksum getChecksums() {
-    return checksums;
-  }
-
   public abstract DexClass definitionFor(DexType type);
 
   public abstract DexProgramClass programDefinitionFor(DexType type);
@@ -137,7 +129,6 @@
 
     public final InternalOptions options;
     public final DexItemFactory dexItemFactory;
-    protected ClassesChecksum checksums;
     ClassNameMapper proguardMap;
     final Timing timing;
 
@@ -150,7 +141,6 @@
       this.dexItemFactory = options.itemFactory;
       this.timing = timing;
       this.synthesizedClasses = new ArrayList<>();
-      this.checksums = null;
     }
 
     abstract T self();
@@ -165,7 +155,6 @@
       dexItemFactory = application.dexItemFactory;
       mainDexList.addAll(application.mainDexList);
       synthesizedClasses = new ArrayList<>();
-      checksums = application.checksums;
     }
 
     public synchronized T setProguardMap(ClassNameMapper proguardMap) {
@@ -224,15 +213,6 @@
       return this;
     }
 
-    public Builder<T> mergeChecksums(ClassesChecksum other) {
-      if (checksums == null) {
-        checksums = other;
-      } else {
-        checksums.merge(other);
-      }
-      return this;
-    }
-
     public abstract DexApplication build();
   }
 
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 e52c246..b9df2a1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement.computeLeastUpperBoundOfInterfaces;
 import static com.google.common.base.Predicates.alwaysTrue;
 
-import com.android.tools.r8.dex.ClassesChecksum;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
@@ -49,7 +48,6 @@
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
@@ -1183,11 +1181,6 @@
     return markers;
   }
 
-  public synchronized ClassesChecksum extractChecksum() {
-    return strings.keySet().stream().map(s -> ClassesChecksum.parse(s)).filter(Objects::nonNull)
-        .reduce(null, (s1, s2) -> s1 == null ? s2 : s1.merge(s2));
-  }
-
   synchronized public DexType createType(DexString descriptor) {
     assert !sorted;
     assert descriptor != null;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 7bfe2a6..92a4963 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.kotlin.KotlinInfo;
 import com.android.tools.r8.origin.Origin;
 import java.util.ArrayList;
@@ -20,6 +21,12 @@
 import java.util.function.Supplier;
 
 public class DexProgramClass extends DexClass implements Supplier<DexProgramClass> {
+
+  @FunctionalInterface
+  public interface ChecksumSupplier {
+    long getChecksum(DexProgramClass programClass);
+  }
+
   public static final DexProgramClass[] EMPTY_ARRAY = {};
 
   private static final DexEncodedArray SENTINEL_NOT_YET_COMPUTED =
@@ -31,9 +38,11 @@
   private int initialClassFileVersion = -1;
   private KotlinInfo kotlinInfo = null;
 
+  private final ChecksumSupplier checksumSupplier;
+
   public DexProgramClass(
       DexType type,
-      ProgramResource.Kind originKind,
+      Kind originKind,
       Origin origin,
       ClassAccessFlags accessFlags,
       DexType superType,
@@ -48,7 +57,8 @@
       DexEncodedField[] instanceFields,
       DexEncodedMethod[] directMethods,
       DexEncodedMethod[] virtualMethods,
-      boolean skipNameValidationForTesting) {
+      boolean skipNameValidationForTesting,
+      ChecksumSupplier checksumSupplier) {
     this(
         type,
         originKind,
@@ -67,12 +77,13 @@
         directMethods,
         virtualMethods,
         skipNameValidationForTesting,
+        checksumSupplier,
         Collections.emptyList());
   }
 
   public DexProgramClass(
       DexType type,
-      ProgramResource.Kind originKind,
+      Kind originKind,
       Origin origin,
       ClassAccessFlags accessFlags,
       DexType superType,
@@ -88,6 +99,7 @@
       DexEncodedMethod[] directMethods,
       DexEncodedMethod[] virtualMethods,
       boolean skipNameValidationForTesting,
+      ChecksumSupplier checksumSupplier,
       Collection<DexProgramClass> synthesizedDirectlyFrom) {
     super(
         sourceFile,
@@ -106,8 +118,10 @@
         classAnnotations,
         origin,
         skipNameValidationForTesting);
+    assert checksumSupplier != null;
     assert classAnnotations != null;
     this.originKind = originKind;
+    this.checksumSupplier = checksumSupplier;
     this.synthesizedFrom = new HashSet<>();
     synthesizedDirectlyFrom.forEach(this::addSynthesizedFrom);
   }
@@ -471,4 +485,17 @@
           }
         };
   }
+
+  public static long invalidChecksumRequest(DexProgramClass clazz) {
+    throw new CompilationError(
+        clazz + " has no checksum information while checksum encoding is requested", clazz.origin);
+  }
+
+  public static long checksumFromType(DexProgramClass clazz) {
+    return clazz.type.hashCode();
+  }
+
+  public long getChecksum() {
+    return checksumSupplier.getChecksum(this);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index efe63f6..6308a16 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -7,7 +7,6 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.DataResourceProvider;
-import com.android.tools.r8.dex.ClassesChecksum;
 import com.android.tools.r8.graph.LazyLoadedDexApplication.AllClasses;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.InternalOptions;
@@ -41,7 +40,6 @@
       ImmutableList<DexLibraryClass> libraryClasses,
       ImmutableList<DataResourceProvider> dataResourceProviders,
       ImmutableSet<DexType> mainDexList,
-      ClassesChecksum checksums,
       InternalOptions options,
       DexString highestSortingString,
       Timing timing) {
@@ -49,7 +47,6 @@
         proguardMap,
         dataResourceProviders,
         mainDexList,
-        checksums,
         options,
         highestSortingString,
         timing);
@@ -203,7 +200,6 @@
           libraryClasses,
           ImmutableList.copyOf(dataResourceProviders),
           ImmutableSet.copyOf(mainDexList),
-          checksums,
           options,
           highestSortingString,
           timing);
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 1c359f3..d6fbdba 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -12,10 +12,10 @@
 import static org.objectweb.asm.Opcodes.V9;
 
 import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.dex.ClassesChecksum;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
 import com.android.tools.r8.graph.DexValue.DexValueBoolean;
@@ -75,13 +75,11 @@
 
   private final JarApplicationReader application;
   private final Consumer<DexClass> classConsumer;
-  private final ClassesChecksum checksums;
 
   public JarClassFileReader(
       JarApplicationReader application, Consumer<DexClass> classConsumer) {
     this.application = application;
     this.classConsumer = classConsumer;
-    this.checksums = new ClassesChecksum();
   }
 
   public void read(Origin origin, ClassKind classKind, InputStream input) throws IOException {
@@ -116,8 +114,9 @@
         parsingOptions |= SKIP_DEBUG;
       }
     }
-    reader.accept(new CreateDexClassVisitor(
-        origin, classKind, reader.b, application, classConsumer, checksums), parsingOptions);
+    reader.accept(
+        new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer),
+        parsingOptions);
 
     // Read marker.
     if (reader.getItemCount() > CfApplicationWriter.MARKER_STRING_CONSTANT_POOL_INDEX
@@ -136,10 +135,6 @@
     }
   }
 
-  public ClassesChecksum getChecksums() {
-    return checksums;
-  }
-
   private static int cleanAccessFlags(int access) {
     // Clear the "synthetic attribute" and "deprecated" attribute-flags if present.
     return access & ~ACC_SYNTHETIC_ATTRIBUTE & ~ACC_DEPRECATED;
@@ -218,22 +213,19 @@
     private final List<DexEncodedMethod> virtualMethods = new ArrayList<>();
     private final Set<Wrapper<DexMethod>> methodSignatures = new HashSet<>();
     private boolean hasReachabilitySensitiveMethod = false;
-    private ClassesChecksum checksums;
 
     public CreateDexClassVisitor(
         Origin origin,
         ClassKind classKind,
         byte[] classCache,
         JarApplicationReader application,
-        Consumer<DexClass> classConsumer,
-        ClassesChecksum checksums) {
+        Consumer<DexClass> classConsumer) {
       super(ASM_VERSION);
       this.origin = origin;
       this.classKind = classKind;
       this.classConsumer = classConsumer;
       this.context.classCache = classCache;
       this.application = application;
-      this.checksums = checksums;
     }
 
     @Override
@@ -419,7 +411,8 @@
               instanceFields.toArray(DexEncodedField.EMPTY_ARRAY),
               directMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
               virtualMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
-              application.getFactory().getSkipNameValidationForTesting());
+              application.getFactory().getSkipNameValidationForTesting(),
+              getChecksumSupplier(classKind));
       InnerClassAttribute innerClassAttribute = clazz.getInnerClassAttributeForThisClass();
       // A member class should not be a local or anonymous class.
       if (innerClassAttribute != null && innerClassAttribute.getOuter() != null) {
@@ -453,16 +446,22 @@
         context.owner = clazz;
       }
       if (clazz.isProgramClass()) {
-        clazz.asProgramClass().setInitialClassFileVersion(version);
-        if (application.options.encodeChecksums) {
-          CRC32 crc = new CRC32();
-          crc.update(this.context.classCache);
-          checksums.addChecksum(type.descriptor.toASCIIString(), crc.getValue());
-        }
+        DexProgramClass programClass = clazz.asProgramClass();
+        programClass.setInitialClassFileVersion(version);
       }
       classConsumer.accept(clazz);
     }
 
+    private ChecksumSupplier getChecksumSupplier(ClassKind classKind) {
+      if (application.options.encodeChecksums && classKind == ClassKind.PROGRAM) {
+        CRC32 crc = new CRC32();
+        crc.update(this.context.classCache, 0, this.context.classCache.length);
+        final long value = crc.getValue();
+        return clazz -> value;
+      }
+      return DexProgramClass::invalidChecksumRequest;
+    }
+
     private void checkName(String name) {
       if (!application.getFactory().getSkipNameValidationForTesting()
           && !DexString.isValidSimpleName(application.options.minApiLevel, name)) {
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index 0458321..24ec3c3 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -8,7 +8,6 @@
 
 import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
-import com.android.tools.r8.dex.ClassesChecksum;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.ClasspathClassCollection;
 import com.android.tools.r8.utils.InternalOptions;
@@ -36,7 +35,6 @@
       ClasspathClassCollection classpathClasses,
       LibraryClassCollection libraryClasses,
       ImmutableSet<DexType> mainDexList,
-      ClassesChecksum checksum,
       InternalOptions options,
       DexString highestSortingString,
       Timing timing) {
@@ -44,7 +42,6 @@
         proguardMap,
         dataResourceProviders,
         mainDexList,
-        checksum,
         options,
         highestSortingString,
         timing);
@@ -236,7 +233,6 @@
           classpathClasses,
           libraryClasses,
           ImmutableSet.copyOf(mainDexList),
-          checksums,
           options,
           highestSortingString,
           timing);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 0e13782..09f2b7e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -48,6 +49,7 @@
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
@@ -184,6 +186,7 @@
               new DexEncodedMethod[] {dexEncodedMethod},
               DexEncodedMethod.EMPTY_ARRAY,
               factory.getSkipNameValidationForTesting(),
+              getChecksumSupplier(dexEncodedMethod),
               referencingClasses);
       boolean addToMainDexList =
           referencingClasses.stream()
@@ -195,6 +198,14 @@
     }
   }
 
+  private ChecksumSupplier getChecksumSupplier(DexEncodedMethod method) {
+    if (!appView.options().encodeChecksums) {
+      return DexProgramClass::invalidChecksumRequest;
+    }
+    long hash = Objects.hash(method, method.getCode());
+    return c -> hash;
+  }
+
   private MethodProvider getMethodProviderOrNull(DexMethod method) {
     DexMethod original = appView.graphLense().getOriginalMethodSignature(method);
     assert original != null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 320935f..0cb8a52 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -732,6 +732,7 @@
         emulationMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
         DexEncodedMethod.EMPTY_ARRAY,
         factory.getSkipNameValidationForTesting(),
+        DexProgramClass::checksumFromType,
         Collections.singletonList(theInterface));
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 8a990f2..8b8ef05 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
@@ -190,10 +191,19 @@
             companionMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
             DexEncodedMethod.EMPTY_ARRAY,
             rewriter.factory.getSkipNameValidationForTesting(),
+            getChecksumSupplier(iface),
             Collections.singletonList(iface));
     syntheticClasses.put(iface.type, companionClass);
   }
 
+  private ChecksumSupplier getChecksumSupplier(DexProgramClass iface) {
+    if (!appView.options().encodeChecksums) {
+      return DexProgramClass::invalidChecksumRequest;
+    }
+    long checksum = iface.getChecksum();
+    return c -> 7 * checksum;
+  }
+
   List<DexEncodedMethod> process(DexLibraryClass iface, Set<DexProgramClass> callers) {
     assert iface.isInterface();
 
@@ -263,6 +273,7 @@
             dispatchMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
             DexEncodedMethod.EMPTY_ARRAY,
             rewriter.factory.getSkipNameValidationForTesting(),
+            DexProgramClass::checksumFromType,
             callers);
     syntheticClasses.put(iface.type, dispatchClass);
     return dispatchMethods;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index a397cd7..40932cb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -30,12 +30,15 @@
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.google.common.base.Suppliers;
+import com.google.common.primitives.Longs;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
+import java.util.zip.CRC32;
 
 /**
  * Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -167,7 +170,8 @@
             synthesizeInstanceFields(),
             synthesizeDirectMethods(),
             synthesizeVirtualMethods(mainMethod),
-            rewriter.factory.getSkipNameValidationForTesting());
+            rewriter.factory.getSkipNameValidationForTesting(),
+            LambdaClass::computeChecksumForSynthesizedClass);
     // Optimize main method.
     rewriter.converter.appView.appInfo().addSynthesizedClass(clazz);
     rewriter.converter.optimizeSynthesizedMethod(clazz.lookupVirtualMethod(mainMethod));
@@ -180,6 +184,24 @@
     return clazz;
   }
 
+  private static long computeChecksumForSynthesizedClass(DexProgramClass clazz) {
+    // Checksum of synthesized classes are compute based off the depending input. This might
+    // create false positives (ie: unchanged lambda class detected as changed even thought only
+    // an unrelated part from a synthesizedFrom class is changed).
+
+    // Ideally, we should use some hashcode of the dex program class that is deterministic across
+    // compiles.
+    Collection<DexProgramClass> synthesizedFrom = clazz.getSynthesizedFrom();
+    ByteBuffer buffer = ByteBuffer.allocate(synthesizedFrom.size() * Longs.BYTES);
+    for (DexProgramClass from : synthesizedFrom) {
+      buffer.putLong(from.getChecksum());
+    }
+    CRC32 crc = new CRC32();
+    byte[] array = buffer.array();
+    crc.update(array, 0, array.length);
+    return crc.getValue();
+  }
+
   final DexField getCaptureField(int index) {
     return rewriter.factory.createField(this.type,
         descriptor.captures.values[index], rewriter.factory.createString("f$" + index));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 449f404..e8e7b14 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -164,7 +164,8 @@
         DexEncodedField.EMPTY_ARRAY,
         DexEncodedMethod.EMPTY_ARRAY,
         DexEncodedMethod.EMPTY_ARRAY,
-        appView.dexItemFactory().getSkipNameValidationForTesting());
+        appView.dexItemFactory().getSkipNameValidationForTesting(),
+        DexProgramClass::checksumFromType);
   }
 
   void synthetizeNestConstructor(DexApplication.Builder<?> builder) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 12d43cb..c94d5b0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -152,6 +152,7 @@
             new DexEncodedMethod[] {method},
             DexEncodedMethod.EMPTY_ARRAY,
             appView.dexItemFactory().getSkipNameValidationForTesting(),
+            DexProgramClass::checksumFromType,
             referencingClasses);
 
     // Process created class and method.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 4837545..cbdf50d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -1342,27 +1343,29 @@
     DexTypeList interfaces = DexTypeList.empty();
     DexString sourceFile = appView.dexItemFactory().createString("outline");
     ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
-    DexProgramClass clazz =
-        new DexProgramClass(
-            type,
-            null,
-            new SynthesizedOrigin("outlining", getClass()),
-            accessFlags,
-            superType,
-            interfaces,
-            sourceFile,
-            null,
-            Collections.emptyList(),
-            null,
-            Collections.emptyList(),
-            // TODO: Build dex annotations structure.
-            DexAnnotationSet.empty(),
-            DexEncodedField.EMPTY_ARRAY, // Static fields.
-            DexEncodedField.EMPTY_ARRAY, // Instance fields.
-            direct,
-            DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
-            appView.dexItemFactory().getSkipNameValidationForTesting());
-    return clazz;
+    assert !appView.options().encodeChecksums;
+    // The outliner is R8 only and checksum is not a supported part of R8 compilation.
+    ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
+    return new DexProgramClass(
+        type,
+        null,
+        new SynthesizedOrigin("outlining", getClass()),
+        accessFlags,
+        superType,
+        interfaces,
+        sourceFile,
+        null,
+        Collections.emptyList(),
+        null,
+        Collections.emptyList(),
+        // TODO: Build dex annotations structure.
+        DexAnnotationSet.empty(),
+        DexEncodedField.EMPTY_ARRAY, // Static fields.
+        DexEncodedField.EMPTY_ARRAY, // Instance fields.
+        direct,
+        DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+        appView.dexItemFactory().getSkipNameValidationForTesting(),
+        checksumSupplier);
   }
 
   private List<Outline> selectOutlines() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 7830f6e..451788f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
@@ -184,6 +185,8 @@
     DexType serviceLoaderType =
         appView.dexItemFactory().createType("L" + SERVICE_LOADER_CLASS_NAME + ";");
     if (synthesizedClass == null) {
+      assert !appView.options().encodeChecksums;
+      ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
       synthesizedClass =
           new DexProgramClass(
               serviceLoaderType,
@@ -203,7 +206,8 @@
               DexEncodedField.EMPTY_ARRAY, // Instance fields.
               DexEncodedMethod.EMPTY_ARRAY,
               DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
-              appView.dexItemFactory().getSkipNameValidationForTesting());
+              appView.dexItemFactory().getSkipNameValidationForTesting(),
+              checksumSupplier);
       appView.appInfo().addSynthesizedClass(synthesizedClass);
     }
     DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index 4ce3033..5c3e91c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
 import com.android.tools.r8.kotlin.Kotlin;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.collect.Lists;
 import com.google.common.io.BaseEncoding;
@@ -166,13 +167,19 @@
 
   protected abstract String getGroupSuffix();
 
-  final DexProgramClass synthesizeClass(DexItemFactory factory) {
+  final DexProgramClass synthesizeClass(InternalOptions options) {
     assert classType == null;
     assert verifyLambdaIds(true);
     List<LambdaInfo> lambdas = Lists.newArrayList(this.lambdas.values());
-    classType = factory.createType(
-        "L" + getTypePackage() + "-$$LambdaGroup$" + getGroupSuffix() + createHash(lambdas) + ";");
-    return getBuilder(factory).synthesizeClass();
+    classType =
+        options.itemFactory.createType(
+            "L"
+                + getTypePackage()
+                + "-$$LambdaGroup$"
+                + getGroupSuffix()
+                + createHash(lambdas)
+                + ";");
+    return getBuilder(options.itemFactory).synthesizeClass(options);
   }
 
   protected abstract LambdaGroupClassBuilder getBuilder(DexItemFactory factory);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
index 4f5f110..e569714 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.InternalOptions;
 import java.util.Collections;
 import java.util.List;
 
@@ -31,28 +32,31 @@
     this.origin = origin;
   }
 
-  public final DexProgramClass synthesizeClass() {
+  public final DexProgramClass synthesizeClass(InternalOptions options) {
     DexType groupClassType = group.getGroupClassType();
     DexType superClassType = getSuperClassType();
-
-    return new DexProgramClass(
-        groupClassType,
-        null,
-        new SynthesizedOrigin(origin, getClass()),
-        buildAccessFlags(),
-        superClassType,
-        buildInterfaces(),
-        factory.createString(origin),
-        null,
-        Collections.emptyList(),
-        buildEnclosingMethodAttribute(),
-        buildInnerClasses(),
-        buildAnnotations(),
-        buildStaticFields(),
-        buildInstanceFields(),
-        buildDirectMethods(),
-        buildVirtualMethods(),
-        factory.getSkipNameValidationForTesting());
+    DexProgramClass programClass =
+        new DexProgramClass(
+            groupClassType,
+            null,
+            new SynthesizedOrigin(origin, getClass()),
+            buildAccessFlags(),
+            superClassType,
+            buildInterfaces(),
+            factory.createString(origin),
+            null,
+            Collections.emptyList(),
+            buildEnclosingMethodAttribute(),
+            buildInnerClasses(),
+            buildAnnotations(),
+            buildStaticFields(),
+            buildInstanceFields(),
+            buildDirectMethods(),
+            buildVirtualMethods(),
+            factory.getSkipNameValidationForTesting(),
+            // The name of the class is based on the hash of the content.
+            DexProgramClass::checksumFromType);
+    return programClass;
   }
 
   protected abstract DexType getSuperClassType();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 89451c9..24d4548 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -327,7 +327,7 @@
     for (LambdaGroup group : groups.values()) {
       assert !group.isTrivial() : "No trivial group is expected here.";
       group.compact();
-      DexProgramClass lambdaGroupClass = group.synthesizeClass(factory);
+      DexProgramClass lambdaGroupClass = group.synthesizeClass(appView.options());
       result.put(group, lambdaGroupClass);
 
       // We have to register this new class as a subtype of object.
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 8b40ea9..5b94aac 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -87,49 +87,60 @@
         code);
   }
 
-  private DexProgramClass makeClass(String name, int stringCount, int startOffset,
+  private DexProgramClass makeClass(
+      InternalOptions options,
+      String name,
+      int stringCount,
+      int startOffset,
       Collection<DexProgramClass> synthesizedFrom) {
     String desc = DescriptorUtils.javaTypeToDescriptor(name);
     DexType type = dexItemFactory.createType(desc);
-    return new DexProgramClass(
-        type,
-        null,
-        new SynthesizedOrigin("test", getClass()),
-        ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC),
-        dexItemFactory.objectType,
-        DexTypeList.empty(),
-        null,
-        null,
-        Collections.emptyList(),
-        null,
-        Collections.emptyList(),
-        DexAnnotationSet.empty(),
-        DexEncodedField.EMPTY_ARRAY,
-        DexEncodedField.EMPTY_ARRAY,
-        DexEncodedMethod.EMPTY_ARRAY,
-        new DexEncodedMethod[] {makeMethod(type, stringCount, startOffset)},
-        false,
-        synthesizedFrom);
+    DexProgramClass programClass =
+        new DexProgramClass(
+            type,
+            null,
+            new SynthesizedOrigin("test", getClass()),
+            ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC),
+            dexItemFactory.objectType,
+            DexTypeList.empty(),
+            null,
+            null,
+            Collections.emptyList(),
+            null,
+            Collections.emptyList(),
+            DexAnnotationSet.empty(),
+            DexEncodedField.EMPTY_ARRAY,
+            DexEncodedField.EMPTY_ARRAY,
+            DexEncodedMethod.EMPTY_ARRAY,
+            new DexEncodedMethod[] {makeMethod(type, stringCount, startOffset)},
+            false,
+            DexProgramClass::invalidChecksumRequest,
+            synthesizedFrom);
+    return programClass;
   }
 
   @Test
   public void manyFilesWithSharedSynthesizedClass() throws ExecutionException, IOException {
+    InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
 
     // Create classes that all reference enough strings to overflow the index, but are all
     // at different offsets in the strings array. This ensures we trigger multiple rounds of
     // rewrites.
     List<DexProgramClass> classes = new ArrayList<>();
     for (int i = 0; i < NUMBER_OF_FILES; i++) {
-      classes.add(makeClass("Class" + i, Constants.MAX_NON_JUMBO_INDEX - 1, i % 100,
-          Collections.emptyList()));
+      classes.add(
+          makeClass(
+              options,
+              "Class" + i,
+              Constants.MAX_NON_JUMBO_INDEX - 1,
+              i % 100,
+              Collections.emptyList()));
     }
 
     // Create a shared class that references strings above the maximum.
-    DexProgramClass sharedSynthesizedClass = makeClass("SharedSynthesized", 100,
-        Constants.MAX_NON_JUMBO_INDEX - 1,
-        classes);
+    DexProgramClass sharedSynthesizedClass =
+        makeClass(options, "SharedSynthesized", 100, Constants.MAX_NON_JUMBO_INDEX - 1, classes);
 
-    InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
     DexApplication.Builder builder =
         DirectMappedDexApplication.builder(options, new Timing("SharedClassWritingTest"));
     builder.addSynthesizedClass(sharedSynthesizedClass, false);
@@ -144,7 +155,6 @@
             null,
             options,
             null,
-            null,
             GraphLense.getIdentityLense(),
             NamingLens.getIdentityLens(),
             null);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index fdc4296..6ebd77c 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -836,7 +836,7 @@
         method.setCode(ir, allocator, appView);
         directMethods[i] = method;
       }
-      builder.addProgramClass(
+      DexProgramClass programClass =
           new DexProgramClass(
               type,
               null,
@@ -854,7 +854,9 @@
               DexEncodedField.EMPTY_ARRAY,
               directMethods,
               DexEncodedMethod.EMPTY_ARRAY,
-              false));
+              false,
+              DexProgramClass::invalidChecksumRequest);
+      builder.addProgramClass(programClass);
     }
     DirectMappedDexApplication application = builder.build().toDirect();
     ApplicationWriter writer =
@@ -863,7 +865,6 @@
             null,
             options,
             null,
-            null,
             GraphLense.getIdentityLense(),
             NamingLens.getIdentityLens(),
             null);
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 3d1ec2b..0092be2 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -117,7 +117,6 @@
               null,
               options,
               null,
-              null,
               GraphLense.getIdentityLense(),
               NamingLens.getIdentityLens(),
               null);