Restrict classpath amendment to DirectMappedDexApplication

Fixes: 178675555
Change-Id: I751f980375fcabb75eb85e84480687571760085d
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 37b8d28..d5ea16e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -98,8 +98,6 @@
 
   abstract List<DexProgramClass> programClasses();
 
-  abstract List<DexClasspathClass> classpathClasses();
-
   public List<DexProgramClass> classes() {
     ReorderBox<DexProgramClass> box = new ReorderBox<>(programClasses());
     assert box.reorderClasses();
@@ -134,7 +132,6 @@
   public abstract static class Builder<T extends Builder<T>> {
 
     private final List<DexProgramClass> programClasses = new ArrayList<>();
-    private final List<DexClasspathClass> classpathClasses = new ArrayList<>();
 
     final List<DataResourceProvider> dataResourceProviders = new ArrayList<>();
 
@@ -157,7 +154,6 @@
 
     public Builder(DexApplication application) {
       programClasses.addAll(application.programClasses());
-      classpathClasses.addAll(application.classpathClasses());
       dataResourceProviders.addAll(application.dataResourceProviders);
       proguardMap = application.getProguardMap();
       timing = application.timing;
@@ -167,6 +163,14 @@
       synthesizedClasses = new ArrayList<>();
     }
 
+    public boolean isDirect() {
+      return false;
+    }
+
+    public DirectMappedDexApplication.Builder asDirect() {
+      return null;
+    }
+
     public synchronized T setProguardMap(ClassNameMapper proguardMap) {
       assert this.proguardMap == null;
       this.proguardMap = proguardMap;
@@ -180,14 +184,6 @@
       return self();
     }
 
-    public synchronized T replaceClasspathClasses(
-        Collection<DexClasspathClass> newClasspathClasses) {
-      assert newClasspathClasses != null;
-      classpathClasses.clear();
-      classpathClasses.addAll(newClasspathClasses);
-      return self();
-    }
-
     public synchronized T addDataResourceProvider(DataResourceProvider provider) {
       dataResourceProviders.add(provider);
       return self();
@@ -208,16 +204,6 @@
       return self();
     }
 
-    public synchronized T addClasspathClass(DexClasspathClass clazz) {
-      classpathClasses.add(clazz);
-      return self();
-    }
-
-    public synchronized T addClasspathClasses(Collection<DexClasspathClass> classes) {
-      classpathClasses.addAll(classes);
-      return self();
-    }
-
     public synchronized T addSynthesizedClass(DexProgramClass synthesizedClass) {
       assert synthesizedClass.isProgramClass() : "All synthesized classes must be program classes";
       addProgramClass(synthesizedClass);
@@ -229,10 +215,6 @@
       return programClasses;
     }
 
-    public List<DexClasspathClass> getClasspathClasses() {
-      return classpathClasses;
-    }
-
     public Collection<DexProgramClass> getSynthesizedClasses() {
       return synthesizedClasses;
     }
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 4f38fb2..86377b3 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.IdentityHashMap;
@@ -58,6 +59,10 @@
     return allClasses.values();
   }
 
+  public List<DexClasspathClass> classpathClasses() {
+    return classpathClasses;
+  }
+
   @Override
   List<DexProgramClass> programClasses() {
     return programClasses;
@@ -68,11 +73,6 @@
   }
 
   @Override
-  public List<DexClasspathClass> classpathClasses() {
-    return classpathClasses;
-  }
-
-  @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
     return allClasses.get(type);
@@ -181,12 +181,16 @@
 
   public static class Builder extends DexApplication.Builder<Builder> {
 
+    private ImmutableList<DexClasspathClass> classpathClasses;
     private ImmutableList<DexLibraryClass> libraryClasses;
 
+    private final List<DexClasspathClass> pendingClasspathClasses = new ArrayList<>();
+
     Builder(LazyLoadedDexApplication application) {
       super(application);
       // As a side-effect, this will force-load all classes.
       AllClasses allClasses = application.loadAllClasses();
+      classpathClasses = allClasses.getClasspathClasses();
       libraryClasses = allClasses.getLibraryClasses();
       replaceProgramClasses(allClasses.getProgramClasses());
       replaceClasspathClasses(allClasses.getClasspathClasses());
@@ -194,14 +198,58 @@
 
     private Builder(DirectMappedDexApplication application) {
       super(application);
+      classpathClasses = application.classpathClasses;
       libraryClasses = application.libraryClasses;
     }
 
     @Override
+    public boolean isDirect() {
+      return true;
+    }
+
+    @Override
+    public Builder asDirect() {
+      return this;
+    }
+
+    @Override
     Builder self() {
       return this;
     }
 
+    public Builder addClasspathClass(DexClasspathClass clazz) {
+      pendingClasspathClasses.add(clazz);
+      return self();
+    }
+
+    public Builder addClasspathClasses(Collection<DexClasspathClass> classes) {
+      pendingClasspathClasses.addAll(classes);
+      return self();
+    }
+
+    private void commitPendingClasspathClasses() {
+      if (!pendingClasspathClasses.isEmpty()) {
+        classpathClasses =
+            ImmutableList.<DexClasspathClass>builder()
+                .addAll(classpathClasses)
+                .addAll(pendingClasspathClasses)
+                .build();
+        pendingClasspathClasses.clear();
+      }
+    }
+
+    public List<DexClasspathClass> getClasspathClasses() {
+      commitPendingClasspathClasses();
+      return classpathClasses;
+    }
+
+    public Builder replaceClasspathClasses(Collection<DexClasspathClass> newClasspathClasses) {
+      assert newClasspathClasses != null;
+      classpathClasses = ImmutableList.copyOf(newClasspathClasses);
+      pendingClasspathClasses.clear();
+      return self();
+    }
+
     public Builder replaceLibraryClasses(Collection<DexLibraryClass> libraryClasses) {
       this.libraryClasses = ImmutableList.copyOf(libraryClasses);
       return self();
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 ba240f6..7005e06 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -54,12 +54,6 @@
   }
 
   @Override
-  List<DexClasspathClass> classpathClasses() {
-    classpathClasses.forceLoad(t -> true);
-    return classpathClasses.getAllClasses();
-  }
-
-  @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
     DexClass clazz = null;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index f9d1391..c198197 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -401,9 +401,9 @@
           if (definition.isProgramDefinition()) {
             committedProgramTypesBuilder.add(definition.getHolder().getType());
             appBuilder.addProgramClass(definition.asProgramDefinition().getHolder());
-          } else {
+          } else if (appBuilder.isDirect()) {
             assert definition.isClasspathDefinition();
-            appBuilder.addClasspathClass(definition.asClasspathDefinition().getHolder());
+            appBuilder.asDirect().addClasspathClass(definition.asClasspathDefinition().getHolder());
           }
           builder.addItem(definition);
         }