Better error handling for GenerateMainDexList

Unwrap ExecutionException, and report all errors on the provided
DiagnosticsHandler same way as the D8, R8 and DexSplitter.

Bug: 112679440
Change-Id: I6b803f0e6585c1461eeacfcac1845b363508ef69
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 475d34f..76e7277 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -201,8 +201,7 @@
           .write(executor);
       options.printWarnings();
     } catch (ExecutionException e) {
-      R8.unwrapExecutionException(e);
-      throw new AssertionError(e); // unwrapping method should have thrown
+      throw R8.unwrapExecutionException(e);
     } finally {
       options.signalFinishedToConsumers();
       // Dump timings.
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index a26924e..d7183c8 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -105,8 +105,7 @@
         writer.write(executor);
         options.printWarnings();
       } catch (ExecutionException e) {
-        R8.unwrapExecutionException(e);
-        throw new AssertionError(e); // unwrapping method should have thrown
+        throw R8.unwrapExecutionException(e);
       } finally {
         options.signalFinishedToConsumers();
       }
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 68dc702..163c46f 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -109,8 +109,7 @@
         }
       }
     } catch (ExecutionException e) {
-      R8.unwrapExecutionException(e);
-      throw new AssertionError(e); // unwrapping method should have thrown
+      throw R8.unwrapExecutionException(e);
     } catch (FeatureMappingException e) {
       options.reporter.error(e.getMessage());
     } finally {
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index fe946da..43989ff 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.shaking.TreePruner;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -36,37 +37,41 @@
   }
 
   private List<String> run(AndroidApp app, ExecutorService executor)
-      throws IOException, ExecutionException {
-    DexApplication application =
-        new ApplicationReader(app, options, timing).read(executor).toDirect();
-    AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
-    RootSet mainDexRootSet =
-        new RootSetBuilder(appInfo, application, options.mainDexKeepRules, options).run(executor);
-    Enqueuer enqueuer = new Enqueuer(appInfo, GraphLense.getIdentityLense(), options, true);
-    AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
-    // LiveTypes is the result.
-    Set<DexType> mainDexClasses =
-        new MainDexListBuilder(new HashSet<>(mainDexAppInfo.liveTypes), application).run();
+      throws IOException {
+    try {
+      DexApplication application =
+          new ApplicationReader(app, options, timing).read(executor).toDirect();
+      AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+      RootSet mainDexRootSet =
+          new RootSetBuilder(appInfo, application, options.mainDexKeepRules, options).run(executor);
+      Enqueuer enqueuer = new Enqueuer(appInfo, GraphLense.getIdentityLense(), options, true);
+      AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
+      // LiveTypes is the result.
+      Set<DexType> mainDexClasses =
+          new MainDexListBuilder(new HashSet<>(mainDexAppInfo.liveTypes), application).run();
 
-    List<String> result = mainDexClasses.stream()
-        .map(c -> c.toSourceString().replace('.', '/') + ".class")
-        .sorted()
-        .collect(Collectors.toList());
+      List<String> result = mainDexClasses.stream()
+          .map(c -> c.toSourceString().replace('.', '/') + ".class")
+          .sorted()
+          .collect(Collectors.toList());
 
-    if (options.mainDexListConsumer != null) {
-      options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
+      if (options.mainDexListConsumer != null) {
+        options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
+      }
+
+      // Print -whyareyoukeeping results if any.
+      if (mainDexRootSet.reasonAsked.size() > 0) {
+        // Print reasons on the application after pruning, so that we reflect the actual result.
+        TreePruner pruner = new TreePruner(application, mainDexAppInfo.withLiveness(), options);
+        application = pruner.run();
+        ReasonPrinter reasonPrinter = enqueuer.getReasonPrinter(mainDexRootSet.reasonAsked);
+        reasonPrinter.run(application);
+      }
+
+      return result;
+    } catch (ExecutionException e) {
+      throw R8.unwrapExecutionException(e);
     }
-
-    // Print -whyareyoukeeping results if any.
-    if (mainDexRootSet.reasonAsked.size() > 0) {
-      // Print reasons on the application after pruning, so that we reflect the actual result.
-      TreePruner pruner = new TreePruner(application, mainDexAppInfo.withLiveness(), options);
-      application = pruner.run();
-      ReasonPrinter reasonPrinter = enqueuer.getReasonPrinter(mainDexRootSet.reasonAsked);
-      reasonPrinter.run(application);
-    }
-
-    return result;
   }
 
   /**
@@ -82,7 +87,7 @@
    * @return classes to keep in the primary dex file.
    */
   public static List<String> run(GenerateMainDexListCommand command)
-      throws IOException, ExecutionException {
+      throws CompilationFailedException {
     ExecutorService executorService = ThreadUtils.getExecutorService(command.getInternalOptions());
     try {
       return run(command, executorService);
@@ -105,14 +110,28 @@
    * @return classes to keep in the primary dex file.
    */
   public static List<String> run(GenerateMainDexListCommand command, ExecutorService executor)
-      throws IOException, ExecutionException {
+      throws CompilationFailedException {
     AndroidApp app = command.getInputApp();
     InternalOptions options = command.getInternalOptions();
-    return new GenerateMainDexList(options).run(app, executor);
+    ResultBox result = new ResultBox();
+
+    ExceptionUtils.withMainDexListHandler(
+        command.getReporter(),
+        () -> {
+          try {
+            result.content = new GenerateMainDexList(options).run(app, executor);
+          } finally {
+            executor.shutdown();
+          }
+        });
+    return result.content;
   }
 
-  public static void main(String[] args)
-      throws IOException, ExecutionException, CompilationFailedException {
+  private static class ResultBox {
+    List<String> content;
+  }
+
+  public static void main(String[] args) throws CompilationFailedException {
     GenerateMainDexListCommand.Builder builder = GenerateMainDexListCommand.parse(args);
     GenerateMainDexListCommand command = builder.build();
     if (command.isPrintHelp()) {
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index f9e0ebf..2d06da8 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -150,6 +150,10 @@
     return mainDexListConsumer;
   }
 
+  Reporter getReporter() {
+    return reporter;
+  }
+
   private static void parse(String[] args, GenerateMainDexListCommand.Builder builder) {
     for (int i = 0; i < args.length; i++) {
       String arg = args[i].trim();
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 4410d8a..78cb75a 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -92,8 +92,7 @@
           System.out,
           type -> descriptors.contains(type.toDescriptorString()));
     } catch (ExecutionException e) {
-      R8.unwrapExecutionException(e);
-      throw new AssertionError(e); // unwrapping method should have thrown
+      throw R8.unwrapExecutionException(e);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9738c8d..1bfe039 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -525,8 +525,7 @@
 
       options.printWarnings();
     } catch (ExecutionException e) {
-      unwrapExecutionException(e);
-      throw new AssertionError(e); // unwrapping method should have thrown
+      throw unwrapExecutionException(e);
     } finally {
       options.signalFinishedToConsumers();
       // Dump timings.
@@ -545,7 +544,7 @@
     }
   }
 
-  static void unwrapExecutionException(ExecutionException executionException) {
+  static RuntimeException unwrapExecutionException(ExecutionException executionException) {
     Throwable cause = executionException.getCause();
     if (cause instanceof CompilationError) {
       // add original exception as suppressed exception to provide the original stack trace
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index fe8adfa..1ff2664 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -48,6 +48,11 @@
     withCompilationHandler(reporter, action);
   }
 
+  public static void withMainDexListHandler(
+      Reporter reporter, CompileAction action) throws CompilationFailedException {
+    withCompilationHandler(reporter, action);
+  }
+
   public static void withCompilationHandler(Reporter reporter, CompileAction action)
       throws CompilationFailedException {
     try {