Merge "Clean up PG config parser."
diff --git a/build.gradle b/build.gradle
index e50073d..6dadb5c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,7 +18,8 @@
     '-Xep:ClassCanBeStatic:WARN',
     '-Xep:OperatorPrecedence:WARN',
     '-Xep:RemoveUnusedImports:WARN',
-    '-Xep:MissingOverride:WARN']
+    '-Xep:MissingOverride:WARN',
+    '-Xep:OvershadowingSubclassFields:WARN']
 
 apply from: 'copyAdditionalJctfCommonFiles.gradle'
 
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index a25d21c..57fc8f0 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -68,15 +68,6 @@
       this(AndroidApp.builder(), false);
     }
 
-    protected Builder(boolean ignoreDexInArchive) {
-      this(AndroidApp.builder(), ignoreDexInArchive);
-    }
-
-    // Internal constructor for testing.
-    Builder(AndroidApp app, CompilationMode mode) {
-      this(AndroidApp.builder(app), false);
-    }
-
     protected Builder(AndroidApp.Builder builder, boolean ignoreDexInArchive) {
       this.app = builder;
       app.setIgnoreDexInArchive(ignoreDexInArchive);
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 876f685..a5213c5 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -180,15 +180,16 @@
         return null;
       }
       Marker marker = getMarker(options);
-      new ApplicationWriter(app, appInfo, options, marker, null, NamingLens.getIdentityLens(), null)
+      new ApplicationWriter(app, options, marker, null, NamingLens.getIdentityLens(), null)
           .write(outputSink, executor);
       CompilationResult output = new CompilationResult(outputSink, app, appInfo);
       options.printWarnings();
-      outputSink.close();
       return output;
     } catch (ExecutionException e) {
       R8.unwrapExecutionException(e);
       throw new AssertionError(e); // unwrapping method should have thrown
+    } finally {
+      outputSink.close();
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9977151..ae6326e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.DexOverflowException;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.ClassAndMemberPublicizer;
 import com.android.tools.r8.graph.DexApplication;
@@ -86,8 +85,8 @@
   public static void writeApplication(
       ExecutorService executorService,
       DexApplication application,
-      AppInfo appInfo,
-      OutputSink outputSink, byte[] deadCode,
+      OutputSink outputSink,
+      byte[] deadCode,
       NamingLens namingLens,
       byte[] proguardSeedsData,
       InternalOptions options)
@@ -95,7 +94,7 @@
     try {
       Marker marker = getMarker(options);
       new ApplicationWriter(
-          application, appInfo, options, marker, deadCode, namingLens, proguardSeedsData)
+          application, options, marker, deadCode, namingLens, proguardSeedsData)
           .write(outputSink, executorService);
     } catch (IOException e) {
       throw new RuntimeException("Cannot write dex application", e);
@@ -342,7 +341,6 @@
       writeApplication(
           executorService,
           application,
-          appInfo,
           outputSink,
           application.deadCode,
           namingLens,
@@ -350,11 +348,11 @@
           options);
 
       options.printWarnings();
-      outputSink.close();
     } catch (ExecutionException e) {
       unwrapExecutionException(e);
       throw new AssertionError(e); // unwrapping method should have thrown
     } finally {
+      outputSink.close();
       // Dump timings.
       if (options.printTimes) {
         timing.report();
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 bd3a069..f99dc92 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.DexOverflowException;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.utils.AndroidApp;
@@ -178,9 +177,8 @@
   private static void writeApp(DexApplication app, Path output, ExecutorService executor)
       throws IOException, ExecutionException, DexOverflowException {
     InternalOptions options = new InternalOptions();
-    AppInfo appInfo = new AppInfo(app);
     AndroidAppOutputSink compatSink = new AndroidAppOutputSink();
-    ApplicationWriter writer = new ApplicationWriter(app, appInfo, options, null, null, null, null);
+    ApplicationWriter writer = new ApplicationWriter(app, options, null, null, null, null);
     writer.write(compatSink, executor);
     compatSink.build().writeToDirectory(output, OutputMode.Indexed);
   }
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectState.java b/src/main/java/com/android/tools/r8/bisect/BisectState.java
index a9836aa..b9359a3 100644
--- a/src/main/java/com/android/tools/r8/bisect/BisectState.java
+++ b/src/main/java/com/android/tools/r8/bisect/BisectState.java
@@ -132,10 +132,6 @@
       range.write(writer);
     }
 
-    public boolean isGood() {
-      return good;
-    }
-
     public boolean isBad() {
       return !good;
     }
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index d95de6f..93b1b24 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -31,7 +31,6 @@
   private String output = null;
   private int numberOfThreads = 8;
   private boolean noLocals = false;
-  private boolean verbose = false;
 
   public static void main(String[] args)
       throws IOException, InterruptedException, ExecutionException {
@@ -73,9 +72,6 @@
         case "--help":
           // Ignore
           break;
-        case "--verbose":
-          verbose = true;
-          break;
         case "--nolocals":
           noLocals = true;
           break;
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index 7da21b9..2789252 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -120,20 +120,6 @@
       }
     }
 
-    // Exception thrown on options parse error.
-    private static class DxParseError extends DxUsageMessage {
-      private final OptionParser parser;
-
-      private DxParseError(OptionParser parser) {
-        this.parser = parser;
-      }
-
-      @Override
-      public void printHelpOn(PrintStream sink) throws IOException {
-        parser.printHelpOn(sink);
-      }
-    }
-
     // Parsing specification.
     private static class Spec {
       final OptionParser parser;
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 a720596..b18f907 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.OutputSink;
 import com.android.tools.r8.errors.DexOverflowException;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -33,6 +32,7 @@
 import java.io.PrintWriter;
 import java.io.Writer;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -43,7 +43,6 @@
 public class ApplicationWriter {
 
   public final DexApplication application;
-  public final AppInfo appInfo;
   public final byte[] deadCode;
   public final NamingLens namingLens;
   public final byte[] proguardSeedsData;
@@ -109,7 +108,6 @@
 
   public ApplicationWriter(
       DexApplication application,
-      AppInfo appInfo,
       InternalOptions options,
       Marker marker,
       byte[] deadCode,
@@ -117,7 +115,6 @@
       byte[] proguardSeedsData) {
     assert application != null;
     this.application = application;
-    this.appInfo = appInfo;
     assert options != null;
     this.options = options;
     this.markerString = (marker == null)
@@ -128,6 +125,23 @@
     this.proguardSeedsData = proguardSeedsData;
   }
 
+  private Iterable<VirtualFile> distribute()
+      throws ExecutionException, IOException, DexOverflowException {
+    // Distribute classes into dex files.
+    VirtualFile.Distributor distributor;
+    if (options.outputMode == OutputMode.FilePerInputClass) {
+      distributor = new VirtualFile.FilePerInputClassDistributor(this);
+    } else if (!options.canUseMultidex()
+        && options.mainDexKeepRules.isEmpty()
+        && application.mainDexList.isEmpty()) {
+      distributor = new VirtualFile.MonoDexDistributor(this, options);
+    } else {
+      distributor = new VirtualFile.FillFilesDistributor(this, options);
+    }
+
+    return distributor.run();
+  }
+
   public void write(OutputSink outputSink, ExecutorService executorService)
       throws IOException, ExecutionException, DexOverflowException {
     application.timing.begin("DexApplication.write");
@@ -138,27 +152,12 @@
       SortAnnotations sortAnnotations = new SortAnnotations();
       application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
 
-      // Distribute classes into dex files.
-      VirtualFile.Distributor distributor;
-      if (options.outputMode == OutputMode.FilePerInputClass) {
-        distributor = new VirtualFile.FilePerInputClassDistributor(this);
-      } else if (!options.canUseMultidex()
-          && options.mainDexKeepRules.isEmpty()
-          && application.mainDexList.isEmpty()) {
-        distributor = new VirtualFile.MonoDexDistributor(this, options);
-      } else {
-        distributor = new VirtualFile.FillFilesDistributor(this, options);
-      }
-      Map<Integer, VirtualFile> newFiles = distributor.run();
-
       // Collect the indexed items sets for all files and perform JumboString processing.
       // This is required to ensure that shared code blocks have a single and consistent code
       // item that is valid for all dex files.
       // Use a linked hash map as the order matters when addDexProgramData is called below.
       Map<VirtualFile, Future<ObjectToOffsetMapping>> offsetMappingFutures = new LinkedHashMap<>();
-      for (int i = 0; i < newFiles.size(); i++) {
-        VirtualFile newFile = newFiles.get(i);
-        assert newFile.getId() == i;
+      for (VirtualFile newFile : distribute()) {
         assert !newFile.isEmpty();
         if (!newFile.isEmpty()) {
           offsetMappingFutures
@@ -197,6 +196,8 @@
         throw new RuntimeException("Interrupted while waiting for future.", e);
       }
 
+      // Clear out the map, as it is no longer needed.
+      offsetMappingFutures.clear();
       // Wait for all files to be processed before moving on.
       ThreadUtils.awaitFutures(dexDataFutures);
 
@@ -236,7 +237,7 @@
    * be used.
    */
   private static void rewriteCodeWithJumboStrings(ObjectToOffsetMapping mapping,
-      List<DexProgramClass> classes, DexApplication application) {
+      Collection<DexProgramClass> classes, DexApplication application) {
     // If there are no strings with jumbo indices at all this is a no-op.
     if (!mapping.hasJumboStrings()) {
       return;
@@ -254,7 +255,7 @@
 
   private byte[] writeDexFile(ObjectToOffsetMapping mapping)
       throws ApiLevelException {
-    FileWriter fileWriter = new FileWriter(mapping, application, appInfo, options, namingLens);
+    FileWriter fileWriter = new FileWriter(mapping, application, options, namingLens);
     // Collect the non-fixed sections.
     fileWriter.collect();
     // Generate and write the bytes.
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index 3a06f1d..50af413 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -135,7 +135,6 @@
   public static final String CLASS_INITIALIZER_NAME = "<clinit>";
 
   public static final int MAX_NON_JUMBO_INDEX = U16BIT_MAX;
-  public static final int FIRST_JUMBO_INDEX = MAX_NON_JUMBO_INDEX + 1;
 
   public static final int KILOBYTE = 1 << 10;
 }
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 70fd655..da3722f 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.Descriptor;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
@@ -38,6 +37,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.KeyedDexItem;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.graph.PresortedComparable;
@@ -77,7 +77,6 @@
 
   private final ObjectToOffsetMapping mapping;
   private final DexApplication application;
-  private final AppInfo appInfo;
   private final InternalOptions options;
   private final NamingLens namingLens;
   private final DexOutputBuffer dest = new DexOutputBuffer();
@@ -86,12 +85,10 @@
   public FileWriter(
       ObjectToOffsetMapping mapping,
       DexApplication application,
-      AppInfo appinfo,
       InternalOptions options,
       NamingLens namingLens) {
     this.mapping = mapping;
     this.application = application;
-    this.appInfo = appinfo;
     this.options = options;
     this.namingLens = namingLens;
     this.mixedSectionOffsets = new MixedSectionOffsets();
@@ -316,7 +313,7 @@
     }
   }
 
-  private <T extends DexItem> void writeFixedSectionItems(T[] items, int offset,
+  private <T extends IndexedDexItem> void writeFixedSectionItems(Collection<T> items, int offset,
       ThrowingConsumer<T, ApiLevelException> writer) throws ApiLevelException {
     assert dest.position() == offset;
     for (T item : items) {
@@ -324,6 +321,14 @@
     }
   }
 
+  private void writeFixedSectionItems(DexProgramClass[] items, int offset,
+      ThrowingConsumer<DexProgramClass, ApiLevelException> writer) throws ApiLevelException {
+    assert dest.position() == offset;
+    for (DexProgramClass item : items) {
+      writer.accept(item);
+    }
+  }
+
   private <T extends DexItem> void writeItems(Collection<T> items, Consumer<Integer> offsetSetter,
       Consumer<T> writer) {
     writeItems(items, offsetSetter, writer, 1);
@@ -691,21 +696,21 @@
     int size = 0;
     size += writeMapItem(Constants.TYPE_HEADER_ITEM, 0, 1);
     size += writeMapItem(Constants.TYPE_STRING_ID_ITEM, layout.stringIdsOffset,
-        mapping.getStrings().length);
+        mapping.getStrings().size());
     size += writeMapItem(Constants.TYPE_TYPE_ID_ITEM, layout.typeIdsOffset,
-        mapping.getTypes().length);
+        mapping.getTypes().size());
     size += writeMapItem(Constants.TYPE_PROTO_ID_ITEM, layout.protoIdsOffset,
-        mapping.getProtos().length);
+        mapping.getProtos().size());
     size += writeMapItem(Constants.TYPE_FIELD_ID_ITEM, layout.fieldIdsOffset,
-        mapping.getFields().length);
+        mapping.getFields().size());
     size += writeMapItem(Constants.TYPE_METHOD_ID_ITEM, layout.methodIdsOffset,
-        mapping.getMethods().length);
+        mapping.getMethods().size());
     size += writeMapItem(Constants.TYPE_CLASS_DEF_ITEM, layout.classDefsOffset,
         mapping.getClasses().length);
     size += writeMapItem(Constants.TYPE_CALL_SITE_ID_ITEM, layout.callSiteIdsOffset,
-        mapping.getCallSites().length);
+        mapping.getCallSites().size());
     size += writeMapItem(Constants.TYPE_METHOD_HANDLE_ITEM, layout.methodHandleIdsOffset,
-        mapping.getMethodHandles().length);
+        mapping.getMethodHandles().size());
     size += writeMapItem(Constants.TYPE_CODE_ITEM, layout.getCodesOffset(),
         mixedSectionOffsets.getCodes().size());
     size += writeMapItem(Constants.TYPE_DEBUG_INFO_ITEM, layout.getDebugInfosOffset(),
@@ -750,19 +755,19 @@
     dest.putInt(0);
     dest.putInt(0);
     dest.putInt(layout.getMapOffset());
-    int numberOfStrings = mapping.getStrings().length;
+    int numberOfStrings = mapping.getStrings().size();
     dest.putInt(numberOfStrings);
     dest.putInt(numberOfStrings == 0 ? 0 : layout.stringIdsOffset);
-    int numberOfTypes = mapping.getTypes().length;
+    int numberOfTypes = mapping.getTypes().size();
     dest.putInt(numberOfTypes);
     dest.putInt(numberOfTypes == 0 ? 0 : layout.typeIdsOffset);
-    int numberOfProtos = mapping.getProtos().length;
+    int numberOfProtos = mapping.getProtos().size();
     dest.putInt(numberOfProtos);
     dest.putInt(numberOfProtos == 0 ? 0 : layout.protoIdsOffset);
-    int numberOfFields = mapping.getFields().length;
+    int numberOfFields = mapping.getFields().size();
     dest.putInt(numberOfFields);
     dest.putInt(numberOfFields == 0 ? 0 : layout.fieldIdsOffset);
-    int numberOfMethods = mapping.getMethods().length;
+    int numberOfMethods = mapping.getMethods().size();
     dest.putInt(numberOfMethods);
     dest.putInt(numberOfMethods == 0 ? 0 : layout.methodIdsOffset);
     int numberOfClasses = mapping.getClasses().length;
@@ -853,14 +858,14 @@
       int offset = 0;
       return new Layout(
           offset = Constants.TYPE_HEADER_ITEM_SIZE,
-          offset += mapping.getStrings().length * Constants.TYPE_STRING_ID_ITEM_SIZE,
-          offset += mapping.getTypes().length * Constants.TYPE_TYPE_ID_ITEM_SIZE,
-          offset += mapping.getProtos().length * Constants.TYPE_PROTO_ID_ITEM_SIZE,
-          offset += mapping.getFields().length * Constants.TYPE_FIELD_ID_ITEM_SIZE,
-          offset += mapping.getMethods().length * Constants.TYPE_METHOD_ID_ITEM_SIZE,
+          offset += mapping.getStrings().size() * Constants.TYPE_STRING_ID_ITEM_SIZE,
+          offset += mapping.getTypes().size() * Constants.TYPE_TYPE_ID_ITEM_SIZE,
+          offset += mapping.getProtos().size() * Constants.TYPE_PROTO_ID_ITEM_SIZE,
+          offset += mapping.getFields().size() * Constants.TYPE_FIELD_ID_ITEM_SIZE,
+          offset += mapping.getMethods().size() * Constants.TYPE_METHOD_ID_ITEM_SIZE,
           offset += mapping.getClasses().length * Constants.TYPE_CLASS_DEF_ITEM_SIZE,
-          offset += mapping.getCallSites().length * Constants.TYPE_CALL_SITE_ID_ITEM_SIZE,
-          offset += mapping.getMethodHandles().length * Constants.TYPE_METHOD_HANDLE_ITEM_SIZE);
+          offset += mapping.getCallSites().size() * Constants.TYPE_CALL_SITE_ID_ITEM_SIZE,
+          offset += mapping.getMethodHandles().size() * Constants.TYPE_METHOD_HANDLE_ITEM_SIZE);
     }
 
     int getDataSectionSize() {
@@ -1224,9 +1229,13 @@
       return lookup(code, codes);
     }
 
-    private <T> void setOffsetFor(T item, int offset, Map<T, Integer> table) {
-      Integer old = table.put(item, offset);
-      assert old != null;
+    private <T> void setOffsetFor(T item, int offset, Object2IntMap<T> map) {
+      int old = map.put(item, offset);
+      assert old <= NOT_SET;
+    }
+
+    private <T> void setOffsetFor(T item, int offset, Reference2IntMap<T> map) {
+      int old = map.put(item, offset);
       assert old <= NOT_SET;
     }
 
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 34ec68c..5408676 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.NamingLens;
@@ -26,12 +25,10 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Iterators;
-import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -76,7 +73,7 @@
 
   private VirtualFile(int id, NamingLens namingLens, DexProgramClass primaryClass) {
     this.id = id;
-    this.indexedItems = new VirtualFileIndexedItemCollection(id);
+    this.indexedItems = new VirtualFileIndexedItemCollection(namingLens);
     this.transaction = new IndexedItemTransaction(indexedItems, namingLens);
     this.primaryClass = primaryClass;
   }
@@ -152,16 +149,15 @@
   public ObjectToOffsetMapping computeMapping(DexApplication application) {
     assert transaction.isEmpty();
     return new ObjectToOffsetMapping(
-        id,
         application,
-        indexedItems.classes.toArray(new DexProgramClass[indexedItems.classes.size()]),
-        indexedItems.protos.toArray(new DexProto[indexedItems.protos.size()]),
-        indexedItems.types.toArray(new DexType[indexedItems.types.size()]),
-        indexedItems.methods.toArray(new DexMethod[indexedItems.methods.size()]),
-        indexedItems.fields.toArray(new DexField[indexedItems.fields.size()]),
-        indexedItems.strings.toArray(new DexString[indexedItems.strings.size()]),
-        indexedItems.callSites.toArray(new DexCallSite[indexedItems.callSites.size()]),
-        indexedItems.methodHandles.toArray(new DexMethodHandle[indexedItems.methodHandles.size()]));
+        indexedItems.classes,
+        indexedItems.protos,
+        indexedItems.types,
+        indexedItems.methods,
+        indexedItems.fields,
+        indexedItems.strings,
+        indexedItems.callSites,
+        indexedItems.methodHandles);
   }
 
   private void addClass(DexProgramClass clazz) {
@@ -206,21 +202,21 @@
     return indexedItems.classes.isEmpty();
   }
 
-  public List<DexProgramClass> classes() {
+  public Collection<DexProgramClass> classes() {
     return indexedItems.classes;
   }
 
   public abstract static class Distributor {
     protected final DexApplication application;
     protected final ApplicationWriter writer;
-    protected final Map<Integer, VirtualFile> nameToFileMap = new HashMap<>();
+    protected final List<VirtualFile> virtualFiles = new ArrayList<>();
 
     Distributor(ApplicationWriter writer) {
       this.application = writer.application;
       this.writer = writer;
     }
 
-    public abstract Map<Integer, VirtualFile> run()
+    public abstract Collection<VirtualFile> run()
         throws ExecutionException, IOException, DexOverflowException;
   }
 
@@ -237,14 +233,14 @@
     }
 
     @Override
-    public Map<Integer, VirtualFile> run() {
+    public Collection<VirtualFile> run() {
       HashMap<DexProgramClass, VirtualFile> files = new HashMap<>();
       Collection<DexProgramClass> synthetics = new ArrayList<>();
       // Assign dedicated virtual files for all program classes.
       for (DexProgramClass clazz : application.classes()) {
         if (clazz.getSynthesizedFrom().isEmpty()) {
-          VirtualFile file = new VirtualFile(nameToFileMap.size(), writer.namingLens, clazz);
-          nameToFileMap.put(nameToFileMap.size(), file);
+          VirtualFile file = new VirtualFile(virtualFiles.size(), writer.namingLens, clazz);
+          virtualFiles.add(file);
           file.addClass(clazz);
           files.put(clazz, file);
           // Commit this early, so that we do not keep the transaction state around longer than
@@ -261,7 +257,7 @@
           file.commitTransaction();
         }
       }
-      return nameToFileMap;
+      return virtualFiles;
     }
   }
 
@@ -277,7 +273,8 @@
 
       // Create the primary dex file. The distribution will add more if needed.
       mainDexFile = new VirtualFile(0, writer.namingLens);
-      nameToFileMap.put(0, mainDexFile);
+      assert virtualFiles.isEmpty();
+      virtualFiles.add(mainDexFile);
       if (writer.markerString != null) {
         mainDexFile.transaction.addString(writer.markerString);
         mainDexFile.commitTransaction();
@@ -289,7 +286,7 @@
 
     protected void fillForMainDexList(Set<DexProgramClass> classes) throws DexOverflowException {
       if (!application.mainDexList.isEmpty()) {
-        VirtualFile mainDexFile = nameToFileMap.get(0);
+        VirtualFile mainDexFile = virtualFiles.get(0);
         for (DexType type : application.mainDexList) {
           DexClass clazz = application.definitionFor(type);
           if (clazz != null && clazz.isProgramClass()) {
@@ -351,21 +348,23 @@
     }
 
     @Override
-    public Map<Integer, VirtualFile> run() throws IOException, DexOverflowException {
+    public Collection<VirtualFile> run() throws IOException, DexOverflowException {
       // First fill required classes into the main dex file.
       fillForMainDexList(classes);
       if (classes.isEmpty()) {
         // All classes ended up in the main dex file, no more to do.
-        return nameToFileMap;
+        return virtualFiles;
       }
 
-      Map<Integer, VirtualFile> filesForDistribution = nameToFileMap;
+      List<VirtualFile> filesForDistribution = virtualFiles;
+      int fileIndexOffset = 0;
       if (options.minimalMainDex && !mainDexFile.isEmpty()) {
-        assert !nameToFileMap.get(0).isEmpty();
-        // The main dex file is filtered out, so create ensure at least one file for the remaining
-        // classes
-        nameToFileMap.put(1, new VirtualFile(1, writer.namingLens));
-        filesForDistribution = Maps.filterKeys(filesForDistribution, key -> key != 0);
+        assert !virtualFiles.get(0).isEmpty();
+        assert virtualFiles.size() == 1;
+        // The main dex file is filtered out, so ensure at least one file for the remaining classes.
+        virtualFiles.add(new VirtualFile(1, writer.namingLens));
+        filesForDistribution = virtualFiles.subList(1, virtualFiles.size());
+        fileIndexOffset = 1;
       }
 
       // Sort the remaining classes based on the original names.
@@ -374,9 +373,9 @@
 
       new PackageSplitPopulator(
           filesForDistribution, classes, originalNames, null, application.dexItemFactory,
-          fillStrategy, writer.namingLens)
+          fillStrategy, fileIndexOffset, writer.namingLens)
           .call();
-      return nameToFileMap;
+      return virtualFiles;
     }
   }
 
@@ -386,7 +385,7 @@
     }
 
     @Override
-    public Map<Integer, VirtualFile> run()
+    public Collection<VirtualFile> run()
         throws ExecutionException, IOException, DexOverflowException {
       // Add all classes to the main dex file.
       for (DexProgramClass programClass : classes) {
@@ -394,93 +393,96 @@
       }
       mainDexFile.commitTransaction();
       mainDexFile.throwIfFull(false);
-      return nameToFileMap;
+      return virtualFiles;
     }
   }
 
   private static class VirtualFileIndexedItemCollection implements IndexedItemCollection {
 
-    final int id;
+    private final NamingLens namingLens;
 
-    private final List<DexProgramClass> classes = new ArrayList<>();
-    private final List<DexProto> protos = new ArrayList<>();
-    private final List<DexType> types = new ArrayList<>();
-    private final List<DexMethod> methods = new ArrayList<>();
-    private final List<DexField> fields = new ArrayList<>();
-    private final List<DexString> strings = new ArrayList<>();
-    private final List<DexCallSite> callSites = new ArrayList<>();
-    private final List<DexMethodHandle> methodHandles = new ArrayList<>();
+    private final Set<DexProgramClass> classes = Sets.newIdentityHashSet();
+    private final Set<DexProto> protos = Sets.newIdentityHashSet();
+    private final Set<DexType> types = Sets.newIdentityHashSet();
+    private final Set<DexMethod> methods = Sets.newIdentityHashSet();
+    private final Set<DexField> fields = Sets.newIdentityHashSet();
+    private final Set<DexString> strings = Sets.newIdentityHashSet();
+    private final Set<DexCallSite> callSites = Sets.newIdentityHashSet();
+    private final Set<DexMethodHandle> methodHandles = Sets.newIdentityHashSet();
 
-    private final Set<DexClass> seenClasses = Sets.newIdentityHashSet();
+    public VirtualFileIndexedItemCollection(
+        NamingLens namingLens) {
+      this.namingLens = namingLens;
 
-    private VirtualFileIndexedItemCollection(int id) {
-      this.id = id;
-    }
-
-    private <T extends IndexedDexItem> boolean addItem(T item, List<T> itemList) {
-      assert item != null;
-      if (item.assignToVirtualFile(id)) {
-        itemList.add(item);
-        return true;
-      }
-      return false;
     }
 
     @Override
     public boolean addClass(DexProgramClass clazz) {
-      if (seenClasses.add(clazz)) {
-        classes.add(clazz);
-        return true;
-      }
-      return false;
+      return classes.add(clazz);
     }
 
     @Override
     public boolean addField(DexField field) {
-      return addItem(field, fields);
+      return fields.add(field);
     }
 
     @Override
     public boolean addMethod(DexMethod method) {
-      return addItem(method, methods);
+      return methods.add(method);
     }
 
     @Override
     public boolean addString(DexString string) {
-      return addItem(string, strings);
+      return strings.add(string);
     }
 
     @Override
     public boolean addProto(DexProto proto) {
-      return addItem(proto, protos);
-    }
-
-    @Override
-    public boolean addCallSite(DexCallSite callSite) {
-      return addItem(callSite, callSites);
-    }
-
-    @Override
-    public boolean addMethodHandle(DexMethodHandle methodHandle) {
-      return addItem(methodHandle, methodHandles);
+      return protos.add(proto);
     }
 
     @Override
     public boolean addType(DexType type) {
-      return addItem(type, types);
+      return types.add(type);
     }
 
-    public int getNumberOfMethods() {
+    @Override
+    public boolean addCallSite(DexCallSite callSite) {
+      return callSites.add(callSite);
+    }
+
+    @Override
+    public boolean addMethodHandle(DexMethodHandle methodHandle) {
+      return methodHandles.add(methodHandle);
+    }
+
+    int getNumberOfMethods() {
       return methods.size();
     }
 
-    public int getNumberOfFields() {
+    int getNumberOfFields() {
       return fields.size();
     }
 
-    public int getNumberOfStrings() {
+    int getNumberOfStrings() {
       return strings.size();
     }
+
+    @Override
+    public DexString getRenamedDescriptor(DexType type) {
+      return namingLens.lookupDescriptor(type);
+    }
+
+    @Override
+    public DexString getRenamedName(DexMethod method) {
+      assert namingLens.checkTargetCanBeTranslated(method);
+      return namingLens.lookupName(method);
+    }
+
+    @Override
+    public DexString getRenamedName(DexField field) {
+      return namingLens.lookupName(field);
+    }
   }
 
   private static class IndexedItemTransaction implements IndexedItemCollection {
@@ -503,8 +505,8 @@
       this.namingLens = namingLens;
     }
 
-    private <T extends IndexedDexItem> boolean maybeInsert(T item, Set<T> set) {
-      if (item.hasVirtualFileData(base.id) || set.contains(item)) {
+    private <T extends DexItem> boolean maybeInsert(T item, Set<T> set, Set<T> baseSet) {
+      if (baseSet.contains(item) || set.contains(item)) {
         return false;
       }
       set.add(item);
@@ -517,46 +519,42 @@
 
     @Override
     public boolean addClass(DexProgramClass dexProgramClass) {
-      if (base.seenClasses.contains(dexProgramClass) || classes.contains(dexProgramClass)) {
-        return false;
-      }
-      classes.add(dexProgramClass);
-      return true;
+      return maybeInsert(dexProgramClass, classes, base.classes);
     }
 
     @Override
     public boolean addField(DexField field) {
-      return maybeInsert(field, fields);
+      return maybeInsert(field, fields, base.fields);
     }
 
     @Override
     public boolean addMethod(DexMethod method) {
-      return maybeInsert(method, methods);
+      return maybeInsert(method, methods, base.methods);
     }
 
     @Override
     public boolean addString(DexString string) {
-      return maybeInsert(string, strings);
+      return maybeInsert(string, strings, base.strings);
     }
 
     @Override
     public boolean addProto(DexProto proto) {
-      return maybeInsert(proto, protos);
+      return maybeInsert(proto, protos, base.protos);
     }
 
     @Override
     public boolean addType(DexType type) {
-      return maybeInsert(type, types);
+      return maybeInsert(type, types, base.types);
     }
 
     @Override
     public boolean addCallSite(DexCallSite callSite) {
-      return maybeInsert(callSite, callSites);
+      return maybeInsert(callSite, callSites, base.callSites);
     }
 
     @Override
     public boolean addMethodHandle(DexMethodHandle methodHandle) {
-      return maybeInsert(methodHandle, methodHandles);
+      return maybeInsert(methodHandle, methodHandles, base.methodHandles);
     }
 
     @Override
@@ -616,10 +614,6 @@
           && types.isEmpty() && strings.isEmpty();
     }
 
-    int getNumberOfStrings() {
-      return strings.size() + base.getNumberOfStrings();
-    }
-
     int getNumberOfClasses() {
       return classes.size() + base.classes.size();
     }
@@ -637,27 +631,25 @@
    * will not be part of the iteration.
    */
   private static class VirtualFileCycler {
-    private Map<Integer, VirtualFile> files;
+
+    private List<VirtualFile> files;
     private final NamingLens namingLens;
-    private final FillStrategy fillStrategy;
 
     private int nextFileId;
     private Iterator<VirtualFile> allFilesCyclic;
     private Iterator<VirtualFile> activeFiles;
 
-    VirtualFileCycler(Map<Integer, VirtualFile> files, NamingLens namingLens,
-        FillStrategy fillStrategy) {
+    VirtualFileCycler(List<VirtualFile> files, NamingLens namingLens, int fileIndexOffset) {
       this.files = files;
       this.namingLens = namingLens;
-      this.fillStrategy = fillStrategy;
 
-      nextFileId = Collections.max(files.keySet()) + 1;
+      nextFileId = files.size() + fileIndexOffset;
 
       reset();
     }
 
     private void reset() {
-      allFilesCyclic = Iterators.cycle(files.values());
+      allFilesCyclic = Iterators.cycle(files);
       restart();
     }
 
@@ -666,8 +658,7 @@
     }
 
     VirtualFile next() {
-      VirtualFile next = activeFiles.next();
-      return next;
+      return activeFiles.next();
     }
 
     // Start a new iteration over all files, starting at the current one.
@@ -676,9 +667,8 @@
     }
 
     VirtualFile addFile() {
-      VirtualFile newFile = new VirtualFile(nextFileId, namingLens);
-      files.put(nextFileId, newFile);
-      nextFileId++;
+      VirtualFile newFile = new VirtualFile(nextFileId++, namingLens);
+      files.add(newFile);
 
       reset();
       return newFile;
@@ -717,19 +707,20 @@
     private final VirtualFileCycler cycler;
 
     PackageSplitPopulator(
-        Map<Integer, VirtualFile> files,
+        List<VirtualFile> files,
         Set<DexProgramClass> classes,
         Map<DexProgramClass, String> originalNames,
         Set<String> previousPrefixes,
         DexItemFactory dexItemFactory,
         FillStrategy fillStrategy,
+        int fileIndexOffset,
         NamingLens namingLens) {
       this.classes = new ArrayList<>(classes);
       this.originalNames = originalNames;
       this.previousPrefixes = previousPrefixes;
       this.dexItemFactory = dexItemFactory;
       this.fillStrategy = fillStrategy;
-      this.cycler = new VirtualFileCycler(files, namingLens, fillStrategy);
+      this.cycler = new VirtualFileCycler(files, namingLens, fileIndexOffset);
     }
 
     static boolean coveredByPrefix(String originalName, String currentPrefix) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 3552cf7..99325f9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -275,9 +275,8 @@
   public void setCode(
       IRCode ir,
       RegisterAllocator registerAllocator,
-      DexItemFactory dexItemFactory,
       InternalOptions options) {
-    final DexBuilder builder = new DexBuilder(ir, registerAllocator, dexItemFactory, options);
+    final DexBuilder builder = new DexBuilder(ir, registerAllocator, options);
     code = builder.build(method.getArity());
   }
 
@@ -690,18 +689,6 @@
       this.method = method;
     }
 
-    public void setAccessFlags(DexAccessFlags accessFlags) {
-      this.accessFlags = accessFlags;
-    }
-
-    public void setAnnotations(DexAnnotationSet annotations) {
-      this.annotations = annotations;
-    }
-
-    public void setParameterAnnotations(DexAnnotationSetRefList parameterAnnotations) {
-      this.parameterAnnotations = parameterAnnotations;
-    }
-
     public void setCode(Code code) {
       this.code = code;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItem.java b/src/main/java/com/android/tools/r8/graph/DexItem.java
index c0df752..88665e9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItem.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Collection;
 import java.util.function.Consumer;
 
 public abstract class DexItem {
@@ -17,6 +18,11 @@
     consumeArray(items, (T item) -> item.collectMixedSectionItems(mixedItems));
   }
 
+  public static <T extends DexItem> void collectAll(MixedSectionCollection mixedItems,
+      Collection<T> items) {
+    items.forEach((T item) -> item.collectMixedSectionItems(mixedItems));
+  }
+
   /**
    * Helper method to iterate over elements in an array.
    * Handles the case where the array is null.
diff --git a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
index 7cc5738..e45a7c7 100644
--- a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
-import java.util.Arrays;
 
 /**
  * Subset of dex items that are referenced by some table index.
@@ -14,27 +13,6 @@
 
   private static final int SORTED_INDEX_UNKNOWN = -1;
   private int sortedIndex = SORTED_INDEX_UNKNOWN; // assigned globally after reading.
-  /**
-   * Contains the indexes assigned to this item for the various virtual output files.
-   *
-   * <p>One DexItem might be assigned to multiple virtual files.
-   *
-   * <p>For a certain virtual file this DexItem has the value:
-   * <ul>
-   * <li>{@link #UNASSOCIATED_VALUE}, when not associated with the virtual file.
-   * <li>{@link #ASSOCIATED_VALUE}, when associated with the virtual file but no index allocated.
-   * <li>A zero or greater value when this item has been associated by the virtual file
-   * and the value denotes the assigned index.
-   * </ul>
-   * <p> Note that, in case of multiple files, for a specific IndexedDexItem, we may not have
-   * as many entries in the index as there are files (we only expand when we need to). If we lookup
-   * the value of an entry that is out of bounds it is equivalent to {@link #UNASSOCIATED_VALUE}
-   *
-   * <p>This field is initialized on first write in {@link #updateVirtualFileData(int)}}. It
-   * is assumed that multiple files are processed concurrently and thus the allocation of the
-   * array is synchronized. However, for any a given file id, sequential access is assumed.
-   */
-  private int[] virtualFileIndexes;
 
   @Override
   public abstract void collectIndexedItems(IndexedItemCollection indexedItems);
@@ -47,90 +25,6 @@
 
   public abstract int getOffset(ObjectToOffsetMapping mapping);
 
-  /**
-   * Constants used inside virtualFileIndexes.
-   */
-  public static final int UNASSOCIATED_VALUE = -2;
-  public static final int ASSOCIATED_VALUE = -1;
-  public static final int MIN_VALID_VALUE = 0;
-
-  /**
-   * Returns whether this item is assigned to the given file id.
-   */
-  public boolean hasVirtualFileData(int virtualFileId) {
-    return getVirtualFileIndex(virtualFileId) != UNASSOCIATED_VALUE;
-  }
-
-  /**
-   * Assigns this item to the given file id if it has not been assigned previously.
-   *
-   * <p>This method returns 'true' if the item was newly assigned, i.e., it was not previously
-   * assigned to the file id.
-   */
-  public boolean assignToVirtualFile(int virtualFileId) {
-    // Fast lock-free check whether already assigned.
-    if (hasVirtualFileData(virtualFileId)) {
-      return false;
-    }
-    return updateVirtualFileData(virtualFileId);
-  }
-
-  /**
-   * Assigns this item to the given file id.
-   *
-   * <p>As a side effect, the {@link #virtualFileIndexes} field might be initialized or expanded.
-   * Hence this method is synchronized. Note that the field is queried without synchronization.
-   * Therefor it has to remain in a valid state at all times and must transition atomically from
-   * null to an initialized allocated value.
-   */
-  private synchronized boolean updateVirtualFileData(int virtualFileId) {
-    if (virtualFileIndexes == null) {
-      int[] fileIndices = new int[virtualFileId + 1];
-      Arrays.fill(fileIndices, UNASSOCIATED_VALUE);
-      // This has to be an atomic transition from null to an initialized array.
-      virtualFileIndexes = fileIndices;
-    }
-    // We increased the number of files, increase the index size.
-    if (virtualFileId >= virtualFileIndexes.length) {
-      int oldLength = virtualFileIndexes.length;
-      int[] fileIndices = Arrays.copyOf(virtualFileIndexes, virtualFileId + 1);
-      Arrays.fill(fileIndices, oldLength, virtualFileId + 1, UNASSOCIATED_VALUE);
-      virtualFileIndexes = fileIndices;
-    }
-    assert virtualFileId < virtualFileIndexes.length;
-    boolean wasAdded = virtualFileIndexes[virtualFileId] == UNASSOCIATED_VALUE;
-    virtualFileIndexes[virtualFileId] = ASSOCIATED_VALUE;
-    return wasAdded;
-  }
-
-  /**
-   * Assigns an actual index for this item in the given file.
-   *
-   * <p>May only be used after this item has been assigned to the file using {@link
-   * #assignToVirtualFile(int)}.
-   */
-  public void assignVirtualFileIndex(int virtualFileId, int index) {
-    assert virtualFileIndexes != null;
-    assert virtualFileIndexes[virtualFileId] < MIN_VALID_VALUE;
-    virtualFileIndexes[virtualFileId] = index;
-  }
-
-  /**
-   * Returns the index associated with this item for the given file id or {@link
-   * #UNASSOCIATED_VALUE} if the item is not associated to the given file id.
-   */
-  public int getVirtualFileIndex(int virtualFileId) {
-    if (virtualFileIndexes == null) {
-      return UNASSOCIATED_VALUE;
-    }
-    // If more files were added, but this entry not associated with it, we would not have extended
-    // the size of the array. So if the {@link virtualFileId} is out of bounds, it means
-    // {@link #UNASSOCIATED_VALUE}
-    return virtualFileIndexes.length > virtualFileId
-        ? virtualFileIndexes[virtualFileId]
-        : UNASSOCIATED_VALUE;
-  }
-
   // Partial implementation of PresortedComparable.
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index c8157d6..c7fc65d 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -4,36 +4,43 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
 import com.google.common.collect.Sets;
-import java.util.Arrays;
+import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 public class ObjectToOffsetMapping {
 
-  private final int virtualFileId;
+  private final static int NOT_FOUND = -1;
+  private final static int NOT_SET = -2;
 
   private final DexProgramClass[] classes;
-  private final DexProto[] protos;
-  private final DexType[] types;
-  private final DexMethod[] methods;
-  private final DexField[] fields;
-  private final DexString[] strings;
-  private final DexCallSite[] callSites;
-  private final DexMethodHandle[] methodHandles;
+  private final Reference2IntMap<DexProto> protos;
+  private final Reference2IntMap<DexType> types;
+  private final Reference2IntMap<DexMethod> methods;
+  private final Reference2IntMap<DexField> fields;
+  private final Reference2IntMap<DexString> strings;
+  private final Reference2IntMap<DexCallSite> callSites;
+  private final Reference2IntMap<DexMethodHandle> methodHandles;
   private DexString firstJumboString;
 
   public ObjectToOffsetMapping(
-      int virtualFileId,
       DexApplication application,
-      DexProgramClass[] classes,
-      DexProto[] protos,
-      DexType[] types,
-      DexMethod[] methods,
-      DexField[] fields,
-      DexString[] strings,
-      DexCallSite[] callSites,
-      DexMethodHandle[] methodHandles) {
+      Collection<DexProgramClass> classes,
+      Collection<DexProto> protos,
+      Collection<DexType> types,
+      Collection<DexMethod> methods,
+      Collection<DexField> fields,
+      Collection<DexString> strings,
+      Collection<DexCallSite> callSites,
+      Collection<DexMethodHandle> methodHandles) {
     assert application != null;
     assert classes != null;
     assert protos != null;
@@ -44,91 +51,90 @@
     assert callSites != null;
     assert methodHandles != null;
 
-    this.virtualFileId = virtualFileId;
     this.classes = sortClasses(application, classes);
-    this.protos = protos;
-    this.types = types;
-    this.methods = methods;
-    this.fields = fields;
-    this.strings = strings;
-    this.callSites = callSites;
-    this.methodHandles = methodHandles;
-
-    Arrays.sort(protos);
-    setIndexes(protos);
-
-    Arrays.sort(types);
-    setIndexes(types);
-
-    Arrays.sort(methods);
-    setIndexes(methods);
-
-    Arrays.sort(fields);
-    setIndexes(fields);
-
-    Arrays.sort(strings);
-    setIndexes(strings);
-
+    this.protos = createMap(protos, true, this::failOnOverflow);
+    this.types = createMap(types, true, this::failOnOverflow);
+    this.methods = createMap(methods, true, this::failOnOverflow);
+    this.fields = createMap(fields, true, this::failOnOverflow);
+    this.strings = createMap(strings, true, this::setFirstJumboString);
     // No need to sort CallSite, they will be written in data section in the callSites order,
     // consequently offset of call site used into the call site section will be in ascending order.
-    setIndexes(callSites);
-
+    this.callSites = createMap(callSites, false, this::failOnOverflow);
     // No need to sort method handle
-    setIndexes(methodHandles);
+    this.methodHandles = createMap(methodHandles, false, this::failOnOverflow);
   }
 
-  private static DexProgramClass[] sortClasses(
-      DexApplication application, DexProgramClass[] classes) {
-    Arrays.sort(classes, (o1, o2) -> o1.type.descriptor.slowCompareTo(o2.type.descriptor));
+  private void setFirstJumboString(DexString string) {
+    assert firstJumboString == null;
+    firstJumboString = string;
+  }
+
+  private void failOnOverflow(DexItem item) {
+    throw new CompilationError("Index overflow for " + item.getClass());
+  }
+
+  private <T extends IndexedDexItem> Reference2IntMap<T> createMap(Collection<T> items,
+      boolean sort, Consumer<T> onUInt16Overflow) {
+    if (items.isEmpty()) {
+      return null;
+    }
+    Reference2IntMap<T> map = new Reference2IntLinkedOpenHashMap<>(items.size());
+    map.defaultReturnValue(NOT_FOUND);
+    Collection<T> sorted = sort ? items.stream().sorted().collect(Collectors.toList()) : items;
+    int index = 0;
+    for (T item : sorted) {
+      if (index == Constants.U16BIT_MAX + 1) {
+        onUInt16Overflow.accept(item);
+      }
+      map.put(item, index++);
+    }
+    return map;
+  }
+
+  private static DexProgramClass[] sortClasses(DexApplication application,
+      Collection<DexProgramClass> classes) {
     SortingProgramClassVisitor classVisitor = new SortingProgramClassVisitor(application, classes);
-    classVisitor.run(classes);
+    // Collect classes in subtyping order, based on a sorted list of classes to start with.
+    classVisitor.run(
+        classes.stream().sorted(Comparator.comparing(DexClass::getType))
+            .collect(Collectors.toList()));
     return classVisitor.getSortedClasses();
   }
 
-  private void setIndexes(IndexedDexItem[] items) {
-    int index = 0;
-    for (IndexedDexItem item : items) {
-      item.assignVirtualFileIndex(virtualFileId, index);
-      // For strings collect the first jumbo string (if any).
-      if ((index > Constants.MAX_NON_JUMBO_INDEX) && (item instanceof DexString)) {
-        if (index == Constants.FIRST_JUMBO_INDEX) {
-          firstJumboString = (DexString) item;
-        }
-      }
-      index++;
-    }
+  private static <T> Collection<T> keysOrEmpty(Map<T, ?> map) {
+    return map == null ? Collections.emptyList() : map.keySet();
   }
 
-  public DexMethod[] getMethods() {
-    return methods;
+  public Collection<DexMethod> getMethods() {
+    return keysOrEmpty(methods);
   }
 
   public DexProgramClass[] getClasses() {
     return classes;
   }
 
-  public DexType[] getTypes() {
-    return types;
+  public Collection<DexType> getTypes() {
+    return keysOrEmpty(types);
   }
 
-  public DexProto[] getProtos() {
-    return protos;
+  public Collection<DexProto> getProtos() {
+    return keysOrEmpty(protos);
   }
 
-  public DexField[] getFields() {
-    return fields;
+  public Collection<DexField> getFields() {
+    return keysOrEmpty(fields);
   }
 
-  public DexString[] getStrings() {
-    return strings;
+  public Collection<DexString> getStrings() {
+    return keysOrEmpty(strings);
   }
 
-  public DexCallSite[] getCallSites() {
-    return callSites;
+  public Collection<DexCallSite> getCallSites() {
+    return keysOrEmpty(callSites);
   }
 
-  public DexMethodHandle[] getMethodHandles() {
-    return methodHandles;
+  public Collection<DexMethodHandle> getMethodHandles() {
+    return keysOrEmpty(methodHandles);
   }
 
   public boolean hasJumboStrings() {
@@ -139,43 +145,39 @@
     return firstJumboString;
   }
 
-  private boolean isContainedInMapping(IndexedDexItem item) {
-    return item.getVirtualFileIndex(virtualFileId) != IndexedDexItem.UNASSOCIATED_VALUE;
+  private <T extends IndexedDexItem> int getOffsetFor(T item, Reference2IntMap<T> map) {
+    int index = map.getInt(item);
+    assert index != NOT_SET : "Index was not set: " + item;
+    assert index != NOT_FOUND : "Missing dependency: " + item;
+    return index;
   }
 
   public int getOffsetFor(DexProto proto) {
-    assert isContainedInMapping(proto) : "Missing dependency: " + proto;
-    return proto.getVirtualFileIndex(virtualFileId);
+    return getOffsetFor(proto, protos);
   }
 
   public int getOffsetFor(DexField field) {
-    assert isContainedInMapping(field) : "Missing dependency: " + field;
-    return field.getVirtualFileIndex(virtualFileId);
+    return getOffsetFor(field, fields);
   }
 
   public int getOffsetFor(DexMethod method) {
-    assert isContainedInMapping(method) : "Missing dependency: " + method;
-    return method.getVirtualFileIndex(virtualFileId);
+    return getOffsetFor(method, methods);
   }
 
   public int getOffsetFor(DexString string) {
-    assert isContainedInMapping(string) : "Missing dependency: " + string;
-    return string.getVirtualFileIndex(virtualFileId);
+    return getOffsetFor(string, strings);
   }
 
   public int getOffsetFor(DexType type) {
-    assert isContainedInMapping(type) : "Missing dependency: " + type;
-    return type.getVirtualFileIndex(virtualFileId);
+    return getOffsetFor(type, types);
   }
 
   public int getOffsetFor(DexCallSite callSite) {
-    assert isContainedInMapping(callSite) : "Missing dependency: " + callSite;
-    return callSite.getVirtualFileIndex(virtualFileId);
+    return getOffsetFor(callSite, callSites);
   }
 
   public int getOffsetFor(DexMethodHandle methodHandle) {
-    assert isContainedInMapping(methodHandle) : "Missing dependency: " + methodHandle;
-    return methodHandle.getVirtualFileIndex(virtualFileId);
+    return getOffsetFor(methodHandle, methodHandles);
   }
 
   private static class SortingProgramClassVisitor extends ProgramClassVisitor {
@@ -184,10 +186,11 @@
 
     private int index = 0;
 
-    public SortingProgramClassVisitor(DexApplication application, DexProgramClass[] classes) {
+    SortingProgramClassVisitor(DexApplication application,
+        Collection<DexProgramClass> classes) {
       super(application);
-      this.sortedClasses = new DexProgramClass[classes.length];
-      Collections.addAll(classSet, classes);
+      this.sortedClasses = new DexProgramClass[classes.size()];
+      classSet.addAll(classes);
     }
 
     @Override
@@ -201,7 +204,7 @@
       }
     }
 
-    public DexProgramClass[] getSortedClasses() {
+    DexProgramClass[] getSortedClasses() {
       assert index == sortedClasses.length;
       return sortedClasses;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java b/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java
index 826e260..3414037 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java
@@ -68,12 +68,16 @@
     }
   }
 
-  public void run() {
-    for (DexProgramClass clazz : application.classes()) {
+  public void run(Iterable<DexProgramClass> classes) {
+    for (DexProgramClass clazz : classes) {
       accept(clazz);
     }
   }
 
+  public void run() {
+    run(application.classes());
+  }
+
   /**
    * Called for each library class used in the class hierarchy. A library class is a class that is
    * not present in the application.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index f6a860d..9c8ed1c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -83,8 +83,6 @@
   // The register allocator providing register assignments for the code to build.
   private final RegisterAllocator registerAllocator;
 
-  private final DexItemFactory dexItemFactory;
-
   private final InternalOptions options;
 
   // List of information about switch payloads that have to be created at the end of the
@@ -118,14 +116,11 @@
   public DexBuilder(
       IRCode ir,
       RegisterAllocator registerAllocator,
-      DexItemFactory dexItemFactory,
       InternalOptions options) {
     assert ir != null;
     assert registerAllocator != null;
-    assert dexItemFactory != null;
     this.ir = ir;
     this.registerAllocator = registerAllocator;
-    this.dexItemFactory = dexItemFactory;
     this.options = options;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index b8324d9..063bee1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -410,7 +410,7 @@
     }
     assert code.isConsistentSSA();
     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
-    method.setCode(code, registerAllocator, appInfo.dexItemFactory, options);
+    method.setCode(code, registerAllocator, options);
     if (Log.ENABLED) {
       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
           method.toSourceString(), logCode(options, method));
@@ -584,7 +584,7 @@
 
     // Perform register allocation.
     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
-    method.setCode(code, registerAllocator, appInfo.dexItemFactory, options);
+    method.setCode(code, registerAllocator, options);
     updateHighestSortingStrings(method);
     if (Log.ENABLED) {
       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
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 9385f11..74afdcd 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
@@ -685,7 +685,6 @@
     private final ListIterator<BasicBlock> blocksIterator;
     private final List<Integer> toRemove;
     int argumentsMapIndex;
-    Value returnValue;
 
     OutlineRewriter(
         DexEncodedMethod method, IRCode code,
@@ -702,7 +701,7 @@
         DexMethod m = generatedOutlines.get(outline);
         assert m != null;
         List<Value> in = new ArrayList<>();
-        returnValue = null;
+        Value returnValue = null;
         argumentsMapIndex = 0;
         Position position = Position.none();
         { // Scope for 'instructions'.
diff --git a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
index 7773e11..ced15f5 100644
--- a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
@@ -53,8 +53,6 @@
  */
 public class ProtoLitePruner extends ProtoLiteBase {
 
-  private final AppInfoWithLiveness appInfo;
-
   private final DexType visitorType;
 
   private final DexType methodEnumType;
@@ -77,7 +75,6 @@
 
   public ProtoLitePruner(AppInfoWithLiveness appInfo) {
     super(appInfo);
-    this.appInfo = appInfo;
     DexItemFactory factory = appInfo.dexItemFactory;
     this.visitorType = factory.createType("Lcom/google/protobuf/GeneratedMessageLite$Visitor;");
     this.methodEnumType = factory
@@ -318,7 +315,7 @@
       throw new CompilationError("dynamicMethod in protoLite without switch.");
     }
     Switch switchInstr = matchingInstr.asSwitch();
-    EnumSwitchInfo info = SwitchUtils.analyzeSwitchOverEnum(switchInstr, appInfo);
+    EnumSwitchInfo info = SwitchUtils.analyzeSwitchOverEnum(switchInstr, appInfo.withLiveness());
     if (info == null || info.enumClass != methodEnumType) {
       throw new CompilationError("Malformed switch in dynamicMethod of proto lite.");
     }
@@ -471,7 +468,7 @@
               // We have to rewrite these as a precaution, as they might be dead due to
               // tree shaking ignoring them.
               DexField field = getterToField(invokedMethod, 5);
-              if (appInfo.liveFields.contains(field)) {
+              if (appInfo.withLiveness().liveFields.contains(field)) {
                 // Effectively inline the code that is normally inside these methods.
                 Value thisReference = invokeMethod.getReceiver();
                 Value newResult = code.createValue(MoveType.SINGLE);
@@ -658,7 +655,7 @@
   }
 
   private boolean isDeadProtoField(DexField field) {
-    return isProtoField(field) && !appInfo.liveFields.contains(field);
+    return isProtoField(field) && !appInfo.withLiveness().liveFields.contains(field);
   }
 
   private boolean isDeadProtoGetter(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryOutputSink.java b/src/main/java/com/android/tools/r8/utils/DirectoryOutputSink.java
index adedfde..56e0ea1 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryOutputSink.java
@@ -35,7 +35,7 @@
   @Override
   public void writeDexFile(byte[] contents, Set<String> classDescriptors, int fileId)
       throws IOException {
-    Path target = outputDirectory.resolve(getOutputPath(fileId));
+    Path target = outputDirectory.resolve(getOutputFileName(fileId));
     Files.createDirectories(target.getParent());
     writeToFile(target, null, contents);
   }
@@ -43,7 +43,7 @@
   @Override
   public void writeDexFile(byte[] contents, Set<String> classDescriptors, String primaryClassName)
       throws IOException {
-    Path target = outputDirectory.resolve(getOutputPath(primaryClassName));
+    Path target = outputDirectory.resolve(getOutputFileName(primaryClassName));
     Files.createDirectories(target.getParent());
     writeToFile(target, null, contents);
   }
@@ -52,17 +52,4 @@
   public void close() throws IOException {
     // Intentionally left empty.
   }
-
-  private Path getOutputPath(int index) {
-    String file = index == 0 ? "classes.dex" : ("classes" + (index + 1) + ".dex");
-    return outputDirectory.resolve(file);
-  }
-
-  private Path getOutputPath(String classDescriptor) throws IOException {
-    assert classDescriptor != null && DescriptorUtils.isClassDescriptor(classDescriptor);
-    Path result = outputDirectory
-        .resolve(classDescriptor.substring(1, classDescriptor.length() - 1) + ".dex");
-    Files.createDirectories(result.getParent());
-    return result;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java b/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java
index e7cfc28..e007147 100644
--- a/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java
@@ -8,7 +8,6 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
 
 public abstract class FileSystemOutputSink implements OutputSink {
@@ -28,17 +27,17 @@
     }
   }
 
-  protected Path getOutputFileName(int index) {
-    String file = index == 0 ? "classes.dex" : ("classes" + (index + 1) + ".dex");
-    return Paths.get(file);
+  String getOutputFileName(int index) {
+    return index == 0 ? "classes.dex" : ("classes" + (index + 1) + FileUtils.DEX_EXTENSION);
   }
 
-  protected Path getOutputFileName(String classDescriptor) throws IOException {
+  String getOutputFileName(String classDescriptor) throws IOException {
     assert classDescriptor != null && DescriptorUtils.isClassDescriptor(classDescriptor);
-    Path result = Paths.get(classDescriptor.substring(1, classDescriptor.length() - 1) + ".dex");
-    return result;
+    return DescriptorUtils.getClassBinaryNameFromDescriptor(classDescriptor)
+        + FileUtils.DEX_EXTENSION;
   }
 
+
   @Override
   public void writePrintUsedInformation(byte[] contents) throws IOException {
     writeToFile(options.proguardConfiguration.getPrintUsageFile(), System.out, contents);
@@ -67,7 +66,9 @@
               closer,
               output,
               defValue,
-              StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+              StandardOpenOption.CREATE,
+              StandardOpenOption.TRUNCATE_EXISTING,
+              StandardOpenOption.WRITE);
       outputStream.write(contents);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index a292423..809a6d4 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -118,10 +118,10 @@
 
   static boolean isClassesDexFile(Path file) {
     String name = file.getFileName().toString().toLowerCase();
-    if (!name.startsWith("classes") || !name.endsWith(".dex")) {
+    if (!name.startsWith("classes") || !name.endsWith(DEX_EXTENSION)) {
       return false;
     }
-    String numeral = name.substring("classes".length(), name.length() - ".dex".length());
+    String numeral = name.substring("classes".length(), name.length() - DEX_EXTENSION.length());
     if (numeral.isEmpty()) {
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 8fe3af6..59abfdb 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -3,9 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.Iterator;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -13,17 +11,26 @@
 
 public class ThreadUtils {
 
-  public static <T> List<T> awaitFutures(Collection<? extends Future<? extends T>> futures)
+  public static void awaitFutures(Iterable<? extends Future<?>> futures)
       throws ExecutionException {
-    ArrayList<T> result = new ArrayList<>(futures.size());
-    for (Future<? extends T> f : futures) {
+    Iterator<? extends Future<?>> it = futures.iterator();
+    try {
+      while (it.hasNext()) {
+        it.next().get();
+      }
+    } catch (InterruptedException e) {
+      throw new RuntimeException("Interrupted while waiting for future.", e);
+    } finally {
+      // In case we get interrupted or one of the threads throws an exception, abort all further
+      // work, if possible.
       try {
-        result.add(f.get());
-      } catch (InterruptedException e) {
-        throw new RuntimeException("Interrupted while waiting for future.", e);
+        while (it.hasNext()) {
+          it.next().cancel(true);
+        }
+      } catch (Throwable t) {
+        // Ignore the new Exception.
       }
     }
-    return result;
   }
 
   public static ExecutorService getExecutorService(int threads) {
diff --git a/src/main/java/com/android/tools/r8/utils/VersionProperties.java b/src/main/java/com/android/tools/r8/utils/VersionProperties.java
index c2253de..57aea8b 100644
--- a/src/main/java/com/android/tools/r8/utils/VersionProperties.java
+++ b/src/main/java/com/android/tools/r8/utils/VersionProperties.java
@@ -13,7 +13,6 @@
  * A class describing version properties.
  */
 public class VersionProperties {
-  private static final int VERSION_CODE = 1;
 
   private static final String VERSION_CODE_KEY = "version-file.version.code";
   private static final String SHA_KEY = "version.sha";
diff --git a/src/main/java/com/android/tools/r8/utils/ZipFileOutputSink.java b/src/main/java/com/android/tools/r8/utils/ZipFileOutputSink.java
index 0feb485..d290b56 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipFileOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipFileOutputSink.java
@@ -39,8 +39,8 @@
     outputStream.close();
   }
 
-  private synchronized void writeToZipFile(Path outputPath, byte[] content) throws IOException {
-    ZipEntry zipEntry = new ZipEntry(outputPath.toString());
+  private synchronized void writeToZipFile(String outputPath, byte[] content) throws IOException {
+    ZipEntry zipEntry = new ZipEntry(outputPath);
     zipEntry.setSize(content.length);
     outputStream.putNextEntry(zipEntry);
     outputStream.write(content);
diff --git a/src/test/examples/regress_62300145/Regress.java b/src/test/examples/regress_62300145/Regress.java
index f151e83..0efc7a6 100644
--- a/src/test/examples/regress_62300145/Regress.java
+++ b/src/test/examples/regress_62300145/Regress.java
@@ -29,10 +29,17 @@
   public static void main(String[] args) throws NoSuchMethodException {
     Constructor<InnerClass> constructor = InnerClass.class.getDeclaredConstructor(
         Regress.class, String.class, String.class, String.class);
-    int i = 0;
-    for (Annotation[] annotations : constructor.getParameterAnnotations()) {
-      System.out.print(i++ + ": ");
-      for (Annotation annotation : annotations) {
+    Annotation[][] annotations = constructor.getParameterAnnotations();
+    int index = 0;
+    for (int i = 0; i < annotations.length; i++) {
+      // TODO(b/67936230): Java 8 and Java 9 runtime does not have the same behavior regarding
+      // implicit parameter such as 'outer this' for instance. Disable this test on Java 9 runtime
+      // due to this divergence.
+      if (System.getProperty("java.specification.version").equals("9") && i == 0) {
+        continue;
+      }
+      System.out.print(index++ + ": ");
+      for (Annotation annotation : annotations[i]) {
         System.out.println(annotation);
       }
     }
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 546d9eb..c8ca57e 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -274,7 +274,6 @@
     D8IncrementalTestRunner test = test(testName, testPackage, mainClass);
     test.compileClassesTogether(inputJarFile, out);
 
-
     String[] topLevelDir = out.toFile().list();
     assert topLevelDir != null;
     assertEquals(1, topLevelDir.length);
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index e2cb5d6..5ad82de 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -98,6 +98,25 @@
   }
 
   @Test
+  public void testMainRelativeDir() throws IOException, InterruptedException {
+    temp.newFolder("outdex");
+    Path out = Paths.get("outdex");
+    Path workingDir = temp.getRoot().toPath();
+    ProcessResult r8 = ToolHelper.forkR8(workingDir,
+        "--lib", Paths.get(ToolHelper.getDefaultAndroidJar()).toAbsolutePath().toString(),
+        "--output", out.toString(),
+        "--pg-conf", PROGUARD_FLAGS.toAbsolutePath().toString(),
+        "--pg-conf", testFlags.toAbsolutePath().toString(),
+        INPUT_JAR.toAbsolutePath().toString());
+    Assert.assertEquals(0, r8.exitCode);
+    Assert.assertTrue(
+        Files.isRegularFile(workingDir.resolve(out).resolve(ToolHelper.DEFAULT_DEX_FILENAME)));
+    Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(MAPPING)));
+    Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(SEEDS)));
+  }
+
+
+  @Test
   public void testMainZip() throws IOException, InterruptedException {
     Path out = temp.newFolder("outdex").toPath().resolve("dex.zip");
     ProcessResult r8 = ToolHelper.forkR8(Paths.get("."),
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index a08f274..9060e55 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -4,41 +4,33 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexApplication.Builder;
-import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexDebugEvent;
 import com.android.tools.r8.graph.DexDebugInfo;
-import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import java.util.Collections;
 import org.junit.Assert;
 import org.junit.Test;
 
 public class DebugByteCodeWriterTest {
 
-  ObjectToOffsetMapping emptyObjectTObjectMapping() {
+  private ObjectToOffsetMapping emptyObjectTObjectMapping() {
     return new ObjectToOffsetMapping(
-        0,
         DexApplication.builder(new DexItemFactory(), null).build(),
-        new DexProgramClass[] {},
-        new DexProto[] {},
-        new DexType[] {},
-        new DexMethod[] {},
-        new DexField[] {},
-        new DexString[] {},
-        new DexCallSite[] {},
-        new DexMethodHandle[] {});
+        Collections.emptyList(),
+        Collections.emptyList(),
+        Collections.emptyList(),
+        Collections.emptyList(),
+        Collections.emptyList(),
+        Collections.emptyList(),
+        Collections.emptyList(),
+        Collections.emptyList());
   }
 
   @Test
   public void testEmptyDebugInfo() {
-    DexDebugInfo debugInfo = new DexDebugInfo(1, new DexString[]{}, new DexDebugEvent[]{});
+    DexDebugInfo debugInfo = new DexDebugInfo(1, DexString.EMPTY_ARRAY, new DexDebugEvent[]{});
     DebugBytecodeWriter writer = new DebugBytecodeWriter(debugInfo, emptyObjectTObjectMapping());
     Assert.assertEquals(3, writer.generate().length);
   }
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 38cca7e..d765174 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.code.ReturnVoid;
 import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexAnnotationSetRefList;
@@ -124,7 +123,7 @@
 
     InternalOptions options = new InternalOptions(dexItemFactory);
     options.outputMode = OutputMode.FilePerInputClass;
-    ApplicationWriter writer = new ApplicationWriter(application, new AppInfo(application),
+    ApplicationWriter writer = new ApplicationWriter(application,
         options, null, null, NamingLens.getIdentityLens(), null);
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     CollectInfoOutputSink sink = new CollectInfoOutputSink();
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index eb87524..6962f97 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.errors.DexOverflowException;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -95,12 +94,10 @@
       throws Exception {
     DexApplication app = builder.read();
     app = process(app, options);
-    AppInfo info = new AppInfo(app);
     AndroidAppOutputSink compatSink = new AndroidAppOutputSink();
     R8.writeApplication(
         Executors.newSingleThreadExecutor(),
         app,
-        info,
         compatSink,
         null,
         NamingLens.getIdentityLens(),
@@ -187,13 +184,11 @@
 
   public AndroidApp writeDex(DexApplication application, InternalOptions options)
       throws DexOverflowException, IOException {
-    AppInfo appInfo = new AppInfo(application);
     try {
       AndroidAppOutputSink compatSink = new AndroidAppOutputSink();
       R8.writeApplication(
           Executors.newSingleThreadExecutor(),
           application,
-          appInfo,
           compatSink,
           null,
           NamingLens.getIdentityLens(),
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java b/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
index 61b624e..27d8d8b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
@@ -23,7 +23,7 @@
   // String constants are split into several class files to ensure both the constant-pool and
   // instruction count are below the class-file limits.
   private static int CLASSES_COUNT = 10;
-  private static int MIN_STRING_COUNT = Constants.FIRST_JUMBO_INDEX + 1;
+  private static int MIN_STRING_COUNT = Constants.MAX_NON_JUMBO_INDEX + 2;
   private static int EXTRA_STRINGS_PER_CLASSES_COUNT = MIN_STRING_COUNT % CLASSES_COUNT;
   private static int STRINGS_PER_CLASSES_COUNT =
       EXTRA_STRINGS_PER_CLASSES_COUNT + MIN_STRING_COUNT / CLASSES_COUNT;
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 4e58b9f..a358e26 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -594,7 +594,7 @@
                 code);
         IRCode ir = code.buildIR(method, options);
         RegisterAllocator allocator = new LinearScanRegisterAllocator(ir, options);
-        method.setCode(ir, allocator, factory, options);
+        method.setCode(ir, allocator, options);
         directMethods[i] = method;
       }
       builder.addProgramClass(
@@ -613,9 +613,8 @@
               DexEncodedMethod.EMPTY_ARRAY));
     }
     DirectMappedDexApplication application = builder.build().toDirect();
-    AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
     ApplicationWriter writer = new ApplicationWriter(
-        application, appInfo, options, null, null, NamingLens.getIdentityLens(), null);
+        application, options, null, null, NamingLens.getIdentityLens(), null);
     ExecutorService executor = ThreadUtils.getExecutorService(options);
     AndroidAppOutputSink compatSink = new AndroidAppOutputSink();
     try {
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index ddab789..2d2f887 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -669,13 +669,11 @@
 
   public AndroidApp writeDex(DexApplication application, InternalOptions options)
       throws DexOverflowException {
-    AppInfo appInfo = new AppInfo(application);
     try {
       AndroidAppOutputSink compatSink = new AndroidAppOutputSink();
       R8.writeApplication(
           Executors.newSingleThreadExecutor(),
           application,
-          appInfo,
           compatSink,
           null,
           NamingLens.getIdentityLens(),
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 f6e5c44..d432532 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -105,7 +105,7 @@
       DexApplication dexApp = new ApplicationReader(
           app, options, new Timing("smali")).read(executor);
       ApplicationWriter writer = new ApplicationWriter(
-          dexApp, null, options, null, null, NamingLens.getIdentityLens(), null);
+          dexApp, options, null, null, NamingLens.getIdentityLens(), null);
       SingleFileSink sink = new SingleFileSink();
       writer.write(sink, executor);
       return sink.contents;