Merge "Add a public ArchiveProgramResourceProvider utility to the API."
diff --git a/src/main/java/com/android/tools/r8/DexRoundTrip.java b/src/main/java/com/android/tools/r8/DexRoundTrip.java
new file mode 100644
index 0000000..01af3b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/DexRoundTrip.java
@@ -0,0 +1,58 @@
+// 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;
+
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class DexRoundTrip {
+
+  public static AndroidApp process(Collection<ProgramResource> dexResources)
+      throws CompilationFailedException {
+    InternalOptions options = new InternalOptions();
+    options.useTreeShaking = false;
+    options.skipMinification = true;
+    options.ignoreMissingClasses = true;
+    options.enableDesugaring = false;
+    AndroidAppConsumers consumer = new AndroidAppConsumers(options);
+    AndroidApp.Builder builder = AndroidApp.builder();
+    ExceptionUtils.withR8CompilationHandler(
+        options.reporter,
+        () -> {
+          for (ProgramResource resource : dexResources) {
+            try (InputStream stream = resource.getByteStream()) {
+              builder.addDexProgramData(ByteStreams.toByteArray(stream), resource.getOrigin());
+            }
+          }
+          R8.runForTesting(builder.build(), options);
+        });
+    return consumer.build();
+  }
+
+  public static void main(String[] args) throws CompilationFailedException, IOException {
+    List<ProgramResource> resources = new ArrayList<>(args.length);
+    for (String arg : args) {
+      Path file = Paths.get(arg);
+      if (!FileUtils.isDexFile(file)) {
+        throw new IllegalArgumentException(
+            "Only DEX files are supported as inputs. Invalid file: " + file);
+      }
+      resources.add(ProgramResource.fromFile(Kind.DEX, file));
+    }
+    AndroidApp result = process(resources);
+    process(result.getDexProgramResources());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
index 3b8dd04..e51e4d4 100644
--- a/src/main/java/com/android/tools/r8/Disassemble.java
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -25,20 +25,21 @@
   public static class DisassembleCommand extends BaseCommand {
 
     private final Path outputPath;
+    private final StringResource proguardMap;
 
-    public static class Builder
-        extends BaseCommand.Builder<DisassembleCommand, DisassembleCommand.Builder> {
+    public static class Builder extends BaseCommand.Builder<DisassembleCommand, Builder> {
 
       private Path outputPath = null;
+      private Path proguardMapFile = null;
       private boolean useSmali = false;
 
       @Override
-      DisassembleCommand.Builder self() {
+      Builder self() {
         return this;
       }
 
-      public DisassembleCommand.Builder setProguardMapFile(Path path) {
-        guard(() -> getAppBuilder().setProguardMapFile(path));
+      public Builder setProguardMapFile(Path path) {
+        proguardMapFile = path;
         return this;
       }
 
@@ -46,12 +47,12 @@
         return outputPath;
       }
 
-      public DisassembleCommand.Builder setOutputPath(Path outputPath) {
+      public Builder setOutputPath(Path outputPath) {
         this.outputPath = outputPath;
         return this;
       }
 
-      public DisassembleCommand.Builder setUseSmali(boolean useSmali) {
+      public Builder setUseSmali(boolean useSmali) {
         this.useSmali = useSmali;
         return this;
       }
@@ -62,10 +63,11 @@
         if (isPrintHelp() || isPrintVersion()) {
           return new DisassembleCommand(isPrintHelp(), isPrintVersion());
         }
-
-        DisassembleCommand command = new DisassembleCommand(getAppBuilder().build(),
-            getOutputPath(), useSmali);
-        return command;
+        return new DisassembleCommand(
+            getAppBuilder().build(),
+            getOutputPath(),
+            proguardMapFile == null ? null : StringResource.fromFile(proguardMapFile),
+            useSmali);
       }
     }
 
@@ -82,17 +84,17 @@
 
     private final boolean useSmali;
 
-    public static DisassembleCommand.Builder builder() {
-      return new DisassembleCommand.Builder();
+    public static Builder builder() {
+      return new Builder();
     }
 
-    public static DisassembleCommand.Builder parse(String[] args) {
-      DisassembleCommand.Builder builder = builder();
+    public static Builder parse(String[] args) {
+      Builder builder = builder();
       parse(args, builder);
       return builder;
     }
 
-    private static void parse(String[] args, DisassembleCommand.Builder builder) {
+    private static void parse(String[] args, Builder builder) {
       for (int i = 0; i < args.length; i++) {
         String arg = args[i].trim();
         if (arg.length() == 0) {
@@ -118,16 +120,19 @@
       }
     }
 
-    private DisassembleCommand(AndroidApp inputApp, Path outputPath, boolean useSmali) {
+    private DisassembleCommand(
+        AndroidApp inputApp, Path outputPath, StringResource proguardMap, boolean useSmali) {
       super(inputApp);
       this.outputPath = outputPath;
+      this.proguardMap = proguardMap;
       this.useSmali = useSmali;
     }
 
     private DisassembleCommand(boolean printHelp, boolean printVersion) {
       super(printHelp, printVersion);
-      this.outputPath = null;
-      this.useSmali = false;
+      outputPath = null;
+      proguardMap = null;
+      useSmali = false;
     }
 
     public Path getOutputPath() {
@@ -168,7 +173,8 @@
     ExecutorService executor = ThreadUtils.getExecutorService(options);
     Timing timing = new Timing("disassemble");
     try {
-      DexApplication application = new ApplicationReader(app, options, timing).read(executor);
+      DexApplication application =
+          new ApplicationReader(app, options, timing).read(command.proguardMap, executor);
       DexByteCodeWriter writer = command.useSmali()
           ? new SmaliWriter(application, options)
           : new AssemblyWriter(application, options);
diff --git a/src/main/java/com/android/tools/r8/PrintClassList.java b/src/main/java/com/android/tools/r8/PrintClassList.java
index 69b2781..ea43a00 100644
--- a/src/main/java/com/android/tools/r8/PrintClassList.java
+++ b/src/main/java/com/android/tools/r8/PrintClassList.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.List;
@@ -32,8 +33,9 @@
   public static void main(String[] args) throws IOException, ExecutionException {
     List<String> dexFiles = Arrays.asList(args);
     Builder builder = AndroidApp.builder();
+    Path proguardMapFile = null;
     if (args[0].endsWith("map")) {
-      builder.setProguardMapFile(Paths.get(args[0]));
+      proguardMapFile = Paths.get(args[0]);
       dexFiles = dexFiles.subList(1, dexFiles.size());
     }
     builder.addProgramFiles(ListUtils.map(dexFiles, Paths::get));
@@ -41,7 +43,10 @@
     ExecutorService executorService = Executors.newCachedThreadPool();
     DexApplication application =
         new ApplicationReader(builder.build(), new InternalOptions(), new Timing("PrintClassList"))
-            .read(executorService);
+            .read(
+                proguardMapFile == null ? null : StringResource.fromFile(proguardMapFile),
+                executorService
+            );
     ClassNameMapper map = application.getProguardMap();
     for (DexProgramClass clazz : application.classes()) {
       System.out.print(maybeDeobfuscateType(map, clazz.type));
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 2e12cb1..304dde3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -154,10 +154,6 @@
   private void run(AndroidApp inputApp, ExecutorService executorService)
       throws IOException, CompilationException {
     assert options.programConsumer != null;
-
-    // Read Proguard-map only with the "applymapping" feature.
-    assert inputApp.getProguardMap() == null;
-
     if (options.quiet) {
       System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
     }
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index b6edebe..cae894d 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -42,7 +42,7 @@
     private Optional<Boolean> discardedChecker = Optional.empty();
     private Optional<Boolean> minification = Optional.empty();
     private boolean forceProguardCompatibility = false;
-    private Path proguardMapOutput = null;
+    private StringConsumer proguardMapConsumer = null;
     protected Path proguardCompatibilityRulesOutput = null;
 
     private StringConsumer mainDexListConsumer = null;
@@ -124,22 +124,37 @@
       return self();
     }
 
-    /**
-     * Add proguard configuration for automatic main dex list calculation.
-     */
+    /** Add proguard rules for automatic main-dex-list calculation. */
     public Builder addMainDexRules(List<String> lines, Origin origin) {
       guard(() -> mainDexRules.add(
           new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin)));
       return self();
     }
 
+    /**
+     * Set an output destination to which main-dex-list content should be written.
+     *
+     * <p>This is a short-hand for setting a {@link StringConsumer.FileConsumer} using {@link
+     * #setMainDexListConsumer}. Note that any subsequent call to this method or {@link
+     * #setMainDexListConsumer} will override the previous setting.
+     *
+     * @param mainDexListOutputPath File-system path to write output at.
+     */
     public Builder setMainDexListOutputPath(Path mainDexListOutputPath) {
       mainDexListConsumer = new StringConsumer.FileConsumer(mainDexListOutputPath);
       return self();
     }
 
-    public Builder setMainDexListConsumer(StringConsumer consumer) {
-      mainDexListConsumer = consumer;
+    /**
+     * Set a consumer for receiving the main-dex-list content.
+     *
+     * <p>Note that any subsequent call to this method or {@link #setMainDexListOutputPath} will
+     * override the previous setting.
+     *
+     * @param mainDexListConsumer Consumer to receive the content once produced.
+     */
+    public Builder setMainDexListConsumer(StringConsumer mainDexListConsumer) {
+      this.mainDexListConsumer = mainDexListConsumer;
       return self();
     }
 
@@ -191,15 +206,30 @@
     }
 
     /**
-     * Set a proguard mapping file resource.
+     * Set an output destination to which proguard-map content should be written.
+     *
+     * <p>This is a short-hand for setting a {@link StringConsumer.FileConsumer} using {@link
+     * #setProguardMapConsumer}. Note that any subsequent call to this method or {@link
+     * #setProguardMapConsumer} will override the previous setting.
+     *
+     * @param proguardMapOutput File-system path to write output at.
      */
-    public Builder setProguardMapFile(Path path) {
-      guard(() -> getAppBuilder().setProguardMapFile(path));
+    public Builder setProguardMapOutput(Path proguardMapOutput) {
+      assert proguardMapOutput != null;
+      this.proguardMapConsumer = new StringConsumer.FileConsumer(proguardMapOutput);
       return self();
     }
 
-    public Builder setProguardMapOutput(Path path) {
-      this.proguardMapOutput = path;
+    /**
+     * Set a consumer for receiving the proguard-map content.
+     *
+     * <p>Note that any subsequent call to this method or {@link #setProguardMapOutput} will
+     * override the previous setting.
+     *
+     * @param proguardMapConsumer Consumer to receive the content once produced.
+     */
+    public Builder setProguardMapConsumer(StringConsumer proguardMapConsumer) {
+      this.proguardMapConsumer = proguardMapConsumer;
       return self();
     }
 
@@ -284,9 +314,6 @@
       boolean useDiscardedChecker = discardedChecker.orElse(true);
       boolean useMinification = minification.orElse(configuration.isObfuscating());
 
-      StringConsumer proguardMapConsumer =
-          proguardMapOutput != null ? new StringConsumer.FileConsumer(proguardMapOutput) : null;
-
       assert getProgramConsumer() != null;
 
       R8Command command =
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 9f13ffc..64dc47d 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -62,9 +62,13 @@
   }
 
   public DexApplication read() throws IOException, ExecutionException {
+    return read((StringResource) null);
+  }
+
+  public DexApplication read(StringResource proguardMap) throws IOException, ExecutionException {
     ExecutorService executor = Executors.newSingleThreadExecutor();
     try {
-      return read(executor);
+      return read(proguardMap, executor);
     } finally {
       executor.shutdown();
     }
@@ -72,6 +76,11 @@
 
   public final DexApplication read(ExecutorService executorService)
       throws IOException, ExecutionException {
+    return read(null, executorService);
+  }
+
+  public final DexApplication read(StringResource proguardMap, ExecutorService executorService)
+      throws IOException, ExecutionException {
     timing.begin("DexApplication.read");
     final LazyLoadedDexApplication.Builder builder = DexApplication.builder(itemFactory, timing);
     try {
@@ -83,7 +92,7 @@
       // (b) some of the class file resources don't provide information
       //     about class descriptor.
       // TODO: try and preload less classes.
-      readProguardMap(builder, executorService, futures);
+      readProguardMap(proguardMap, builder, executorService, futures);
       readMainDexList(builder, executorService, futures);
       ClassReader classReader = new ClassReader(executorService, futures);
       classReader.readSources();
@@ -110,20 +119,25 @@
     return computedMinApiLevel;
   }
 
-  private void readProguardMap(DexApplication.Builder<?> builder, ExecutorService executorService,
+  private void readProguardMap(
+      StringResource map,
+      DexApplication.Builder<?> builder,
+      ExecutorService executorService,
       List<Future<?>> futures) {
     // Read the Proguard mapping file in parallel with DexCode and DexProgramClass items.
-    StringResource map = inputApp.getProguardMap();
-    if (map != null) {
-      futures.add(executorService.submit(() -> {
-        try {
-          String content = map.getString();
-          builder.setProguardMap(ClassNameMapper.mapperFromString(content));
-        } catch (IOException | ResourceException e) {
-          throw new CompilationError("Failure to read proguard map file", e, map.getOrigin());
-        }
-      }));
+    if (map == null) {
+      return;
     }
+    futures.add(
+        executorService.submit(
+            () -> {
+              try {
+                String content = map.getString();
+                builder.setProguardMap(ClassNameMapper.mapperFromString(content));
+              } catch (IOException | ResourceException e) {
+                throw new CompilationError("Failure to read proguard map file", e, map.getOrigin());
+              }
+            }));
   }
 
   private void readMainDexList(DexApplication.Builder<?> builder, ExecutorService executorService,
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 3e685a2..34a3e79 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -355,7 +355,7 @@
     return filled;
   }
 
-  void setFilledForTesting() {
+  public void setFilledForTesting() {
     filled = true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
index 511293f..cea2b0b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
@@ -104,7 +104,12 @@
 
           @Override
           public boolean hasNext() {
-            return dominatedBy(sorted[current], dominator);
+            boolean found = false;
+            while (current < sorted.length
+                && !(found = dominatedBy(sorted[current], dominator))) {
+              current++;
+            }
+            return found && current < sorted.length;
           }
 
           @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 5685d22..64c4236 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -166,8 +166,6 @@
       }
     }
 
-
-
     if (!needed) {
       blocksToRemove.add(block);
       for (BasicBlock pred : block.getPredecessors()) {
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 df3801b..71e1516 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
@@ -575,8 +575,8 @@
     DominatorTree dom = new DominatorTree(code);
     for (BasicBlock current : dom.dominatedBlocks(caseBlock)) {
       InstructionIterator it = current.iterator();
-      Switch switchInstr;
-      if ((switchInstr = (Switch) it.nextUntil(Instruction::isSwitch)) != null) {
+      Switch switchInstr = (Switch) it.nextUntil(Instruction::isSwitch);
+      if (switchInstr != null) {
         int nextBlock = code.getHighestBlockNumber() + 1;
         IntList liveKeys = new IntArrayList(switchInstr.numberOfKeys());
         List<BasicBlock> liveBlocks = new ArrayList<>(switchInstr.numberOfKeys());
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index b279dff..9e6d79c 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -49,7 +49,7 @@
   private final ImmutableList<ClassFileResourceProvider> classpathResourceProviders;
   private final ImmutableList<ClassFileResourceProvider> libraryResourceProviders;
 
-  private final StringResource proguardMap;
+  private final StringResource proguardMapOutputData;
   private final List<StringResource> mainDexListResources;
   private final List<String> mainDexClasses;
 
@@ -59,14 +59,14 @@
       ImmutableMap<Resource, String> programResourcesMainDescriptor,
       ImmutableList<ClassFileResourceProvider> classpathResourceProviders,
       ImmutableList<ClassFileResourceProvider> libraryResourceProviders,
-      StringResource proguardMap,
+      StringResource proguardMapOutputData,
       List<StringResource> mainDexListResources,
       List<String> mainDexClasses) {
     this.programResourceProviders = programResourceProviders;
     this.programResourcesMainDescriptor = programResourcesMainDescriptor;
     this.classpathResourceProviders = classpathResourceProviders;
     this.libraryResourceProviders = libraryResourceProviders;
-    this.proguardMap = proguardMap;
+    this.proguardMapOutputData = proguardMapOutputData;
     this.mainDexListResources = mainDexListResources;
     this.mainDexClasses = mainDexClasses;
   }
@@ -140,10 +140,13 @@
   }
 
   /**
-   * Get the input stream of the proguard-map resource if it exists.
+   * Get the proguard-map associated with an output "app" if it exists.
+   *
+   * <p>Note: this should never be used as the input to a compilation. See proguards ApplyMapping
+   * for such use cases.
    */
-  public StringResource getProguardMap() {
-    return proguardMap;
+  public StringResource getProguardMapOutputData() {
+    return proguardMapOutputData;
   }
 
   /**
@@ -229,11 +232,13 @@
     private final Map<ProgramResource, String> programResourcesMainDescriptor = new HashMap<>();
     private final List<ClassFileResourceProvider> classpathResourceProviders = new ArrayList<>();
     private final List<ClassFileResourceProvider> libraryResourceProviders = new ArrayList<>();
-    private StringResource proguardMap;
     private List<StringResource> mainDexListResources = new ArrayList<>();
     private List<String> mainDexListClasses = new ArrayList<>();
     private boolean ignoreDexInArchive = false;
 
+    // Proguard map data is output only data. This should never be used as input to a compilation.
+    private StringResource proguardMapOutputData;
+
     // See AndroidApp::builder().
     private Builder() {
     }
@@ -243,7 +248,6 @@
       programResourceProviders.addAll(app.programResourceProviders);
       classpathResourceProviders.addAll(app.classpathResourceProviders);
       libraryResourceProviders.addAll(app.libraryResourceProviders);
-      proguardMap = app.proguardMap;
       mainDexListResources = app.mainDexListResources;
       mainDexListClasses = app.mainDexClasses;
     }
@@ -412,18 +416,13 @@
     }
 
     /**
-     * Set proguard-map file.
+     * Set proguard-map output data.
+     *
+     * <p>Note: this should not be used as inputs to compilation!
      */
-    public Builder setProguardMapFile(Path file) {
-      proguardMap = file == null ? null : StringResource.fromFile(file);
-      return this;
-    }
-
-    /**
-     * Set proguard-map data.
-     */
-    public Builder setProguardMapData(String content) {
-      proguardMap = content == null ? null : StringResource.fromString(content, Origin.unknown());
+    public Builder setProguardMapOutputData(String content) {
+      proguardMapOutputData =
+          content == null ? null : StringResource.fromString(content, Origin.unknown());
       return this;
     }
 
@@ -498,7 +497,7 @@
           ImmutableMap.copyOf(programResourcesMainDescriptor),
           ImmutableList.copyOf(classpathResourceProviders),
           ImmutableList.copyOf(libraryResourceProviders),
-          proguardMap,
+          proguardMapOutputData,
           mainDexListResources,
           mainDexListClasses);
     }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
index c83b61c..afd97ae 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -65,7 +65,7 @@
             @Override
             public void accept(String string, DiagnosticsHandler handler) {
               super.accept(string, handler);
-              builder.setProguardMapData(string);
+              builder.setProguardMapOutputData(string);
             }
           };
     }
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 cc5f0f9..d5a9274 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.errors.CompilationError;
 import java.io.IOException;
@@ -31,7 +32,7 @@
   }
 
   public interface CompileAction {
-    void run() throws IOException, CompilationException, CompilationError;
+    void run() throws IOException, CompilationException, CompilationError, ResourceException;
   }
 
   private enum Compiler {
@@ -64,6 +65,11 @@
             e);
       } catch (CompilationError e) {
         throw reporter.fatalError(e);
+      } catch (ResourceException e) {
+        throw reporter.fatalError(
+            e.getCause() instanceof IOException
+                ? new IOExceptionDiagnostic((IOException) e.getCause(), e.getOrigin())
+                : new StringDiagnostic(e.getMessage(), e.getOrigin()));
       }
       reporter.failIfPendingErrors();
     } catch (AbortException e) {
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index a5c1b69..33fab08 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -836,6 +836,8 @@
           // Contains an illegal invoke that R8 will fail to compile.
           .put("594-invoke-super", TestCondition.match(
               TestCondition.R8_COMPILER))
+          .put("974-verify-interface-super", TestCondition.match(
+              TestCondition.R8_COMPILER))
           .build();
 
   // Tests that are invalid dex files and on which R8/D8 fails and that is OK.
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d4e699f..a6ab7e9 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1112,10 +1112,6 @@
             return FileVisitResult.CONTINUE;
           }
         });
-    File mapFile = new File(directory.toFile(), DEFAULT_PROGUARD_MAP_FILE);
-    if (mapFile.exists()) {
-      builder.setProguardMapFile(mapFile.toPath());
-    }
     return builder;
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
index 2ba52d6..182d3b0 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
@@ -27,16 +27,18 @@
     Path outdir = temp.newFolder().toPath();
     Path outjar = outdir.resolve("r8_compiled.jar");
     Path proguardMapPath = writeProguardMap ? outdir.resolve("proguard.map") : null;
-    ToolHelper.runR8(
+    R8Command.Builder builder =
         R8Command.builder()
             .addProgramFiles(DEBUGGEE_JAR)
             .setMinApiLevel(minSdk)
             .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
             .setMode(CompilationMode.RELEASE)
-            .setOutput(outjar, OutputMode.DexIndexed)
-            .setProguardMapOutput(proguardMapPath)
-            .build(),
-        options -> options.lineNumberOptimization = lineNumberOptimization);
+            .setOutput(outjar, OutputMode.DexIndexed);
+    if (proguardMapPath != null) {
+      builder.setProguardMapOutput(proguardMapPath);
+    }
+    ToolHelper.runR8(
+        builder.build(), options -> options.lineNumberOptimization = lineNumberOptimization);
     DebugTestConfig config = new DexDebugTestConfig(outjar);
     config.setProguardMap(proguardMapPath);
     return config;
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
index 1d6d528..5243073 100644
--- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -35,15 +35,18 @@
     Path outdir = temp.newFolder().toPath();
     Path outjar = outdir.resolve("r8_compiled.jar");
     Path proguardMapPath = writeProguardMap ? outdir.resolve("proguard.map") : null;
-    ToolHelper.runR8(
+    R8Command.Builder builder =
         R8Command.builder()
             .addProgramFiles(DEBUGGEE_JAR)
             .setMinApiLevel(minSdk)
             .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
             .setMode(dontOptimizeByEnablingDebug ? CompilationMode.DEBUG : CompilationMode.RELEASE)
-            .setOutput(outjar, OutputMode.DexIndexed)
-            .setProguardMapOutput(proguardMapPath)
-            .build(),
+            .setOutput(outjar, OutputMode.DexIndexed);
+    if (proguardMapPath != null) {
+      builder.setProguardMapOutput(proguardMapPath);
+    }
+    ToolHelper.runR8(
+        builder.build(),
         options -> {
           if (!dontOptimizeByEnablingDebug) {
             options.lineNumberOptimization = lineNumberOptimization;
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
index 9101a6e..bff846b 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
@@ -15,10 +15,7 @@
   @Test
   public void fixedPoint() throws Exception {
     // First compilation.
-    AndroidApp app =
-        ToolHelper.builderFromProgramDirectory(Paths.get(GMSCORE_V7_DIR))
-            .setProguardMapFile(null)
-            .build();
+    AndroidApp app = ToolHelper.builderFromProgramDirectory(Paths.get(GMSCORE_V7_DIR)).build();
 
     AndroidApp app1 =
         ToolHelper.runR8(
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index 957ee0c..b350d5a 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.StringResource;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -19,6 +20,8 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -36,11 +39,19 @@
 
   @Before
   public void readGMSCore() throws IOException, ExecutionException {
-    app = ToolHelper.builderFromProgramDirectory(Paths.get(APP_DIR)).build();
+    Path directory = Paths.get(APP_DIR);
+    app = ToolHelper.builderFromProgramDirectory(directory).build();
+    Path mapFile = directory.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE);
+    StringResource proguardMap = null;
+    if (Files.exists(mapFile)) {
+      proguardMap = StringResource.fromFile(mapFile);
+    }
     ExecutorService executorService = Executors.newSingleThreadExecutor();
     Timing timing = new Timing("ReadGMSCore");
-    program = new ApplicationReader(app, new InternalOptions(), timing)
-        .read(executorService).toDirect();
+    program =
+        new ApplicationReader(app, new InternalOptions(), timing)
+            .read(proguardMap, executorService)
+            .toDirect();
     appInfo = new AppInfoWithSubtyping(program);
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
new file mode 100644
index 0000000..07e38e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -0,0 +1,65 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.code.ValueType;
+import com.google.common.collect.ImmutableList;
+import java.util.LinkedList;
+import org.junit.Test;
+
+public class TrivialGotoEliminationTest {
+  @Test
+  public void trivialGotoInEntryBlock() {
+    // Setup silly block structure:
+    //
+    // block0:
+    //   goto block2
+    // block1:
+    //   v0 = const-number 0
+    //   throw v0
+    // block2:
+    //   return
+    BasicBlock block2 = new BasicBlock();
+    block2.setNumber(2);
+    BasicBlock block0 = BasicBlock.createGotoBlock(0, block2);
+    block0.setFilledForTesting();
+    block2.getPredecessors().add(block0);
+    Instruction ret = new Return();
+    ret.setPosition(Position.none());
+    block2.add(ret);
+    block2.setFilledForTesting();
+    BasicBlock block1 = new BasicBlock();
+    block1.setNumber(1);
+    Value value = new Value(0, ValueType.INT, null);
+    Instruction number = new ConstNumber(value, 0);
+    number.setPosition(Position.none());
+    block1.add(number);
+    Instruction throwing = new Throw(value);
+    throwing.setPosition(Position.none());
+    block1.add(throwing);
+    block1.setFilledForTesting();
+    LinkedList<BasicBlock> blocks = new LinkedList();
+    blocks.add(block0);
+    blocks.add(block1);
+    blocks.add(block2);
+    // Check that the goto in block0 remains. There was a bug in the trivial goto elimination
+    // that ended up removing that goto changing the code to start with the unreachable
+    // throw.
+    IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+    CodeRewriter.collapsTrivialGotos(null, code);
+    assert code.blocks.get(0).isTrivialGoto();
+    assert blocks.contains(block0);
+    assert blocks.contains(block1);
+    assert blocks.contains(block2);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index 6e48cfb..070b6da 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -10,7 +10,9 @@
 import static org.junit.Assert.fail;
 
 import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.R8Command;
+import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -64,17 +66,28 @@
     Path flagForObfuscation =
         Paths.get(ToolHelper.EXAMPLES_DIR, "naming044", "keep-rules-005.txt");
     Path proguardMap = out.resolve(MAPPING);
-    AndroidApp obfuscatedApp = runR8(
+    class ProguardMapConsumer implements StringConsumer {
+      String map;
+
+      @Override
+      public void accept(String string, DiagnosticsHandler handler) {
+        map = string;
+      }
+    }
+    ProguardMapConsumer mapConsumer = new ProguardMapConsumer();
+    runR8(
         getCommandForApps(out, flagForObfuscation, NAMING044_JAR)
-            .addProguardConfigurationConsumer(c -> {
-              c.setPrintMapping(true);
-              c.setPrintMappingFile(proguardMap);
-            }).build());
+            .setProguardMapConsumer(mapConsumer)
+            .addProguardConfigurationConsumer(
+                c -> {
+                  c.setPrintMapping(true);
+                  c.setPrintMappingFile(proguardMap);
+                })
+            .build());
 
     // Obviously, dumped map and resource inside the app should be *identical*.
     ClassNameMapper mapperFromFile = ClassNameMapper.mapperFromFile(proguardMap);
-    ClassNameMapper mapperFromApp =
-        ClassNameMapper.mapperFromString(obfuscatedApp.getProguardMap().getString());
+    ClassNameMapper mapperFromApp = ClassNameMapper.mapperFromString(mapConsumer.map);
     assertEquals(mapperFromFile, mapperFromApp);
 
     Path instrOut = temp.newFolder("instr").toPath();
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 2c7c683..56ba5a5 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -75,8 +75,6 @@
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -108,7 +106,6 @@
 
   public DexInspector(List<Path> files, String mappingFile)
       throws IOException, ExecutionException {
-    ExecutorService executor = Executors.newSingleThreadExecutor();
     if (mappingFile != null) {
       this.mapping = ClassNameMapper.mapperFromFile(Paths.get(mappingFile));
       originalToObfuscatedMapping = this.mapping.getObfuscatedToOriginalMapping().inverse();
@@ -120,12 +117,13 @@
     InternalOptions options = new InternalOptions();
     dexItemFactory = options.itemFactory;
     AndroidApp input = AndroidApp.builder().addProgramFiles(files).build();
-    application = new ApplicationReader(input, options, timing).read(executor);
-    executor.shutdown();
+    application = new ApplicationReader(input, options, timing).read();
   }
 
   public DexInspector(AndroidApp app) throws IOException, ExecutionException {
-    this(new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector")).read());
+    this(
+        new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector"))
+            .read(app.getProguardMapOutputData()));
   }
 
   public DexInspector(DexApplication application) {