Merge "Mark obsolete when DexEncodedMethod instance became obsolete."
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index b57ff61..6ae611c 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -235,11 +235,16 @@
      * @param outputMode Mode in which to write the output.
      */
     public B setOutput(Path outputPath, OutputMode outputMode) {
+      return setOutput(outputPath, outputMode, false);
+    }
+
+    // This is only public in R8Command.
+    protected B setOutput(Path outputPath, OutputMode outputMode, boolean includeDataResources) {
       assert outputPath != null;
       assert outputMode != null;
       this.outputPath = outputPath;
       this.outputMode = outputMode;
-      programConsumer = createProgramOutputConsumer(outputPath, outputMode, false);
+      programConsumer = createProgramOutputConsumer(outputPath, outputMode, includeDataResources);
       return self();
     }
 
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 5195a82..c53f367 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -239,6 +239,47 @@
       return self();
     }
 
+    /**
+     * Set the output path-and-mode.
+     *
+     * <p>Setting the output path-and-mode will override any previous set consumer or any previous
+     * output path-and-mode, and implicitly sets the appropriate program consumer to write the
+     * output.
+     *
+     * <p>By default data resources from the input will be included in the output. (see {@link
+     * #setOutput(Path, OutputMode, boolean) for details}
+     *
+     * @param outputPath Path to write the output to. Must be an archive or and existing directory.
+     * @param outputMode Mode in which to write the output.
+     */
+    @Override
+    public Builder setOutput(Path outputPath, OutputMode outputMode) {
+      setOutput(outputPath, outputMode, true);
+      return self();
+    }
+
+    /**
+     * Set the output path-and-mode and control if data resources are included.
+     *
+     * <p>In addition to setting the output path-and-mode (see {@link #setOutput(Path, OutputMode)})
+     * this can control if data resources should be included or not.
+     *
+     * <p>Data resources are non Java classfile items in the input.
+     *
+     * <p>If data resources are not included they are ignored in the input and will not produce
+     * anything in the output. If data resources are included they are processed according to the
+     * configuration and written to the output.
+     *
+     * @param outputPath Path to write the output to. Must be an archive or and existing directory.
+     * @param outputMode Mode in which to write the output.
+     * @param includeDataResources If data resources from the input should be included in the
+     *     output.
+     */
+    @Override
+    public Builder setOutput(Path outputPath, OutputMode outputMode, boolean includeDataResources) {
+      return super.setOutput(outputPath, outputMode, includeDataResources);
+    }
+
     @Override
     public Builder addProgramResourceProvider(ProgramResourceProvider programProvider) {
       return super.addProgramResourceProvider(
@@ -250,7 +291,7 @@
         Path path,
         OutputMode mode,
         boolean consumeDataResources) {
-      return super.createProgramOutputConsumer(path, mode, false);
+      return super.createProgramOutputConsumer(path, mode, consumeDataResources);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index 3d33037..580b068 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -27,6 +27,7 @@
     OutputMode outputMode = null;
     Path outputPath = null;
     boolean hasDefinedApiLevel = false;
+    private boolean includeDataResources = true;
   }
 
   static final String USAGE_MESSAGE =
@@ -49,6 +50,7 @@
               "  --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-data-resources      # Ignore all data resources.",
               "  --no-desugaring          # Force disable desugaring.",
               "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
               "                           # primary dex file.",
@@ -91,7 +93,7 @@
     }
     Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
     OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
-    builder.setOutput(outputPath, outputMode);
+    builder.setOutput(outputPath, outputMode, state.includeDataResources);
     return builder;
   }
 
@@ -175,6 +177,8 @@
         builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
       } else if (arg.equals("--pg-map-output")) {
         builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--no-data-resources")) {
+        state.includeDataResources = false;
       } else {
         if (arg.startsWith("--")) {
           builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index dad3120..ec15120 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -34,6 +34,7 @@
     public final String output;
     public final int minApi;
     public final boolean forceProguardCompatibility;
+    public final boolean includeDataResources;
     public final boolean multiDex;
     public final String mainDexList;
     public final List<String> proguardConfig;
@@ -48,12 +49,14 @@
         int minApi,
         boolean multiDex,
         boolean forceProguardCompatibility,
+        boolean includeDataResources,
         String mainDexList,
         boolean printHelpAndExit,
         boolean verticalClassMerging) {
       this.output = output;
       this.minApi = minApi;
       this.forceProguardCompatibility = forceProguardCompatibility;
+      this.includeDataResources = includeDataResources;
       this.multiDex = multiDex;
       this.mainDexList = mainDexList;
       this.proguardConfig = proguardConfig;
@@ -65,6 +68,7 @@
       String output = null;
       int minApi = 1;
       boolean forceProguardCompatibility = false;
+      boolean includeDataResources = true;
       boolean multiDex = false;
       String mainDexList = null;
       boolean printHelpAndExit = false;
@@ -87,6 +91,8 @@
               minApi = Integer.valueOf(args[++i]);
             } else if (arg.equals("--force-proguard-compatibility")) {
               forceProguardCompatibility = true;
+            } else if (arg.equals("--no-data-resources")) {
+              includeDataResources = false;
             } else if (arg.equals("--output")) {
               output = args[++i];
             } else if (arg.equals("--multi-dex")) {
@@ -129,6 +135,7 @@
           minApi,
           multiDex,
           forceProguardCompatibility,
+          includeDataResources,
           mainDexList,
           printHelpAndExit,
           verticalClassMerging);
@@ -142,6 +149,8 @@
       System.out.println("--multi-dex          : ignored (provided for compatibility)");
       System.out.println("--no-locals          : ignored (provided for compatibility)");
       System.out.println("--core-library       : ignored (provided for compatibility)");
+      System.out.println("--force-proguard-compatibility : Proguard compatibility mode");
+      System.out.println("--no-data-resources  : ignore all data resources");
     }
   }
 
@@ -170,7 +179,7 @@
         new CompatProguardCommandBuilder(
             options.forceProguardCompatibility, options.enableVerticalClassMerging);
     builder
-        .setOutput(Paths.get(options.output), OutputMode.DexIndexed)
+        .setOutput(Paths.get(options.output), OutputMode.DexIndexed, options.includeDataResources)
         .addProguardConfiguration(options.proguardConfig, CommandLineOrigin.INSTANCE)
         .setMinApiLevel(options.minApi);
     if (options.mainDexList != null) {
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index fc00d91..6b64017 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -22,7 +22,7 @@
 class NamingState<ProtoType extends CachedHashValueDexItem, KeyType> {
 
   private final NamingState<ProtoType, KeyType> parent;
-  private final Map<KeyType, InternalState<ProtoType>> usedNames = new HashMap<>();
+  private final Map<KeyType, InternalState> usedNames = new HashMap<>();
   private final DexItemFactory itemFactory;
   private final ImmutableList<String> dictionary;
   private final Function<ProtoType, KeyType> keyTransform;
@@ -53,25 +53,25 @@
     return new NamingState<>(this, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
   }
 
-  private InternalState<ProtoType> findInternalStateFor(ProtoType proto) {
+  private InternalState findInternalStateFor(ProtoType proto) {
     KeyType key = keyTransform.apply(proto);
-    InternalState<ProtoType> result = usedNames.get(key);
+    InternalState result = usedNames.get(key);
     if (result == null && parent != null) {
       result = parent.findInternalStateFor(proto);
     }
     return result;
   }
 
-  private InternalState<ProtoType> getOrCreateInternalStateFor(ProtoType proto) {
+  private InternalState getOrCreateInternalStateFor(ProtoType proto) {
     // TODO(herhut): Maybe allocate these sparsely and search via state chain.
     KeyType key = keyTransform.apply(proto);
-    InternalState<ProtoType> result = usedNames.get(key);
+    InternalState result = usedNames.get(key);
     if (result == null) {
       if (parent != null) {
-        InternalState<ProtoType> parentState = parent.getOrCreateInternalStateFor(proto);
+        InternalState parentState = parent.getOrCreateInternalStateFor(proto);
         result = parentState.createChild();
       } else {
-        result = new InternalState<>(itemFactory, null, dictionary);
+        result = new InternalState(itemFactory, null, dictionary);
       }
       usedNames.put(key, result);
     }
@@ -79,7 +79,7 @@
   }
 
   private DexString getAssignedNameFor(DexString name, ProtoType proto) {
-    InternalState<ProtoType> state = findInternalStateFor(proto);
+    InternalState state = findInternalStateFor(proto);
     if (state == null) {
       return null;
     }
@@ -89,19 +89,19 @@
   public DexString assignNewNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
     DexString result = getAssignedNameFor(original, proto);
     if (result == null) {
-      InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+      InternalState state = getOrCreateInternalStateFor(proto);
       result = state.getNameFor(original, proto, markAsUsed);
     }
     return result;
   }
 
   public void reserveName(DexString name, ProtoType proto) {
-    InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+    InternalState state = getOrCreateInternalStateFor(proto);
     state.reserveName(name);
   }
 
   public boolean isReserved(DexString name, ProtoType proto) {
-    InternalState<ProtoType> state = findInternalStateFor(proto);
+    InternalState state = findInternalStateFor(proto);
     if (state == null) {
       return false;
     }
@@ -109,7 +109,7 @@
   }
 
   public boolean isAvailable(DexString original, ProtoType proto, DexString candidate) {
-    InternalState<ProtoType> state = findInternalStateFor(proto);
+    InternalState state = findInternalStateFor(proto);
     if (state == null) {
       return true;
     }
@@ -118,25 +118,25 @@
   }
 
   public void addRenaming(DexString original, ProtoType proto, DexString newName) {
-    InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+    InternalState state = getOrCreateInternalStateFor(proto);
     state.addRenaming(original, proto, newName);
   }
 
-  private class InternalState<InternalProtoType extends CachedHashValueDexItem> {
+  private class InternalState {
 
     private static final int INITIAL_NAME_COUNT = 1;
     private final char[] EMPTY_CHAR_ARRARY = new char[0];
 
     protected final DexItemFactory itemFactory;
-    private final InternalState<InternalProtoType> parentInternalState;
+    private final InternalState parentInternalState;
     private Set<DexString> reservedNames = null;
-    private Table<DexString, InternalProtoType, DexString> renamings = null;
+    private Table<DexString, ProtoType, DexString> renamings = null;
     private int nameCount;
     private final Iterator<String> dictionaryIterator;
 
     private InternalState(
         DexItemFactory itemFactory,
-        InternalState<InternalProtoType> parentInternalState,
+        InternalState parentInternalState,
         Iterator<String> dictionaryIterator) {
       this.itemFactory = itemFactory;
       this.parentInternalState = parentInternalState;
@@ -146,9 +146,7 @@
     }
 
     private InternalState(
-        DexItemFactory itemFactory,
-        InternalState<InternalProtoType> parentInternalState,
-        List<String> dictionary) {
+        DexItemFactory itemFactory, InternalState parentInternalState, List<String> dictionary) {
       this(itemFactory, parentInternalState, dictionary.iterator());
     }
 
@@ -163,8 +161,8 @@
           && (parentInternalState == null || parentInternalState.isAvailable(name));
     }
 
-    InternalState<InternalProtoType> createChild() {
-      return new InternalState<>(itemFactory, this, dictionaryIterator);
+    InternalState createChild() {
+      return new InternalState(itemFactory, this, dictionaryIterator);
     }
 
     void reserveName(DexString name) {
@@ -174,11 +172,11 @@
       reservedNames.add(name);
     }
 
-    DexString getAssignedNameFor(DexString original, InternalProtoType proto) {
+    DexString getAssignedNameFor(DexString original, ProtoType proto) {
       DexString result = null;
       if (renamings != null) {
         if (useUniqueMemberNames) {
-          Map<InternalProtoType, DexString> row = renamings.row(original);
+          Map<ProtoType, DexString> row = renamings.row(original);
           if (row != null) {
             // Either not renamed yet (0) or renamed (1). If renamed, return the same renamed name
             // so that other members with the same name can be renamed to the same renamed name.
@@ -196,7 +194,7 @@
       return result;
     }
 
-    DexString getNameFor(DexString original, InternalProtoType proto, boolean markAsUsed) {
+    DexString getNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
       DexString name = getAssignedNameFor(original, proto);
       if (name != null) {
         return name;
@@ -210,7 +208,7 @@
       return name;
     }
 
-    void addRenaming(DexString original, InternalProtoType proto, DexString newName) {
+    void addRenaming(DexString original, ProtoType proto, DexString newName) {
       if (renamings == null) {
         renamings = HashBasedTable.create();
       }
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 16bbae9..0f0bc2f 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.lang.reflect.Method;
@@ -22,10 +23,13 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -463,6 +467,116 @@
     assertEquals(0, new ZipFile(emptyZip.toFile(), StandardCharsets.UTF_8).size());
   }
 
+  private Path writeZipWithDataResource(String name) throws Exception {
+    Path dataResourceZip = temp.newFolder().toPath().resolve(name);
+    try (ZipOutputStream out =
+        new ZipOutputStream(
+            Files.newOutputStream(
+                dataResourceZip,
+                StandardOpenOption.CREATE,
+                StandardOpenOption.TRUNCATE_EXISTING))) {
+      // Write a directory entry and a normal entry.
+      ZipUtils.writeToZipStream(out, "org/", new byte[] {}, ZipEntry.STORED);
+      ZipUtils.writeToZipStream(
+          out, "org/resource.txt", "Hello world!".getBytes(), ZipEntry.STORED);
+    }
+    return dataResourceZip;
+  }
+
+  @Test
+  public void defaultResourceProcessing() throws Exception {
+    Path dataResourceZip = writeZipWithDataResource("dataResource.zip");
+    Path outputZip = temp.getRoot().toPath().resolve("output.zip");
+    R8.run(
+        R8Command.builder()
+            .addProgramFiles(dataResourceZip)
+            .setOutput(outputZip, OutputMode.ClassFile)
+            .build());
+    assertTrue(Files.exists(outputZip));
+    assertEquals(2, new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8).size());
+  }
+
+  public void runCustomResourceProcessing(boolean includeDataResources, int expectedZipEntries)
+      throws Exception {
+    Path dataResourceZip = writeZipWithDataResource("dataResource.zip");
+    Path outputZip = temp.newFolder().toPath().resolve("output.zip");
+    R8.run(
+        R8Command.builder()
+            .addProgramFiles(dataResourceZip)
+            .setOutput(outputZip, OutputMode.ClassFile, includeDataResources)
+            .build());
+    assertTrue(Files.exists(outputZip));
+    assertEquals(
+        expectedZipEntries, new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8).size());
+  }
+
+  private Path simpleProguardConfiguration() throws Exception {
+    Path proguardConfiguration = temp.newFile("printseedsandprintusage.txt").toPath();
+    FileUtils.writeTextFile(proguardConfiguration, ImmutableList.of("-keep class A { *; }"));
+    return proguardConfiguration;
+  }
+
+  @Test
+  public void noTreeShakingOption() throws Throwable {
+    // Default "keep all" rule implies no tree shaking.
+    assertFalse(parse().getEnableTreeShaking());
+    assertFalse(parse("--no-tree-shaking").getEnableTreeShaking());
+
+    // With a Proguard configuration --no-tree-shaking takes effect.
+    String proguardConfiguration = simpleProguardConfiguration().toAbsolutePath().toString();
+    assertTrue(parse("--pg-conf", proguardConfiguration).getEnableTreeShaking());
+    assertFalse(
+        parse("--no-tree-shaking", "--pg-conf", proguardConfiguration).getEnableTreeShaking());
+  }
+
+  @Test
+  public void noMinificationOption() throws Throwable {
+    // Default "keep all" rule implies no tree minification.
+    assertFalse(parse().getEnableMinification());
+    assertFalse(parse("--no-minification").getEnableMinification());
+
+    // With a Proguard configuration --no-tree-shaking takes effect.
+    String proguardConfiguration = simpleProguardConfiguration().toAbsolutePath().toString();
+    assertTrue(parse("--pg-conf", proguardConfiguration).getEnableMinification());
+    assertFalse(
+        parse("--no-minification", "--pg-conf", proguardConfiguration).getEnableMinification());
+  }
+
+  @Test
+  public void defaultDataResourcesOption() throws Throwable {
+    Path dataResourceZip = writeZipWithDataResource("dataResource.zip");
+    Path outputZip = temp.newFolder().toPath().resolve("output.zip");
+
+    R8.run(
+        parse(
+            dataResourceZip.toAbsolutePath().toString(),
+            "--output",
+            outputZip.toAbsolutePath().toString()));
+    assertTrue(Files.exists(outputZip));
+    assertEquals(2, new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8).size());
+  }
+
+  @Test
+  public void noDataResourcesOption() throws Throwable {
+    Path dataResourceZip = writeZipWithDataResource("dataResource.zip");
+    Path outputZip = temp.newFolder().toPath().resolve("output.zip");
+
+    R8.run(
+        parse(
+            "--no-data-resources",
+            dataResourceZip.toAbsolutePath().toString(),
+            "--output",
+            outputZip.toAbsolutePath().toString()));
+    assertTrue(Files.exists(outputZip));
+    assertEquals(0, new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8).size());
+  }
+
+  @Test
+  public void customResourceProcessing() throws Exception {
+    runCustomResourceProcessing(true, 2);
+    runCustomResourceProcessing(false, 0);
+  }
+
   private R8Command parse(String... args) throws CompilationFailedException {
     return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
   }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index f3ada7d..22bb5e5 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.SmaliWriter;
@@ -581,13 +582,8 @@
    */
   protected String runOnJava(Class mainClass) throws Exception {
     ProcessResult result = ToolHelper.runJava(mainClass);
-    if (result.exitCode != 0) {
-      System.out.println("Std out:");
-      System.out.println(result.stdout);
-      System.out.println("Std err:");
-      System.out.println(result.stderr);
-      assertEquals(0, result.exitCode);
-    }
+    ToolHelper.failOnProcessFailure(result);
+    ToolHelper.failOnVerificationErrors(result);
     return result.stdout;
   }
 
@@ -603,9 +599,17 @@
   }
 
   /** Run application on Java with the specified main class and provided arguments. */
+  protected String runOnJava(AndroidApp app, String mainClass, String... args) throws IOException {
+    return runOnJava(app, mainClass, Arrays.asList(args));
+  }
+
+  /** Run application on Java with the specified main class and provided arguments. */
   protected String runOnJava(AndroidApp app, String mainClass, List<String> args)
       throws IOException {
-    return runOnJavaRaw(app, mainClass, args).stdout;
+    ProcessResult result = runOnJavaRaw(app, mainClass, args);
+    ToolHelper.failOnProcessFailure(result);
+    ToolHelper.failOnVerificationErrors(result);
+    return result.stdout;
   }
 
   protected ProcessResult runOnJavaRawNoVerify(String main, byte[]... classes) throws IOException {
@@ -647,6 +651,18 @@
     return ToolHelper.runJavaNoVerify(out, mainClass, args.toArray(new String[0]));
   }
 
+  /** Run application on Art or Java with the specified main class. */
+  protected String runOnVM(AndroidApp app, String mainClass, Backend backend) throws IOException {
+    switch (backend) {
+      case CF:
+        return runOnJava(app, mainClass);
+      case DEX:
+        return runOnArt(app, mainClass);
+      default:
+        throw new Unreachable("Unexpected backend: " + backend);
+    }
+  }
+
   private String extractClassName(byte[] ccc) {
     class ClassNameExtractor extends ClassVisitor {
       private String className;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 76edce7..e34282d 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1296,13 +1296,13 @@
     return runArtNoVerificationErrorsRaw(files, mainClass, extras, version).stdout;
   }
 
-  private static void failOnProcessFailure(ProcessResult result) {
+  protected static void failOnProcessFailure(ProcessResult result) {
     if (result.exitCode != 0) {
-      fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
+      fail("Unexpected failure: '" + result.stderr + "'\n" + result.stdout);
     }
   }
 
-  private static void failOnVerificationErrors(ProcessResult result) {
+  protected static void failOnVerificationErrors(ProcessResult result) {
     if (result.stderr.contains("Verification error")) {
       fail("Verification error: \n" + result.stderr);
     }
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java
index 8da93a5..cf95846 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java
@@ -5,6 +5,9 @@
 package com.android.tools.r8.compatproguard;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.compatproguard.CompatProguard.CompatProguardOptions;
 import org.junit.Test;
@@ -16,9 +19,20 @@
   }
 
   @Test
+  public void testDefaultDataResources() throws Exception {
+    CompatProguardOptions options = parseArgs();
+    assertNull(options.output);
+    assertEquals(1, options.minApi);
+    assertFalse(options.forceProguardCompatibility);
+    assertTrue(options.includeDataResources);
+    assertFalse(options.multiDex);
+    assertNull(options.mainDexList);
+    assertEquals(0, options.proguardConfig.size());
+  }
+
+  @Test
   public void testShortLine() throws Exception {
-    CompatProguardOptions options;
-    options = parseArgs("-");
+    CompatProguardOptions options = parseArgs("-");
     assertEquals(1, options.proguardConfig.size());
   }
 
@@ -65,18 +79,20 @@
 
   @Test
   public void testInclude() throws Exception {
-    CompatProguardOptions options;
-
-    options = parseArgs("-include --my-include-file.txt");
+    CompatProguardOptions options = parseArgs("-include --my-include-file.txt");
     assertEquals(1, options.proguardConfig.size());
     assertEquals("-include --my-include-file.txt", options.proguardConfig.get(0));
   }
 
   @Test
   public void testNoLocalsOption() throws Exception {
-    CompatProguardOptions options;
-
-    options = parseArgs("--no-locals");
+    CompatProguardOptions options = parseArgs("--no-locals");
     assertEquals(0, options.proguardConfig.size());
   }
+
+  @Test
+  public void testNoDataResources() throws Exception {
+    CompatProguardOptions options = parseArgs("--no-data-resources");
+    assertFalse(options.includeDataResources);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index 2b73eaa..3674108 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.VmTestRunner;
@@ -20,7 +21,6 @@
 import com.android.tools.r8.desugaring.interfacemethods.default2.TestMainDefault2;
 import com.android.tools.r8.desugaring.interfacemethods.static0.TestMainStatic0;
 import com.android.tools.r8.desugaring.interfacemethods.static1.TestMainStatic1;
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -139,7 +139,7 @@
         ToolHelper.getClassAsBytes(TestMainDefault0.class));
   }
 
-  @Test(expected = CompilationError.class)
+  @Test(expected = CompilationFailedException.class)
   @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.DEFAULT) // No desugaring
   public void testInvokeDefault1() throws Exception {
     ensureSameOutput(
diff --git a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
new file mode 100644
index 0000000..93f6215
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
@@ -0,0 +1,113 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class CovariantReturnTypeTest extends TestBase {
+
+  @Ignore("b/115613850")
+  @Test
+  public void test() throws Exception {
+    for (int i = 0; i < 3; i++) {
+      JasminBuilder appBuilder = new JasminBuilder();
+      ClassBuilder classBuilder = appBuilder.addClass("package.TestClass");
+
+      classBuilder.addVirtualMethod(
+          "method1",
+          "Ljava/lang/Object;",
+          ".limit stack 1",
+          ".limit locals 1",
+          "aconst_null",
+          "areturn");
+      classBuilder.addVirtualMethod(
+          "method1", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+      classBuilder.addVirtualMethod(
+          "method1", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+
+      classBuilder.addVirtualMethod(
+          "method2", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+      classBuilder.addVirtualMethod(
+          "method2",
+          "Ljava/lang/Object;",
+          ".limit stack 1",
+          ".limit locals 1",
+          "aconst_null",
+          "areturn");
+      classBuilder.addVirtualMethod(
+          "method2", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+
+      classBuilder.addVirtualMethod(
+          "method3", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+      classBuilder.addVirtualMethod(
+          "method3", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+      classBuilder.addVirtualMethod(
+          "method3",
+          "Ljava/lang/Object;",
+          ".limit stack 1",
+          ".limit locals 1",
+          "aconst_null",
+          "areturn");
+
+      appBuilder.addInterface("package.A");
+      appBuilder.addInterface("package.B");
+
+      Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
+      AndroidApp output =
+          compileWithR8(
+              appBuilder.build(),
+              keepMainProguardConfiguration("package.TestClass"),
+              options -> {
+                options.enableTreeShaking = false;
+                options.proguardMapConsumer = new FileConsumer(proguardMapPath);
+              });
+
+      CodeInspector inspector = new CodeInspector(output, proguardMapPath);
+      ClassSubject clazz = inspector.clazz("package.TestClass");
+      assertThat(clazz, isPresent());
+
+      Map<String, Set<MethodSubject>> methodSubjectsByName = new HashMap<>();
+      for (String name : ImmutableList.of("method1", "method2", "method3")) {
+        methodSubjectsByName.put(
+            name,
+            ImmutableSet.of(
+                clazz.method("java.lang.Object", name, ImmutableList.of()),
+                clazz.method("package.A", name, ImmutableList.of()),
+                clazz.method("package.B", name, ImmutableList.of())));
+      }
+
+      Set<String> minifiedMethodNames = new HashSet<>();
+      for (Set<MethodSubject> methodSubjects : methodSubjectsByName.values()) {
+        for (MethodSubject methodSubject : methodSubjects) {
+          assertThat(methodSubject, isRenamed());
+          minifiedMethodNames.add(methodSubject.getFinalName());
+        }
+      }
+      assertEquals(3, minifiedMethodNames.size());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
new file mode 100644
index 0000000..edf77b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
@@ -0,0 +1,136 @@
+// 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.naming.b114554345;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class B114554345 extends TestBase {
+
+  private final Backend backend;
+
+  @Parameters(name = "mode:{0}")
+  public static Collection<Object[]> data() {
+    List<Object[]> parameters = new ArrayList<>();
+    for (Backend backend : Backend.values()) {
+      parameters.add(new Object[] {backend});
+    }
+    return parameters;
+  }
+
+  public B114554345(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Ignore("b/114554345")
+  @Test
+  public void test() throws Exception {
+    AndroidApp input =
+        AndroidApp.builder()
+            .addProgramFiles(
+                ToolHelper.getClassFilesForTestDirectory(
+                    ToolHelper.getPackageDirectoryForTestPackage(this.getClass().getPackage()),
+                    path -> !path.toString().contains("B114554345")))
+            .build();
+    AndroidApp output =
+        compileWithR8(
+            input,
+            keepMainProguardConfiguration(TestDriver.class),
+            options -> options.enableInlining = false,
+            backend);
+    String mainClass = TestDriver.class.getName();
+    assertEquals(runOnJava(TestDriver.class), runOnVM(output, mainClass, backend));
+  }
+}
+
+// Interface and two implementations.
+
+interface Interface {
+  Interface method();
+}
+
+class InterfaceImpl implements Interface {
+
+  @Override
+  public InterfaceImpl method() {
+    System.out.println("In InterfaceImpl.method()");
+    return this;
+  }
+}
+
+class OtherInterfaceImpl extends InterfaceImpl {
+
+  @Override
+  public OtherInterfaceImpl method() {
+    System.out.println("In OtherInterfaceImpl.method()");
+    return this;
+  }
+}
+
+// Sub-interface and three implementations.
+
+interface SubInterface extends Interface {
+  SubInterface method();
+}
+
+class SubInterfaceImpl implements SubInterface {
+
+  @Override
+  public SubInterfaceImpl method() {
+    System.out.println("In SubInterfaceImpl.method()");
+    return this;
+  }
+}
+
+class OtherSubInterfaceImpl implements SubInterface {
+
+  @Override
+  public OtherSubInterfaceImpl method() {
+    System.out.println("In OtherSubInterfaceImpl.method()");
+    return this;
+  }
+}
+
+class YetAnotherSubInterfaceImpl extends InterfaceImpl implements SubInterface {
+
+  @Override
+  public YetAnotherSubInterfaceImpl method() {
+    System.out.println("In YetAnotherSubInterfaceImpl.method()");
+    return this;
+  }
+}
+
+class TestDriver {
+
+  public static void main(String[] args) {
+    foo(new InterfaceImpl());
+    foo(new OtherInterfaceImpl());
+    foo(new SubInterfaceImpl());
+    foo(new YetAnotherSubInterfaceImpl());
+    bar(new SubInterfaceImpl());
+    bar(new OtherSubInterfaceImpl());
+    bar(new YetAnotherSubInterfaceImpl());
+  }
+
+  private static void foo(Interface obj) {
+    obj.method();
+  }
+
+  private static void bar(SubInterface obj) {
+    obj.method();
+  }
+}