Merge "Resolve single vs object in IRBuilder."
diff --git a/build.gradle b/build.gradle
index cd831cc..f379bbf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -111,7 +111,12 @@
     }
     apiUsageSample {
         java {
-            srcDirs = ['src/test/apiUsageSample']
+            srcDirs = ['src/test/apiUsageSample', 'src/main/java']
+            include 'com/android/tools/apiusagesample/*.java'
+            include 'com/android/tools/r8/BaseCompilerCommandParser.java'
+            include 'com/android/tools/r8/D8CommandParser.java'
+            include 'com/android/tools/r8/R8CommandParser.java'
+            include 'com/android/tools/r8/utils/FlagFile.java'
         }
     }
     debugTestResources {
diff --git a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index e17acd0..b3d59fb 100644
--- a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -35,6 +35,7 @@
  * <p>The descriptor index is built eagerly upon creating the provider and subsequent requests for
  * resources in the descriptor set will then force the read of zip entry contents.
  */
+@Keep
 public class ArchiveClassFileProvider implements ClassFileResourceProvider, Closeable {
   private final Origin origin;
   private final ZipFile zipFile;
diff --git a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
index b3a4bfb..baac9bc 100644
--- a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
@@ -29,8 +29,10 @@
 import java.util.zip.ZipFile;
 
 /** Provider for archives of program resources. */
+@KeepForSubclassing
 public class ArchiveProgramResourceProvider implements ProgramResourceProvider {
 
+  @KeepForSubclassing
   public interface ZipFileSupplier {
     ZipFile open() throws IOException;
   }
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index fbc6ca3..8828248 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -28,6 +28,7 @@
  * <p>For concrete builders, see for example {@link D8Command.Builder} and {@link
  * R8Command.Builder}.
  */
+@Keep
 public abstract class BaseCommand {
 
   private final boolean printHelp;
@@ -101,6 +102,7 @@
    * @param <B> Concrete builder extending this base, e.g., {@link R8Command.Builder} or {@link
    *     D8Command.Builder}.
    */
+  @Keep
   public abstract static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
 
     private final Reporter reporter;
@@ -304,6 +306,20 @@
       return self();
     }
 
+    /** Signal an error. */
+    public void error(Diagnostic diagnostic) {
+      reporter.error(diagnostic);
+    }
+
+    /**
+     * Signal an error and throw {@link AbortException}.
+     *
+     * @throws AbortException always.
+     */
+    public RuntimeException fatalError(Diagnostic diagnostic) {
+      return reporter.fatalError(diagnostic);
+    }
+
     // Internal helper for compat tools to make them ignore DEX code in input archives.
     void setIgnoreDexInArchive(boolean value) {
       guard(() -> app.setIgnoreDexInArchive(value));
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 799d834..5980a0c 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -4,13 +4,11 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
@@ -23,6 +21,7 @@
  * <p>For concrete builders, see for example {@link D8Command.Builder} and {@link
  * R8Command.Builder}.
  */
+@Keep
 public abstract class BaseCompilerCommand extends BaseCommand {
 
   private final CompilationMode mode;
@@ -106,6 +105,7 @@
    * @param <B> Concrete builder extending this base, e.g., {@link R8Command.Builder} or {@link
    *     D8Command.Builder}.
    */
+  @Keep
   public abstract static class Builder<C extends BaseCompilerCommand, B extends Builder<C, B>>
       extends BaseCommand.Builder<C, B> {
 
@@ -341,31 +341,4 @@
     }
   }
 
-  static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
-  boolean parseMinApi(
-      BaseCompilerCommand.Builder<C, B> builder,
-      String minApiString,
-      boolean hasDefinedApiLevel,
-      Origin origin) {
-    if (hasDefinedApiLevel) {
-      builder.getReporter().error(new StringDiagnostic(
-          "Cannot set multiple --min-api options", origin));
-      return false;
-    }
-    int minApi;
-    try {
-      minApi = Integer.valueOf(minApiString);
-    } catch (NumberFormatException e) {
-      builder.getReporter().error(new StringDiagnostic(
-          "Invalid argument to --min-api: " + minApiString, origin));
-      return false;
-    }
-    if (minApi < 1) {
-      builder.getReporter().error(new StringDiagnostic(
-          "Invalid argument to --min-api: " + minApiString, origin));
-      return false;
-    }
-    builder.setMinApiLevel(minApi);
-    return true;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
new file mode 100644
index 0000000..054db58
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, 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.origin.Origin;
+import com.android.tools.r8.utils.StringDiagnostic;
+
+public class BaseCompilerCommandParser {
+
+  static void parseMinApi(BaseCompilerCommand.Builder builder, String minApiString, Origin origin) {
+    int minApi;
+    try {
+      minApi = Integer.valueOf(minApiString);
+    } catch (NumberFormatException e) {
+      builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+      return;
+    }
+    if (minApi < 1) {
+      builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+      return;
+    }
+    builder.setMinApiLevel(minApi);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 6c9a286..3bab0ab 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -26,6 +26,7 @@
  *
  * <p>This consumer can only be provided to R8.
  */
+@KeepForSubclassing
 public interface ClassFileConsumer extends ProgramConsumer {
 
   /**
@@ -49,6 +50,7 @@
   }
 
   /** Forwarding consumer to delegate to an optional existing consumer. */
+  @Keep
   class ForwardingConsumer implements ClassFileConsumer {
 
     private static final ClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -80,6 +82,7 @@
   }
 
   /** Consumer to write program resources to an output. */
+  @Keep
   class ArchiveConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     private final OutputBuilder outputBuilder;
@@ -163,6 +166,7 @@
   }
 
   /** Directory consumer to write program resources to a directory. */
+  @Keep
   class DirectoryConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
     private final OutputBuilder outputBuilder;
     protected final boolean consumeDataResouces;
diff --git a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
index 4c71d88..db0a9f8 100644
--- a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
@@ -12,6 +12,7 @@
  * only created on-demand when they are needed by the compiler. If never needed, the resource will
  * never be loaded.
  */
+@KeepForSubclassing
 public interface ClassFileResourceProvider {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index a6923ba..89a33c1 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -6,6 +6,8 @@
 
 import java.nio.file.Path;
 
+// This class is used by the Android Studio Gradle plugin and is thus part of the R8 API.
+@Keep
 public class CompatProguardCommandBuilder extends R8Command.Builder {
   public CompatProguardCommandBuilder() {
     this(true);
diff --git a/src/main/java/com/android/tools/r8/CompilationFailedException.java b/src/main/java/com/android/tools/r8/CompilationFailedException.java
index 561ad0c..bcc4ad1 100644
--- a/src/main/java/com/android/tools/r8/CompilationFailedException.java
+++ b/src/main/java/com/android/tools/r8/CompilationFailedException.java
@@ -7,6 +7,7 @@
  * Exception thrown when compilation failed to complete because of errors previously reported
  * through {@link com.android.tools.r8.DiagnosticsHandler}.
  */
+@Keep
 public class CompilationFailedException extends Exception {
 
   public CompilationFailedException() {
diff --git a/src/main/java/com/android/tools/r8/CompilationMode.java b/src/main/java/com/android/tools/r8/CompilationMode.java
index f7aa4f0..fb02504 100644
--- a/src/main/java/com/android/tools/r8/CompilationMode.java
+++ b/src/main/java/com/android/tools/r8/CompilationMode.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 /** Compilation mode. */
+@Keep
 public enum CompilationMode {
   /** Preserves debugging information during compilation, eg, line-numbers and locals. */
   DEBUG,
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 06cf0bb..f4d378a 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -52,6 +52,7 @@
  * them to DEX bytecode (compiling from Java bytecode for such inputs and merging for DEX inputs),
  * and then writes the result to the directory or zip archive specified by {@code outputPath}.
  */
+@Keep
 public final class D8 {
 
   private D8() {}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 6f956db..8c6c02b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -3,18 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FlagFile;
 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.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Collection;
 
@@ -31,7 +26,8 @@
  *     .build();
  * </pre>
  */
-public class D8Command extends BaseCompilerCommand {
+@Keep
+public final class D8Command extends BaseCompilerCommand {
 
   private static class ClasspathInputOrigin extends InputFileOrigin {
 
@@ -45,6 +41,7 @@
    *
    * <p>A builder is obtained by calling {@link D8Command#builder}.
    */
+  @Keep
   public static class Builder extends BaseCompilerCommand.Builder<D8Command, Builder> {
 
     private boolean intermediate = false;
@@ -150,24 +147,7 @@
     }
   }
 
-  static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
-      "Usage: d8 [options] <input-files>",
-      " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-      " and options are:",
-      "  --debug                 # Compile with debugging information (default).",
-      "  --release               # Compile without debugging information.",
-      "  --output <file>         # Output result in <outfile>.",
-      "                          # <file> must be an existing directory or a zip file.",
-      "  --lib <file>            # Add <file> as a library resource.",
-      "  --classpath <file>      # Add <file> as a classpath resource.",
-      "  --min-api               # Minimum Android API level compatibility",
-      "  --intermediate          # Compile an intermediate result intended for later",
-      "                          # merging.",
-      "  --file-per-class        # Produce a separate dex file per input class",
-      "  --no-desugaring         # Force disable desugaring.",
-      "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
-      "  --version               # Print the version of d8.",
-      "  --help                  # Print this message."));
+  static final String USAGE_MESSAGE = D8CommandParser.USAGE_MESSAGE;
 
   private boolean intermediate = false;
 
@@ -194,7 +174,7 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static Builder parse(String[] args, Origin origin) {
-    return parse(args, origin, builder());
+    return D8CommandParser.parse(args, origin);
   }
 
   /**
@@ -208,87 +188,7 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
-    return parse(args, origin, builder(handler));
-  }
-
-  private static Builder parse(String[] args, Origin origin, Builder builder) {
-    CompilationMode compilationMode = null;
-    Path outputPath = null;
-    OutputMode outputMode = null;
-    boolean hasDefinedApiLevel = false;
-    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
-    try {
-      for (int i = 0; i < expandedArgs.length; i++) {
-        String arg = expandedArgs[i].trim();
-        if (arg.length() == 0) {
-          continue;
-        } else if (arg.equals("--help")) {
-          builder.setPrintHelp(true);
-        } else if (arg.equals("--version")) {
-          builder.setPrintVersion(true);
-        } else if (arg.equals("--debug")) {
-          if (compilationMode == CompilationMode.RELEASE) {
-            builder.getReporter().error(new StringDiagnostic(
-                "Cannot compile in both --debug and --release mode.",
-                origin));
-            continue;
-          }
-          compilationMode = CompilationMode.DEBUG;
-        } else if (arg.equals("--release")) {
-          if (compilationMode == CompilationMode.DEBUG) {
-            builder.getReporter().error(new StringDiagnostic(
-                "Cannot compile in both --debug and --release mode.",
-                origin));
-            continue;
-          }
-          compilationMode = CompilationMode.RELEASE;
-        } else if (arg.equals("--file-per-class")) {
-          outputMode = OutputMode.DexFilePerClassFile;
-        } else if (arg.equals("--output")) {
-          String output = expandedArgs[++i];
-          if (outputPath != null) {
-            builder.getReporter().error(new StringDiagnostic(
-                "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
-                origin));
-            continue;
-          }
-          outputPath = Paths.get(output);
-        } else if (arg.equals("--lib")) {
-          builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
-        } else if (arg.equals("--classpath")) {
-          builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
-        } else if (arg.equals("--main-dex-list")) {
-          builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
-        } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
-          builder.setOptimizeMultidexForLinearAlloc(true);
-        } else if (arg.equals("--min-api")) {
-          hasDefinedApiLevel = parseMinApi(builder, expandedArgs[++i], hasDefinedApiLevel, origin);
-        } else if (arg.equals("--intermediate")) {
-          builder.setIntermediate(true);
-        } else if (arg.equals("--no-desugaring")) {
-          builder.setDisableDesugaring(true);
-        } else {
-          if (arg.startsWith("--")) {
-            builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg,
-                origin));
-            continue;
-          }
-          builder.addProgramFiles(Paths.get(arg));
-        }
-      }
-      if (compilationMode != null) {
-        builder.setMode(compilationMode);
-      }
-      if (outputMode == null) {
-        outputMode = OutputMode.DexIndexed;
-      }
-      if (outputPath == null) {
-        outputPath = Paths.get(".");
-      }
-      return builder.setOutput(outputPath, outputMode);
-    } catch (CompilationError e) {
-      throw builder.getReporter().fatalError(e);
-    }
+    return D8CommandParser.parse(args, origin, handler);
   }
 
   private D8Command(
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
new file mode 100644
index 0000000..9e5d36a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2018, 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.errors.CompilationError;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class D8CommandParser extends BaseCompilerCommandParser {
+
+  public static void main(String[] args) throws CompilationFailedException {
+    D8Command command = parse(args, Origin.root()).build();
+    if (command.isPrintHelp()) {
+      System.out.println(USAGE_MESSAGE);
+      System.exit(1);
+    }
+    D8.run(command);
+  }
+
+  static final String USAGE_MESSAGE =
+      String.join(
+          "\n",
+          Arrays.asList(
+              "Usage: d8 [options] <input-files>",
+              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+              " and options are:",
+              "  --debug                 # Compile with debugging information (default).",
+              "  --release               # Compile without debugging information.",
+              "  --output <file>         # Output result in <outfile>.",
+              "                          # <file> must be an existing directory or a zip file.",
+              "  --lib <file>            # Add <file> as a library resource.",
+              "  --classpath <file>      # Add <file> as a classpath resource.",
+              "  --min-api               # Minimum Android API level compatibility",
+              "  --intermediate          # Compile an intermediate result intended for later",
+              "                          # merging.",
+              "  --file-per-class        # Produce a separate dex file per input class",
+              "  --no-desugaring         # Force disable desugaring.",
+              "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
+              "  --version               # Print the version of d8.",
+              "  --help                  # Print this message."));
+
+  /**
+   * Parse the D8 command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @return D8 command builder with state set up according to parsed command line.
+   */
+  public static D8Command.Builder parse(String[] args, Origin origin) {
+    return parse(args, origin, D8Command.builder());
+  }
+
+  /**
+   * Parse the D8 command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @param handler Custom defined diagnostics handler.
+   * @return D8 command builder with state set up according to parsed command line.
+   */
+  public static D8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+    return parse(args, origin, D8Command.builder(handler));
+  }
+
+  private static D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
+    CompilationMode compilationMode = null;
+    Path outputPath = null;
+    OutputMode outputMode = null;
+    boolean hasDefinedApiLevel = false;
+    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+    try {
+      for (int i = 0; i < expandedArgs.length; i++) {
+        String arg = expandedArgs[i].trim();
+        if (arg.length() == 0) {
+          continue;
+        } else if (arg.equals("--help")) {
+          builder.setPrintHelp(true);
+        } else if (arg.equals("--version")) {
+          builder.setPrintVersion(true);
+        } else if (arg.equals("--debug")) {
+          if (compilationMode == CompilationMode.RELEASE) {
+            builder.error(
+                new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+            continue;
+          }
+          compilationMode = CompilationMode.DEBUG;
+        } else if (arg.equals("--release")) {
+          if (compilationMode == CompilationMode.DEBUG) {
+            builder.error(
+                new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+            continue;
+          }
+          compilationMode = CompilationMode.RELEASE;
+        } else if (arg.equals("--file-per-class")) {
+          outputMode = OutputMode.DexFilePerClassFile;
+        } else if (arg.equals("--output")) {
+          String output = expandedArgs[++i];
+          if (outputPath != null) {
+            builder.error(
+                new StringDiagnostic(
+                    "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
+                    origin));
+            continue;
+          }
+          outputPath = Paths.get(output);
+        } else if (arg.equals("--lib")) {
+          builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
+        } else if (arg.equals("--classpath")) {
+          builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
+        } else if (arg.equals("--main-dex-list")) {
+          builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
+        } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
+          builder.setOptimizeMultidexForLinearAlloc(true);
+        } else if (arg.equals("--min-api")) {
+          String minApiString = expandedArgs[++i];
+          if (hasDefinedApiLevel) {
+            builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+          } else {
+            parseMinApi(builder, minApiString, origin);
+            hasDefinedApiLevel = true;
+          }
+        } else if (arg.equals("--intermediate")) {
+          builder.setIntermediate(true);
+        } else if (arg.equals("--no-desugaring")) {
+          builder.setDisableDesugaring(true);
+        } else {
+          if (arg.startsWith("--")) {
+            builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
+            continue;
+          }
+          builder.addProgramFiles(Paths.get(arg));
+        }
+      }
+      if (compilationMode != null) {
+        builder.setMode(compilationMode);
+      }
+      if (outputMode == null) {
+        outputMode = OutputMode.DexIndexed;
+      }
+      if (outputPath == null) {
+        outputPath = Paths.get(".");
+      }
+      return builder.setOutput(outputPath, outputMode);
+    } catch (CompilationError e) {
+      throw builder.fatalError(e);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/DataDirectoryResource.java b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
index 2f421f7..3e860e3 100644
--- a/src/main/java/com/android/tools/r8/DataDirectoryResource.java
+++ b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
@@ -12,6 +12,7 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+@Keep
 public interface DataDirectoryResource extends DataResource {
 
   static DataDirectoryResource fromFile(Path dir, Path file) {
diff --git a/src/main/java/com/android/tools/r8/DataEntryResource.java b/src/main/java/com/android/tools/r8/DataEntryResource.java
index 189d5eb..aa9c73f 100644
--- a/src/main/java/com/android/tools/r8/DataEntryResource.java
+++ b/src/main/java/com/android/tools/r8/DataEntryResource.java
@@ -15,6 +15,7 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+@Keep
 public interface DataEntryResource extends DataResource {
 
   /** Get the bytes of the data entry resource. */
diff --git a/src/main/java/com/android/tools/r8/DataResource.java b/src/main/java/com/android/tools/r8/DataResource.java
index 4609c97..eac8b6c 100644
--- a/src/main/java/com/android/tools/r8/DataResource.java
+++ b/src/main/java/com/android/tools/r8/DataResource.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+@Keep
 public interface DataResource extends Resource {
   char SEPARATOR = '/';
 
diff --git a/src/main/java/com/android/tools/r8/DataResourceConsumer.java b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
index 215ca59..e306b9b 100644
--- a/src/main/java/com/android/tools/r8/DataResourceConsumer.java
+++ b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+@KeepForSubclassing
 public interface DataResourceConsumer {
 
   void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler);
diff --git a/src/main/java/com/android/tools/r8/DataResourceProvider.java b/src/main/java/com/android/tools/r8/DataResourceProvider.java
index e851f0f..5e0a24f 100644
--- a/src/main/java/com/android/tools/r8/DataResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/DataResourceProvider.java
@@ -3,8 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+@KeepForSubclassing
 public interface DataResourceProvider {
 
+  @KeepForSubclassing
   interface Visitor {
     void visit(DataDirectoryResource directory);
     void visit(DataEntryResource file);
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index f1c0fac..311a3dd 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -29,6 +29,7 @@
  *
  * <p>This consumer receives DEX file content for each Java class-file input.
  */
+@KeepForSubclassing
 public interface DexFilePerClassFileConsumer extends ProgramConsumer {
 
   /**
@@ -57,6 +58,7 @@
   }
 
   /** Forwarding consumer to delegate to an optional existing consumer. */
+  @Keep
   class ForwardingConsumer implements DexFilePerClassFileConsumer {
 
     private static final DexFilePerClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -92,6 +94,7 @@
   }
 
   /** Consumer to write program resources to an output. */
+  @Keep
   class ArchiveConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     private final OutputBuilder outputBuilder;
@@ -181,6 +184,7 @@
   }
 
   /** Directory consumer to write program resources to a directory. */
+  @Keep
   class DirectoryConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     private final OutputBuilder outputBuilder;
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index 3bbab64..e802103 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -31,6 +31,7 @@
  * <p>This consumer receives DEX file content using standard indexed-multidex for programs larger
  * than a single DEX file. This is the default consumer for DEX programs.
  */
+@KeepForSubclassing
 public interface DexIndexedConsumer extends ProgramConsumer {
 
   /**
@@ -58,6 +59,7 @@
   }
 
   /** Forwarding consumer to delegate to an optional existing consumer. */
+  @Keep
   class ForwardingConsumer implements DexIndexedConsumer {
 
     private static final DexIndexedConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -100,6 +102,7 @@
   }
 
   /** Consumer to write program resources to an output. */
+  @Keep
   class ArchiveConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     protected final OutputBuilder outputBuilder;
@@ -181,6 +184,7 @@
     }
   }
 
+  @Keep
   class DirectoryConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     private final Path directory;
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 2d52ec9..3e299f2 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -31,7 +31,8 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
-public class DexSplitterHelper {
+@Keep
+public final class DexSplitterHelper {
 
   public static void run(
       D8Command command, FeatureClassMapping featureClassMapping, String output, String proguardMap)
diff --git a/src/main/java/com/android/tools/r8/Diagnostic.java b/src/main/java/com/android/tools/r8/Diagnostic.java
index 7896251..3184d54 100644
--- a/src/main/java/com/android/tools/r8/Diagnostic.java
+++ b/src/main/java/com/android/tools/r8/Diagnostic.java
@@ -9,6 +9,7 @@
 /**
  * Interface for all diagnostic message produced by D8 and R8.
  */
+@Keep
 public interface Diagnostic {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 8212835..74a9a96 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -10,6 +10,7 @@
  *
  * <p>During compilation the warning and info methods will be called.
  */
+@Keep
 public interface DiagnosticsHandler {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java b/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
index 5d5f751..50aff3f 100644
--- a/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
@@ -19,6 +19,7 @@
  * Lazy resource provider returning class file resources based
  * on filesystem directory content.
  */
+@Keep
 public final class DirectoryClassFileProvider implements ClassFileResourceProvider {
   private final Path root;
 
diff --git a/src/main/java/com/android/tools/r8/Keep.java b/src/main/java/com/android/tools/r8/Keep.java
new file mode 100644
index 0000000..13ed8c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Keep.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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;
+
+@Keep
+public @interface Keep {
+
+}
diff --git a/src/main/java/com/android/tools/r8/KeepForSubclassing.java b/src/main/java/com/android/tools/r8/KeepForSubclassing.java
new file mode 100644
index 0000000..6e57114
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/KeepForSubclassing.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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;
+
+@Keep
+public @interface KeepForSubclassing {
+
+}
diff --git a/src/main/java/com/android/tools/r8/OutputMode.java b/src/main/java/com/android/tools/r8/OutputMode.java
index 0da34b7..bc8a8d8 100644
--- a/src/main/java/com/android/tools/r8/OutputMode.java
+++ b/src/main/java/com/android/tools/r8/OutputMode.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 /** Enumeration of the possible output formats of compilation. */
+@Keep
 public enum OutputMode {
 
   /** Produce DEX files using standard indexed-multidex for programs larger that a single file. */
diff --git a/src/main/java/com/android/tools/r8/ProgramConsumer.java b/src/main/java/com/android/tools/r8/ProgramConsumer.java
index 08d7d7c..b3143a0 100644
--- a/src/main/java/com/android/tools/r8/ProgramConsumer.java
+++ b/src/main/java/com/android/tools/r8/ProgramConsumer.java
@@ -6,6 +6,7 @@
 /**
  * Base for all program consumers to allow abstracting which concrete consumer is provided to D8/R8.
  */
+@KeepForSubclassing
 public interface ProgramConsumer {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ProgramResource.java b/src/main/java/com/android/tools/r8/ProgramResource.java
index cabeabc..ffeb558 100644
--- a/src/main/java/com/android/tools/r8/ProgramResource.java
+++ b/src/main/java/com/android/tools/r8/ProgramResource.java
@@ -20,9 +20,11 @@
  * A resource may optionally include a set describing the class descriptors for each type that is
  * defined by the resource.
  */
+@KeepForSubclassing
 public interface ProgramResource extends Resource {
 
   /** Type of program-format kinds. */
+  @Keep
   enum Kind {
     /** Format-kind for Java class-file resources. */
     CF,
@@ -66,6 +68,7 @@
   Set<String> getClassDescriptors();
 
   /** File-based program resource. */
+  @Keep
   class FileResource implements ProgramResource {
     private final Origin origin;
     private final Kind kind;
@@ -105,6 +108,7 @@
   }
 
   /** Byte-content based program resource. */
+  @Keep
   class ByteResource implements ProgramResource {
     private final Origin origin;
     private final Kind kind;
diff --git a/src/main/java/com/android/tools/r8/ProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
index 76d340c..d4b8a87 100644
--- a/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
@@ -6,6 +6,7 @@
 import java.util.Collection;
 
 /** Program resource provider. */
+@KeepForSubclassing
 public interface ProgramResourceProvider {
 
   Collection<ProgramResource> getProgramResources() throws ResourceException;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5d29d90..077e220 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -103,6 +103,7 @@
  * them to DEX bytecode, using {@code androidJar} as the reference of the system runtime library,
  * and then writes the result to the directory or zip archive specified by {@code outputPath}.
  */
+@Keep
 public class R8 {
 
   private final Timing timing = new Timing("R8");
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 573f6df..86ff5a5 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.Reporter;
@@ -46,13 +45,15 @@
  *     .build();
  * </pre>
  */
-public class R8Command extends BaseCompilerCommand {
+@Keep
+public final class R8Command extends BaseCompilerCommand {
 
   /**
    * Builder for constructing a R8Command.
    *
    * <p>A builder is obtained by calling {@link R8Command#builder}.
    */
+  @Keep
   public static class Builder extends BaseCompilerCommand.Builder<R8Command, Builder> {
 
     private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
@@ -421,38 +422,7 @@
     }
   }
 
-  // Internal state to verify parsing properties not enforced by the builder.
-  private static class ParseState {
-    CompilationMode mode = null;
-    OutputMode outputMode = null;
-    Path outputPath = null;
-    boolean hasDefinedApiLevel = false;
-  }
-
-  static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
-      "Usage: r8 [options] <input-files>",
-      " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-      " and options are:",
-      "  --release                # Compile without debugging information (default).",
-      "  --debug                  # Compile with debugging information.",
-      // TODO(b/65390962): Add help for output-mode flags once the CF backend is complete.
-      //"  --dex                    # Compile program to DEX file format (default).",
-      //"  --classfile              # Compile program to Java classfile format.",
-      "  --output <file>          # Output result in <file>.",
-      "                           # <file> must be an existing directory or a zip file.",
-      "  --lib <file>             # Add <file> as a library resource.",
-      "  --min-api                # Minimum Android API level compatibility.",
-      "  --pg-conf <file>         # Proguard configuration <file>.",
-      "  --pg-map-output <file>   # Output the resulting name and line mapping to <file>.",
-      "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
-      "  --no-minification        # Force disable minification of names.",
-      "  --no-desugaring          # Force disable desugaring.",
-      "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
-      "                           # primary dex file.",
-      "  --main-dex-list <file>   # List of classes to place in the primary dex file.",
-      "  --main-dex-list-output <file>  # Output the full main-dex list in <file>.",
-      "  --version                # Print the version of r8.",
-      "  --help                   # Print this message."));
+  static final String USAGE_MESSAGE = R8CommandParser.USAGE_MESSAGE;
 
   private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
   private final StringConsumer mainDexListConsumer;
@@ -488,7 +458,7 @@
    * @return R8 command builder with state set up according to parsed command line.
    */
   public static Builder parse(String[] args, Origin origin) {
-    return parse(args, origin, builder());
+    return R8CommandParser.parse(args, origin);
   }
 
   /**
@@ -502,102 +472,7 @@
    * @return R8 command builder with state set up according to parsed command line.
    */
   public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
-    return parse(args, origin, builder(handler));
-  }
-
-  private static Builder parse(String[] args, Origin origin, Builder builder) {
-    ParseState state = new ParseState();
-    parse(args, origin, builder, state);
-    if (state.mode != null) {
-      builder.setMode(state.mode);
-    }
-    Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
-    OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
-    builder.setOutput(outputPath, outputMode);
-    return builder;
-  }
-
-  private static ParseState parse(
-      String[] args,
-      Origin argsOrigin,
-      Builder builder,
-      ParseState state) {
-    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
-    for (int i = 0; i < expandedArgs.length; i++) {
-      String arg = expandedArgs[i].trim();
-      if (arg.length() == 0) {
-        continue;
-      } else if (arg.equals("--help")) {
-        builder.setPrintHelp(true);
-      } else if (arg.equals("--version")) {
-        builder.setPrintVersion(true);
-      } else if (arg.equals("--debug")) {
-        if (state.mode == CompilationMode.RELEASE) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot compile in both --debug and --release mode.", argsOrigin));
-        }
-        state.mode = CompilationMode.DEBUG;
-      } else if (arg.equals("--release")) {
-        if (state.mode == CompilationMode.DEBUG) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot compile in both --debug and --release mode.", argsOrigin));
-        }
-        state.mode = CompilationMode.RELEASE;
-      } else if (arg.equals("--dex")) {
-        if (state.outputMode == OutputMode.ClassFile) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
-        }
-        state.outputMode = OutputMode.DexIndexed;
-      } else if (arg.equals("--classfile")) {
-        if (state.outputMode == OutputMode.DexIndexed) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
-        }
-        state.outputMode = OutputMode.ClassFile;
-      } else if (arg.equals("--output")) {
-        String outputPath = expandedArgs[++i];
-        if (state.outputPath != null) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot output both to '"
-                  + state.outputPath.toString()
-                  + "' and '"
-                  + outputPath
-                  + "'",
-              argsOrigin));
-        }
-        state.outputPath = Paths.get(outputPath);
-      } else if (arg.equals("--lib")) {
-        builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--min-api")) {
-        state.hasDefinedApiLevel =
-            parseMinApi(builder, expandedArgs[++i], state.hasDefinedApiLevel, argsOrigin);
-      } else if (arg.equals("--no-tree-shaking")) {
-        builder.setDisableTreeShaking(true);
-      } else if (arg.equals("--no-minification")) {
-        builder.setDisableMinification(true);
-      } else if (arg.equals("--no-desugaring")) {
-        builder.setDisableDesugaring(true);
-      } else if (arg.equals("--main-dex-rules")) {
-        builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--main-dex-list")) {
-        builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--main-dex-list-output")) {
-        builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
-        builder.setOptimizeMultidexForLinearAlloc(true);
-      } else if (arg.equals("--pg-conf")) {
-        builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--pg-map-output")) {
-        builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
-      } else {
-        if (arg.startsWith("--")) {
-          builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
-        }
-        builder.addProgramFiles(Paths.get(arg));
-      }
-    }
-    return state;
+    return R8CommandParser.parse(args, origin, handler);
   }
 
   private R8Command(
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
new file mode 100644
index 0000000..3d33037
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2018, 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.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class R8CommandParser extends BaseCompilerCommandParser {
+
+  public static void main(String[] args) throws CompilationFailedException {
+    R8Command command = parse(args, Origin.root()).build();
+    if (command.isPrintHelp()) {
+      System.out.println(USAGE_MESSAGE);
+      System.exit(1);
+    }
+    R8.run(command);
+  }
+
+  // Internal state to verify parsing properties not enforced by the builder.
+  private static class ParseState {
+    CompilationMode mode = null;
+    OutputMode outputMode = null;
+    Path outputPath = null;
+    boolean hasDefinedApiLevel = false;
+  }
+
+  static final String USAGE_MESSAGE =
+      String.join(
+          "\n",
+          Arrays.asList(
+              "Usage: r8 [options] <input-files>",
+              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+              " and options are:",
+              "  --release                # Compile without debugging information (default).",
+              "  --debug                  # Compile with debugging information.",
+              // TODO(b/65390962): Add help for output-mode flags once the CF backend is complete.
+              // "  --dex                    # Compile program to DEX file format (default).",
+              // "  --classfile              # Compile program to Java classfile format.",
+              "  --output <file>          # Output result in <file>.",
+              "                           # <file> must be an existing directory or a zip file.",
+              "  --lib <file>             # Add <file> as a library resource.",
+              "  --min-api                # Minimum Android API level compatibility.",
+              "  --pg-conf <file>         # Proguard configuration <file>.",
+              "  --pg-map-output <file>   # Output the resulting name and line mapping to <file>.",
+              "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
+              "  --no-minification        # Force disable minification of names.",
+              "  --no-desugaring          # Force disable desugaring.",
+              "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
+              "                           # primary dex file.",
+              "  --main-dex-list <file>   # List of classes to place in the primary dex file.",
+              "  --main-dex-list-output <file>  # Output the full main-dex list in <file>.",
+              "  --version                # Print the version of r8.",
+              "  --help                   # Print this message."));
+  /**
+   * Parse the R8 command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @return R8 command builder with state set up according to parsed command line.
+   */
+  public static R8Command.Builder parse(String[] args, Origin origin) {
+    return new R8CommandParser().parse(args, origin, R8Command.builder());
+  }
+
+  /**
+   * Parse the R8 command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @param handler Custom defined diagnostics handler.
+   * @return R8 command builder with state set up according to parsed command line.
+   */
+  public static R8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+    return new R8CommandParser().parse(args, origin, R8Command.builder(handler));
+  }
+
+  private R8Command.Builder parse(String[] args, Origin origin, R8Command.Builder builder) {
+    ParseState state = new ParseState();
+    parse(args, origin, builder, state);
+    if (state.mode != null) {
+      builder.setMode(state.mode);
+    }
+    Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
+    OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
+    builder.setOutput(outputPath, outputMode);
+    return builder;
+  }
+
+  private void parse(
+      String[] args, Origin argsOrigin, R8Command.Builder builder, ParseState state) {
+    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+    for (int i = 0; i < expandedArgs.length; i++) {
+      String arg = expandedArgs[i].trim();
+      if (arg.length() == 0) {
+        continue;
+      } else if (arg.equals("--help")) {
+        builder.setPrintHelp(true);
+      } else if (arg.equals("--version")) {
+        builder.setPrintVersion(true);
+      } else if (arg.equals("--debug")) {
+        if (state.mode == CompilationMode.RELEASE) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot compile in both --debug and --release mode.", argsOrigin));
+        }
+        state.mode = CompilationMode.DEBUG;
+      } else if (arg.equals("--release")) {
+        if (state.mode == CompilationMode.DEBUG) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot compile in both --debug and --release mode.", argsOrigin));
+        }
+        state.mode = CompilationMode.RELEASE;
+      } else if (arg.equals("--dex")) {
+        if (state.outputMode == OutputMode.ClassFile) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
+        }
+        state.outputMode = OutputMode.DexIndexed;
+      } else if (arg.equals("--classfile")) {
+        if (state.outputMode == OutputMode.DexIndexed) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
+        }
+        state.outputMode = OutputMode.ClassFile;
+      } else if (arg.equals("--output")) {
+        String outputPath = expandedArgs[++i];
+        if (state.outputPath != null) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot output both to '"
+                      + state.outputPath.toString()
+                      + "' and '"
+                      + outputPath
+                      + "'",
+                  argsOrigin));
+        }
+        state.outputPath = Paths.get(outputPath);
+      } else if (arg.equals("--lib")) {
+        builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--min-api")) {
+        String minApiString = expandedArgs[++i];
+        if (state.hasDefinedApiLevel) {
+          builder.error(new StringDiagnostic("Cannot set multiple --min-api options", argsOrigin));
+        } else {
+          parseMinApi(builder, minApiString, argsOrigin);
+          state.hasDefinedApiLevel = true;
+        }
+      } else if (arg.equals("--no-tree-shaking")) {
+        builder.setDisableTreeShaking(true);
+      } else if (arg.equals("--no-minification")) {
+        builder.setDisableMinification(true);
+      } else if (arg.equals("--no-desugaring")) {
+        builder.setDisableDesugaring(true);
+      } else if (arg.equals("--main-dex-rules")) {
+        builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--main-dex-list")) {
+        builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--main-dex-list-output")) {
+        builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
+        builder.setOptimizeMultidexForLinearAlloc(true);
+      } else if (arg.equals("--pg-conf")) {
+        builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--pg-map-output")) {
+        builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
+      } else {
+        if (arg.startsWith("--")) {
+          builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
+        }
+        builder.addProgramFiles(Paths.get(arg));
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index b64fe01..50c50cc 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -19,6 +19,7 @@
  * The D8/R8 compilers uses default implementations for various file-system resources, but the
  * client is free to provide their own.
  */
+@KeepForSubclassing
 public interface Resource {
   /**
    * Get the origin of the resource.
diff --git a/src/main/java/com/android/tools/r8/ResourceException.java b/src/main/java/com/android/tools/r8/ResourceException.java
index 3af7efa..2726b92 100644
--- a/src/main/java/com/android/tools/r8/ResourceException.java
+++ b/src/main/java/com/android/tools/r8/ResourceException.java
@@ -11,6 +11,7 @@
  * For example, this is the expected exception that must be thrown if a resource fails to produce
  * its content. See {@link ProgramResource#getByteStream()} and {@link StringResource#getString()}.
  */
+@Keep
 public class ResourceException extends Exception {
 
   private final Origin origin;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 7daef42..6cbad63 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -481,13 +481,14 @@
     return result;
   }
 
-  public boolean canTriggerStaticInitializer(DexType clazz) {
+  public boolean canTriggerStaticInitializer(DexType type) {
     Set<DexType> knownInterfaces = Sets.newIdentityHashSet();
 
     // Process superclass chain.
+    DexType clazz = type;
     while (clazz != null && clazz != dexItemFactory.objectType) {
       DexClass definition = definitionFor(clazz);
-      if (definition == null || definition.hasClassInitializer()) {
+      if (canTriggerStaticInitializer(definition)) {
         return true; // Assume it *may* trigger if we didn't find the definition.
       }
       knownInterfaces.addAll(Arrays.asList(definition.interfaces.values));
@@ -499,10 +500,10 @@
     while (!queue.isEmpty()) {
       DexType iface = queue.remove();
       DexClass definition = definitionFor(iface);
-      if (definition == null || definition.hasClassInitializer()) {
+      if (canTriggerStaticInitializer(definition)) {
         return true; // Assume it *may* trigger if we didn't find the definition.
       }
-      if (!definition.accessFlags.isInterface()) {
+      if (!definition.isInterface()) {
         throw new Unreachable(iface.toSourceString() + " is expected to be an interface");
       }
 
@@ -515,6 +516,10 @@
     return false;
   }
 
+  private static boolean canTriggerStaticInitializer(DexClass clazz) {
+    return clazz == null || clazz.hasClassInitializer();
+  }
+
   public interface ResolutionResult {
 
     DexEncodedMethod asResultOfResolve();
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index d178e4a..02d2898 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -224,6 +224,34 @@
     }
   }
 
+  /**
+   * Collect all interfaces that this type directly or indirectly implements.
+   * @param appInfo where the definition of a certain {@link DexType} is looked up.
+   * @return a set of interfaces of {@link DexType}.
+   */
+  public Set<DexType> implementedInterfaces(AppInfo appInfo) {
+    Set<DexType> interfaces = Sets.newIdentityHashSet();
+    implementedInterfaces(appInfo, interfaces);
+    return interfaces;
+  }
+
+  private void implementedInterfaces(AppInfo appInfo, Set<DexType> interfaces) {
+    DexClass dexClass = appInfo.definitionFor(this);
+    // Loop to traverse the super type hierarchy of the current type.
+    while (dexClass != null) {
+      if (dexClass.isInterface()) {
+        interfaces.add(dexClass.type);
+      }
+      for (DexType itf : dexClass.interfaces.values) {
+        itf.implementedInterfaces(appInfo, interfaces);
+      }
+      if (dexClass.superType == null) {
+        break;
+      }
+      dexClass = appInfo.definitionFor(dexClass.superType);
+    }
+  }
+
   public boolean isSamePackage(DexType other) {
     return getPackageDescriptor().equals(other.getPackageDescriptor());
   }
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index ef247a0..df30c18 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -813,8 +813,8 @@
       if (overlappingMoveExceptionIntervals) {
         for (LiveIntervals intervals : moveExceptionIntervals) {
           if (intervals.getUses().size() > 1) {
-            LiveIntervalsUse secondUse = intervals.getUses().stream().skip(1).findFirst().get();
-            LiveIntervals split = intervals.splitBefore(secondUse.getPosition());
+            LiveIntervals split =
+                intervals.splitBefore(intervals.getFirstUse() + INSTRUCTION_NUMBER_DELTA);
             unhandled.add(split);
           }
         }
@@ -1354,6 +1354,12 @@
     if (!hasDedicatedMoveExceptionRegister()) {
       return false;
     }
+    // If there are that many move exception intervals we don't spent the time
+    // going through them all. In that case it is unlikely that we can reuse the move exception
+    // register in any case.
+    if (moveExceptionIntervals.size() > 1000) {
+      return true;
+    }
     for (LiveIntervals moveExceptionInterval : moveExceptionIntervals) {
       if (intervals.anySplitOverlaps(moveExceptionInterval)) {
         return true;
diff --git a/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java b/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
index 0a6c84a..37ae662 100644
--- a/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
+++ b/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
@@ -4,9 +4,12 @@
 
 package com.android.tools.r8.origin;
 
+import com.android.tools.r8.Keep;
+
 /**
  * Origin representing an entry in an archive.
  */
+@Keep
 public class ArchiveEntryOrigin extends Origin {
 
   final String entryName;
diff --git a/src/main/java/com/android/tools/r8/origin/Origin.java b/src/main/java/com/android/tools/r8/origin/Origin.java
index c0bc2ec..3a8aa97 100644
--- a/src/main/java/com/android/tools/r8/origin/Origin.java
+++ b/src/main/java/com/android/tools/r8/origin/Origin.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.origin;
 
+import com.android.tools.r8.KeepForSubclassing;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -22,6 +23,7 @@
  * Origin.unknown()}. The former is the parent of any file path, while the latter is an unknown
  * origin (e.g., for generated resources of raw bytes).
  */
+@KeepForSubclassing
 public abstract class Origin implements Comparable<Origin> {
 
   private static final Origin ROOT =
diff --git a/src/main/java/com/android/tools/r8/origin/PathOrigin.java b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
index 95ee46f..0ff8080 100644
--- a/src/main/java/com/android/tools/r8/origin/PathOrigin.java
+++ b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
@@ -4,11 +4,13 @@
 
 package com.android.tools.r8.origin;
 
+import com.android.tools.r8.Keep;
 import java.nio.file.Path;
 
 /**
  * Path component in an origin description.
  */
+@Keep
 public class PathOrigin extends Origin {
 
   private final Path path;
diff --git a/src/main/java/com/android/tools/r8/position/BinaryPosition.java b/src/main/java/com/android/tools/r8/position/BinaryPosition.java
deleted file mode 100644
index 5eb2da2..0000000
--- a/src/main/java/com/android/tools/r8/position/BinaryPosition.java
+++ /dev/null
@@ -1,49 +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.position;
-
-public class BinaryPosition implements Position {
-
-  /**
-   * Byte offset from start of the resource.
-   */
-  private final long offset;
-
-  public BinaryPosition(long offset) {
-    assert offset >= 0;
-    this.offset = offset;
-  }
-
-  public long getOffset() {
-    return offset;
-  }
-
-  @Override
-  public String toString() {
-    return "Index : " + offset;
-  }
-
-  @Override
-  public int hashCode() {
-    return Long.hashCode(offset);
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (o == this) {
-      return true;
-    }
-    if (o != null  && o.getClass().equals(BinaryPosition.class)) {
-      BinaryPosition other = (BinaryPosition) o;
-      return offset == other.offset;
-    }
-    return false;
-  }
-
-  @Override
-  public String getDescription() {
-    return "Offset " + offset;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/position/Position.java b/src/main/java/com/android/tools/r8/position/Position.java
index 902fad5..618fd51 100644
--- a/src/main/java/com/android/tools/r8/position/Position.java
+++ b/src/main/java/com/android/tools/r8/position/Position.java
@@ -4,10 +4,13 @@
 
 package com.android.tools.r8.position;
 
+import com.android.tools.r8.Keep;
+
 /**
  * Represent a position in a resource, it can for example be line in a text file of the byte offset
  * in a binary stream.
  */
+@Keep
 public interface Position {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/position/TextPosition.java b/src/main/java/com/android/tools/r8/position/TextPosition.java
index 9136fa7..0dd4090 100644
--- a/src/main/java/com/android/tools/r8/position/TextPosition.java
+++ b/src/main/java/com/android/tools/r8/position/TextPosition.java
@@ -3,10 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.position;
 
+import com.android.tools.r8.Keep;
+
 /**
  * A {@link Position} in a text file determined by line and column.
  * Line and column numbers start at 1.
  */
+@Keep
 public class TextPosition implements Position {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/position/TextRange.java b/src/main/java/com/android/tools/r8/position/TextRange.java
index 5f35aad..0e642d8 100644
--- a/src/main/java/com/android/tools/r8/position/TextRange.java
+++ b/src/main/java/com/android/tools/r8/position/TextRange.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.position;
 
+import com.android.tools.r8.Keep;
+
+@Keep
 public class TextRange implements Position {
   private final TextPosition start;
   private final TextPosition end;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 19fe18e..085d9b3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -785,6 +785,9 @@
     // Mark the type live here, so that the class exists at runtime. Note that this also marks all
     // supertypes as live, so even if the field is actually on a supertype, its class will be live.
     markTypeAsLive(field.clazz);
+    if (field.type.isClassType()) {
+      markTypeAsLive(field.type);
+    }
     // Find the actual field.
     DexEncodedField encodedField = appInfo.resolveFieldOn(field.clazz, field);
     if (encodedField == null) {
@@ -814,6 +817,9 @@
   private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
     assert field != null;
     markTypeAsLive(field.field.clazz);
+    if (field.field.type.isClassType()) {
+      markTypeAsLive(field.field.type);
+    }
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
     }
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index 873013e..8e84935 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.Keep;
 import com.android.tools.r8.dexsplitter.DexSplitter;
 import com.android.tools.r8.dexsplitter.DexSplitter.FeatureJar;
 import com.android.tools.r8.origin.PathOrigin;
@@ -38,6 +39,7 @@
  * <p>Note that this format does not allow specifying inter-module dependencies, this is simply a
  * placement tool.
  */
+@Keep
 public class FeatureClassMapping {
 
   HashMap<String, String> parsedRules = new HashMap<>(); // Already parsed rules.
@@ -198,6 +200,7 @@
         "Invalid mappings specification: " + error + "\n in file " + mappingFile + ":" + line);
   }
 
+  @Keep
   public static class FeatureMappingException extends Exception {
     FeatureMappingException(String message) {
       super(message);
diff --git a/src/main/java/com/android/tools/r8/utils/FlagFile.java b/src/main/java/com/android/tools/r8/utils/FlagFile.java
index 4791217..5f5003f 100644
--- a/src/main/java/com/android/tools/r8/utils/FlagFile.java
+++ b/src/main/java/com/android/tools/r8/utils/FlagFile.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.BaseCommand;
 import com.android.tools.r8.origin.Origin;
 import java.io.IOException;
 import java.nio.file.Files;
@@ -28,7 +29,7 @@
     }
   }
 
-  public static String[] expandFlagFiles(String[] args, Reporter reporter) {
+  public static String[] expandFlagFiles(String[] args, BaseCommand.Builder builder) {
     List<String> flags = new ArrayList<>(args.length);
     for (String arg : args) {
       if (arg.startsWith("@")) {
@@ -37,7 +38,7 @@
           flags.addAll(Files.readAllLines(flagFilePath));
         } catch (IOException e) {
           Origin origin = new FlagFileOrigin(flagFilePath);
-          reporter.error(new ExceptionDiagnostic(e, origin));
+          builder.error(new ExceptionDiagnostic(e, origin));
         }
       } else {
         flags.add(arg);
diff --git a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
index b7cd855..06ab8a9 100644
--- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
@@ -4,9 +4,11 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 
+@Keep
 public class StringDiagnostic implements Diagnostic {
 
   private final Origin origin;
diff --git a/src/main/keep.txt b/src/main/keep.txt
new file mode 100644
index 0000000..2d74e47
--- /dev/null
+++ b/src/main/keep.txt
@@ -0,0 +1,6 @@
+# Copyright (c) 2018, 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.
+
+-keep @com.android.tools.r8.Keep class * { public *; }
+-keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
diff --git a/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
new file mode 100644
index 0000000..7a4950c
--- /dev/null
+++ b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2018, 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 catchhandleroverlap;
+
+public class CatchHandlerOverlap {
+  private static void f() throws Exception {
+    throw new Exception("f");
+  }
+
+  private static void g() throws Exception {
+    throw new Exception("g");
+  }
+
+  private static void h(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9,
+      int i10, int i11, int i12, int i13, int i14, int i15, int i16, int i17) {
+    System.out.println(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 +
+        i12 + i13 + i14 + i15 + i16 + i17);
+    try {
+      f();
+    } catch (Exception e0) {
+      try {
+        g();
+      } catch (Exception e1) {
+        System.out.println(e0.getMessage() + " " + e1.getMessage());
+      }
+    }
+  }
+
+  public static void main(String[] args) {
+    h(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+  }
+}
diff --git a/src/test/examples/multidex005/ref-list-4-r8.txt b/src/test/examples/multidex005/ref-list-4-r8.txt
new file mode 100644
index 0000000..ea7b0f7
--- /dev/null
+++ b/src/test/examples/multidex005/ref-list-4-r8.txt
@@ -0,0 +1,8 @@
+Lmultidex005/DirectlyReferenced;
+Lmultidex005/FieldReference;
+Lmultidex005/Interface1;
+Lmultidex005/Interface2;
+Lmultidex005/Interface3;
+Lmultidex005/SuperClass;
+Lmultidex005/SuperInterface;
+Lmultidex005/SuperSuperClass;
\ No newline at end of file
diff --git a/src/test/examples/multidex005/ref-list-4.txt b/src/test/examples/multidex005/ref-list-4.txt
index ea7b0f7..7733fba 100644
--- a/src/test/examples/multidex005/ref-list-4.txt
+++ b/src/test/examples/multidex005/ref-list-4.txt
@@ -1,5 +1,8 @@
 Lmultidex005/DirectlyReferenced;
 Lmultidex005/FieldReference;
+Lmultidex005/IndirectlyReferenced;
+Lmultidex005/IndirectlyReferencedInterface;
+Lmultidex005/IndirectlyReferencedSuperClass;
 Lmultidex005/Interface1;
 Lmultidex005/Interface2;
 Lmultidex005/Interface3;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 2ac50a6..d8b886c 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -32,6 +32,7 @@
         "arrayaccess.ArrayAccess",
         "barray.BArray",
         "bridge.BridgeMethod",
+        "catchhandleroverlap.CatchHandlerOverlap",
         "cse.CommonSubexpressionElimination",
         "constants.Constants",
         "controlflow.ControlFlow",
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 45ec280..68d87c1 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -86,6 +86,7 @@
   public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
 
   public static final String JAVA_8_RUNTIME = "third_party/openjdk/openjdk-rt-1.8/rt.jar";
+  public static final String KT_RUNTIME = "third_party/kotlin/kotlinc/lib/kotlin-runtime.jar";
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
   private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I;
 
@@ -539,9 +540,16 @@
     String jar = String.format(
         ANDROID_JAR_PATTERN,
         (apiLevel == AndroidApiLevel.getDefault() ? DEFAULT_MIN_SDK : apiLevel).getLevel());
-    assert Files.exists(Paths.get(jar))
+    Path path = Paths.get(jar);
+    assert Files.exists(path)
         : "Expected android jar to exist for API level " + apiLevel;
-    return Paths.get(jar);
+    return path;
+  }
+
+  public static Path getKotlinRuntimeJar() {
+    Path path = Paths.get(KT_RUNTIME);
+    assert Files.exists(path) : "Expected kotlin runtime jar";
+    return path;
   }
 
   public static Path getJdwpTestsCfJarPath(AndroidApiLevel minSdk) {
diff --git a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
new file mode 100644
index 0000000..b32284a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
@@ -0,0 +1,161 @@
+// Copyright (c) 2018, 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.graph;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class DexTypeTest {
+
+  private static DexItemFactory factory;
+  private static AppInfoWithSubtyping appInfo;
+
+  @BeforeClass
+  public static void makeAppInfo() throws Exception {
+    InternalOptions options = new InternalOptions();
+    DexApplication application =
+        new ApplicationReader(
+            AndroidApp.builder()
+                .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+                .addLibraryFiles(ToolHelper.getKotlinRuntimeJar())
+                .build(),
+            options,
+            new Timing(DexType.class.getName()))
+        .read()
+        .toDirect();
+    factory = options.itemFactory;
+    appInfo = new AppInfoWithSubtyping(application);
+  }
+
+  @Test
+  public void implementedInterfaces_collections_java() {
+    DexType serializable = factory.createType("Ljava/io/Serializable;");
+    DexType iterable = factory.createType("Ljava/lang/Iterable;");
+    // interface Collection extends Iterable
+    DexType collection = factory.createType("Ljava/util/Collection;");
+    // interface List extends Collection
+    DexType list = factory.createType("Ljava/util/List;");
+    // interface Queue extends Collection
+    DexType queue = factory.createType("Ljava/util/Queue;");
+    // interface Deque extends Queue
+    DexType deque = factory.createType("Ljava/util/Deque;");
+
+    // class ArrayList implements List
+    DexType arrayList = factory.createType("Ljava/util/ArrayList;");
+    Set<DexType> interfaces = arrayList.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(serializable));
+    assertThat(interfaces, hasItems(iterable));
+    assertThat(interfaces, hasItems(collection));
+    assertThat(interfaces, hasItems(list));
+    assertThat(interfaces, not(hasItems(queue)));
+
+    // class LinkedList implements List, Deque
+    DexType linkedList = factory.createType("Ljava/util/LinkedList;");
+    interfaces = linkedList.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(serializable));
+    assertThat(interfaces, hasItems(iterable));
+    assertThat(interfaces, hasItems(collection));
+    assertThat(interfaces, hasItems(list));
+    assertThat(interfaces, hasItems(deque));
+    assertThat(interfaces, hasItems(queue));
+  }
+
+  @Test
+  public void implementedInterfaces_collections_kotlin() {
+    DexType iterable = factory.createType("Ljava/lang/Iterable;");
+    // interface Collection extends Iterable
+    DexType collection = factory.createType("Ljava/util/Collection;");
+    // interface List extends Collection
+    DexType list = factory.createType("Ljava/util/List;");
+    // interface Set extends Collection
+    DexType set = factory.createType("Ljava/util/Set;");
+
+    DexType ktAbsList = factory.createType("Lkotlin/collections/AbstractList;");
+    DexType ktAbsSet = factory.createType("Lkotlin/collections/AbstractSet;");
+
+    Set<DexType> interfaces = ktAbsList.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(iterable));
+    assertThat(interfaces, hasItems(collection));
+    assertThat(interfaces, hasItems(list));
+    assertThat(interfaces, not(hasItems(set)));
+
+    interfaces = ktAbsSet.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(iterable));
+    assertThat(interfaces, hasItems(collection));
+    assertThat(interfaces, hasItems(set));
+    assertThat(interfaces, not(hasItems(list)));
+  }
+
+  @Test
+  public void implementedInterfaces_reflect_java() {
+    DexType serializable = factory.createType("Ljava/io/Serializable;");
+    DexType annotatedElement = factory.createType("Ljava/lang/reflect/AnnotatedElement;");
+    // interface GenericDeclaration extends AnnotatedElement
+    DexType genericDeclaration = factory.createType("Ljava/lang/reflect/GenericDeclaration;");
+    DexType type = factory.createType("Ljava/lang/reflect/Type;");
+    DexType pType = factory.createType("Ljava/lang/reflect/ParameterizedType;");
+    DexType klass = factory.createType("Ljava/lang/Class;");
+
+    Set<DexType> interfaces = klass.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(serializable));
+    assertThat(interfaces, hasItems(annotatedElement));
+    assertThat(interfaces, hasItems(genericDeclaration));
+    assertThat(interfaces, hasItems(type));
+    assertThat(interfaces, not(hasItems(pType)));
+  }
+
+  @Test
+  public void implementedInterfaces_reflect_kotlin() {
+    DexType kCallable = factory.createType("Lkotlin/reflect/KCallable;");
+    // interface KProperty : KFunction
+    DexType kFunction = factory.createType("Lkotlin/reflect/KFunction;");
+    // interface KProperty : KCallable
+    DexType kProperty = factory.createType("Lkotlin/reflect/KProperty;");
+    // interface KMutableProperty : KProperty
+    DexType kMutableProperty = factory.createType("Lkotlin/reflect/KMutableProperty;");
+    // interface KMutableProperty0 : KProperty, KMutableProperty
+    DexType kMutableProperty0 = factory.createType("Lkotlin/reflect/KMutableProperty0;");
+    // class MutablePropertyReference0 : KMutableProperty0
+    DexType mutableReference0 =
+        factory.createType("Lkotlin/jvm/internal/MutablePropertyReference0;");
+
+    Set<DexType> interfaces = mutableReference0.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(kCallable));
+    assertThat(interfaces, hasItems(kProperty));
+    assertThat(interfaces, hasItems(kMutableProperty));
+    assertThat(interfaces, hasItems(kMutableProperty0));
+    assertThat(interfaces, not(hasItems(kFunction)));
+  }
+
+  @Test
+  public void implementedInterfaces_lambda_kotlin() {
+    DexType function = factory.createType("Lkotlin/Function;");
+    DexType functionBase = factory.createType("Lkotlin/jvm/internal/FunctionBase;");
+    // class Lambda : Function, FunctionBase
+    DexType lambda = factory.createType("Lkotlin/jvm/internal/Lambda;");
+    // interface Function0 : Function
+    DexType function0 = factory.createType("Lkotlin/jvm/functions/Function0;");
+
+    Set<DexType> interfaces = lambda.implementedInterfaces(appInfo);
+    assertThat(interfaces, not(hasItems(lambda)));
+    assertThat(interfaces, hasItems(function));
+    assertThat(interfaces, hasItems(functionBase));
+
+    interfaces = function0.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(function0));
+    assertThat(interfaces, hasItems(function));
+    assertThat(interfaces, not(hasItems(functionBase)));
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 43b4c0f..374d58f 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.GenerateMainDexListCommand;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -30,19 +31,15 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.junit.Assert;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
 
-public class MainDexTracingTest {
+public class MainDexTracingTest extends TestBase {
 
   private static final String EXAMPLE_BUILD_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
   private static final String EXAMPLE_O_BUILD_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
   private static final String EXAMPLE_SRC_DIR = ToolHelper.EXAMPLES_DIR;
   private static final String EXAMPLE_O_SRC_DIR = ToolHelper.EXAMPLES_ANDROID_O_DIR;
 
-  @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
   @Test
   public void traceMainDexList001_whyareyoukeeping() throws Throwable {
     PrintStream stdout = System.out;
@@ -54,6 +51,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules-whyareyoukeeping.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
         AndroidApiLevel.I);
     String output = new String(baos.toByteArray(), Charset.defaultCharset());
     Assert.assertTrue(output.contains("is live because referenced in keep rule:"));
@@ -68,6 +66,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -79,6 +78,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "main-dex-rules-2.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
         AndroidApiLevel.I);
   }
 
@@ -90,6 +90,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -101,6 +102,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -112,6 +114,7 @@
         EXAMPLE_O_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -132,7 +135,14 @@
 
   @Test
   public void traceMainDexList005_4() throws Throwable {
-    doTest5(4);
+    doTest(
+        "traceMainDexList005",
+        "multidex005",
+        EXAMPLE_BUILD_DIR,
+        Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-4.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-4-r8.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-4.txt"),
+        AndroidApiLevel.I);
   }
 
   @Test
@@ -158,6 +168,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex006", "main-dex-rules-1.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex006", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex006", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -168,6 +179,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-" + variant + ".txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
         AndroidApiLevel.I);
   }
 
@@ -176,6 +188,7 @@
       String packageName,
       String buildDir,
       Path mainDexRules,
+      Path expectedR8MainDexList,
       Path expectedMainDexList,
       AndroidApiLevel minSdk)
       throws Throwable {
@@ -184,6 +197,7 @@
         packageName,
         buildDir,
         mainDexRules,
+        expectedR8MainDexList,
         expectedMainDexList,
         minSdk,
         (options) -> {
@@ -196,6 +210,7 @@
       String packageName,
       String buildDir,
       Path mainDexRules,
+      Path expectedR8MainDexList,
       Path expectedMainDexList,
       AndroidApiLevel minSdk,
       Consumer<InternalOptions> optionsConsumer)
@@ -259,17 +274,22 @@
               .map(this::mainDexStringToDescriptor)
               .sorted()
               .collect(Collectors.toList());
-      // Check that both generated lists are the same as the reference list, except for lambda
+      // Check that generated lists are the same as the reference list, except for lambda
       // classes which are only produced when running R8.
+      String[] r8RefList = new String(Files.readAllBytes(
+          expectedR8MainDexList), StandardCharsets.UTF_8).split("\n");
+      for (int i = 0; i < r8RefList.length; i++) {
+        String reference = r8RefList[i].trim();
+        if (r8MainDexList.size() <= i) {
+          Assert.fail("R8 main dex list is missing '" + reference + "'");
+        }
+        checkSameMainDexEntry(reference, r8MainDexList.get(i));
+      }
       String[] refList = new String(Files.readAllBytes(
           expectedMainDexList), StandardCharsets.UTF_8).split("\n");
       int nonLambdaOffset = 0;
       for (int i = 0; i < refList.length; i++) {
         String reference = refList[i].trim();
-        if (r8MainDexList.size() <= i) {
-          Assert.fail("R8 main dex list is missing '" + reference + "'");
-        }
-        checkSameMainDexEntry(reference, r8MainDexList.get(i));
         // The main dex list generator does not do any lambda desugaring.
         if (!isLambda(reference)) {
           if (mainDexGeneratorMainDexList.size() <= i - nonLambdaOffset) {
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
index 9e0429b..8f00f8d 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -21,22 +21,15 @@
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(VmTestRunner.class)
 public class FieldTypeTest extends TestBase {
 
-  @Ignore("b/78788577")
-  @Test
-  public void test_brokenTypeHierarchy() throws Exception {
-    JasminBuilder jasminBuilder = new JasminBuilder();
-    // interface Itf
-    ClassBuilder itf = jasminBuilder.addInterface("Itf");
-    MethodSignature foo = itf.addAbstractMethod("foo", ImmutableList.of(), "V");
-    // class Impl /* implements Itf */
-    ClassBuilder impl = jasminBuilder.addClass("Impl");
+  private ClassBuilder addImplementor(
+      JasminBuilder jasminBuilder, String name, String superName, String... interfaces) {
+    ClassBuilder impl = jasminBuilder.addClass(name, superName, interfaces);
     impl.addDefaultConstructor();
     impl.addVirtualMethod("foo", ImmutableList.of(), "V",
         ".limit locals 2",
@@ -50,15 +43,43 @@
         ".limit stack 2",
         "ldc \"" + impl.name + "\"",
         "areturn");
+    return impl;
+  }
+
+  @Test
+  public void test_brokenTypeHierarchy() throws Exception {
+    JasminBuilder jasminBuilder = new JasminBuilder();
+    // interface Itf1
+    ClassBuilder itf1 = jasminBuilder.addInterface("Itf1");
+    MethodSignature foo1 = itf1.addAbstractMethod("foo", ImmutableList.of(), "V");
+    // class Impl1 /* implements Itf1 */
+    ClassBuilder impl1 = addImplementor(jasminBuilder, "Impl1", "java/lang/Object");
+
+    // Another interface and implementer with a correct relation.
+    ClassBuilder itf2 = jasminBuilder.addInterface("Itf2");
+    MethodSignature foo2 = itf2.addAbstractMethod("foo", ImmutableList.of(), "V");
+    ClassBuilder impl2 = addImplementor(jasminBuilder, "Impl2", "java/lang/Object", itf2.name);
+
     ClassBuilder client = jasminBuilder.addClass("Client");
-    FieldSignature obj = client.addStaticFinalField("obj", itf.getDescriptor(), null);
+    client.setAccess("final");
+    client.addDefaultConstructor();
+    FieldSignature a = client.addStaticFinalField("a", "Ljava/lang/Object;", null);
+    FieldSignature obj1 = client.addField("private static", "obj1", itf1.getDescriptor(), null);
+    FieldSignature obj2 = client.addStaticFinalField("obj2", itf2.getDescriptor(), null);
     client.addClassInitializer(
         ".limit locals 1",
         ".limit stack 2",
-        "new " + impl.name,
+        "aconst_null",
+        "putstatic " + client.name + "/" + a.name  + " " + "Ljava/lang/Object;",
+        "new " + impl1.name,
         "dup",
-        "invokespecial " + impl.name + "/<init>()V",
-        "putstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
+        "invokespecial " + impl1.name + "/<init>()V",
+        // Unused, i.e., not read, field, yet still remained in the output.
+        "putstatic " + client.name + "/" + obj1.name + " " + itf1.getDescriptor(),
+        "new " + impl2.name,
+        "dup",
+        "invokespecial " + impl2.name + "/<init>()V",
+        "putstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
         "return"
     );
 
@@ -67,8 +88,8 @@
         ".limit locals 2",
         ".limit stack 2",
         "getstatic java/lang/System/out Ljava/io/PrintStream;",
-        "getstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
         /*
+        "getstatic " + client.name + "/" + obj1.name + " " + itf1.getDescriptor(),
         "astore_0",
         "aload_0",
         // java.lang.IncompatibleClassChangeError:
@@ -76,19 +97,24 @@
         "invokeinterface " + itf.name + "/" + foo.name + "()V 1",
         "aload_0",
         */
+        "getstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
         "invokevirtual java/io/PrintStream/print(Ljava/lang/Object;)V",
         "return"
     );
 
     final String mainClassName = mainClass.name;
-    String proguardConfig = keepMainProguardConfiguration(mainClass.name, false, false);
+    String proguardConfig =
+        keepMainProguardConfiguration(mainClass.name, false, false)
+            // AGP default is to not turn optimizations on, which disables MemberValuePropagation,
+            // resulting in the problematic putstatic being remained.
+            + "-dontoptimize\n";
 
     // Run input program on java.
     Path outputDirectory = temp.newFolder().toPath();
     jasminBuilder.writeClassFiles(outputDirectory);
     ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
     assertEquals(0, javaResult.exitCode);
-    assertThat(javaResult.stdout, containsString(impl.name));
+    assertThat(javaResult.stdout, containsString(impl2.name));
 
     AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
         // Disable inlining to avoid the (short) tested method from being inlined and then removed.
@@ -97,12 +123,12 @@
     // Run processed (output) program on ART
     ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
     assertEquals(0, artResult.exitCode);
-    assertThat(artResult.stdout, containsString(impl.name));
+    assertThat(artResult.stdout, containsString(impl2.name));
     assertEquals(-1, artResult.stderr.indexOf("DoFieldPut"));
 
     DexInspector inspector = new DexInspector(processedApp);
-    ClassSubject itfSubject = inspector.clazz(itf.name);
-    assertThat(itfSubject, isPresent());
+    ClassSubject itf1Subject = inspector.clazz(itf1.name);
+    assertThat(itf1Subject, isPresent());
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index d578815..cae3b06 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -94,12 +94,13 @@
 
       Result result = runTest(mainClass, proguardConfig);
 
-      // Without includedescriptorclasses return type argument type and field type are removed.
+      // Without includedescriptorclasses return type and argument type are removed.
       result.assertKept(ClassWithNativeMethods.class);
       result.assertRemoved(NativeArgumentType.class);
       result.assertRemoved(NativeReturnType.class);
-      result.assertRemoved(InstanceFieldType.class);
-      result.assertRemoved(StaticFieldType.class);
+      // Field type is not removed due to the concern about the broken type hierarchy.
+      result.assertRenamed(InstanceFieldType.class);
+      result.assertRenamed(StaticFieldType.class);
     }
   }
 
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
index f901b9d..7d503a5 100644
--- a/tests/d8_api_usage_sample.jar
+++ b/tests/d8_api_usage_sample.jar
Binary files differ
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
index f901b9d..7d503a5 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/build_r8lib.py b/tools/build_r8lib.py
new file mode 100755
index 0000000..abaa619
--- /dev/null
+++ b/tools/build_r8lib.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, 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.
+
+'''
+Build r8lib.jar using src/main/keep.txt and test that d8_api_usage_sample.jar
+works with the minified R8.
+'''
+
+import argparse
+import os
+import subprocess
+import toolhelper
+import utils
+
+parser = argparse.ArgumentParser(description=__doc__.strip(),
+                                 formatter_class=argparse.RawTextHelpFormatter)
+
+SAMPLE_JAR = os.path.join(utils.REPO_ROOT, 'tests/d8_api_usage_sample.jar')
+KEEP_RULES = os.path.join(utils.REPO_ROOT, 'src/main/keep.txt')
+R8LIB_JAR = os.path.join(utils.LIBS, 'r8lib.jar')
+R8LIB_MAP_FILE = os.path.join(utils.LIBS, 'r8lib-map.txt')
+
+API_LEVEL = 26
+ANDROID_JAR = 'third_party/android_jar/lib-v%s/android.jar' % API_LEVEL
+
+
+def build_r8lib():
+  toolhelper.run(
+      'r8',
+      ('--release',
+       '--classfile',
+       '--lib', utils.RT_JAR,
+       utils.R8_JAR,
+       '--output', R8LIB_JAR,
+       '--pg-conf', KEEP_RULES,
+       '--pg-map-output', R8LIB_MAP_FILE))
+
+
+def test_d8sample():
+  with utils.TempDir() as path:
+    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+            'com.android.tools.apiusagesample.D8ApiUsageSample',
+            '--output', path,
+            '--min-api', str(API_LEVEL),
+            '--lib', ANDROID_JAR,
+            '--classpath', utils.R8_JAR,
+            '--main-dex-list', '/dev/null',
+            os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+    utils.PrintCmd(args)
+    subprocess.check_call(args)
+
+
+def test_r8command():
+  with utils.TempDir() as path:
+    # SAMPLE_JAR and R8LIB_JAR should not have any classes in common, since e.g.
+    # R8CommandParser should have been minified in R8LIB_JAR.
+    # Just in case R8CommandParser is also present in R8LIB_JAR, we put
+    # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
+    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+            'com.android.tools.r8.R8CommandParser',
+            '--output', path + "/output.zip",
+            '--min-api', str(API_LEVEL),
+            '--lib', ANDROID_JAR,
+            '--main-dex-list', '/dev/null',
+            os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+    utils.PrintCmd(args)
+    subprocess.check_call(args)
+
+
+def test_r8cfcommand():
+  with utils.TempDir() as path:
+    # SAMPLE_JAR and R8LIB_JAR should not have any classes in common, since e.g.
+    # R8CommandParser should have been minified in R8LIB_JAR.
+    # Just in case R8CommandParser is also present in R8LIB_JAR, we put
+    # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
+    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+            'com.android.tools.r8.R8CommandParser',
+            '--classfile',
+            '--output', path + "/output.jar",
+            '--lib', utils.RT_JAR,
+            os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+    utils.PrintCmd(args)
+    subprocess.check_call(args)
+
+
+def main():
+  # Handle --help
+  parser.parse_args()
+
+  build_r8lib()
+  test_d8sample()
+  test_r8command()
+  test_r8cfcommand()
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index 787c1e1..8f440dd 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -27,7 +27,6 @@
 MANIFEST_PATH = 'META-INF/MANIFEST.MF'
 MANIFEST = 'Manifest-Version: 1.0\nMain-Class: %s\n\n'
 MANIFEST_PATTERN = r'Main-Class:\s*(\S+)'
-RT = os.path.join(utils.REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
 
 parser = argparse.ArgumentParser(description=__doc__.strip(),
                                  formatter_class=argparse.RawTextHelpFormatter)
@@ -38,7 +37,7 @@
     '-o', '--output-jar',
     help='Path to output JAR (default: build/libs/<MainClass>-min.jar)')
 parser.add_argument(
-    '-l', '--lib', default=RT,
+    '-l', '--lib', default=utils.RT_JAR,
     help='Path to rt.jar to use instead of OpenJDK 1.8')
 parser.add_argument(
     '-m', '--mainclass',
@@ -85,8 +84,8 @@
           'No --mainclass specified and no Main-Class in input JAR manifest.')
     return mo.group(1)
 
-def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None, lib=RT,
-                debug=True, build=True, benchmark_name=None):
+def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None,
+                lib=utils.RT_JAR, debug=True, build=True, benchmark_name=None):
   if output_jar is None:
     output_jar = generate_output_name(input_jar, mainclass)
   with utils.TempDir() as path:
diff --git a/tools/utils.py b/tools/utils.py
index 9f6959f..3aab572 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -35,6 +35,7 @@
 COMPATPROGUARD_JAR = os.path.join(LIBS, 'compatproguard.jar')
 MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
 GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
+RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
 
 def PrintCmd(s):
   if type(s) is list: