Reland "Load all library and program classes when running R8"

This reverts commit 687e8f3fe8bfd4e38ac3663c26059b164ccdb6bd.

After landing e0e0586bf37899856e1802585f59cdaa551af9b7 r8lib should
work again.

Bug: 120884788
Change-Id: Ic7dbc7a7066765d9a2ddcb19982b991c6c529e53
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 e4f1c8c..012c4a4 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -7,6 +7,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.graph.LazyLoadedDexApplication.AllClasses;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.ProgramClassCollection;
 import com.android.tools.r8.utils.Timing;
@@ -21,9 +22,11 @@
 
 public class DirectMappedDexApplication extends DexApplication {
 
+  private final AllClasses allClasses;
   private final ImmutableMap<DexType, DexLibraryClass> libraryClasses;
 
   private DirectMappedDexApplication(ClassNameMapper proguardMap,
+      AllClasses allClasses,
       ProgramClassCollection programClasses,
       ImmutableList<ProgramResourceProvider> programResourceProviders,
       ImmutableMap<DexType, DexLibraryClass> libraryClasses,
@@ -32,6 +35,7 @@
       Timing timing) {
     super(proguardMap, programClasses, programResourceProviders, mainDexList, deadCode,
         dexItemFactory, highestSortingString, timing);
+    this.allClasses = allClasses;
     this.libraryClasses = libraryClasses;
   }
 
@@ -92,17 +96,21 @@
 
   public static class Builder extends DexApplication.Builder<Builder> {
 
+    private final AllClasses allClasses;
     private final List<DexLibraryClass> libraryClasses = new ArrayList<>();
 
     Builder(LazyLoadedDexApplication application) {
       super(application);
       // As a side-effect, this will force-load all classes.
-      Map<DexType, DexClass> allClasses = application.getFullClassMap();
+      this.allClasses = application.loadAllClasses();
+      Map<DexType, DexClass> allClasses = this.allClasses.getClasses();
+      // TODO(120884788): This filter will only add library classes which are not program classes.
       Iterables.filter(allClasses.values(), DexLibraryClass.class).forEach(libraryClasses::add);
     }
 
     private Builder(DirectMappedDexApplication application) {
       super(application);
+      this.allClasses = application.allClasses;
       this.libraryClasses.addAll(application.libraryClasses.values());
     }
 
@@ -116,6 +124,7 @@
       // Rebuild the map. This will fail if keys are not unique.
       return new DirectMappedDexApplication(
           proguardMap,
+          allClasses,
           ProgramClassCollection.create(
               programClasses, ProgramClassCollection::resolveClassConflictImpl),
           ImmutableList.copyOf(programResourceProviders),
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 b648e41..6dea825 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -55,31 +55,67 @@
     return clazz;
   }
 
-  private Map<DexType, DexClass> forceLoadAllClasses() {
-    Map<DexType, DexClass> loaded = new IdentityHashMap<>();
+  static class AllClasses {
+    private Map<DexType, DexClass> libraryClasses;
+    private Map<DexType, DexClass> classpathClasses;
+    private Map<DexType, DexClass> programClasses;
+    private Map<DexType, DexClass> classes;
 
-    // Program classes are supposed to be loaded, but force-loading them is no-op.
-    programClasses.forceLoad(type -> true);
-    programClasses.getAllClasses().forEach(clazz -> loaded.put(clazz.type, clazz));
+    AllClasses(
+        LibraryClassCollection libraryClasses,
+        ClasspathClassCollection classpathClasses,
+        ProgramClassCollection programClasses) {
+      load(libraryClasses, classpathClasses, programClasses);
 
-    if (classpathClasses != null) {
-      classpathClasses.forceLoad(type -> !loaded.containsKey(type));
-      classpathClasses.getAllClasses().forEach(clazz -> loaded.putIfAbsent(clazz.type, clazz));
+      // Collect loaded classes in the precedence order program classes, class path classes and
+      // library classes.
+      // TODO(b/120884788): Change this.
+      classes = new IdentityHashMap<>();
+      classes.putAll(this.programClasses);
+      if (classpathClasses != null) {
+        classpathClasses.getAllClasses().forEach(clazz -> classes.putIfAbsent(clazz.type, clazz));
+      }
+      if (libraryClasses != null) {
+        libraryClasses.getAllClasses().forEach(clazz -> classes.putIfAbsent(clazz.type, clazz));
+      }
     }
 
-    if (libraryClasses != null) {
-      libraryClasses.forceLoad(type -> !loaded.containsKey(type));
-      libraryClasses.getAllClasses().forEach(clazz -> loaded.putIfAbsent(clazz.type, clazz));
+    public Map<DexType, DexClass> getLibraryClasses() {
+      return libraryClasses;
     }
 
-    return loaded;
+    public Map<DexType, DexClass> getClasspathClasses() {
+      return classpathClasses;
+    }
+
+    public Map<DexType, DexClass> getClasses() {
+      return classes;
+    }
+
+    private void load(
+        LibraryClassCollection libraryClasses,
+        ClasspathClassCollection classpathClasses,
+        ProgramClassCollection programClasses) {
+      if (libraryClasses != null) {
+        libraryClasses.forceLoad(type -> true);
+        this.libraryClasses = libraryClasses.getAllClassesInMap();
+      }
+      if (classpathClasses != null) {
+        classpathClasses.forceLoad(type -> true);
+        this.classpathClasses = classpathClasses.getAllClassesInMap();
+      }
+      assert programClasses != null;
+      // Program classes are supposed to be loaded, but force-loading them is no-op.
+      programClasses.forceLoad(type -> true);
+      this.programClasses = programClasses.getAllClassesInMap();
+    }
   }
 
   /**
    * Force load all classes and return type -> class map containing all the classes.
    */
-  public Map<DexType, DexClass> getFullClassMap() {
-    return forceLoadAllClasses();
+  public AllClasses loadAllClasses() {
+    return new AllClasses(libraryClasses, classpathClasses, programClasses);
   }
 
   public static class Builder extends DexApplication.Builder<Builder> {
diff --git a/src/main/java/com/android/tools/r8/utils/ClassMap.java b/src/main/java/com/android/tools/r8/utils/ClassMap.java
index 0ebfb9d..33c1bf3 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassMap.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassMap.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.ClassKind;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -134,6 +135,18 @@
     return loadedClasses;
   }
 
+  public Map<DexType, DexClass> getAllClassesInMap() {
+    if (classProvider.get() != null) {
+      throw new Unreachable("Getting all classes from not fully loaded collection.");
+    }
+    ImmutableMap.Builder<DexType, DexClass> builder = ImmutableMap.builder();
+    // This is fully loaded, so the class map will no longer change.
+    for (Map.Entry<DexType, Supplier<T>> entry : classes.entrySet()) {
+      builder.put(entry.getKey(), entry.getValue().get());
+    }
+    return builder.build();
+  }
+
   public Iterable<DexType> getAllTypes() {
     return classes.keySet();
   }