Merge "Remove DexOverflowException"
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index e393f32..799d834 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -37,7 +37,7 @@
     programConsumer = null;
     mode = null;
     minApiLevel = 0;
-    reporter = new Reporter(new DefaultDiagnosticsHandler());
+    reporter = new Reporter(new DefaultDiagnosticsHandler(), this);
     enableDesugaring = true;
     optimizeMultidexForLinearAlloc = false;
   }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d14f96e..5d29d90 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.dex.Marker;
 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.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.ClassAndMemberPublicizer;
 import com.android.tools.r8.graph.DexApplication;
@@ -179,7 +178,7 @@
       String proguardSeedsData,
       InternalOptions options,
       ProguardMapSupplier proguardMapSupplier)
-      throws ExecutionException, DexOverflowException {
+      throws ExecutionException {
     try {
       Marker marker = getMarker(options);
       if (options.isGeneratingClassFiles()) {
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 dcc89c6..7ef43a3 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.ApplicationReader;
 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.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.utils.AndroidApp;
@@ -175,7 +174,7 @@
   }
 
   private static void writeApp(DexApplication app, Path output, ExecutorService executor)
-      throws IOException, ExecutionException, DexOverflowException {
+      throws IOException, ExecutionException {
     InternalOptions options = new InternalOptions();
     AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
     ApplicationWriter writer = new ApplicationWriter(app, options, null, null, null, null, null);
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 9721d3b..e1905ff 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -162,7 +161,7 @@
   }
 
   private Iterable<VirtualFile> distribute(ExecutorService executorService)
-      throws ExecutionException, IOException, DexOverflowException {
+      throws ExecutionException, IOException {
     // Distribute classes into dex files.
     VirtualFile.Distributor distributor;
     if (options.isGeneratingDexFilePerClassFile()) {
@@ -179,8 +178,7 @@
     return distributor.run();
   }
 
-  public void write(ExecutorService executorService)
-      throws IOException, ExecutionException, DexOverflowException {
+  public void write(ExecutorService executorService) throws IOException, ExecutionException {
     application.timing.begin("DexApplication.write");
     try {
       insertAttributeAnnotations();
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index 3d0b01c..9e75e45 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.dex.VirtualFile.VirtualFileCycler;
-import com.android.tools.r8.errors.DexOverflowException;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -306,7 +306,7 @@
     directSubClasses = new DirectSubClassesInfo(app, classes);
   }
 
-  public void distribute() throws DexOverflowException {
+  public void distribute() {
     List<ClassGroup> remainingInheritanceGroups = collectInheritanceGroups();
     // Sort to ensure reproducible allocation
     remainingInheritanceGroups.sort(null);
@@ -369,8 +369,7 @@
     return groupClassNumber;
   }
 
-  private Collection<VirtualFile> assignGroup(ClassGroup group,
-      List<VirtualFile> dexBlackList) throws DexOverflowException {
+  private Collection<VirtualFile> assignGroup(ClassGroup group, List<VirtualFile> dexBlackList) {
     VirtualFileCycler cycler = new VirtualFileCycler(dexes, namingLens, dexIndexOffset);
     if (group.members.isEmpty()) {
       return Collections.emptyList();
@@ -411,8 +410,8 @@
    * They will fail to link during DexOpt but they will be loaded only once.
    * @param classes set of classes to assign, the set will be destroyed during assignment.
    */
-  private Collection<VirtualFile> assignClassesWithLinkingError(Set<DexProgramClass> classes,
-      Collection<VirtualFile> dexBlackList) throws DexOverflowException {
+  private Collection<VirtualFile> assignClassesWithLinkingError(
+      Set<DexProgramClass> classes, Collection<VirtualFile> dexBlackList) {
 
     List<ClassGroup> layers = collectNoDirectInheritanceGroups(classes);
 
@@ -440,7 +439,7 @@
             dexForLayer.abortTransaction();
             if (dexForLayer.isEmpty()) {
               // The class is too big to fit in one dex
-              throw new DexOverflowException("Class '" + dexProgramClass.toSourceString()
+              throw new CompilationError("Class '" + dexProgramClass.toSourceString()
                   + "' from " + dexProgramClass.getOrigin().toString()
                   + " is too big to fit in a dex.");
             }
@@ -614,8 +613,8 @@
    * Assign as many classes as possible by layer starting by roots.
    * @return the list of classes that were not assigned.
    */
-  private Set<DexProgramClass> assignFromRoot(VirtualFile dex,
-      Collection<DexProgramClass> classes) throws DexOverflowException {
+  private Set<DexProgramClass> assignFromRoot(
+      VirtualFile dex, Collection<DexProgramClass> classes) {
 
     int totalClasses = classes.size();
     int assignedClasses = 0;
@@ -635,7 +634,7 @@
             dex.abortTransaction();
             if (dex.isEmpty()) {
               // The class is too big to fit in one dex
-              throw new DexOverflowException("Class '" + clazz.toSourceString() + "' from "
+              throw new CompilationError("Class '" + clazz.toSourceString() + "' from "
                   + clazz.getOrigin().toString() + " is too big to fit in a dex.");
             }
             isLayerFullyAssigned = false;
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 7f55b19..9ac752a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -3,9 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.dex;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.InternalCompilerError;
-import com.android.tools.r8.errors.MainDexOverflowException;
+import com.android.tools.r8.errors.MainDexOverflow;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
@@ -24,6 +23,7 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Sets;
@@ -185,15 +185,16 @@
     return transaction.getNumberOfFields();
   }
 
-  void throwIfFull(boolean hasMainDexList) throws DexOverflowException {
+  void throwIfFull(boolean hasMainDexList, Reporter reporter) {
     if (!isFull()) {
       return;
     }
-    throw new MainDexOverflowException(
-        hasMainDexList,
-        transaction.getNumberOfMethods(),
-        transaction.getNumberOfFields(),
-        MAX_ENTRIES);
+    throw reporter.fatalError(
+        new MainDexOverflow(
+            hasMainDexList,
+            transaction.getNumberOfMethods(),
+            transaction.getNumberOfFields(),
+            MAX_ENTRIES));
   }
 
   private boolean isFilledEnough(FillStrategy fillStrategy) {
@@ -226,8 +227,7 @@
       this.writer = writer;
     }
 
-    public abstract Collection<VirtualFile> run()
-        throws ExecutionException, IOException, DexOverflowException;
+    public abstract Collection<VirtualFile> run() throws ExecutionException, IOException;
   }
 
   /**
@@ -296,7 +296,7 @@
       originalNames = computeOriginalNameMapping(classes, application.getProguardMap());
     }
 
-    protected void fillForMainDexList(Set<DexProgramClass> classes) throws DexOverflowException {
+    protected void fillForMainDexList(Set<DexProgramClass> classes) {
       if (!application.mainDexList.isEmpty()) {
         VirtualFile mainDexFile = virtualFiles.get(0);
         for (DexType type : application.mainDexList) {
@@ -314,7 +314,7 @@
           }
           mainDexFile.commitTransaction();
         }
-        mainDexFile.throwIfFull(true);
+        mainDexFile.throwIfFull(true, options.reporter);
       }
     }
 
@@ -363,7 +363,7 @@
     }
 
     @Override
-    public Collection<VirtualFile> run() throws IOException, DexOverflowException {
+    public Collection<VirtualFile> run() throws IOException {
       int totalClassNumber = classes.size();
       // First fill required classes into the main dex file.
       fillForMainDexList(classes);
@@ -408,14 +408,13 @@
     }
 
     @Override
-    public Collection<VirtualFile> run()
-        throws ExecutionException, IOException, DexOverflowException {
+    public Collection<VirtualFile> run() throws ExecutionException, IOException {
       // Add all classes to the main dex file.
       for (DexProgramClass programClass : classes) {
         mainDexFile.addClass(programClass);
       }
       mainDexFile.commitTransaction();
-      mainDexFile.throwIfFull(false);
+      mainDexFile.throwIfFull(false, options.reporter);
       return virtualFiles;
     }
   }
diff --git a/src/main/java/com/android/tools/r8/errors/DexOverflowException.java b/src/main/java/com/android/tools/r8/errors/DexOverflowException.java
deleted file mode 100644
index 818c980..0000000
--- a/src/main/java/com/android/tools/r8/errors/DexOverflowException.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.errors;
-
-/**
- * Signals when there were too many items to fit in a given dex file.
- */
-public class DexOverflowException extends CompilationError {
-
-  public DexOverflowException(String message) {
-    super(message);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java b/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
new file mode 100644
index 0000000..e33cf59
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Info about error when running mono dex and not all classes can fit in a dex or when running for
+ * multidex legacy and there are too many classes to fit in the main dex.
+ */
+public class MainDexOverflow {
+  private final boolean hasMainDexList;
+  private final long numOfMethods;
+  private final long numOfFields;
+  private final long maxNumOfEntries;
+
+  public MainDexOverflow(
+      boolean hasMainDexList, long numOfMethods, long numOfFields, long maxNumOfEntries) {
+    super();
+    this.hasMainDexList = hasMainDexList;
+    this.numOfMethods = numOfMethods;
+    this.numOfFields = numOfFields;
+    this.maxNumOfEntries = maxNumOfEntries;
+  }
+
+  private StringBuilder getGeneralMessage() {
+    StringBuilder messageBuilder = new StringBuilder();
+    // General message: Cannot fit.
+    messageBuilder.append("Cannot fit requested classes in ");
+    messageBuilder.append(hasMainDexList ? "the main-" : "a single ");
+    messageBuilder.append("dex file");
+
+    return messageBuilder;
+  }
+
+  private String getNumberRelatedMessage() {
+    StringBuilder messageBuilder = new StringBuilder();
+    // Show the numbers of methods and/or fields that exceed the limit.
+    if (numOfMethods > maxNumOfEntries) {
+      messageBuilder.append("# methods: ");
+      messageBuilder.append(numOfMethods);
+      messageBuilder.append(" > ").append(maxNumOfEntries);
+      if (numOfFields > maxNumOfEntries) {
+        messageBuilder.append(" ; ");
+      }
+    }
+    if (numOfFields > maxNumOfEntries) {
+      messageBuilder.append("# fields: ");
+      messageBuilder.append(numOfFields);
+      messageBuilder.append(" > ").append(maxNumOfEntries);
+    }
+
+    return messageBuilder.toString();
+  }
+
+  public String getMessage() {
+    // Default message
+    return getGeneralMessage()
+        .append(" (")
+        .append(getNumberRelatedMessage())
+        .append(")")
+        .toString();
+  }
+
+  public String getMessageForD8() {
+    StringBuilder messageBuilder = getGeneralMessage();
+    if (!hasMainDexList) {
+      messageBuilder.append(". ");
+      messageBuilder.append("Try supplying a main-dex list");
+    }
+    messageBuilder.append(".").append(System.getProperty("line.separator"));
+    messageBuilder.append(getNumberRelatedMessage());
+    return messageBuilder.toString();
+  }
+
+  public String getMessageForR8() {
+    StringBuilder messageBuilder = getGeneralMessage();
+    if (!hasMainDexList) {
+      messageBuilder.append(". ");
+      messageBuilder.append("Try supplying a main-dex list or main dex rules");
+    }
+    messageBuilder.append(".").append(System.getProperty("line.separator"));
+    messageBuilder.append(getNumberRelatedMessage());
+    return messageBuilder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java b/src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java
deleted file mode 100644
index 7c3d87d..0000000
--- a/src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.errors;
-
-/**
- * Thrown when running mono dex and not all classes can fit in a dex or when running for multidex
- * legacy and there are too many classes to fit in the main dex.
- */
-public class MainDexOverflowException extends DexOverflowException {
-
-  public MainDexOverflowException(
-      boolean hasMainDexList, long numOfMethods, long numOfFields, long maxNumOfEntries) {
-    super(getMessage(hasMainDexList, numOfMethods, numOfFields, maxNumOfEntries));
-  }
-
-  private static String getMessage(
-      boolean hasMainDexList, long numOfMethods, long maxNumOfEntries, long numOfFields) {
-    StringBuilder messageBuilder = new StringBuilder();
-    // General message: Cannot fit.
-    messageBuilder.append("Cannot fit requested classes in ");
-    messageBuilder.append(hasMainDexList ? "the main-" : "a single ");
-    messageBuilder.append("dex file");
-    messageBuilder.append(" (");
-    // Show the numbers of methods and/or fields that exceed the limit.
-    if (numOfMethods > maxNumOfEntries) {
-      messageBuilder.append("# methods: ");
-      messageBuilder.append(numOfMethods);
-      messageBuilder.append(" > ").append(maxNumOfEntries);
-      if (numOfFields > maxNumOfEntries) {
-        messageBuilder.append(" ; ");
-      }
-    }
-    if (numOfFields > maxNumOfEntries) {
-      messageBuilder.append("# fields: ");
-      messageBuilder.append(numOfFields);
-      messageBuilder.append(" > ").append(maxNumOfEntries);
-    }
-    messageBuilder.append(")");
-    return messageBuilder.toString();
-  }
-
-}
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
index ebb7ce6..b09f164 100644
--- a/src/main/java/com/android/tools/r8/utils/Reporter.java
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -3,10 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.BaseCompilerCommand;
 import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8Command;
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.R8Command;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.MainDexOverflow;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
@@ -16,12 +20,18 @@
 public class Reporter implements DiagnosticsHandler {
 
   private final DiagnosticsHandler clientHandler;
+  private final BaseCompilerCommand command;
   private int errorCount = 0;
   private Diagnostic lastError;
   private final Collection<Throwable> suppressedExceptions = new ArrayList<>();
 
   public Reporter(DiagnosticsHandler clientHandler) {
+    this(clientHandler, null);
+  }
+
+  public Reporter(DiagnosticsHandler clientHandler, BaseCompilerCommand command) {
     this.clientHandler = clientHandler;
+    this.command = command;
   }
 
   @Override
@@ -75,6 +85,19 @@
   }
 
   /**
+   * @throws AbortException always.
+   */
+  public RuntimeException fatalError(MainDexOverflow e) {
+    if (command instanceof R8Command) {
+      return fatalError(new StringDiagnostic(e.getMessageForR8()));
+    } else if (command instanceof D8Command) {
+      return fatalError(new StringDiagnostic(e.getMessageForD8()));
+    } else {
+      return fatalError(new StringDiagnostic(e.getMessage()));
+    }
+  }
+
+  /**
    * @throws AbortException if any error was reported.
    */
   public void failIfPendingErrors() {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 6e8ea93..0e99a7c 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -1353,7 +1352,7 @@
   }
 
   public static void writeApplication(DexApplication application, InternalOptions options)
-      throws ExecutionException, DexOverflowException {
+      throws ExecutionException {
     R8.writeApplication(
         Executors.newSingleThreadExecutor(),
         application,
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 89935a7..ead9175 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.ConstString;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -108,8 +107,7 @@
   }
 
   @Test
-  public void manyFilesWithSharedSynthesizedClass()
-      throws ExecutionException, IOException, DexOverflowException {
+  public void manyFilesWithSharedSynthesizedClass() throws ExecutionException, IOException {
 
     // Create classes that all reference enough strings to overflow the index, but are all
     // at different offsets in the strings array. This ensures we trigger multiple rounds of
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 3cab10a..bda9ac5 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -10,7 +10,6 @@
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.smali.SmaliTestBase;
 import com.android.tools.r8.utils.AndroidApp;
@@ -165,7 +164,7 @@
   }
 
   @Test
-  public void lookupFieldWithDefaultInInterface() throws DexOverflowException {
+  public void lookupFieldWithDefaultInInterface() {
     SmaliBuilder builder = new SmaliBuilder();
 
     builder.addInterface("Interface");
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 5afb068..d4ab6c6 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.ToolHelper;
 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;
@@ -37,7 +36,7 @@
       return buildApplication(
           AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build(),
           options);
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -118,8 +117,7 @@
       return iterator;
     }
 
-    private AndroidApp writeDex(DexApplication application, InternalOptions options)
-        throws DexOverflowException {
+    private AndroidApp writeDex(DexApplication application, InternalOptions options) {
       try {
         ToolHelper.writeApplication(application, options);
         options.signalFinishedToConsumers();
@@ -129,7 +127,7 @@
       }
     }
 
-    public String run() throws DexOverflowException, IOException {
+    public String run() throws IOException {
       AppInfo appInfo = new AppInfo(application);
       IRConverter converter = new IRConverter(appInfo, options);
       converter.replaceCodeForTesting(method, code);
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 99ffc11..4214a67 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -14,6 +14,8 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.StringResource;
@@ -22,7 +24,6 @@
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
@@ -52,9 +53,11 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
@@ -62,6 +65,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.MainDexList;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
@@ -100,6 +104,8 @@
   public static TemporaryFolder generatedApplicationsFolder =
       ToolHelper.getTemporaryFolderForTest();
 
+  private List<Diagnostic> errors = new ArrayList<>();
+
   // Generate the test applications in a @BeforeClass method, as they are used by several tests.
   @BeforeClass
   public static void generateTestApplications() throws Throwable {
@@ -177,12 +183,15 @@
     try {
       verifyMainDexContains(TWO_LARGE_CLASSES, getTwoLargeClassesAppPath(), false);
       fail("Expect to fail, for there are too many classes for the main-dex list.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was _not_ used.
-      assertFalse(e.getMessage().contains("single dex file"));
+      assertFalse(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(TWO_LARGE_CLASSES.size() * MAX_METHOD_COUNT)));
+      assertTrue(
+          message.contains(
+              "# methods: " + String.valueOf(TWO_LARGE_CLASSES.size() * MAX_METHOD_COUNT)));
     }
   }
 
@@ -218,12 +227,16 @@
     try {
       verifyMainDexContains(MANY_CLASSES, getManyClassesMultiDexAppPath(), false);
       fail("Expect to fail, for there are too many classes for the main-dex list.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was _not_ used.
-      assertFalse(e.getMessage().contains("single dex file"));
+      assertFalse(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
+      assertTrue(
+          message.contains(
+              "# methods: "
+                  + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
     }
   }
 
@@ -442,15 +455,22 @@
     // Notice that this one fails due to the min API.
     try {
       generateApplication(
-          MANY_CLASSES, AndroidApiLevel.K.getLevel(), false,
-          MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
+          MANY_CLASSES,
+          AndroidApiLevel.K.getLevel(),
+          false,
+          MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS,
+          new TestDiagnosticsHandler());
       fail("Expect to fail, for there are many classes while multidex is not enabled.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was used.
-      assertTrue(e.getMessage().contains("single dex file"));
+      assertTrue(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
+      assertTrue(
+          message.contains(
+              "# methods: "
+                  + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
     }
   }
 
@@ -506,7 +526,7 @@
     }
     Path outDir = temp.newFolder().toPath();
     R8Command.Builder builder =
-        R8Command.builder()
+        R8Command.builder(new TestDiagnosticsHandler())
             .addProgramFiles(app)
             .setMode(
                 minimalMainDex && mainDex.size() > 0
@@ -592,8 +612,20 @@
   private static AndroidApp generateApplication(
       List<String> classes, int minApi, boolean intermediate, int methodCount)
       throws IOException, ExecutionException {
+    return generateApplication(
+        classes, minApi, intermediate, methodCount, new DefaultDiagnosticsHandler());
+  }
+
+  private static AndroidApp generateApplication(
+      List<String> classes,
+      int minApi,
+      boolean intermediate,
+      int methodCount,
+      DiagnosticsHandler diagnosticsHandler)
+      throws IOException, ExecutionException {
     Timing timing = new Timing("MainDexListTests");
-    InternalOptions options = new InternalOptions();
+    InternalOptions options =
+        new InternalOptions(new DexItemFactory(), new Reporter(diagnosticsHandler));
     options.minApiLevel = minApi;
     options.intermediate = intermediate;
     DexItemFactory factory = options.itemFactory;
@@ -763,4 +795,12 @@
       throw new Unreachable();
     }
   }
+
+  private class TestDiagnosticsHandler implements DiagnosticsHandler {
+
+    @Override
+    public void error(Diagnostic error) {
+      errors.add(error);
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
index 2f6eb51..9a6e778 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.smali;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -334,13 +333,11 @@
     return result;
   }
 
-  public byte[] compile()
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+  public byte[] compile() throws IOException, RecognitionException, ExecutionException {
     return Smali.compile(buildSource());
   }
 
-  public AndroidApp build()
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+  public AndroidApp build() throws IOException, RecognitionException, ExecutionException {
     return AndroidApp.builder().addDexProgramData(compile(), Origin.unknown()).build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
index a380834..2297ebd 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
@@ -6,7 +6,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.SmaliWriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -27,7 +26,7 @@
       AndroidApp application =
           AndroidApp.builder().addDexProgramData(Smali.compile(smali), Origin.unknown()).build();
       assertEquals(smali, SmaliWriter.smali(application, new InternalOptions()));
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
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 1d3fd93..6c4b884 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 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.DexClass;
@@ -45,7 +44,7 @@
   protected AndroidApp buildApplication(SmaliBuilder builder) {
     try {
       return AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
-    } catch (IOException | RecognitionException | DexOverflowException | ExecutionException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -56,7 +55,7 @@
           .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE)
           .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
           .build();
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -233,13 +232,11 @@
         processdApplication, DEFAULT_CLASS_NAME, returnType, DEFAULT_METHOD_NAME, parameters);
   }
 
-  public String runArt(AndroidApp application) throws DexOverflowException {
+  public String runArt(AndroidApp application) {
     return runArt(application, DEFAULT_MAIN_CLASS_NAME);
   }
 
-
-  public String runArt(AndroidApp application, String mainClass)
-      throws DexOverflowException {
+  public String runArt(AndroidApp application, String mainClass) {
     try {
       Path out = temp.getRoot().toPath().resolve("run-art-input.zip");
       // TODO(sgjesse): Pass in a unique temp directory for each run.
@@ -258,8 +255,7 @@
     }
   }
 
-  public void runDex2Oat(AndroidApp application)
-      throws DexOverflowException {
+  public void runDex2Oat(AndroidApp application) {
     try {
       Path dexOut = temp.getRoot().toPath().resolve("run-dex2oat-input.zip");
       Path oatFile = temp.getRoot().toPath().resolve("oat-file");
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 30aa7ef..68de752 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
@@ -37,27 +36,27 @@
 public class Smali {
 
   public static byte[] compile(String smaliText)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(smaliText, 15);
   }
 
   public static byte[] compile(String... smaliText)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(Arrays.asList(smaliText), 15);
   }
 
   public static byte[] compile(String smaliText, int apiLevel)
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+      throws IOException, RecognitionException, ExecutionException {
     return compile(ImmutableList.of(smaliText), apiLevel);
   }
 
   public static byte[] compile(List<String> smaliTexts)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(smaliTexts, 15);
   }
 
   public static byte[] compile(List<String> smaliTexts, int apiLevel)
-      throws RecognitionException, IOException, ExecutionException, DexOverflowException {
+      throws RecognitionException, IOException, ExecutionException {
     DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(apiLevel));
 
     for (String smaliText : smaliTexts) {